diff --git a/app/(auth)/forgot-password/page.tsx b/app/(auth)/forgot-password/page.tsx index ed0471f6..97ba826d 100644 --- a/app/(auth)/forgot-password/page.tsx +++ b/app/(auth)/forgot-password/page.tsx @@ -1,5 +1,6 @@ 'use client'; import { useForm } from 'react-hook-form'; +import { useState } from 'react'; import { createClient } from '@/app/lib/supabase/browser-client'; type ForgotPasswordFormValues = { @@ -8,7 +9,8 @@ type ForgotPasswordFormValues = { export default function ForgotPasswordPage() { const supabase = createClient(); - + const [errorMessage, setErrorMessage] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); const { register, handleSubmit, @@ -19,14 +21,19 @@ export default function ForgotPasswordPage() { }); const onSubmit = async (values: ForgotPasswordFormValues) => { + setErrorMessage(''); + setSuccessMessage(''); + const { error } = await supabase.auth.resetPasswordForEmail(values.email); if (error) { - alert(error.message); + setErrorMessage('Failed to send reset password email: ' + error.message); return; } - alert('Instructions to reset your password have been sent to your email.'); + setSuccessMessage( + 'Instructions to reset your password have been sent to your email.', + ); }; return ( @@ -53,6 +60,9 @@ export default function ForgotPasswordPage() { + + {errorMessage &&

{errorMessage}

} + {successMessage &&

{successMessage}

} ); diff --git a/app/(auth)/reset-password/page.tsx b/app/(auth)/reset-password/page.tsx index c7e7ddfe..98bd3374 100644 --- a/app/(auth)/reset-password/page.tsx +++ b/app/(auth)/reset-password/page.tsx @@ -3,6 +3,7 @@ import { useForm, useWatch } from 'react-hook-form'; import { createClient } from '@/app/lib/supabase/browser-client'; import { useRouter } from 'next/navigation'; +import { useState } from 'react'; type ResetPasswordFormValues = { password: string; @@ -12,6 +13,8 @@ type ResetPasswordFormValues = { export default function ResetPasswordPage() { const supabase = createClient(); const router = useRouter(); + const [errorMessage, setErrorMessage] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); const { register, @@ -29,16 +32,19 @@ export default function ResetPasswordPage() { }); const onSubmit = async (values: ResetPasswordFormValues) => { + setErrorMessage(''); + setSuccessMessage(''); + const { error } = await supabase.auth.updateUser({ password: values.password, }); if (error) { - alert(error.message); + setErrorMessage(error.message); return; } - alert('Your password has been updated.'); + setSuccessMessage('Your password has been updated.'); router.push('/home'); }; @@ -82,6 +88,9 @@ export default function ResetPasswordPage() {
+ {errorMessage &&

{errorMessage}

} + {successMessage &&

{successMessage}

} + diff --git a/app/(auth)/sign-in/page.tsx b/app/(auth)/sign-in/page.tsx index e09aa9dd..3c8a4283 100644 --- a/app/(auth)/sign-in/page.tsx +++ b/app/(auth)/sign-in/page.tsx @@ -2,6 +2,7 @@ import { createClient } from '@/app/lib/supabase/browser-client'; import { useForm } from 'react-hook-form'; +import { useState } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; @@ -14,20 +15,25 @@ export default function SignInPage() { const { register, handleSubmit, - formState: { errors }, + formState: { errors, isSubmitting }, } = useForm(); + const [errorMessage, setErrorMessage] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); const router = useRouter(); const supabase = createClient(); const onSubmit = async (formData: Inputs) => { + setErrorMessage(''); + setSuccessMessage(''); const { error } = await supabase.auth.signInWithPassword({ email: formData.email, password: formData.password, }); if (error) { - alert(error.message); + setErrorMessage('Failed to sign in: ' + error.message); return; } + setSuccessMessage('Signed in successfully! Redirecting...'); router.push('/home'); }; @@ -56,11 +62,15 @@ export default function SignInPage() {

Password is required.

)}
- +
Forgot password?
Sign up + {errorMessage &&

{errorMessage}

} + {successMessage &&

{successMessage}

} ); } diff --git a/app/(auth)/sign-up/page.tsx b/app/(auth)/sign-up/page.tsx index 55d69f0b..87d18dd2 100644 --- a/app/(auth)/sign-up/page.tsx +++ b/app/(auth)/sign-up/page.tsx @@ -4,7 +4,6 @@ import { useForm, useWatch, SubmitHandler } from 'react-hook-form'; import { createClient } from '@/app/lib/supabase/browser-client'; import { useState } from 'react'; import Link from 'next/link'; -import { useRouter } from 'next/navigation'; type Inputs = { firstName: string; @@ -22,14 +21,17 @@ export default function SignUpPage() { formState: { errors }, } = useForm(); const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); const supabase = createClient(); - const router = useRouter(); const passwordValue = useWatch({ control, name: 'password', }); const onSubmit: SubmitHandler = async (formData) => { + setErrorMessage(''); + setSuccessMessage(''); setIsLoading(true); const { error } = await supabase.auth.signUp({ email: formData.email, @@ -43,9 +45,12 @@ export default function SignUpPage() { }); setIsLoading(false); if (error) { - console.error('Sign-up error:', error); + setErrorMessage('Failed to sign up: ' + error.message); + return; } - router.push('/home'); + setSuccessMessage( + 'Account created successfully! Please check your email to confirm your account.', + ); }; return ( @@ -126,10 +131,12 @@ export default function SignUpPage() { )}

Sign in + {errorMessage &&

{errorMessage}

} + {successMessage &&

{successMessage}

} ); } diff --git a/app/(main)/components/DeleteTicketButton.tsx b/app/(main)/components/DeleteTicketButton.tsx index dbd352c0..d0ce3fad 100644 --- a/app/(main)/components/DeleteTicketButton.tsx +++ b/app/(main)/components/DeleteTicketButton.tsx @@ -2,26 +2,39 @@ import { deleteTicket } from '@/app/actions/ticket'; import { usePathname, useRouter } from 'next/navigation'; +import { useState, useTransition } from 'react'; export default function DeleteTicketButton({ ticketId }: { ticketId: string }) { const pathname = usePathname(); const router = useRouter(); + const [errorMessage, setErrorMessage] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); + const [isPending, startTransition] = useTransition(); async function handleDelete() { - const result = await deleteTicket(ticketId); + setErrorMessage(''); + setSuccessMessage(''); + startTransition(async () => { + const result = await deleteTicket(ticketId); - if (!result.success) { - console.error('Failed to delete ticket:', result.error); - return; - } + if (!result.success) { + setErrorMessage('Failed to delete ticket.' + result.error); + return; + } - const parentPath = pathname.split('/').slice(0, -1).join('/') || '/'; - router.push(parentPath); + setSuccessMessage('Ticket deleted successfully!'); + const parentPath = pathname.split('/').slice(0, -1).join('/') || '/'; + router.push(parentPath); + }); } return ( - + <> + {errorMessage &&
{errorMessage}
} + {successMessage &&
{successMessage}
} + + ); } diff --git a/app/(main)/components/InStockTicketItemCard.tsx b/app/(main)/components/InStockTicketItemCard.tsx index 3b1b5287..a7a5439c 100644 --- a/app/(main)/components/InStockTicketItemCard.tsx +++ b/app/(main)/components/InStockTicketItemCard.tsx @@ -24,24 +24,33 @@ export default function InStockTicketItemCard({ const [quantity, setQuantity] = useState(quantityRequested); const [savedQuantity, setSavedQuantity] = useState(quantityRequested); const [errorMessage, setErrorMessage] = useState(''); + const [isSaving, setIsSaving] = useState(false); const hasChanged = quantity !== savedQuantity; const handleSave = async () => { + setErrorMessage(''); if (quantity < 1) { setErrorMessage('Please input a number greater than 0.'); return; } - const result = await updateTicketItemQuantity(ticketItemId, quantity); + setIsSaving(true); + try { + const result = await updateTicketItemQuantity(ticketItemId, quantity); - if (!result.success) { - console.error('Error changing ticket item quantity:', result.error); - return; - } + if (!result.success) { + setErrorMessage( + 'Failed to update ticket item quantity.' + result.error, + ); + return; + } - setSavedQuantity(quantity); - setErrorMessage(''); + setSavedQuantity(quantity); + setErrorMessage(''); + } finally { + setIsSaving(false); + } }; const handleCancel = () => { @@ -66,13 +75,22 @@ export default function InStockTicketItemCard({ setQuantity(Number(e.target.value)); setErrorMessage(''); }} + disabled={isSaving} /> - {errorMessage &&

{errorMessage}

} + {errorMessage && ( +

+ {errorMessage} +

+ )} {hasChanged && ( <> - - + + )} diff --git a/app/(main)/components/OutOfStockTicketItemCard.tsx b/app/(main)/components/OutOfStockTicketItemCard.tsx index 330a77e3..eebb25b7 100644 --- a/app/(main)/components/OutOfStockTicketItemCard.tsx +++ b/app/(main)/components/OutOfStockTicketItemCard.tsx @@ -1,4 +1,5 @@ 'use client'; + import { updateTicketItemDescription } from '@/app/actions/ticket'; import { useState } from 'react'; @@ -9,33 +10,73 @@ interface OutOfStockTicketItemCardProps { export default function OutOfStockTicketItemCard({ ticketItemId, - freeTextDescription, + freeTextDescription: initialDescription, }: OutOfStockTicketItemCardProps) { - const [description, setDescription] = useState(freeTextDescription); + const [description, setDescription] = useState(initialDescription); const [isChanged, setIsChanged] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [isSaving, setIsSaving] = useState(false); const handleChange = (e: React.ChangeEvent) => { const newValue = e.target.value; setDescription(newValue); - setIsChanged(newValue !== freeTextDescription); + setIsChanged(newValue !== initialDescription); }; + const handleCancel = () => { - setDescription(freeTextDescription); + setDescription(initialDescription); setIsChanged(false); + setErrorMessage(''); }; + const handleSave = async () => { - await updateTicketItemDescription(ticketItemId, description); - setIsChanged(false); + setErrorMessage(''); + setIsSaving(true); + + try { + const { error } = await updateTicketItemDescription( + ticketItemId, + description, + ); + + if (error) { + setErrorMessage('Failed to update description: ' + error); + setIsSaving(false); + return; + } + + // Success Path + setIsChanged(false); + } catch (e) { + // Handle infrastructure/network errors + setErrorMessage('A critical connection error occurred. ' + e); + return; + } finally { + setIsSaving(false); + } }; + return ( -
-