feat: added birthday selector

Signed-off-by: Varun Raj <mailme@varunraj.in>
This commit is contained in:
Varun Raj
2024-08-17 23:34:15 +05:30
parent a8bce40b3a
commit 4f9b154fb8
9 changed files with 164 additions and 9 deletions
BIN
View File
Binary file not shown.
+4
View File
@@ -11,15 +11,19 @@
"dependencies": {
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@tanstack/react-table": "^8.20.1",
"@types/qs": "^6.9.15",
"axios": "^1.7.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"lucide-react": "^0.428.0",
"next": "14.2.5",
"qs": "^6.13.0",
"react": "^18",
"react-day-picker": "9.0.8",
"react-dom": "^18",
"sass": "^1.77.8",
"tailwind-merge": "^2.5.2",
+27 -6
View File
@@ -1,5 +1,8 @@
import { IPerson } from '@/types/person'
import React from 'react'
import React, { useState } from 'react'
import { DatePicker } from '../ui/datepicker'
import { updatePerson } from '@/handlers/api/people.handler';
import { formatDate } from '@/helpers/date.helper';
interface IProps {
person: IPerson
@@ -8,13 +11,31 @@ interface IProps {
export default function PersonBirthdayCell(
{ person }: IProps
) {
const [formData, setFormData] = useState(person);
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const handleEdit = (date?: Date | null) => {
if (!date) return;
const formatedDate = formatDate(date.toString(), 'yyyy-MM-dd');
setLoading(true);
return updatePerson(person.id, {
birthDate: formatedDate,
})
.then(() => {
})
.catch(() => {})
.finally(() => {
setLoading(false);
});
}
return (
<div>
{person.birthDate ? (
<span>{person.birthDate.toDateString()}</span>
) : (
<span>Unknown</span>
)}
<DatePicker date={person.birthDate} onSelect={handleEdit}/>
</div>
)
}
+28
View File
@@ -0,0 +1,28 @@
import * as React from "react"
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons"
import { DayPicker } from "react-day-picker"
import "react-day-picker/dist/style.css";
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
export type CalendarProps = React.ComponentProps<typeof DayPicker>
function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
captionLayout="dropdown"
{...props}
/>
)
}
Calendar.displayName = "Calendar"
export { Calendar }
+52
View File
@@ -0,0 +1,52 @@
"use client"
import * as React from "react"
import { format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
interface IProps {
date?: Date | null
onSelect?: (date?: Date | null) => any
}
export function DatePicker(
{ date: _date, onSelect }: IProps
) {
const [date, setDate] = React.useState<Date | null>(_date || null)
const handleSelect = (date?: Date) => {
setDate(date || null)
onSelect?.(date)
}
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={date || undefined}
onSelect={handleSelect}
/>
</PopoverContent>
</Popover>
)
}
+31
View File
@@ -0,0 +1,31 @@
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverAnchor = PopoverPrimitive.Anchor
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
+5 -1
View File
@@ -12,6 +12,10 @@ export const listPeople = (): Promise<IPeopleListResponse> => {
});
}
export const updatePerson = (id: string, data: Partial<IPerson>) => {
export const updatePerson = (id: string, data: Partial<{
name: string;
birthDate: string;
isHidden: boolean;
}>) => {
return API.put(UPDATE_PERSON_PATH(id), data)
}
+9
View File
@@ -0,0 +1,9 @@
import { format, parse } from "date-fns"
export const formatDate = (date: string, outputFormat?: string): string => {
return format(date, outputFormat || "PPP")
}
export const parseDate = (date: string, inputFormat: string): Date => {
return parse(date, inputFormat, new Date());
}
+8 -2
View File
@@ -1,11 +1,17 @@
import { PERSON_THUBNAIL_PATH } from "@/config/routes"
import { IPerson } from "@/types/person"
import { parseDate } from "./date.helper"
export const cleanUpPerson = (person: IPerson): IPerson => {
interface IAPIPerson extends Omit<IPerson, 'birthDate' | 'updatedAt'> {
updatedAt: string;
birthDate: string | null;
}
export const cleanUpPerson = (person: IAPIPerson): IPerson => {
return {
...person,
thumbnailPath: PERSON_THUBNAIL_PATH(person.id),
birthDate: person.birthDate ? new Date(person.birthDate) : null,
birthDate: person.birthDate ? new Date(parseDate(person.birthDate, 'yyyy-MM-dd')) : null,
updatedAt: new Date(person.updatedAt),
}
}