fix: landing navbar not sticky to top, fix onboarding UI

This commit is contained in:
Hafid Nur
2025-11-26 14:33:17 +07:00
parent 19aa5bb452
commit 7e385c6385
5 changed files with 38 additions and 24 deletions
+27 -16
View File
@@ -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">
+4 -4
View File
@@ -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}
/> />
); );
}; };