| @@ -12,6 +12,7 @@ import { | |||
| fetchAssignedProjects, | |||
| fetchProjectWithTasks, | |||
| } from "@/app/api/projects"; | |||
| import { fetchHolidays } from "@/app/api/holidays"; | |||
| export const metadata: Metadata = { | |||
| title: "User Workspace", | |||
| @@ -27,6 +28,7 @@ const Home: React.FC = async () => { | |||
| fetchLeaves(username); | |||
| fetchLeaveTypes(); | |||
| fetchProjectWithTasks(); | |||
| fetchHolidays(); | |||
| return ( | |||
| <I18nProvider namespaces={["home"]}> | |||
| @@ -0,0 +1,49 @@ | |||
| import Holidays from "date-holidays"; | |||
| const hd = new Holidays("HK"); | |||
| export const getPublicHolidaysForNYears = (years: number = 1) => { | |||
| return Array(years) | |||
| .fill(undefined) | |||
| .flatMap((_, index) => { | |||
| const currentYear = new Date().getFullYear(); | |||
| const holidays = hd.getHolidays(currentYear + index); | |||
| return holidays.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" }, | |||
| }; | |||
| }); | |||
| }); | |||
| }; | |||
| @@ -1,20 +1,28 @@ | |||
| "use client"; | |||
| import { HolidaysList, 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 { HolidaysList } from "@/app/api/holidays"; | |||
| import React, { useCallback, useMemo, useState } from "react"; | |||
| import { Button, 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 listPlugin from '@fullcalendar/list'; | |||
| import Holidays from "date-holidays"; | |||
| import FullCalendar from "@fullcalendar/react"; | |||
| import dayGridPlugin from "@fullcalendar/daygrid"; // a plugin! | |||
| import interactionPlugin from "@fullcalendar/interaction"; // needed for dayClick | |||
| import listPlugin from "@fullcalendar/list"; | |||
| import CompanyHolidayDialog from "./CompanyHolidayDialog"; | |||
| import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm, useFormContext } from "react-hook-form"; | |||
| import { EventBusy } from "@mui/icons-material"; | |||
| import { deleteCompanyHoliday, saveCompanyHoliday } from "@/app/api/holidays/actions"; | |||
| import { | |||
| FormProvider, | |||
| SubmitErrorHandler, | |||
| SubmitHandler, | |||
| useForm, | |||
| useFormContext, | |||
| } from "react-hook-form"; | |||
| import { | |||
| deleteCompanyHoliday, | |||
| saveCompanyHoliday, | |||
| } from "@/app/api/holidays/actions"; | |||
| import { useRouter } from "next/navigation"; | |||
| import { deleteDialog, submitDialog } from "../Swal/CustomAlerts"; | |||
| import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils"; | |||
| interface Props { | |||
| holidays: HolidaysList[]; | |||
| @@ -23,118 +31,47 @@ interface Props { | |||
| const CompanyHoliday: React.FC<Props> = ({ holidays }) => { | |||
| const { t } = useTranslation("holidays"); | |||
| const router = useRouter(); | |||
| const formValues = useFormContext(); | |||
| const formValues = useFormContext(); | |||
| const [serverError, setServerError] = useState(""); | |||
| const hd = new Holidays('HK') | |||
| console.log(holidays) | |||
| const companyHolidays = useMemo( | |||
| () => [...getPublicHolidaysForNYears(2), ...holidays], | |||
| [holidays], | |||
| ); | |||
| const [companyHolidays, setCompanyHolidays] = useState<HolidaysList[]>([]) | |||
| const [dateContent, setDateContent] = useState<{ date: string }>({date: ''}) | |||
| const [dateContent, setDateContent] = useState<{ date: string }>({ | |||
| date: "", | |||
| }); | |||
| const [open, setOpen] = useState(false); | |||
| const [isEdit, setIsEdit] = useState(false); | |||
| const [editable, setEditable] = useState(true); | |||
| const handleClose = () => { | |||
| setOpen(false); | |||
| setEditable(true) | |||
| setIsEdit(false) | |||
| formProps.setValue("name", "") | |||
| formProps.setValue("id", null) | |||
| setEditable(true); | |||
| setIsEdit(false); | |||
| formProps.setValue("name", ""); | |||
| formProps.setValue("id", null); | |||
| }; | |||
| 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, ...holidays] as HolidaysList[]) | |||
| } | |||
| useEffect(()=>{ | |||
| getPublicHolidaysList() | |||
| },[]) | |||
| useEffect(()=>{ | |||
| },[holidays]) | |||
| const handleDateClick = (event:any) => { | |||
| const handleDateClick = (event: any) => { | |||
| // console.log(event.dateStr) | |||
| setDateContent({date: event.dateStr}) | |||
| setDateContent({ date: event.dateStr }); | |||
| setOpen(true); | |||
| } | |||
| const handleEventClick = (event:any) => { | |||
| }; | |||
| const handleEventClick = (event: any) => { | |||
| // event.event.id: if id !== "", holiday is created by company | |||
| console.log(event.event.id) | |||
| if (event.event.id === null || event.event.id === ""){ | |||
| setEditable(false) | |||
| console.log(event.event.id); | |||
| if (event.event.id === null || event.event.id === "") { | |||
| setEditable(false); | |||
| } | |||
| formProps.setValue("name", event.event.title) | |||
| formProps.setValue("id", event.event.id) | |||
| setDateContent({date: event.event.startStr}) | |||
| formProps.setValue("name", event.event.title); | |||
| formProps.setValue("id", event.event.id); | |||
| setDateContent({ date: event.event.startStr }); | |||
| setOpen(true); | |||
| setIsEdit(true); | |||
| } | |||
| }; | |||
| const onSubmit = useCallback<SubmitHandler<any>>( | |||
| async (data) => { | |||
| @@ -142,11 +79,11 @@ const CompanyHoliday: React.FC<Props> = ({ holidays }) => { | |||
| // console.log(data); | |||
| setServerError(""); | |||
| submitDialog(async () => { | |||
| await saveCompanyHoliday(data) | |||
| window.location.reload() | |||
| await saveCompanyHoliday(data); | |||
| window.location.reload(); | |||
| setOpen(false); | |||
| setIsEdit(false); | |||
| }, t) | |||
| }, t); | |||
| } catch (e) { | |||
| console.log(e); | |||
| setServerError(t("An error has occurred. Please try again later.")); | |||
| @@ -155,12 +92,12 @@ const CompanyHoliday: React.FC<Props> = ({ holidays }) => { | |||
| [t, router], | |||
| ); | |||
| const handleDelete = async (event:any) => { | |||
| const handleDelete = async (event: any) => { | |||
| try { | |||
| setServerError(""); | |||
| deleteDialog(async () => { | |||
| await deleteCompanyHoliday(parseInt(formProps.getValues("id"))) | |||
| window.location.reload() | |||
| await deleteCompanyHoliday(parseInt(formProps.getValues("id"))); | |||
| window.location.reload(); | |||
| setOpen(false); | |||
| setIsEdit(false); | |||
| }, t); | |||
| @@ -168,58 +105,72 @@ const CompanyHoliday: React.FC<Props> = ({ holidays }) => { | |||
| console.log(e); | |||
| setServerError(t("An error has occurred. Please try again later.")); | |||
| } | |||
| } | |||
| const onSubmitError = useCallback<SubmitErrorHandler<any>>( | |||
| (errors) => { | |||
| console.log(errors) | |||
| }, | |||
| [], | |||
| ); | |||
| }; | |||
| const onSubmitError = useCallback<SubmitErrorHandler<any>>((errors) => { | |||
| console.log(errors); | |||
| }, []); | |||
| const formProps = useForm<any>({ | |||
| defaultValues: { | |||
| id: null, | |||
| name: "" | |||
| name: "", | |||
| }, | |||
| }); | |||
| return ( | |||
| <> | |||
| <FormProvider {...formProps}> | |||
| <FullCalendar | |||
| plugins={[ dayGridPlugin, interactionPlugin, listPlugin ]} | |||
| initialView="dayGridMonth" | |||
| events={companyHolidays} | |||
| eventColor='#ff0000' | |||
| dateClick={handleDateClick} | |||
| eventClick={handleEventClick} | |||
| headerToolbar={{ | |||
| start: "today prev next", | |||
| end: "dayGridMonth listMonth" | |||
| }} | |||
| buttonText={{ | |||
| month: t("Calender View"), | |||
| list: t("List View"), | |||
| today: t("Today") | |||
| }} | |||
| /> | |||
| <CompanyHolidayDialog | |||
| open={open} | |||
| onClose={handleClose} | |||
| title={!editable ? "Bank Holiday" : isEdit ? "Edit Holiday" : "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> | |||
| {isEdit && <Button disabled={!editable} onClick={handleDelete}>Delete</Button>} | |||
| <Button disabled={!editable} type="submit">Submit</Button> | |||
| </Stack> | |||
| } | |||
| editable={editable} | |||
| /> | |||
| </FormProvider> | |||
| <FormProvider {...formProps}> | |||
| <FullCalendar | |||
| plugins={[dayGridPlugin, interactionPlugin, listPlugin]} | |||
| initialView="dayGridMonth" | |||
| events={companyHolidays} | |||
| eventColor="#ff0000" | |||
| dateClick={handleDateClick} | |||
| eventClick={handleEventClick} | |||
| headerToolbar={{ | |||
| start: "today prev next", | |||
| end: "dayGridMonth listMonth", | |||
| }} | |||
| buttonText={{ | |||
| month: t("Calender View"), | |||
| list: t("List View"), | |||
| today: t("Today"), | |||
| }} | |||
| /> | |||
| <CompanyHolidayDialog | |||
| open={open} | |||
| onClose={handleClose} | |||
| title={ | |||
| !editable | |||
| ? "Bank Holiday" | |||
| : isEdit | |||
| ? "Edit Holiday" | |||
| : "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> | |||
| {isEdit && ( | |||
| <Button disabled={!editable} onClick={handleDelete}> | |||
| Delete | |||
| </Button> | |||
| )} | |||
| <Button disabled={!editable} type="submit"> | |||
| Submit | |||
| </Button> | |||
| </Stack> | |||
| } | |||
| editable={editable} | |||
| /> | |||
| </FormProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -24,7 +24,7 @@ import LeaveTable from "../LeaveTable"; | |||
| import { LeaveType } from "@/app/api/timesheets"; | |||
| import FullscreenModal from "../FullscreenModal"; | |||
| import MobileLeaveTable from "../LeaveTable/MobileLeaveTable"; | |||
| import useIsMobile from "../utils/useIsMobile"; | |||
| import useIsMobile from "@/app/utils/useIsMobile"; | |||
| interface Props { | |||
| isOpen: boolean; | |||
| @@ -18,42 +18,54 @@ import Settings from "@mui/icons-material/Settings"; | |||
| import Analytics from "@mui/icons-material/Analytics"; | |||
| import Payments from "@mui/icons-material/Payments"; | |||
| import Staff from "@mui/icons-material/PeopleAlt"; | |||
| import Company from '@mui/icons-material/Store'; | |||
| import Department from '@mui/icons-material/Diversity3'; | |||
| import Position from '@mui/icons-material/Paragliding'; | |||
| import Salary from '@mui/icons-material/AttachMoney'; | |||
| import Team from '@mui/icons-material/Paragliding'; | |||
| import Holiday from '@mui/icons-material/CalendarMonth'; | |||
| import Company from "@mui/icons-material/Store"; | |||
| import Department from "@mui/icons-material/Diversity3"; | |||
| import Position from "@mui/icons-material/Paragliding"; | |||
| import Salary from "@mui/icons-material/AttachMoney"; | |||
| import Team from "@mui/icons-material/Paragliding"; | |||
| import Holiday from "@mui/icons-material/CalendarMonth"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import Typography from "@mui/material/Typography"; | |||
| import { usePathname } from "next/navigation"; | |||
| import Link from "next/link"; | |||
| import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig"; | |||
| import Logo from "../Logo"; | |||
| import GroupIcon from '@mui/icons-material/Group'; | |||
| import BusinessIcon from '@mui/icons-material/Business'; | |||
| import ViewWeekIcon from '@mui/icons-material/ViewWeek'; | |||
| import ManageAccountsIcon from '@mui/icons-material/ManageAccounts'; | |||
| import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; | |||
| import { GENERATE_REPORTS, MAINTAIN_MASTERDATA, MAINTAIN_USER, VIEW_MASTERDATA, VIEW_USER } from "@/middleware"; | |||
| import GroupIcon from "@mui/icons-material/Group"; | |||
| import BusinessIcon from "@mui/icons-material/Business"; | |||
| import ViewWeekIcon from "@mui/icons-material/ViewWeek"; | |||
| import ManageAccountsIcon from "@mui/icons-material/ManageAccounts"; | |||
| import EmojiEventsIcon from "@mui/icons-material/EmojiEvents"; | |||
| import { | |||
| GENERATE_REPORTS, | |||
| MAINTAIN_MASTERDATA, | |||
| MAINTAIN_USER, | |||
| VIEW_MASTERDATA, | |||
| VIEW_USER, | |||
| } from "@/middleware"; | |||
| import { SessionWithAbilities } from "../AppBar/NavigationToggle"; | |||
| import { authOptions } from "@/config/authConfig"; | |||
| import { getServerSession } from "next-auth"; | |||
| import useIsMobile from "@/app/utils/useIsMobile"; | |||
| interface NavigationItem { | |||
| icon: React.ReactNode; | |||
| label: string; | |||
| path: string; | |||
| isHidden?: boolean; | |||
| showOnMobile?: boolean; | |||
| children?: NavigationItem[]; | |||
| } | |||
| interface Props { | |||
| abilities?: string[] | |||
| abilities?: string[]; | |||
| } | |||
| const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||
| const navigationItems: NavigationItem[] = [ | |||
| { icon: <WorkHistory />, label: "User Workspace", path: "/home" }, | |||
| { | |||
| icon: <WorkHistory />, | |||
| label: "User Workspace", | |||
| path: "/home", | |||
| showOnMobile: true, | |||
| }, | |||
| { | |||
| icon: <Dashboard />, | |||
| label: "Dashboard", | |||
| @@ -93,7 +105,7 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||
| icon: <ViewWeekIcon />, | |||
| label: "Project Resource Summary", | |||
| path: "/dashboard/ProjectResourceSummary", | |||
| } | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| @@ -116,40 +128,109 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||
| { icon: <Assignment />, label: "Project Management", path: "/projects" }, | |||
| { icon: <Task />, label: "Task Template", path: "/tasks" }, | |||
| { icon: <Payments />, label: "Invoice", path: "/invoice" }, | |||
| { icon: <Analytics />, label: "Analysis Report", path: "", isHidden: ![GENERATE_REPORTS].some((ability) => abilities!!.includes(ability)), | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Analysis Report", | |||
| path: "", | |||
| isHidden: ![GENERATE_REPORTS].some((ability) => | |||
| abilities!.includes(ability), | |||
| ), | |||
| children: [ | |||
| {icon: <Analytics />, label:"Late Start Report", path: "/analytics/LateStartReport"}, | |||
| {icon: <Analytics />, label:"Delay Report", path: "/analytics/DelayReport"}, | |||
| {icon: <Analytics />, label:"Resource Overconsumption Report", path: "/analytics/ResourceOverconsumptionReport"}, | |||
| {icon: <Analytics />, label:"Cost and Expense Report", path: "/analytics/CostandExpenseReport"}, | |||
| {icon: <Analytics />, label:"Completion Report", path: "/analytics/ProjectCompletionReport"}, | |||
| {icon: <Analytics />, label:"Completion Report with Outstanding Un-billed Hours Report", path: "/analytics/ProjectCompletionReportWO"}, | |||
| {icon: <Analytics />, label:"Project Claims Report", path: "/analytics/ProjectClaimsReport"}, | |||
| {icon: <Analytics />, label:"Project P&L Report", path: "/analytics/ProjectPLReport"}, | |||
| {icon: <Analytics />, label:"Financial Status Report", path: "/analytics/FinancialStatusReport"}, | |||
| {icon: <Analytics />, label:"Project Cash Flow Report", path: "/analytics/ProjectCashFlowReport"}, | |||
| {icon: <Analytics />, label:"Staff Monthly Work Hours Analysis Report", path: "/analytics/StaffMonthlyWorkHoursAnalysisReport"}, | |||
| ], | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Late Start Report", | |||
| path: "/analytics/LateStartReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Delay Report", | |||
| path: "/analytics/DelayReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Resource Overconsumption Report", | |||
| path: "/analytics/ResourceOverconsumptionReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Cost and Expense Report", | |||
| path: "/analytics/CostandExpenseReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Completion Report", | |||
| path: "/analytics/ProjectCompletionReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Completion Report with Outstanding Un-billed Hours Report", | |||
| path: "/analytics/ProjectCompletionReportWO", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Project Claims Report", | |||
| path: "/analytics/ProjectClaimsReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Project P&L Report", | |||
| path: "/analytics/ProjectPLReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Financial Status Report", | |||
| path: "/analytics/FinancialStatusReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Project Cash Flow Report", | |||
| path: "/analytics/ProjectCashFlowReport", | |||
| }, | |||
| { | |||
| icon: <Analytics />, | |||
| label: "Staff Monthly Work Hours Analysis Report", | |||
| path: "/analytics/StaffMonthlyWorkHoursAnalysisReport", | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| icon: <Settings />, label: "Setting", path: "", isHidden: ![VIEW_MASTERDATA, MAINTAIN_MASTERDATA].some((ability) => abilities!!.includes(ability)), | |||
| icon: <Settings />, | |||
| label: "Setting", | |||
| path: "", | |||
| isHidden: ![VIEW_MASTERDATA, MAINTAIN_MASTERDATA].some((ability) => | |||
| abilities!.includes(ability), | |||
| ), | |||
| children: [ | |||
| { icon: <GroupIcon />, label: "Client", path: "/settings/customer" }, | |||
| { icon: <BusinessIcon />, label: "Subsidiary", path: "/settings/subsidiary" }, | |||
| { | |||
| icon: <BusinessIcon />, | |||
| label: "Subsidiary", | |||
| path: "/settings/subsidiary", | |||
| }, | |||
| { icon: <Staff />, label: "Staff", path: "/settings/staff" }, | |||
| { icon: <Company />, label: "Company", path: "/settings/company" }, | |||
| { icon: <EmojiEventsIcon />, label: "Skill", path: "/settings/skill" }, | |||
| { icon: <Department />, label: "Department", path: "/settings/department" }, | |||
| { | |||
| icon: <Department />, | |||
| label: "Department", | |||
| path: "/settings/department", | |||
| }, | |||
| { icon: <Position />, label: "Position", path: "/settings/position" }, | |||
| { icon: <Salary />, label: "Salary", path: "/settings/salary" }, | |||
| { icon: <Team />, label: "Team", path: "/settings/team" }, | |||
| // { icon: <ManageAccountsIcon />, label: "User", path: "/settings/user" }, | |||
| { icon: <ManageAccountsIcon />, label: "User Group", path: "/settings/group" }, | |||
| { | |||
| icon: <ManageAccountsIcon />, | |||
| label: "User Group", | |||
| path: "/settings/group", | |||
| }, | |||
| { icon: <Holiday />, label: "Holiday", path: "/settings/holiday" }, | |||
| ], | |||
| }, | |||
| ]; | |||
| const isMobile = useIsMobile(); | |||
| const { t } = useTranslation("common"); | |||
| const pathname = usePathname(); | |||
| const [openItems, setOpenItems] = React.useState<string[]>([]); | |||
| @@ -197,7 +278,11 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||
| </Box> | |||
| <Divider /> | |||
| <List component="nav"> | |||
| {navigationItems.filter(item => item.isHidden !== true).map((item) => renderNavigationItem(item))} | |||
| {navigationItems | |||
| .filter( | |||
| (item) => !item.isHidden && (isMobile ? item.showOnMobile : true), | |||
| ) | |||
| .map((item) => renderNavigationItem(item))} | |||
| {/* {navigationItems.map(({ icon, label, path }, index) => { | |||
| return ( | |||
| <Box | |||
| @@ -18,7 +18,7 @@ import { ArrowBack } from "@mui/icons-material"; | |||
| import PastEntryList from "./PastEntryList"; | |||
| import { ProjectWithTasks } from "@/app/api/projects"; | |||
| import { LeaveType } from "@/app/api/timesheets"; | |||
| import useIsMobile from "../utils/useIsMobile"; | |||
| import useIsMobile from "@/app/utils/useIsMobile"; | |||
| import FullscreenModal from "../FullscreenModal"; | |||
| interface Props extends Omit<PastEntryCalendarProps, "onDateSelect"> { | |||
| @@ -95,7 +95,7 @@ const PastEntryCalendarModal: React.FC<Props> = ({ | |||
| return isMobile ? ( | |||
| <FullscreenModal open={open} onClose={onClose} closeModal={onClose}> | |||
| <Box display="flex" flexDirection="column" gap={2} height="100%"> | |||
| <Typography variant="h6" flex="none" paddingInline={2}> | |||
| <Typography variant="h6" flex="none" padding={2}> | |||
| {t("Past Entries")} | |||
| </Typography> | |||
| <Box | |||
| @@ -109,13 +109,15 @@ const PastEntryCalendarModal: React.FC<Props> = ({ | |||
| {content} | |||
| </Box> | |||
| <Box padding={2} display="flex" justifyContent="flex-end"> | |||
| <Button | |||
| variant="outlined" | |||
| startIcon={<ArrowBack />} | |||
| onClick={clearDate} | |||
| > | |||
| {t("Back")} | |||
| </Button> | |||
| {selectedDate && ( | |||
| <Button | |||
| variant="outlined" | |||
| startIcon={<ArrowBack />} | |||
| onClick={clearDate} | |||
| > | |||
| {t("Back")} | |||
| </Button> | |||
| )} | |||
| </Box> | |||
| </Box> | |||
| </FullscreenModal> | |||
| @@ -24,7 +24,7 @@ import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||
| import { AssignedProject, ProjectWithTasks } from "@/app/api/projects"; | |||
| import FullscreenModal from "../FullscreenModal"; | |||
| import MobileTimesheetTable from "../TimesheetTable/MobileTimesheetTable"; | |||
| import useIsMobile from "../utils/useIsMobile"; | |||
| import useIsMobile from "@/app/utils/useIsMobile"; | |||
| interface Props { | |||
| isOpen: boolean; | |||
| @@ -3,7 +3,7 @@ | |||
| import React from "react"; | |||
| import TransferList, { TransferListProps } from "./TransferList"; | |||
| import MultiSelectList from "./MultiSelectList"; | |||
| import useIsMobile from "../utils/useIsMobile"; | |||
| import useIsMobile from "@/app/utils/useIsMobile"; | |||
| const TransferListWrapper: React.FC<TransferListProps> = (props) => { | |||
| const matches = useIsMobile(); | |||
| @@ -18,6 +18,7 @@ import LeaveModal from "../LeaveModal"; | |||
| import { LeaveType } from "@/app/api/timesheets"; | |||
| import { CalendarIcon } from "@mui/x-date-pickers"; | |||
| import PastEntryCalendarModal from "../PastEntryCalendar/PastEntryCalendarModal"; | |||
| import { HolidaysResult } from "@/app/api/holidays"; | |||
| export interface Props { | |||
| leaveTypes: LeaveType[]; | |||
| @@ -26,6 +27,7 @@ export interface Props { | |||
| username: string; | |||
| defaultLeaveRecords: RecordLeaveInput; | |||
| defaultTimesheets: RecordTimesheetInput; | |||
| holidays: HolidaysResult[]; | |||
| } | |||
| const UserWorkspacePage: React.FC<Props> = ({ | |||
| @@ -35,6 +37,7 @@ const UserWorkspacePage: React.FC<Props> = ({ | |||
| username, | |||
| defaultLeaveRecords, | |||
| defaultTimesheets, | |||
| holidays | |||
| }) => { | |||
| const [isTimeheetModalVisible, setTimeheetModalVisible] = useState(false); | |||
| const [isLeaveModalVisible, setLeaveModalVisible] = useState(false); | |||
| @@ -8,20 +8,28 @@ import { | |||
| fetchLeaves, | |||
| fetchTimesheets, | |||
| } from "@/app/api/timesheets"; | |||
| import { fetchHolidays } from "@/app/api/holidays"; | |||
| interface Props { | |||
| username: string; | |||
| } | |||
| const UserWorkspaceWrapper: React.FC<Props> = async ({ username }) => { | |||
| const [assignedProjects, allProjects, timesheets, leaves, leaveTypes] = | |||
| await Promise.all([ | |||
| fetchAssignedProjects(username), | |||
| fetchProjectWithTasks(), | |||
| fetchTimesheets(username), | |||
| fetchLeaves(username), | |||
| fetchLeaveTypes(), | |||
| ]); | |||
| const [ | |||
| assignedProjects, | |||
| allProjects, | |||
| timesheets, | |||
| leaves, | |||
| leaveTypes, | |||
| holidays, | |||
| ] = await Promise.all([ | |||
| fetchAssignedProjects(username), | |||
| fetchProjectWithTasks(), | |||
| fetchTimesheets(username), | |||
| fetchLeaves(username), | |||
| fetchLeaveTypes(), | |||
| fetchHolidays(), | |||
| ]); | |||
| return ( | |||
| <UserWorkspacePage | |||
| @@ -31,6 +39,7 @@ const UserWorkspaceWrapper: React.FC<Props> = async ({ username }) => { | |||
| defaultTimesheets={timesheets} | |||
| defaultLeaveRecords={leaves} | |||
| leaveTypes={leaveTypes} | |||
| holidays={holidays} | |||
| /> | |||
| ); | |||
| }; | |||