Lunch time reminder is now actually functional

This commit is contained in:
2025-09-17 15:56:59 -05:00
parent 84bfc21877
commit 92854382db
5 changed files with 52 additions and 14 deletions

View File

@@ -50,9 +50,7 @@ const RootLayout = async ({
<Header />
{children}
<NotificationsPermission />
<LunchReminder
lunchTime='15:24'
/>
<LunchReminder />
<Toaster richColors />
</TVModeProvider>
</ConvexClientProvider>

View File

@@ -32,6 +32,10 @@ const formSchema = z.object({
email: z.email({
message: 'Please enter a valid email address.',
}),
lunchTime: z
.string()
.trim()
.min(3, { message: 'Must be a valid 24-hour time. Example: 13:00'}),
});
type UserInfoFormProps = {
@@ -44,12 +48,14 @@ export const UserInfoForm = ({ preloadedUser }: UserInfoFormProps) => {
const updateUserName = useMutation(api.auth.updateUserName);
const updateUserEmail = useMutation(api.auth.updateUserEmail);
const updateUserLunchtime = useMutation(api.auth.updateUserLunchtime);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: user?.name ?? '',
email: user?.email ?? '',
lunchTime: user?.lunchTime ?? '',
},
});
@@ -57,13 +63,16 @@ export const UserInfoForm = ({ preloadedUser }: UserInfoFormProps) => {
const ops: Promise<unknown>[] = [];
const name = values.name.trim();
const email = values.email.trim().toLowerCase();
const lunchTime = values.lunchTime.trim();
if (name !== (user?.name ?? '')) ops.push(updateUserName({ name }));
if (email !== (user?.email ?? '')) ops.push(updateUserEmail({ email }));
if (lunchTime !== (user?.lunchTime ?? ''))
ops.push(updateUserLunchtime({ lunchTime }))
if (ops.length === 0) return;
setLoading(true);
try {
await Promise.all(ops);
form.reset({ name, email });
form.reset({ name, email, lunchTime });
toast.success('Profile updated successfully.');
} catch (error) {
console.error(error);
@@ -109,6 +118,23 @@ export const UserInfoForm = ({ preloadedUser }: UserInfoFormProps) => {
)}
/>
<FormField
control={form.control}
name='lunchTime'
render={({ field }) => (
<FormItem>
<FormLabel>Regular Lunch Time</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormDescription>
Enter the time you take your lunch most often in 24 hour time.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className='flex justify-center'>
<SubmitButton disabled={loading} pendingText='Saving...'>
Save Changes

View File

@@ -2,14 +2,9 @@
import { useEffect, useRef } from 'react';
import { toast } from 'sonner';
import { useMutation } from 'convex/react';
import { useMutation, useQuery } from 'convex/react';
import { api } from '~/convex/_generated/api';
type Props = {
lunchTime: string;
enabled?: boolean;
};
const nextOccurrenceMs = (hhmm: string, from = new Date()): number => {
const [hStr, mStr] = hhmm.split(':');
const target = new Date(from);
@@ -18,13 +13,13 @@ const nextOccurrenceMs = (hhmm: string, from = new Date()): number => {
return target.getTime() - from.getTime();
};
export const LunchReminder = ({ lunchTime, enabled = true }: Props) => {
export const LunchReminder = () => {
const setStatus = useMutation(api.statuses.create);
const timeoutRef = useRef<number | null>(null);
console.log('LunchReminder is running!')
const user = useQuery(api.auth.getUser);
const lunchTime = user?.lunchTime ?? '';
useEffect(() => {
if (!enabled) return;
const schedule = () => {
const ms = nextOccurrenceMs(lunchTime);
console.log('Ms = ', ms)
@@ -66,7 +61,7 @@ export const LunchReminder = ({ lunchTime, enabled = true }: Props) => {
return () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
};
}, [enabled, lunchTime, setStatus]);
}, [lunchTime, setStatus]);
return null;
};

View File

@@ -33,6 +33,7 @@ export const getUser = query(async (ctx) => {
email: user.email ?? null,
name: user.name ?? null,
image,
lunchTime: user.lunchTime ?? null,
};
});
@@ -43,6 +44,7 @@ export const getAllUsers = query(async (ctx) => {
email: u.email ?? null,
name: u.name ?? null,
image: u.image ?? null,
lunchTime: u.lunchTime ?? null,
}));
});
@@ -96,6 +98,22 @@ export const updateUserImage = mutation({
},
});
export const updateUserLunchtime = mutation({
args: {
lunchTime: v.string(),
},
handler: async (ctx, { lunchTime }) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new ConvexError('Not authenticated.');
const user = await ctx.db.get(userId);
if (!user) throw new ConvexError('User not found.');
if (!lunchTime.includes(':'))
throw new ConvexError('Lunch time is invalid.');
await ctx.db.patch(userId, { lunchTime });
return { success: true };
},
});
export const validatePassword = (password: string): boolean => {
if (
password.length < 8 ||

View File

@@ -12,6 +12,7 @@ export default defineSchema({
image: v.optional(v.string()),
email: v.optional(v.string()),
currentStatusId: v.optional(v.id('statuses')),
lunchTime: v.optional(v.string()),
emailVerificationTime: v.optional(v.number()),
phone: v.optional(v.string()),
phoneVerificationTime: v.optional(v.number()),