Untitled

mail@pastecode.io avatar
unknown
plain_text
20 days ago
4.9 kB
1
Indexable
Never
'use client';
import React, { useState, useEffect } from 'react';
import BackButton from '@/components/ui/back-button';
import FadeIn from '@/components/ui/fade-in';
import DateOfBirth, { AgeLabel } from '@/components/ui/dob';
import {
  Form,
  FormControl,
  FormDescription,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { Button } from '@/components/ui/button';
import { parse, format, isValid } from 'date-fns';
import { toast } from 'sonner';

const thisYear = new Date().getFullYear();
const thisMonth = new Date().getMonth();
const today = new Date().getDate();

const formSchema = z
  .object({
    'bday-day': z
      .string()
      .min(1, 'Day is required')
      .refine((val) => parseInt(val) >= 1 && parseInt(val) <= 31, {
        message: 'Day must be between 01 and 31',
      }),
    'bday-month': z
      .string()
      .min(1, 'Month is required')
      .refine((val) => parseInt(val) >= 1 && parseInt(val) <= 12, {
        message: 'Month must be between 01 and 12',
      }),
    'bday-year': z
      .string()
      .min(4, 'Year is required')
      .refine((val) => parseInt(val) >= 1900 && parseInt(val) <= thisYear, {
        message: `Year must be between 1900 and ${thisYear}`,
      }),
  })
  .refine(
    (data) => {
      const date = parse(
        `${data['bday-year']}-${data['bday-month']}-${data['bday-day']}`,
        'yyyy-MM-dd',
        new Date(),
      );
      return isValid(date) && date <= new Date();
    },
    {
      message: 'Date of birth cannot be in the future',
      path: ['bday-year'],
    },
  );

const Page: React.FC = () => {
  const [age, setAge] = useState<number | null>(null);

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      'bday-day': '',
      'bday-month': '',
      'bday-year': '',
    },
  });

  const {
    handleSubmit,
    formState: { errors },
    control,
    setValue,
  } = form;

  // Load saved date of birth from local storage on component mount
  useEffect(() => {
    const savedDOB = localStorage.getItem('userDateOfBirth');
    if (savedDOB) {
      const [year, month, day] = savedDOB.split('-');
      setValue('bday-year', year);
      setValue('bday-month', month);
      setValue('bday-day', day);
    }
  }, [setValue]);

  function onSubmit(values: z.infer<typeof formSchema>) {
    const { 'bday-day': day, 'bday-month': month, 'bday-year': year } = values;

    // Parse the input into a Date object
    const dateOfBirth = parse(`${year}-${month}-${day}`, 'yyyy-MM-dd', new Date());

    // Check if the date is valid
    if (isValid(dateOfBirth)) {
      // Format the date as ISO 8601 (YYYY-MM-DD)
      const formattedDateOfBirth = format(dateOfBirth, 'yyyy-MM-dd');

      // Save to local storage
      localStorage.setItem('userDateOfBirth', formattedDateOfBirth);

      // Show toast with the formatted date
      toast.success(`Date of Birth Saved · ${formattedDateOfBirth}`);
    }
  }

  return (
    <FadeIn>
      <BackButton />
      <div className="mx-auto w-full max-w-md px-[calc(theme('spacing[8]')+2vw)] py-12 sm:px-8 sm:py-16">
        <div className="mb-12">
          <div className="mb-4 text-lg font-medium sm:text-xl">Date of Birth Input</div>
          <div className="text-balance text-sm/normal text-gray-600 sm:text-base">
            This input component autofills, handles invalid date errors, and shows the user&apos;s
            age to prevent typos. It&apos;s been implemented at three companies, all of which
            continue to use it successfully, and has been thoroughly tested on users across various
            devices.
          </div>
        </div>
        <div>
          <Form {...form}>
            <form onSubmit={handleSubmit(onSubmit)} className="space-y-8">
              <FormItem>
                <FormLabel>
                  Date of birth
                  <AgeLabel age={age} year={form.watch('bday-year')} />
                </FormLabel>
                <FormControl>
                  <DateOfBirth control={control} setAge={setAge} errors={errors} />
                </FormControl>
                <FormDescription>Example · 30/04/1970</FormDescription>
                <FormMessage className="not-sr-only">
                  {errors['bday-day']?.message ||
                    errors['bday-month']?.message ||
                    errors['bday-year']?.message ||
                    (errors as any)?.root?.message}
                </FormMessage>
              </FormItem>
              <Button variant="primary" type="submit">
                Submit
              </Button>
            </form>
          </Form>
        </div>
      </div>
    </FadeIn>
  );
};

export default Page;
Leave a Comment