feat: added birthday selector
Signed-off-by: Varun Raj <mailme@varunraj.in>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user