@@ -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} | |||
/> | |||
); | |||
}; | |||