From 12100698ab4ca863e6dcc86457647435f19c2f98 Mon Sep 17 00:00:00 2001 From: "MSI\\2Fi" Date: Fri, 3 May 2024 18:05:50 +0800 Subject: [PATCH] Company Holiday --- package.json | 2 + src/app/(main)/settings/holiday/page.tsx | 48 +++++ src/app/api/holidays/index.ts | 23 +++ .../CompanyHoliday/CompanyHoliday.tsx | 166 ++++++++++++++++++ .../CompanyHoliday/CompanyHolidayDialog.tsx | 79 +++++++++ .../CompanyHoliday/CompanyHolidayLoading.tsx | 40 +++++ .../CompanyHoliday/CompanyHolidayWrapper.tsx | 24 +++ src/components/CompanyHoliday/index.ts | 1 + 8 files changed, 383 insertions(+) create mode 100644 src/app/(main)/settings/holiday/page.tsx create mode 100644 src/app/api/holidays/index.ts create mode 100644 src/components/CompanyHoliday/CompanyHoliday.tsx create mode 100644 src/components/CompanyHoliday/CompanyHolidayDialog.tsx create mode 100644 src/components/CompanyHoliday/CompanyHolidayLoading.tsx create mode 100644 src/components/CompanyHoliday/CompanyHolidayWrapper.tsx create mode 100644 src/components/CompanyHoliday/index.ts diff --git a/package.json b/package.json index 2ffb000..14ae99a 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@faker-js/faker": "^8.4.1", "@fontsource/inter": "^5.0.16", "@fontsource/plus-jakarta-sans": "^5.0.18", + "@fullcalendar/react": "^6.1.11", "@mui/icons-material": "^5.15.0", "@mui/material": "^5.15.0", "@mui/material-nextjs": "^5.15.0", @@ -22,6 +23,7 @@ "@mui/x-date-pickers": "^6.18.7", "@unly/universal-language-detector": "^2.0.3", "apexcharts": "^3.45.2", + "date-holidays": "^3.23.11", "dayjs": "^1.11.10", "fullcalendar": "^6.1.11", "i18next": "^23.7.11", diff --git a/src/app/(main)/settings/holiday/page.tsx b/src/app/(main)/settings/holiday/page.tsx new file mode 100644 index 0000000..16a5ac2 --- /dev/null +++ b/src/app/(main)/settings/holiday/page.tsx @@ -0,0 +1,48 @@ +import CompanyHoliday from "@/components/CompanyHoliday"; +import { Metadata } from "next"; +import { getServerI18n } from "@/i18n"; +import Add from "@mui/icons-material/Add"; +import Button from "@mui/material/Button"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import Link from "next/link"; +import { Suspense } from "react"; +import { fetchCompanys, preloadCompanys } from "@/app/api/companys"; + +export const metadata: Metadata = { + title: "Holiday", +}; + +const Company: React.FC = async () => { + const { t } = await getServerI18n("holiday"); + + // Preload necessary dependencies + + return ( + <> + + + {t("Company Holiday")} + + {/* */} + + }> + + + + ) +}; + +export default Company; diff --git a/src/app/api/holidays/index.ts b/src/app/api/holidays/index.ts new file mode 100644 index 0000000..0a90ffc --- /dev/null +++ b/src/app/api/holidays/index.ts @@ -0,0 +1,23 @@ +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { BASE_API_URL } from "@/config/api"; +import { cache } from "react"; +import "server-only"; +import EventInput from '@fullcalendar/react'; + +export interface HolidaysResult extends EventInput { + title: string; + date: string; + extendedProps: { + calendar: string; + }; +} + +export const preloadCompanys = () => { + fetchHolidays(); +}; + +export const fetchHolidays = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/companys`, { + next: { tags: ["companys"] }, + }); +}); \ No newline at end of file diff --git a/src/components/CompanyHoliday/CompanyHoliday.tsx b/src/components/CompanyHoliday/CompanyHoliday.tsx new file mode 100644 index 0000000..bfa5ecb --- /dev/null +++ b/src/components/CompanyHoliday/CompanyHoliday.tsx @@ -0,0 +1,166 @@ +"use client"; + +import { HolidaysResult } from "@/app/api/holidays"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, Grid, Stack } from '@mui/material/'; +import { useTranslation } from "react-i18next"; +import FullCalendar from '@fullcalendar/react' +import dayGridPlugin from '@fullcalendar/daygrid' // a plugin! +import interactionPlugin from "@fullcalendar/interaction" // needed for dayClick +import Holidays from "date-holidays"; +import CompanyHolidayDialog from "./CompanyHolidayDialog"; +import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form"; +import { EventBusy } from "@mui/icons-material"; +interface Props { + holidays: HolidaysResult[]; +} + +const CompanyHoliday: React.FC = ({ holidays }) => { + const { t } = useTranslation("holidays"); + + const hd = new Holidays('HK') + console.log(holidays) + + const [companyHolidays, setCompanyHolidays] = useState([]) + const [dateContent, setDateContent] = useState<{ date: string }>({date: ''}) + const [open, setOpen] = useState(false); + + const handleClose = () => { + setOpen(false); + }; + + const getPublicHolidaysList = () => { + const currentYear = new Date().getFullYear() + const currentYearHolidays = hd.getHolidays(currentYear) + const nextYearHolidays = hd.getHolidays(currentYear + 1) + const events_cyhd = currentYearHolidays.map(ele => { + const tempDay = new Date(ele.date) + const tempYear = tempDay.getFullYear() + const tempMonth = tempDay.getMonth() + 1 < 10 ? `0${ tempDay.getMonth() + 1}` : tempDay.getMonth() + 1 + const tempDate = tempDay.getDate() < 10 ? `0${tempDay.getDate()}` : tempDay.getDate() + let tempName = "" + switch (ele.name) { + case "复活节": + tempName = "復活節" + break + case "劳动节": + tempName = "勞動節" + break + case "端午节": + tempName = "端午節" + break + case "重阳节": + tempName = "重陽節" + break + case "圣诞节后的第一个工作日": + tempName = "聖誕節後的第一个工作日" + break + default: + tempName = ele.name + break + } + + return {date: `${tempYear}-${tempMonth}-${tempDate}`, title: tempName, extendedProps: {calendar: 'holiday'}} + }) + + const events_nyhd = nextYearHolidays.map(ele => { + const tempDay = new Date(ele.date) + const tempYear = tempDay.getFullYear() + const tempMonth = tempDay.getMonth() + 1 < 10 ? `0${ tempDay.getMonth() + 1}` : tempDay.getMonth() + 1 + const tempDate = tempDay.getDate() < 10 ? `0${tempDay.getDate()}` : tempDay.getDate() + let tempName = "" + switch (ele.name) { + case "复活节": + tempName = "復活節" + break + case "劳动节": + tempName = "勞動節" + break + case "端午节": + tempName = "端午節" + break + case "重阳节": + tempName = "重陽節" + break + case "圣诞节后的第一个工作日": + tempName = "聖誕節後的第一个工作日" + break + default: + tempName = ele.name + break + } + return {date: `${tempYear}-${tempMonth}-${tempDate}`, title: tempName, extendedProps: {calendar: 'holiday'}} + }) + + setCompanyHolidays([...events_cyhd, ...events_nyhd] as HolidaysResult[]) + } + + useEffect(()=>{ + getPublicHolidaysList() + },[]) + + const handleDateClick = (event:any) => { + console.log(event.dateStr) + setDateContent({date: event.dateStr}) + setOpen(true); + } + + const handleEventClick = (event:any) => { + console.log(event) + } + + const onSubmit = useCallback>( + async (data) => { + try { + console.log(data); + // console.log(JSON.stringify(data)); + } catch (e) { + console.log(e); + } + }, + [t, ], + ); + + const onSubmitError = useCallback>( + (errors) => { + console.log(errors) + }, + [], + ); + + + const formProps = useForm({ + defaultValues: { + title: "" + }, + }); + + return ( + <> + + + + + + + } + /> + + + ); +}; + +export default CompanyHoliday; diff --git a/src/components/CompanyHoliday/CompanyHolidayDialog.tsx b/src/components/CompanyHoliday/CompanyHolidayDialog.tsx new file mode 100644 index 0000000..3b9419f --- /dev/null +++ b/src/components/CompanyHoliday/CompanyHolidayDialog.tsx @@ -0,0 +1,79 @@ +import React, { useState } from 'react'; +import { Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, Grid, FormControl } from '@mui/material/'; +import { useForm, useFormContext } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import dayjs from 'dayjs'; +import { INPUT_DATE_FORMAT } from '@/app/utils/formatUtil'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; + +interface CompanyHolidayDialogProps { + open: boolean; + onClose: () => void; + title: string; + actions: React.ReactNode; + content: Content +} + +interface Content { + date: string +} + +const CompanyHolidayDialog: React.FC = ({ open, onClose, title, actions, content }) => { + const { + t, + i18n: { language }, + } = useTranslation(); + + const { + register, + formState: { errors }, + setValue, + getValues, + } = useFormContext(); + + return ( + + + {title} + + + + + + + + { + if (!date) return; + setValue("dueDate", date.format(INPUT_DATE_FORMAT)); + }} + slotProps={{ + textField: { + helperText: 'MM/DD/YYYY', + }, + }} + /> + + + + + {actions} + + + ); +}; + +export default CompanyHolidayDialog; \ No newline at end of file diff --git a/src/components/CompanyHoliday/CompanyHolidayLoading.tsx b/src/components/CompanyHoliday/CompanyHolidayLoading.tsx new file mode 100644 index 0000000..5b8c02d --- /dev/null +++ b/src/components/CompanyHoliday/CompanyHolidayLoading.tsx @@ -0,0 +1,40 @@ +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import Skeleton from "@mui/material/Skeleton"; +import Stack from "@mui/material/Stack"; +import React from "react"; + +// Can make this nicer +export const CompanyHolidayLoading: React.FC = () => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default CompanyHolidayLoading; diff --git a/src/components/CompanyHoliday/CompanyHolidayWrapper.tsx b/src/components/CompanyHoliday/CompanyHolidayWrapper.tsx new file mode 100644 index 0000000..0c21148 --- /dev/null +++ b/src/components/CompanyHoliday/CompanyHolidayWrapper.tsx @@ -0,0 +1,24 @@ +// import { fetchCompanyCategories, fetchCompanys } from "@/app/api/companys"; +import React, { useState, } from "react"; +import CompanyHoliday from "./CompanyHoliday"; +import CompanyHolidayLoading from "./CompanyHolidayLoading"; +import { fetchCompanys } from "@/app/api/companys"; +import Holidays from "date-holidays"; +import { HolidaysResult, fetchHolidays } from "@/app/api/holidays"; + +interface SubComponents { + Loading: typeof CompanyHolidayLoading; +} + +const CompanyHolidayWrapper: React.FC & SubComponents = async () => { + // const Companys = await fetchCompanys(); + + const companyHolidays: HolidaysResult[] = await fetchHolidays() + + + return ; +}; + +CompanyHolidayWrapper.Loading = CompanyHolidayLoading; + +export default CompanyHolidayWrapper; diff --git a/src/components/CompanyHoliday/index.ts b/src/components/CompanyHoliday/index.ts new file mode 100644 index 0000000..3dddafc --- /dev/null +++ b/src/components/CompanyHoliday/index.ts @@ -0,0 +1 @@ +export { default } from "./CompanyHolidayWrapper";