update ui update and fix layout issue

This commit is contained in:
2026-08-06 13:21:10 +05:30
parent 92322bff16
commit e5f8144fb3
251 changed files with 9041 additions and 2119 deletions

View File

@@ -0,0 +1,114 @@
'use client';
import {useCallback, useState} from 'react';
import {useRouter, useSearchParams} from 'next/navigation';
import {useToast} from '@astryxdesign/core/Toast';
import {useSession} from '@/features/auth/providers/SessionProvider';
import {resolveRedirectTarget} from '@/features/auth/services/redirectTarget';
import type {LoginError} from '@/features/auth/types/auth';
/**
* Every behaviour of the sign-in form, with no markup attached.
*
* Pulled out of the component for the reason the brief asks for: the form was
* a 314-line file where a fake `setTimeout` login sat in the middle of layout
* markup. Now the view renders state and the rules live here — which also
* makes the flow testable without mounting a page, and means a redesign of the
* screen cannot silently change what "signed in" means.
*
* The chain below this hook is: SessionProvider → authService (validation and
* domain rules) → authRepository (transport) → POST /api/auth/login. This hook
* knows about none of it beyond the first link.
*/
export interface LoginFormState {
email: string;
password: string;
rememberMe: boolean;
/** Field-addressed, so a message renders under the input that caused it. */
errors: Partial<Record<LoginError['field'], string>>;
isSubmitting: boolean;
}
export function useLoginForm() {
const router = useRouter();
const searchParams = useSearchParams();
const toast = useToast();
const {login} = useSession();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [rememberMe, setRememberMe] = useState(false);
const [errors, setErrors] = useState<LoginFormState['errors']>({});
const [isSubmitting, setIsSubmitting] = useState(false);
// Shared with GuestGuard, which redirects on the same event — see
// resolveRedirectTarget for why that matters and how `next` is validated.
const destination = resolveRedirectTarget(searchParams.get('next'));
const submit = useCallback(
async (event: React.FormEvent) => {
event.preventDefault();
if (isSubmitting) return;
setErrors({});
setIsSubmitting(true);
const result = await login({email, password, rememberMe});
if (!result.ok) {
setErrors({[result.error.field]: result.error.message});
// The toast carries what the inline message cannot: it is announced,
// it survives a field the user has scrolled past, and it distinguishes
// "we rejected your credentials" from "we never reached the server".
toast({type: 'error', body: result.error.message});
setIsSubmitting(false);
return;
}
// Deliberately NOT clearing isSubmitting on success: the button stays in
// its loading state until the navigation commits, so the form cannot be
// submitted twice while the route transition is in flight.
router.replace(destination);
// The session cookie changed, so any server-rendered layout above this
// route is stale. Without this, the workspace can paint its signed-out
// seed until something else happens to refetch.
router.refresh();
},
[
isSubmitting,
login,
email,
password,
rememberMe,
toast,
router,
destination,
],
);
/** Clearing on edit: an error that outlives the typo it describes is noise. */
const updateEmail = useCallback((value: string) => {
setEmail(value);
setErrors((prev) => (prev.email ? {...prev, email: undefined} : prev));
}, []);
const updatePassword = useCallback((value: string) => {
setPassword(value);
setErrors((prev) =>
prev.password ? {...prev, password: undefined} : prev,
);
}, []);
return {
email,
password,
rememberMe,
errors,
isSubmitting,
setEmail: updateEmail,
setPassword: updatePassword,
setRememberMe,
submit,
};
}