fix: landing navbar not sticky to top, fix onboarding UI
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
|||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
import { CitySelect } from '../../../components/city-select';
|
import { CitySelect } from '../../../components/city-select';
|
||||||
|
import { Icon } from '@iconify/react';
|
||||||
|
|
||||||
const ROLE_OPTIONS = [
|
const ROLE_OPTIONS = [
|
||||||
'Frontend Developer',
|
'Frontend Developer',
|
||||||
@@ -69,19 +70,19 @@ const UserOnboardingPage: FC = (): ReactElement => {
|
|||||||
|
|
||||||
const onSubmit = form.handleSubmit(async (data) => {
|
const onSubmit = form.handleSubmit(async (data) => {
|
||||||
try {
|
try {
|
||||||
console.log('[Onboarding] Starting submission...', data);
|
// console.log('[Onboarding] Starting submission...', data);
|
||||||
let avatarUrl = session?.user?.avatar || null;
|
let avatarUrl = session?.user?.avatar || null;
|
||||||
|
|
||||||
// Upload avatar if a new file was selected
|
// Upload avatar if a new file was selected
|
||||||
if (avatarFile) {
|
if (avatarFile) {
|
||||||
console.log('[Onboarding] Uploading avatar...');
|
// console.log('[Onboarding] Uploading avatar...');
|
||||||
const uploadResult = await uploadAvatar(avatarFile);
|
const uploadResult = await uploadAvatar(avatarFile);
|
||||||
avatarUrl = uploadResult.data.url;
|
avatarUrl = uploadResult.data.url;
|
||||||
console.log('[Onboarding] Avatar uploaded:', avatarUrl);
|
// console.log('[Onboarding] Avatar uploaded:', avatarUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update user in Supabase
|
// Update user in Supabase
|
||||||
console.log('[Onboarding] Updating user in Supabase...');
|
// console.log('[Onboarding] Updating user in Supabase...');
|
||||||
const result = await updateUser({
|
const result = await updateUser({
|
||||||
fullname: data.fullname,
|
fullname: data.fullname,
|
||||||
avatar: avatarUrl,
|
avatar: avatarUrl,
|
||||||
@@ -89,17 +90,17 @@ const UserOnboardingPage: FC = (): ReactElement => {
|
|||||||
bio: data.bio,
|
bio: data.bio,
|
||||||
skills: data.skills,
|
skills: data.skills,
|
||||||
});
|
});
|
||||||
console.log('[Onboarding] User updated successfully:', result);
|
// console.log('[Onboarding] User updated successfully:', result);
|
||||||
|
|
||||||
// Wait a bit for the onSuccess handler to update localStorage
|
// Wait a bit for the onSuccess handler to update localStorage
|
||||||
// The updateUser mutation's onSuccess handler updates the Zustand store and localStorage
|
// The updateUser mutation's onSuccess handler updates the Zustand store and localStorage
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
|
||||||
console.log('[Onboarding] Navigating to dashboard...');
|
// console.log('[Onboarding] Navigating to dashboard...');
|
||||||
// Use window.location for a full page reload to ensure middleware sees updated localStorage
|
// Use window.location for a full page reload to ensure middleware sees updated localStorage
|
||||||
globalThis.location.href = '/dashboard';
|
globalThis.location.href = '/dashboard';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Onboarding] Onboarding failed:', error);
|
// console.error('[Onboarding] Onboarding failed:', error);
|
||||||
alert(
|
alert(
|
||||||
`Onboarding failed: ${
|
`Onboarding failed: ${
|
||||||
error instanceof Error ? error.message : 'Unknown error'
|
error instanceof Error ? error.message : 'Unknown error'
|
||||||
@@ -115,7 +116,7 @@ const UserOnboardingPage: FC = (): ReactElement => {
|
|||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||||
Complete Your Profile
|
Complete Your Profile
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600 font-sans">
|
||||||
Tell us more about yourself to get started
|
Tell us more about yourself to get started
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,13 +133,19 @@ const UserOnboardingPage: FC = (): ReactElement => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-32 h-32 rounded-full bg-gray-200 flex items-center justify-center">
|
<div className="w-32 h-32 rounded-full bg-gray-200 flex items-center justify-center">
|
||||||
<span className="text-gray-400 text-4xl">👤</span>
|
{/* <span className="text-gray-400 text-4xl">👤</span> */}
|
||||||
|
<Icon
|
||||||
|
icon="ic:baseline-person"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
className="text-gray-400"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex flex-col items-center">
|
||||||
<label htmlFor="avatar" className="cursor-pointer">
|
<label htmlFor="avatar" className="cursor-pointer">
|
||||||
<span className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 inline-block">
|
<span className="px-4 py-2 bg-primary-500 text-white rounded-lg hover:bg-primary-600 inline-block">
|
||||||
{avatarPreview ? 'Change Photo' : 'Upload Photo'}
|
{avatarPreview ? 'Change Photo' : 'Upload Photo'}
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
@@ -161,11 +168,14 @@ const UserOnboardingPage: FC = (): ReactElement => {
|
|||||||
label="Full Name"
|
label="Full Name"
|
||||||
placeholder="Enter your full name"
|
placeholder="Enter your full name"
|
||||||
name="fullname"
|
name="fullname"
|
||||||
|
className="px-3 py-2 text-label2 rounded-lg max-h-auto text-base"
|
||||||
|
size="md"
|
||||||
|
isRequired={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* City */}
|
{/* City */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-base font-medium text-gray-700">
|
||||||
City <span className="text-red-500">*</span>
|
City <span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@@ -184,7 +194,7 @@ const UserOnboardingPage: FC = (): ReactElement => {
|
|||||||
|
|
||||||
{/* Role/Skills */}
|
{/* Role/Skills */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-base font-medium text-gray-700">
|
||||||
Role / Skills
|
Role / Skills
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@@ -220,19 +230,20 @@ const UserOnboardingPage: FC = (): ReactElement => {
|
|||||||
|
|
||||||
{/* Bio */}
|
{/* Bio */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-base font-medium text-gray-700">
|
||||||
Bio <span className="text-gray-400">(Optional)</span>
|
Bio <span className="text-gray-400">(Optional)</span>
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="bio"
|
name="bio"
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<div>
|
<div className="flex">
|
||||||
<Textarea
|
<Textarea
|
||||||
{...field}
|
{...field}
|
||||||
placeholder="Tell us about yourself..."
|
placeholder="Tell us about yourself..."
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full"
|
className="w-full rounded-lg text-sm"
|
||||||
|
style={{ resize: 'vertical' }}
|
||||||
/>
|
/>
|
||||||
{fieldState.error && (
|
{fieldState.error && (
|
||||||
<p className="text-sm text-red-500 mt-1">
|
<p className="text-sm text-red-500 mt-1">
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default function HomePage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white">
|
<main className="min-h-screen bg-white">
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<div id="#top" className="hidden"></div>
|
<div id="#top" className="hidden"></div>
|
||||||
<nav className="border-b border-gray-200 bg-white sticky top-0 z-50">
|
<nav className="border-b border-gray-200 bg-white sticky top-0 z-50">
|
||||||
@@ -136,7 +136,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
{/* Mobile Menu */}
|
{/* Mobile Menu */}
|
||||||
{mobileMenuOpen && (
|
{mobileMenuOpen && (
|
||||||
<div className="md:hidden border-b border-gray-200 bg-white">
|
<nav className="md:hidden sticky top-18 border-b border-gray-200 bg-white z-40">
|
||||||
<div className="flex flex-col items-start gap-4 px-4 py-4">
|
<div className="flex flex-col items-start gap-4 px-4 py-4">
|
||||||
<a
|
<a
|
||||||
href="#timeline"
|
href="#timeline"
|
||||||
@@ -163,7 +163,7 @@ export default function HomePage() {
|
|||||||
Daftar Sekarang
|
Daftar Sekarang
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</nav>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
@@ -792,6 +792,6 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const CitySelect: FC<CitySelectProps> = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClearSelection}
|
onClick={handleClearSelection}
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 cursor-pointer"
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
@@ -88,7 +88,7 @@ export const CitySelect: FC<CitySelectProps> = ({
|
|||||||
{error && <p className="text-sm text-red-500 mt-1">{error}</p>}
|
{error && <p className="text-sm text-red-500 mt-1">{error}</p>}
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className="absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg max-h-60 overflow-y-auto">
|
<div className="absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg max-h-60 overflow-y-auto text-label2">
|
||||||
{filteredCities.length > 0 ? (
|
{filteredCities.length > 0 ? (
|
||||||
<ul className="py-1">
|
<ul className="py-1">
|
||||||
{filteredCities.map((city) => (
|
{filteredCities.map((city) => (
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export type TInputFieldProps = Omit<
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
helperText?: string;
|
helperText?: string;
|
||||||
htmlFor?: string;
|
htmlFor?: string;
|
||||||
|
isRequired?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const sizeClasses: Record<TInputSize, { label: string; helperText: string }> = {
|
const sizeClasses: Record<TInputSize, { label: string; helperText: string }> = {
|
||||||
@@ -47,6 +48,7 @@ export const InputField: FC<TInputFieldProps> = ({
|
|||||||
htmlFor,
|
htmlFor,
|
||||||
className,
|
className,
|
||||||
disabled,
|
disabled,
|
||||||
|
isRequired = false,
|
||||||
...rest
|
...rest
|
||||||
}): ReactElement => {
|
}): ReactElement => {
|
||||||
return (
|
return (
|
||||||
@@ -58,7 +60,7 @@ export const InputField: FC<TInputFieldProps> = ({
|
|||||||
sizeClasses[size].label
|
sizeClasses[size].label
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{label}
|
{label} {isRequired ? <span className="text-red-500">*</span> : null}
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
{...(htmlFor && { id: htmlFor })}
|
{...(htmlFor && { id: htmlFor })}
|
||||||
@@ -75,7 +77,7 @@ export const InputField: FC<TInputFieldProps> = ({
|
|||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
{error ? (
|
{error ? (
|
||||||
<p className="text-danger-500 text-label1 text-left">{error}</p>
|
<p className="text-danger-500 text-label2 text-left">{error}</p>
|
||||||
) : (
|
) : (
|
||||||
helperText && (
|
helperText && (
|
||||||
<p
|
<p
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export const ControlledInputField = <T extends FieldValues>(
|
|||||||
error={fieldState.error?.message}
|
error={fieldState.error?.message}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
isRequired={props.isRequired}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user