Procházet zdrojové kódy

Company Holiday

tags/Baseline_30082024_FRONTEND_UAT
MSI\2Fi před 1 rokem
rodič
revize
12100698ab
8 změnil soubory, kde provedl 383 přidání a 0 odebrání
  1. +2
    -0
      package.json
  2. +48
    -0
      src/app/(main)/settings/holiday/page.tsx
  3. +23
    -0
      src/app/api/holidays/index.ts
  4. +166
    -0
      src/components/CompanyHoliday/CompanyHoliday.tsx
  5. +79
    -0
      src/components/CompanyHoliday/CompanyHolidayDialog.tsx
  6. +40
    -0
      src/components/CompanyHoliday/CompanyHolidayLoading.tsx
  7. +24
    -0
      src/components/CompanyHoliday/CompanyHolidayWrapper.tsx
  8. +1
    -0
      src/components/CompanyHoliday/index.ts

+ 2
- 0
package.json Zobrazit soubor

@@ -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",


+ 48
- 0
src/app/(main)/settings/holiday/page.tsx Zobrazit soubor

@@ -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 (
<>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Typography variant="h4" marginInlineEnd={2}>
{t("Company Holiday")}
</Typography>
{/* <Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/settings/holiday/create"
>
{t("Create Holiday")}
</Button> */}
</Stack>
<Suspense fallback={<CompanyHoliday.Loading />}>
<CompanyHoliday/>
</Suspense>
</>
)
};

export default Company;

+ 23
- 0
src/app/api/holidays/index.ts Zobrazit soubor

@@ -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<HolidaysResult[]>(`${BASE_API_URL}/companys`, {
next: { tags: ["companys"] },
});
});

+ 166
- 0
src/components/CompanyHoliday/CompanyHoliday.tsx Zobrazit soubor

@@ -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<Props> = ({ holidays }) => {
const { t } = useTranslation("holidays");

const hd = new Holidays('HK')
console.log(holidays)

const [companyHolidays, setCompanyHolidays] = useState<HolidaysResult[]>([])
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<SubmitHandler<any>>(
async (data) => {
try {
console.log(data);
// console.log(JSON.stringify(data));
} catch (e) {
console.log(e);
}
},
[t, ],
);
const onSubmitError = useCallback<SubmitErrorHandler<any>>(
(errors) => {
console.log(errors)
},
[],
);

const formProps = useForm<any>({
defaultValues: {
title: ""
},
});

return (
<>
<FormProvider {...formProps}>
<FullCalendar
plugins={[ dayGridPlugin, interactionPlugin ]}
initialView="dayGridMonth"
events={companyHolidays}
eventColor='#ff0000'
dateClick={handleDateClick}
eventClick={handleEventClick}
/>
<CompanyHolidayDialog
open={open}
onClose={handleClose}
title={("Create Holiday")}
content={dateContent}
actions={
<Stack direction="row" justifyContent="flex-end" gap={1} component="form" onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}>
<Button onClick={handleClose}>Close</Button>
<Button type="submit">Submit</Button>
</Stack>
}
/>
</FormProvider>
</>
);
};

export default CompanyHoliday;

+ 79
- 0
src/components/CompanyHoliday/CompanyHolidayDialog.tsx Zobrazit soubor

@@ -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<CompanyHolidayDialogProps> = ({ open, onClose, title, actions, content }) => {
const {
t,
i18n: { language },
} = useTranslation();

const {
register,
formState: { errors },
setValue,
getValues,
} = useFormContext<any>();

return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<Dialog open={open} onClose={onClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={12}>
<TextField
label={t("title")}
fullWidth
{...register("title", {
required: "title required!",
})}
error={Boolean(errors.title)}
/>
</Grid>
<Grid item xs={12}>
<FormControl fullWidth>
<DatePicker
label={t("Company Holiday")}
value={dayjs(content.date)}
onChange={(date) => {
if (!date) return;
setValue("dueDate", date.format(INPUT_DATE_FORMAT));
}}
slotProps={{
textField: {
helperText: 'MM/DD/YYYY',
},
}}
/>
</FormControl>
</Grid>
</Grid>
</DialogContent>
<DialogActions>{actions}</DialogActions>
</Dialog>
</LocalizationProvider>
);
};

export default CompanyHolidayDialog;

+ 40
- 0
src/components/CompanyHoliday/CompanyHolidayLoading.tsx Zobrazit soubor

@@ -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 (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default CompanyHolidayLoading;

+ 24
- 0
src/components/CompanyHoliday/CompanyHolidayWrapper.tsx Zobrazit soubor

@@ -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 <CompanyHoliday holidays={companyHolidays} />;
};

CompanyHolidayWrapper.Loading = CompanyHolidayLoading;

export default CompanyHolidayWrapper;

+ 1
- 0
src/components/CompanyHoliday/index.ts Zobrazit soubor

@@ -0,0 +1 @@
export { default } from "./CompanyHolidayWrapper";

Načítá se…
Zrušit
Uložit