| @@ -8,6 +8,8 @@ import Typography from "@mui/material/Typography"; | |||||
| import Link from "next/link"; | import Link from "next/link"; | ||||
| import { Suspense } from "react"; | import { Suspense } from "react"; | ||||
| import { fetchCompanys, preloadCompanys } from "@/app/api/companys"; | import { fetchCompanys, preloadCompanys } from "@/app/api/companys"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| import { MAINTAIN_COMPANY } from "@/middleware"; | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "Company", | title: "Company", | ||||
| @@ -20,6 +22,9 @@ const Company: React.FC = async () => { | |||||
| fetchCompanys(); | fetchCompanys(); | ||||
| preloadCompanys(); | preloadCompanys(); | ||||
| const abilities = await getUserAbilities() | |||||
| const maintainCompany = abilities.includes(MAINTAIN_COMPANY) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -31,14 +36,14 @@ const Company: React.FC = async () => { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Company")} | {t("Company")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainCompany && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/company/create" | href="/settings/company/create" | ||||
| > | > | ||||
| {t("Create Company")} | {t("Create Company")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <Suspense fallback={<CompanySearch.Loading />}> | <Suspense fallback={<CompanySearch.Loading />}> | ||||
| <CompanySearch/> | <CompanySearch/> | ||||
| @@ -9,6 +9,8 @@ import { Metadata } from "next"; | |||||
| import Link from "next/link"; | import Link from "next/link"; | ||||
| import { Suspense } from "react"; | import { Suspense } from "react"; | ||||
| import { I18nProvider } from "@/i18n"; | import { I18nProvider } from "@/i18n"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| import { MAINTAIN_CLIENT } from "@/middleware"; | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "Customer", | title: "Customer", | ||||
| @@ -17,6 +19,8 @@ export const metadata: Metadata = { | |||||
| const Customer: React.FC = async () => { | const Customer: React.FC = async () => { | ||||
| const { t } = await getServerI18n("customer"); | const { t } = await getServerI18n("customer"); | ||||
| preloadAllCustomers(); | preloadAllCustomers(); | ||||
| const abilities = await getUserAbilities() | |||||
| const maintainClient = abilities.includes(MAINTAIN_CLIENT) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| @@ -29,14 +33,14 @@ const Customer: React.FC = async () => { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Customer")} | {t("Customer")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainClient && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/customer/create" | href="/settings/customer/create" | ||||
| > | > | ||||
| {t("Create Customer")} | {t("Create Customer")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <I18nProvider namespaces={["customer", "common"]}> | <I18nProvider namespaces={["customer", "common"]}> | ||||
| <Suspense fallback={<CustomerSearch.Loading />}> | <Suspense fallback={<CustomerSearch.Loading />}> | ||||
| @@ -8,6 +8,8 @@ import Typography from "@mui/material/Typography"; | |||||
| import Link from "next/link"; | import Link from "next/link"; | ||||
| import { Suspense } from "react"; | import { Suspense } from "react"; | ||||
| import { fetchDepartments, preloadDepartments } from "@/app/api/departments"; | import { fetchDepartments, preloadDepartments } from "@/app/api/departments"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| import { MAINTAIN_DEPARTMENT } from "@/middleware"; | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "Department", | title: "Department", | ||||
| @@ -20,6 +22,9 @@ const Department: React.FC = async () => { | |||||
| // fetchDepartments(); | // fetchDepartments(); | ||||
| // preloadDepartments(); | // preloadDepartments(); | ||||
| const abilities = await getUserAbilities() | |||||
| const maintainDepartment = abilities.includes(MAINTAIN_DEPARTMENT) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -31,14 +36,14 @@ const Department: React.FC = async () => { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Department")} | {t("Department")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainDepartment && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/department/new" | href="/settings/department/new" | ||||
| > | > | ||||
| {t("Create Department")} | {t("Create Department")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <Suspense fallback={<DepartmentSearch.Loading />}> | <Suspense fallback={<DepartmentSearch.Loading />}> | ||||
| <DepartmentSearch/> | <DepartmentSearch/> | ||||
| @@ -8,6 +8,8 @@ import Typography from "@mui/material/Typography"; | |||||
| import Link from "next/link"; | import Link from "next/link"; | ||||
| import { Suspense } from "react"; | import { Suspense } from "react"; | ||||
| import { fetchPositions, preloadPositions } from "@/app/api/positions"; | import { fetchPositions, preloadPositions } from "@/app/api/positions"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| import { MAINTAIN_POSITION } from "@/middleware"; | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "Position", | title: "Position", | ||||
| @@ -20,6 +22,9 @@ const Position: React.FC = async () => { | |||||
| // fetchPositions(); | // fetchPositions(); | ||||
| // preloadPositions(); | // preloadPositions(); | ||||
| const abilities = await getUserAbilities() | |||||
| const maintainPosition = abilities.includes(MAINTAIN_POSITION) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -31,14 +36,14 @@ const Position: React.FC = async () => { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Position")} | {t("Position")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainPosition && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/position/new" | href="/settings/position/new" | ||||
| > | > | ||||
| {t("Create Position")} | {t("Create Position")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <Suspense fallback={<PositionSearch.Loading />}> | <Suspense fallback={<PositionSearch.Loading />}> | ||||
| <PositionSearch/> | <PositionSearch/> | ||||
| @@ -1,7 +1,9 @@ | |||||
| import { preloadClaims } from "@/app/api/claims"; | import { preloadClaims } from "@/app/api/claims"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| // import { preloadSkill, preloadTeamLeads } from "@/app/api/staff"; | // import { preloadSkill, preloadTeamLeads } from "@/app/api/staff"; | ||||
| import SkillSearch from "@/components/SkillSearch"; | import SkillSearch from "@/components/SkillSearch"; | ||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | import { I18nProvider, getServerI18n } from "@/i18n"; | ||||
| import { MAINTAIN_SKILL } from "@/middleware"; | |||||
| import Add from "@mui/icons-material/Add"; | import Add from "@mui/icons-material/Add"; | ||||
| import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||
| import Stack from "@mui/material/Stack"; | import Stack from "@mui/material/Stack"; | ||||
| @@ -18,6 +20,10 @@ const Skill: React.FC = async () => { | |||||
| const { t } = await getServerI18n("skill"); | const { t } = await getServerI18n("skill"); | ||||
| // preloadTeamLeads(); | // preloadTeamLeads(); | ||||
| // preloadSkill(); | // preloadSkill(); | ||||
| const abilities = await getUserAbilities() | |||||
| const maintainSkill = abilities.includes(MAINTAIN_SKILL) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -29,14 +35,14 @@ const Skill: React.FC = async () => { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Skill")} | {t("Skill")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainSkill && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/skill/create" | href="/settings/skill/create" | ||||
| > | > | ||||
| {t("Create Skill")} | {t("Create Skill")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <I18nProvider namespaces={["skill", "common"]}> | <I18nProvider namespaces={["skill", "common"]}> | ||||
| <Suspense fallback={<SkillSearch.Loading />}> | <Suspense fallback={<SkillSearch.Loading />}> | ||||
| @@ -1,7 +1,9 @@ | |||||
| import { preloadClaims } from "@/app/api/claims"; | import { preloadClaims } from "@/app/api/claims"; | ||||
| import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; | import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| import StaffSearch from "@/components/StaffSearch"; | import StaffSearch from "@/components/StaffSearch"; | ||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | import { I18nProvider, getServerI18n } from "@/i18n"; | ||||
| import { MAINTAIN_STAFF } from "@/middleware"; | |||||
| import Add from "@mui/icons-material/Add"; | import Add from "@mui/icons-material/Add"; | ||||
| import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||
| import Stack from "@mui/material/Stack"; | import Stack from "@mui/material/Stack"; | ||||
| @@ -18,6 +20,10 @@ const Staff: React.FC = async () => { | |||||
| const { t } = await getServerI18n("staff"); | const { t } = await getServerI18n("staff"); | ||||
| preloadTeamLeads(); | preloadTeamLeads(); | ||||
| preloadStaff(); | preloadStaff(); | ||||
| const abilities = await getUserAbilities(); | |||||
| const maintainStaff = abilities.includes(MAINTAIN_STAFF) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -29,14 +35,14 @@ const Staff: React.FC = async () => { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Staff")} | {t("Staff")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainStaff && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/staff/create" | href="/settings/staff/create" | ||||
| > | > | ||||
| {t("Create Staff")} | {t("Create Staff")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <I18nProvider namespaces={["staff", "common"]}> | <I18nProvider namespaces={["staff", "common"]}> | ||||
| <Suspense fallback={<StaffSearch.Loading />}> | <Suspense fallback={<StaffSearch.Loading />}> | ||||
| @@ -9,6 +9,8 @@ import { Suspense } from "react"; | |||||
| import { I18nProvider } from "@/i18n"; | import { I18nProvider } from "@/i18n"; | ||||
| import { preloadAllSubsidiaries } from "@/app/api/subsidiary"; | import { preloadAllSubsidiaries } from "@/app/api/subsidiary"; | ||||
| import SubsidiarySearch from "@/components/SubsidiarySearch"; | import SubsidiarySearch from "@/components/SubsidiarySearch"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| import { MAINTAIN_SUBSIDIARY } from "@/middleware"; | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "Subsidiary", | title: "Subsidiary", | ||||
| @@ -17,6 +19,8 @@ export const metadata: Metadata = { | |||||
| const Subsidiary: React.FC = async () => { | const Subsidiary: React.FC = async () => { | ||||
| const { t } = await getServerI18n("subsidiary"); | const { t } = await getServerI18n("subsidiary"); | ||||
| preloadAllSubsidiaries(); | preloadAllSubsidiaries(); | ||||
| const abilities = await getUserAbilities() | |||||
| const maintainSubsidiary = abilities.includes(MAINTAIN_SUBSIDIARY) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| @@ -29,14 +33,14 @@ const Subsidiary: React.FC = async () => { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Subsidiary")} | {t("Subsidiary")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainSubsidiary && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/subsidiary/create" | href="/settings/subsidiary/create" | ||||
| > | > | ||||
| {t("Create Subsidiary")} | {t("Create Subsidiary")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <I18nProvider namespaces={["subsidiary", "common"]}> | <I18nProvider namespaces={["subsidiary", "common"]}> | ||||
| <Suspense fallback={<SubsidiarySearch.Loading />}> | <Suspense fallback={<SubsidiarySearch.Loading />}> | ||||
| @@ -1,8 +1,10 @@ | |||||
| import { preloadClaims } from "@/app/api/claims"; | import { preloadClaims } from "@/app/api/claims"; | ||||
| import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; | import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| import StaffSearch from "@/components/StaffSearch"; | import StaffSearch from "@/components/StaffSearch"; | ||||
| import TeamSearch from "@/components/TeamSearch"; | import TeamSearch from "@/components/TeamSearch"; | ||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | import { I18nProvider, getServerI18n } from "@/i18n"; | ||||
| import { MAINTAIN_TEAM } from "@/middleware"; | |||||
| import Add from "@mui/icons-material/Add"; | import Add from "@mui/icons-material/Add"; | ||||
| import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||
| import Stack from "@mui/material/Stack"; | import Stack from "@mui/material/Stack"; | ||||
| @@ -21,6 +23,10 @@ export const metadata: Metadata = { | |||||
| const { t } = await getServerI18n("team"); | const { t } = await getServerI18n("team"); | ||||
| // preloadTeamLeads(); | // preloadTeamLeads(); | ||||
| // preloadStaff(); | // preloadStaff(); | ||||
| const abilities = await getUserAbilities() | |||||
| const maintainTeam = abilities.includes(MAINTAIN_TEAM) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -32,14 +38,14 @@ export const metadata: Metadata = { | |||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| {t("Team")} | {t("Team")} | ||||
| </Typography> | </Typography> | ||||
| <Button | |||||
| {maintainTeam && <Button | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| LinkComponent={Link} | LinkComponent={Link} | ||||
| href="/settings/team/create" | href="/settings/team/create" | ||||
| > | > | ||||
| {t("Create Team")} | {t("Create Team")} | ||||
| </Button> | |||||
| </Button>} | |||||
| </Stack> | </Stack> | ||||
| <I18nProvider namespaces={["team", "common"]}> | <I18nProvider namespaces={["team", "common"]}> | ||||
| <Suspense fallback={<TeamSearch.Loading />}> | <Suspense fallback={<TeamSearch.Loading />}> | ||||
| @@ -23,16 +23,19 @@ import { | |||||
| import { useRouter } from "next/navigation"; | import { useRouter } from "next/navigation"; | ||||
| import { deleteDialog, submitDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, submitDialog } from "../Swal/CustomAlerts"; | ||||
| import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils"; | import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils"; | ||||
| import { MAINTAIN_HOLIDAY } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| holidays: HolidaysList[]; | holidays: HolidaysList[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| const CompanyHoliday: React.FC<Props> = ({ holidays }) => { | |||||
| const CompanyHoliday: React.FC<Props> = ({ holidays, abilities }) => { | |||||
| const { t } = useTranslation("holidays"); | const { t } = useTranslation("holidays"); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| const formValues = useFormContext(); | const formValues = useFormContext(); | ||||
| const [serverError, setServerError] = useState(""); | const [serverError, setServerError] = useState(""); | ||||
| const maintainHoliday = abilities.includes(MAINTAIN_HOLIDAY) | |||||
| const companyHolidays = useMemo( | const companyHolidays = useMemo( | ||||
| () => [...getPublicHolidaysForNYears(2), ...holidays], | () => [...getPublicHolidaysForNYears(2), ...holidays], | ||||
| @@ -139,7 +142,7 @@ const CompanyHoliday: React.FC<Props> = ({ holidays }) => { | |||||
| }} | }} | ||||
| /> | /> | ||||
| <CompanyHolidayDialog | <CompanyHolidayDialog | ||||
| open={open} | |||||
| open={maintainHoliday && open} | |||||
| onClose={handleClose} | onClose={handleClose} | ||||
| title={ | title={ | ||||
| !editable | !editable | ||||
| @@ -6,6 +6,7 @@ import { fetchCompanys } from "@/app/api/companys"; | |||||
| import Holidays from "date-holidays"; | import Holidays from "date-holidays"; | ||||
| import { HolidaysResult, fetchHolidays, HolidaysList } from "@/app/api/holidays"; | import { HolidaysResult, fetchHolidays, HolidaysList } from "@/app/api/holidays"; | ||||
| import { convertDateArrayToString } from "@/app/utils/formatUtil"; | import { convertDateArrayToString } from "@/app/utils/formatUtil"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof CompanyHolidayLoading; | Loading: typeof CompanyHolidayLoading; | ||||
| @@ -14,7 +15,7 @@ interface SubComponents { | |||||
| const CompanyHolidayWrapper: React.FC & SubComponents = async () => { | const CompanyHolidayWrapper: React.FC & SubComponents = async () => { | ||||
| // const Companys = await fetchCompanys(); | // const Companys = await fetchCompanys(); | ||||
| const companyHolidays: HolidaysResult[] = await fetchHolidays() | |||||
| const [companyHolidays, abilities] = await Promise.all([fetchHolidays(), getUserAbilities()]) | |||||
| // console.log(companyHolidays) | // console.log(companyHolidays) | ||||
| const convertedHolidays = companyHolidays.map((holiday) => { | const convertedHolidays = companyHolidays.map((holiday) => { | ||||
| @@ -26,7 +27,7 @@ const CompanyHolidayWrapper: React.FC & SubComponents = async () => { | |||||
| }) | }) | ||||
| return <CompanyHoliday holidays={convertedHolidays as HolidaysList[]} />; | |||||
| return <CompanyHoliday holidays={convertedHolidays as HolidaysList[]} abilities={abilities}/>; | |||||
| }; | }; | ||||
| CompanyHolidayWrapper.Loading = CompanyHolidayLoading; | CompanyHolidayWrapper.Loading = CompanyHolidayLoading; | ||||
| @@ -10,21 +10,25 @@ import { useRouter } from "next/navigation"; | |||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { deleteCompany } from "@/app/api/companys/actions"; | import { deleteCompany } from "@/app/api/companys/actions"; | ||||
| import DeleteIcon from '@mui/icons-material/Delete'; | import DeleteIcon from '@mui/icons-material/Delete'; | ||||
| import { MAINTAIN_COMPANY } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| companys: CompanyResult[]; | companys: CompanyResult[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<CompanyResult, "id">>; | type SearchQuery = Partial<Omit<CompanyResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const CompanySearch: React.FC<Props> = ({ companys }) => { | |||||
| const CompanySearch: React.FC<Props> = ({ companys, abilities }) => { | |||||
| const { t } = useTranslation("companys"); | const { t } = useTranslation("companys"); | ||||
| const router = useRouter() | const router = useRouter() | ||||
| const [filteredCompanys, setFilteredCompanys] = useState(companys); | const [filteredCompanys, setFilteredCompanys] = useState(companys); | ||||
| const maintainCompany = abilities.includes(MAINTAIN_COMPANY) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| { label: t("Company code"), paramName: "companyCode", type: "text" }, | { label: t("Company code"), paramName: "companyCode", type: "text" }, | ||||
| @@ -62,6 +66,7 @@ const CompanySearch: React.FC<Props> = ({ companys }) => { | |||||
| label: t("Details"), | label: t("Details"), | ||||
| onClick: onProjectClick, | onClick: onProjectClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainCompany, | |||||
| }, | }, | ||||
| { name: "companyCode", label: t("Company Code") }, | { name: "companyCode", label: t("Company Code") }, | ||||
| { name: "name", label: t("Company Name") }, | { name: "name", label: t("Company Name") }, | ||||
| @@ -74,7 +79,8 @@ const CompanySearch: React.FC<Props> = ({ companys }) => { | |||||
| label: t("Delete"), | label: t("Delete"), | ||||
| onClick: onDeleteClick, | onClick: onDeleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error" | |||||
| color: "error", | |||||
| isHidden: !maintainCompany, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [t, onProjectClick], | [t, onProjectClick], | ||||
| @@ -3,6 +3,7 @@ import React from "react"; | |||||
| import CompanySearch from "./CompanySearch"; | import CompanySearch from "./CompanySearch"; | ||||
| import CompanySearchLoading from "./CompanySearchLoading"; | import CompanySearchLoading from "./CompanySearchLoading"; | ||||
| import { fetchCompanys } from "@/app/api/companys"; | import { fetchCompanys } from "@/app/api/companys"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof CompanySearchLoading; | Loading: typeof CompanySearchLoading; | ||||
| @@ -10,9 +11,9 @@ interface SubComponents { | |||||
| const CompanySearchWrapper: React.FC & SubComponents = async () => { | const CompanySearchWrapper: React.FC & SubComponents = async () => { | ||||
| // const Companys = await fetchCompanys(); | // const Companys = await fetchCompanys(); | ||||
| const Companys = await fetchCompanys(); | |||||
| const [Companys, abilities] = await Promise.all([fetchCompanys(), getUserAbilities()]); | |||||
| return <CompanySearch companys={Companys} />; | |||||
| return <CompanySearch companys={Companys} abilities={abilities}/>; | |||||
| }; | }; | ||||
| CompanySearchWrapper.Loading = CompanySearchLoading; | CompanySearchWrapper.Loading = CompanySearchLoading; | ||||
| @@ -10,18 +10,21 @@ import DeleteIcon from '@mui/icons-material/Delete'; | |||||
| import { useRouter, useSearchParams } from "next/navigation"; | import { useRouter, useSearchParams } from "next/navigation"; | ||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { deleteCustomer } from "@/app/api/customer/actions"; | import { deleteCustomer } from "@/app/api/customer/actions"; | ||||
| import { MAINTAIN_CLIENT } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| customers: Customer[]; | customers: Customer[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<Customer, "id">>; | type SearchQuery = Partial<Omit<Customer, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const CustomerSearch: React.FC<Props> = ({ customers }) => { | |||||
| const CustomerSearch: React.FC<Props> = ({ customers, abilities }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const router = useRouter() | const router = useRouter() | ||||
| const searchParams = useSearchParams() | const searchParams = useSearchParams() | ||||
| const maintainClient = abilities.includes(MAINTAIN_CLIENT) | |||||
| const [filteredCustomers, setFilteredCustomers] = useState(customers); | const [filteredCustomers, setFilteredCustomers] = useState(customers); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -60,6 +63,7 @@ const CustomerSearch: React.FC<Props> = ({ customers }) => { | |||||
| label: t("Details"), | label: t("Details"), | ||||
| onClick: onTaskClick, | onClick: onTaskClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainClient | |||||
| }, | }, | ||||
| { name: "code", label: t("Customer Code") }, | { name: "code", label: t("Customer Code") }, | ||||
| { name: "name", label: t("Customer Name") }, | { name: "name", label: t("Customer Name") }, | ||||
| @@ -68,7 +72,8 @@ const CustomerSearch: React.FC<Props> = ({ customers }) => { | |||||
| label: t("Delete"), | label: t("Delete"), | ||||
| onClick: onDeleteClick, | onClick: onDeleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error" | |||||
| color: "error", | |||||
| isHidden: !maintainClient | |||||
| }, | }, | ||||
| ], | ], | ||||
| [onTaskClick, t], | [onTaskClick, t], | ||||
| @@ -2,15 +2,16 @@ import { fetchAllCustomers } from "@/app/api/customer"; | |||||
| import React from "react"; | import React from "react"; | ||||
| import CustomerSearch from "./CustomerSearch"; | import CustomerSearch from "./CustomerSearch"; | ||||
| import CustomerSearchLoading from "./CustomerSearchLoading"; | import CustomerSearchLoading from "./CustomerSearchLoading"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof CustomerSearchLoading; | Loading: typeof CustomerSearchLoading; | ||||
| } | } | ||||
| const CustomerSearchWrapper: React.FC & SubComponents = async () => { | const CustomerSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const [customers] = await Promise.all([fetchAllCustomers()]); | |||||
| const [customers, abilities] = await Promise.all([fetchAllCustomers(), getUserAbilities()]); | |||||
| return <CustomerSearch customers={customers} />; | |||||
| return <CustomerSearch customers={customers} abilities={abilities}/>; | |||||
| }; | }; | ||||
| CustomerSearchWrapper.Loading = CustomerSearchLoading; | CustomerSearchWrapper.Loading = CustomerSearchLoading; | ||||
| @@ -10,19 +10,22 @@ import { useRouter } from "next/navigation"; | |||||
| import DeleteIcon from '@mui/icons-material/Delete'; | import DeleteIcon from '@mui/icons-material/Delete'; | ||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { deleteDepartment } from "@/app/api/departments/actions"; | import { deleteDepartment } from "@/app/api/departments/actions"; | ||||
| import { MAINTAIN_DEPARTMENT } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| departments: DepartmentResult[]; | departments: DepartmentResult[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<DepartmentResult, "id">>; | type SearchQuery = Partial<Omit<DepartmentResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const DepartmentSearch: React.FC<Props> = ({ departments }) => { | |||||
| const DepartmentSearch: React.FC<Props> = ({ departments, abilities }) => { | |||||
| const { t } = useTranslation("departments"); | const { t } = useTranslation("departments"); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| const [filteredDepartments, setFilteredDepartments] = useState(departments); | const [filteredDepartments, setFilteredDepartments] = useState(departments); | ||||
| const maintainDepartment = abilities.includes(MAINTAIN_DEPARTMENT) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| @@ -60,6 +63,7 @@ const DepartmentSearch: React.FC<Props> = ({ departments }) => { | |||||
| label: t("Details"), | label: t("Details"), | ||||
| onClick: onProjectClick, | onClick: onProjectClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainDepartment, | |||||
| }, | }, | ||||
| { name: "code", label: t("Department Code") }, | { name: "code", label: t("Department Code") }, | ||||
| { name: "name", label: t("Department Name") }, | { name: "name", label: t("Department Name") }, | ||||
| @@ -69,7 +73,8 @@ const DepartmentSearch: React.FC<Props> = ({ departments }) => { | |||||
| label: t("Delete"), | label: t("Delete"), | ||||
| onClick: onDeleteClick, | onClick: onDeleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error" | |||||
| color: "error", | |||||
| isHidden: !maintainDepartment, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [t, onProjectClick], | [t, onProjectClick], | ||||
| @@ -3,16 +3,17 @@ import React from "react"; | |||||
| import DepartmentSearch from "./DepartmentSearch"; | import DepartmentSearch from "./DepartmentSearch"; | ||||
| import DepartmentSearchLoading from "./DepartmentSearchLoading"; | import DepartmentSearchLoading from "./DepartmentSearchLoading"; | ||||
| import { fetchDepartments } from "@/app/api/departments"; | import { fetchDepartments } from "@/app/api/departments"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof DepartmentSearchLoading; | Loading: typeof DepartmentSearchLoading; | ||||
| } | } | ||||
| const DepartmentSearchWrapper: React.FC & SubComponents = async () => { | const DepartmentSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const Departments = await fetchDepartments(); | |||||
| const [Departments, abilities] = await Promise.all([fetchDepartments(), getUserAbilities()]); | |||||
| // const Departments:any[] = [] | // const Departments:any[] = [] | ||||
| return <DepartmentSearch departments={Departments} />; | |||||
| return <DepartmentSearch departments={Departments} abilities={abilities} />; | |||||
| }; | }; | ||||
| DepartmentSearchWrapper.Loading = DepartmentSearchLoading; | DepartmentSearchWrapper.Loading = DepartmentSearchLoading; | ||||
| @@ -39,15 +39,34 @@ import { | |||||
| GENERATE_REPORTS, | GENERATE_REPORTS, | ||||
| IMPORT_INVOICE, | IMPORT_INVOICE, | ||||
| IMPORT_RECEIPT, | IMPORT_RECEIPT, | ||||
| MAINTAIN_MASTERDATA, | |||||
| MAINTAIN_PROJECT, | MAINTAIN_PROJECT, | ||||
| MAINTAIN_TASK_TEMPLATE, | MAINTAIN_TASK_TEMPLATE, | ||||
| MAINTAIN_USER, | MAINTAIN_USER, | ||||
| VIEW_DASHBOARD_ALL, | VIEW_DASHBOARD_ALL, | ||||
| VIEW_DASHBOARD_SELF, | VIEW_DASHBOARD_SELF, | ||||
| VIEW_MASTERDATA, | |||||
| VIEW_PROJECT, | VIEW_PROJECT, | ||||
| VIEW_USER, | |||||
| VIEW_CLIENT, | |||||
| VIEW_SUBSIDIARY, | |||||
| VIEW_STAFF, | |||||
| VIEW_COMPANY, | |||||
| VIEW_SKILL, | |||||
| VIEW_DEPARTMENT, | |||||
| VIEW_POSITION, | |||||
| VIEW_SALARY, | |||||
| VIEW_TEAM, | |||||
| VIEW_USER_GROUP, | |||||
| VIEW_HOLIDAY, | |||||
| MAINTAIN_CLIENT, | |||||
| MAINTAIN_SUBSIDIARY, | |||||
| MAINTAIN_STAFF, | |||||
| MAINTAIN_COMPANY, | |||||
| MAINTAIN_SKILL, | |||||
| MAINTAIN_DEPARTMENT, | |||||
| MAINTAIN_POSITION, | |||||
| MAINTAIN_SALARY, | |||||
| MAINTAIN_TEAM, | |||||
| MAINTAIN_USER_GROUP, | |||||
| MAINTAIN_HOLIDAY, | |||||
| } from "@/middleware"; | } from "@/middleware"; | ||||
| import { SessionWithAbilities } from "../AppBar/NavigationToggle"; | import { SessionWithAbilities } from "../AppBar/NavigationToggle"; | ||||
| import { authOptions } from "@/config/authConfig"; | import { authOptions } from "@/config/authConfig"; | ||||
| @@ -217,35 +236,101 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||||
| icon: <Settings />, | icon: <Settings />, | ||||
| label: "Setting", | label: "Setting", | ||||
| path: "", | path: "", | ||||
| isHidden: ![VIEW_MASTERDATA, MAINTAIN_MASTERDATA].some((ability) => | |||||
| isHidden: ![ | |||||
| VIEW_CLIENT, | |||||
| VIEW_SUBSIDIARY, | |||||
| VIEW_STAFF, | |||||
| VIEW_COMPANY, | |||||
| VIEW_SKILL, | |||||
| VIEW_DEPARTMENT, | |||||
| VIEW_POSITION, | |||||
| VIEW_SALARY, | |||||
| VIEW_TEAM, | |||||
| VIEW_USER_GROUP, | |||||
| VIEW_HOLIDAY, | |||||
| MAINTAIN_CLIENT, | |||||
| MAINTAIN_SUBSIDIARY, | |||||
| MAINTAIN_STAFF, | |||||
| MAINTAIN_COMPANY, | |||||
| MAINTAIN_SKILL, | |||||
| MAINTAIN_DEPARTMENT, | |||||
| MAINTAIN_POSITION, | |||||
| MAINTAIN_SALARY, | |||||
| MAINTAIN_TEAM, | |||||
| MAINTAIN_USER_GROUP, | |||||
| MAINTAIN_HOLIDAY | |||||
| ].some((ability) => | |||||
| abilities!.includes(ability), | abilities!.includes(ability), | ||||
| ), | ), | ||||
| children: [ | children: [ | ||||
| { icon: <GroupIcon />, label: "Client", path: "/settings/customer" }, | |||||
| { | |||||
| icon: <GroupIcon />, | |||||
| label: "Client", | |||||
| path: "/settings/customer", | |||||
| isHidden: ![VIEW_CLIENT, MAINTAIN_CLIENT].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | { | ||||
| icon: <BusinessIcon />, | icon: <BusinessIcon />, | ||||
| label: "Subsidiary", | label: "Subsidiary", | ||||
| path: "/settings/subsidiary", | path: "/settings/subsidiary", | ||||
| isHidden: ![VIEW_SUBSIDIARY, MAINTAIN_SUBSIDIARY].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | |||||
| icon: <Staff />, | |||||
| label: "Staff", | |||||
| path: "/settings/staff", | |||||
| isHidden: ![VIEW_STAFF, MAINTAIN_STAFF].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | |||||
| icon: <Company />, | |||||
| label: "Company", | |||||
| path: "/settings/company", | |||||
| isHidden: ![VIEW_COMPANY, MAINTAIN_COMPANY].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | |||||
| icon: <EmojiEventsIcon />, | |||||
| label: "Skill", | |||||
| path: "/settings/skill", | |||||
| isHidden: ![VIEW_SKILL, MAINTAIN_SKILL].some((ability) => abilities!.includes(ability),), | |||||
| }, | }, | ||||
| { icon: <Staff />, label: "Staff", path: "/settings/staff" }, | |||||
| { icon: <Company />, label: "Company", path: "/settings/company" }, | |||||
| { icon: <EmojiEventsIcon />, label: "Skill", path: "/settings/skill" }, | |||||
| { | { | ||||
| icon: <Department />, | icon: <Department />, | ||||
| label: "Department", | label: "Department", | ||||
| path: "/settings/department", | path: "/settings/department", | ||||
| isHidden: ![VIEW_DEPARTMENT, MAINTAIN_DEPARTMENT].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | |||||
| icon: <Position />, | |||||
| label: "Position", | |||||
| path: "/settings/position", | |||||
| isHidden: ![VIEW_POSITION, MAINTAIN_POSITION].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | |||||
| icon: <Salary />, | |||||
| label: "Salary", | |||||
| path: "/settings/salary", | |||||
| isHidden: ![VIEW_SALARY, MAINTAIN_SALARY].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | |||||
| icon: <Team />, | |||||
| label: "Team", | |||||
| path: "/settings/team", | |||||
| isHidden: ![VIEW_TEAM, MAINTAIN_TEAM].some((ability) => abilities!.includes(ability),), | |||||
| }, | }, | ||||
| { 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", path: "/settings/user" }, | ||||
| { | { | ||||
| icon: <ManageAccountsIcon />, | icon: <ManageAccountsIcon />, | ||||
| label: "User Group", | label: "User Group", | ||||
| path: "/settings/group", | path: "/settings/group", | ||||
| isHidden: ![VIEW_USER_GROUP, MAINTAIN_USER_GROUP].some((ability) => abilities!.includes(ability),), | |||||
| }, | |||||
| { | |||||
| icon: <Holiday />, | |||||
| label: "Holiday", | |||||
| path: "/settings/holiday", | |||||
| isHidden: ![VIEW_HOLIDAY, MAINTAIN_HOLIDAY].some((ability) => abilities!.includes(ability),), | |||||
| }, | }, | ||||
| { icon: <Holiday />, label: "Holiday", path: "/settings/holiday"}, | |||||
| { icon: <FileUploadIcon />, label: "Import Excel File", path: "/settings/import", isHidden: username !== "2fi"}, | |||||
| { icon: <FileUploadIcon />, label: "Import Excel File", path: "/settings/import", isHidden: username !== "2fi" }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| ]; | ]; | ||||
| @@ -10,19 +10,22 @@ import { useRouter } from "next/navigation"; | |||||
| import DeleteIcon from '@mui/icons-material/Delete'; | import DeleteIcon from '@mui/icons-material/Delete'; | ||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { deletePosition } from "@/app/api/positions/actions"; | import { deletePosition } from "@/app/api/positions/actions"; | ||||
| import { MAINTAIN_POSITION } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| positions: PositionResult[]; | positions: PositionResult[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<PositionResult, "id">>; | type SearchQuery = Partial<Omit<PositionResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const PositionSearch: React.FC<Props> = ({ positions }) => { | |||||
| const PositionSearch: React.FC<Props> = ({ positions, abilities }) => { | |||||
| const { t } = useTranslation("positions"); | const { t } = useTranslation("positions"); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| const [filteredPositions, setFilteredPositions] = useState(positions); | const [filteredPositions, setFilteredPositions] = useState(positions); | ||||
| const maintainPosition = abilities.includes(MAINTAIN_POSITION) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| @@ -61,6 +64,7 @@ const PositionSearch: React.FC<Props> = ({ positions }) => { | |||||
| label: t("Details"), | label: t("Details"), | ||||
| onClick: onPositionClick, | onClick: onPositionClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainPosition, | |||||
| }, | }, | ||||
| { name: "code", label: t("Position Code") }, | { name: "code", label: t("Position Code") }, | ||||
| { name: "name", label: t("Position Name") }, | { name: "name", label: t("Position Name") }, | ||||
| @@ -70,7 +74,8 @@ const PositionSearch: React.FC<Props> = ({ positions }) => { | |||||
| label: t("Delete"), | label: t("Delete"), | ||||
| onClick: onDeleteClick, | onClick: onDeleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error" | |||||
| color: "error", | |||||
| isHidden: !maintainPosition, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [t, onPositionClick], | [t, onPositionClick], | ||||
| @@ -3,16 +3,17 @@ import React from "react"; | |||||
| import PositionSearch from "./PositionSearch"; | import PositionSearch from "./PositionSearch"; | ||||
| import PositionSearchLoading from "./PositionSearchLoading"; | import PositionSearchLoading from "./PositionSearchLoading"; | ||||
| import { fetchPositions } from "@/app/api/positions"; | import { fetchPositions } from "@/app/api/positions"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof PositionSearchLoading; | Loading: typeof PositionSearchLoading; | ||||
| } | } | ||||
| const PositionSearchWrapper: React.FC & SubComponents = async () => { | const PositionSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const Positions = await fetchPositions(); | |||||
| const [Positions, abilities] = await Promise.all([fetchPositions(), getUserAbilities()]); | |||||
| // const Positions:any[] = [] | // const Positions:any[] = [] | ||||
| return <PositionSearch positions={Positions} />; | |||||
| return <PositionSearch positions={Positions} abilities={abilities}/>; | |||||
| }; | }; | ||||
| PositionSearchWrapper.Loading = PositionSearchLoading; | PositionSearchWrapper.Loading = PositionSearchLoading; | ||||
| @@ -13,18 +13,21 @@ import FileUploadIcon from '@mui/icons-material/FileUpload'; | |||||
| import { exportSalary, importSalarys } from "@/app/api/salarys/actions"; | import { exportSalary, importSalarys } from "@/app/api/salarys/actions"; | ||||
| import { downloadFile } from "@/app/utils/commonUtil"; | import { downloadFile } from "@/app/utils/commonUtil"; | ||||
| import { errorDialog, successDialog } from "../Swal/CustomAlerts"; | import { errorDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { MAINTAIN_SALARY } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| salarys: SalaryResult[]; | salarys: SalaryResult[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<SalaryResult, "id">>; | type SearchQuery = Partial<Omit<SalaryResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const SalarySearch: React.FC<Props> = ({ salarys }) => { | |||||
| const SalarySearch: React.FC<Props> = ({ salarys, abilities }) => { | |||||
| const { t } = useTranslation("salarys"); | const { t } = useTranslation("salarys"); | ||||
| const [filteredSalarys, setFilteredSalarys] = useState(salarys); | const [filteredSalarys, setFilteredSalarys] = useState(salarys); | ||||
| const maintainSalary = abilities.includes(MAINTAIN_SALARY) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| @@ -109,7 +112,7 @@ const SalarySearch: React.FC<Props> = ({ salarys }) => { | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | |||||
| {maintainSalary && <Stack | |||||
| direction="row" | direction="row" | ||||
| justifyContent="right" | justifyContent="right" | ||||
| flexWrap="wrap" | flexWrap="wrap" | ||||
| @@ -130,7 +133,7 @@ const SalarySearch: React.FC<Props> = ({ salarys }) => { | |||||
| {t("Export Salary")} | {t("Export Salary")} | ||||
| </Button> | </Button> | ||||
| </ButtonGroup> | </ButtonGroup> | ||||
| </Stack> | |||||
| </Stack>} | |||||
| <SearchBox | <SearchBox | ||||
| criteria={searchCriteria} | criteria={searchCriteria} | ||||
| onSearch={(query) => { | onSearch={(query) => { | ||||
| @@ -3,6 +3,7 @@ import React from "react"; | |||||
| import SalarySearch from "./SalarySearch"; | import SalarySearch from "./SalarySearch"; | ||||
| import SalarySearchLoading from "./SalarySearchLoading"; | import SalarySearchLoading from "./SalarySearchLoading"; | ||||
| import { fetchSalarys } from "@/app/api/salarys"; | import { fetchSalarys } from "@/app/api/salarys"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof SalarySearchLoading; | Loading: typeof SalarySearchLoading; | ||||
| @@ -14,7 +15,7 @@ interface SubComponents { | |||||
| // } | // } | ||||
| const SalarySearchWrapper: React.FC & SubComponents = async () => { | const SalarySearchWrapper: React.FC & SubComponents = async () => { | ||||
| const Salarys = await fetchSalarys(); | |||||
| const [Salarys, abilities] = await Promise.all([fetchSalarys(), getUserAbilities()]); | |||||
| // const Salarys:any[] = [] | // const Salarys:any[] = [] | ||||
| const salarysWithHourlyRate = Salarys.map((salary) => { | const salarysWithHourlyRate = Salarys.map((salary) => { | ||||
| // const hourlyRate = calculateHourlyRate(Number(salary.lowerLimit), Number(salary.upperLimit),22, 8) | // const hourlyRate = calculateHourlyRate(Number(salary.lowerLimit), Number(salary.upperLimit),22, 8) | ||||
| @@ -27,7 +28,7 @@ const SalarySearchWrapper: React.FC & SubComponents = async () => { | |||||
| }) | }) | ||||
| // console.log(salarysWithHourlyRate) | // console.log(salarysWithHourlyRate) | ||||
| return <SalarySearch salarys={salarysWithHourlyRate} />; | |||||
| return <SalarySearch salarys={salarysWithHourlyRate} abilities={abilities}/>; | |||||
| }; | }; | ||||
| SalarySearchWrapper.Loading = SalarySearchLoading; | SalarySearchWrapper.Loading = SalarySearchLoading; | ||||
| @@ -9,15 +9,17 @@ import { useRouter } from "next/navigation"; | |||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { SkillResult } from "@/app/api/skill"; | import { SkillResult } from "@/app/api/skill"; | ||||
| import { deleteSkill } from "@/app/api/skill/actions"; | import { deleteSkill } from "@/app/api/skill/actions"; | ||||
| import { MAINTAIN_SKILL } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| skill: SkillResult[]; | skill: SkillResult[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<SkillResult, "id">>; | type SearchQuery = Partial<Omit<SkillResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const SkillSearch: React.FC<Props> = ({ skill }) => { | |||||
| const SkillSearch: React.FC<Props> = ({ skill, abilities }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const [filteredSkill, setFilteredSkill] = useState(skill); | const [filteredSkill, setFilteredSkill] = useState(skill); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| @@ -30,6 +32,8 @@ const SkillSearch: React.FC<Props> = ({ skill }) => { | |||||
| const code = t("code") | const code = t("code") | ||||
| const description = t("description") | const description = t("description") | ||||
| const maintainSkill = abilities.includes(MAINTAIN_SKILL) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| { | { | ||||
| @@ -73,6 +77,7 @@ const SkillSearch: React.FC<Props> = ({ skill }) => { | |||||
| label: edit, | label: edit, | ||||
| onClick: onSkillClick, | onClick: onSkillClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainSkill, | |||||
| }, | }, | ||||
| { name: "name", label: name }, | { name: "name", label: name }, | ||||
| { name: "code", label: code }, | { name: "code", label: code }, | ||||
| @@ -83,6 +88,7 @@ const SkillSearch: React.FC<Props> = ({ skill }) => { | |||||
| onClick: deleteClick, | onClick: deleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error", | color: "error", | ||||
| isHidden: !maintainSkill, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [t, onSkillClick, deleteClick] | [t, onSkillClick, deleteClick] | ||||
| @@ -2,15 +2,16 @@ import React from "react"; | |||||
| import SkillSearch from "./SkillSearch"; | import SkillSearch from "./SkillSearch"; | ||||
| import SkillSearchLoading from "./SkillSearchLoading"; | import SkillSearchLoading from "./SkillSearchLoading"; | ||||
| import { fetchSkill } from "@/app/api/skill"; | import { fetchSkill } from "@/app/api/skill"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof SkillSearchLoading; | Loading: typeof SkillSearchLoading; | ||||
| } | } | ||||
| const SkillSearchWrapper: React.FC & SubComponents = async () => { | const SkillSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const skill = await fetchSkill() | |||||
| const [skill, abilities] = await Promise.all([fetchSkill(), getUserAbilities()]) | |||||
| return <SkillSearch skill={skill} />; | |||||
| return <SkillSearch skill={skill} abilities={abilities}/>; | |||||
| }; | }; | ||||
| SkillSearchWrapper.Loading = SkillSearchLoading; | SkillSearchWrapper.Loading = SkillSearchLoading; | ||||
| @@ -10,7 +10,7 @@ import { deleteStaff } from "@/app/api/staff/actions"; | |||||
| import { useRouter } from "next/navigation"; | import { useRouter } from "next/navigation"; | ||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import Person from '@mui/icons-material/Person'; | import Person from '@mui/icons-material/Person'; | ||||
| import { MAINTAIN_USER, VIEW_USER } from "@/middleware"; | |||||
| import { MAINTAIN_STAFF, MAINTAIN_USER } from "@/middleware"; | |||||
| import { TeamResult } from "@/app/api/team"; | import { TeamResult } from "@/app/api/team"; | ||||
| import { Grade } from "@/app/api/grades"; | import { Grade } from "@/app/api/grades"; | ||||
| import { PositionResult } from "@/app/api/positions"; | import { PositionResult } from "@/app/api/positions"; | ||||
| @@ -20,13 +20,13 @@ interface Props { | |||||
| teams: TeamResult[] | teams: TeamResult[] | ||||
| grades: Grade[] | grades: Grade[] | ||||
| positions: PositionResult[] | positions: PositionResult[] | ||||
| isAuthed: boolean | |||||
| abilities: String[] | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<StaffResult, "id">>; | type SearchQuery = Partial<Omit<StaffResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, isAuthed }) => { | |||||
| const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, abilities }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const [filteredStaff, setFilteredStaff] = useState(staff); | const [filteredStaff, setFilteredStaff] = useState(staff); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| @@ -35,6 +35,9 @@ const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, isAuthe | |||||
| const gradeCombo = grades.map(g => `${g.name}`) | const gradeCombo = grades.map(g => `${g.name}`) | ||||
| const positionCombo = positions.map(p => `${p.name}`) | const positionCombo = positions.map(p => `${p.name}`) | ||||
| const maintainUser = abilities.includes(MAINTAIN_USER) | |||||
| const maintainStaff = abilities.includes(MAINTAIN_STAFF) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| { | { | ||||
| @@ -104,13 +107,14 @@ const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, isAuthe | |||||
| label: t("Actions"), | label: t("Actions"), | ||||
| onClick: onStaffClick, | onClick: onStaffClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainStaff, | |||||
| }, | }, | ||||
| { | { | ||||
| name: "id", | name: "id", | ||||
| label: t("Users"), | label: t("Users"), | ||||
| onClick: onUserClick, | onClick: onUserClick, | ||||
| buttonIcon: <Person />, | buttonIcon: <Person />, | ||||
| isHidden: isAuthed, | |||||
| isHidden: !maintainUser, | |||||
| }, | }, | ||||
| { name: "team", label: t("Team") }, | { name: "team", label: t("Team") }, | ||||
| { name: "name", label: t("Staff Name") }, | { name: "name", label: t("Staff Name") }, | ||||
| @@ -123,6 +127,7 @@ const StaffSearch: React.FC<Props> = ({ staff, teams, grades, positions, isAuthe | |||||
| onClick: deleteClick, | onClick: deleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error", | color: "error", | ||||
| isHidden: !maintainStaff, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [t, onStaffClick, deleteClick] | [t, onStaffClick, deleteClick] | ||||
| @@ -7,7 +7,7 @@ import { authOptions } from "@/config/authConfig"; | |||||
| import { fetchTeam } from "@/app/api/team"; | import { fetchTeam } from "@/app/api/team"; | ||||
| import { fetchPositions } from "@/app/api/positions"; | import { fetchPositions } from "@/app/api/positions"; | ||||
| import { fetchGrades } from "@/app/api/grades"; | import { fetchGrades } from "@/app/api/grades"; | ||||
| import { MAINTAIN_USER, VIEW_USER } from "@/middleware"; | |||||
| import { MAINTAIN_USER } from "@/middleware"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof StaffSearchLoading; | Loading: typeof StaffSearchLoading; | ||||
| @@ -19,14 +19,14 @@ interface SessionWithAbilities extends Session { | |||||
| const StaffSearchWrapper: React.FC & SubComponents = async () => { | const StaffSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const session = await getServerSession(authOptions) as SessionWithAbilities; | const session = await getServerSession(authOptions) as SessionWithAbilities; | ||||
| const isAuthed = ![MAINTAIN_USER, VIEW_USER].some((ability) => session.abilities!.includes(ability)) | |||||
| const abilities = session.abilities! | |||||
| const staff = await fetchStaff(); | const staff = await fetchStaff(); | ||||
| const teams = await fetchTeam(); | const teams = await fetchTeam(); | ||||
| const grades = await fetchGrades(); | const grades = await fetchGrades(); | ||||
| const positions = await fetchPositions(); | const positions = await fetchPositions(); | ||||
| return <StaffSearch staff={staff} teams={teams} grades={grades} positions={positions} isAuthed={isAuthed}/>; | |||||
| return <StaffSearch staff={staff} teams={teams} grades={grades} positions={positions} abilities={abilities}/>; | |||||
| }; | }; | ||||
| StaffSearchWrapper.Loading = StaffSearchLoading; | StaffSearchWrapper.Loading = StaffSearchLoading; | ||||
| @@ -10,18 +10,21 @@ import { useRouter, useSearchParams } from "next/navigation"; | |||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { Subsidiary } from "@/app/api/subsidiary"; | import { Subsidiary } from "@/app/api/subsidiary"; | ||||
| import { deleteSubsidiary } from "@/app/api/subsidiary/actions"; | import { deleteSubsidiary } from "@/app/api/subsidiary/actions"; | ||||
| import { MAINTAIN_SUBSIDIARY } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| subsidiaries: Subsidiary[]; | subsidiaries: Subsidiary[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<Subsidiary, "id">>; | type SearchQuery = Partial<Omit<Subsidiary, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const SubsidiarySearch: React.FC<Props> = ({ subsidiaries }) => { | |||||
| const SubsidiarySearch: React.FC<Props> = ({ subsidiaries, abilities }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const router = useRouter() | const router = useRouter() | ||||
| const searchParams = useSearchParams() | const searchParams = useSearchParams() | ||||
| const maintainSubsidiary = abilities.includes(MAINTAIN_SUBSIDIARY) | |||||
| const [filteredSubsidiaries, setFilteredSubsidiaries] = useState(subsidiaries); | const [filteredSubsidiaries, setFilteredSubsidiaries] = useState(subsidiaries); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -63,6 +66,7 @@ const SubsidiarySearch: React.FC<Props> = ({ subsidiaries }) => { | |||||
| label: t("Details"), | label: t("Details"), | ||||
| onClick: onTaskClick, | onClick: onTaskClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainSubsidiary, | |||||
| }, | }, | ||||
| { name: "code", label: t("Subsidiary Code") }, | { name: "code", label: t("Subsidiary Code") }, | ||||
| { name: "name", label: t("Subsidiary Name") }, | { name: "name", label: t("Subsidiary Name") }, | ||||
| @@ -71,7 +75,8 @@ const SubsidiarySearch: React.FC<Props> = ({ subsidiaries }) => { | |||||
| label: t("Delete"), | label: t("Delete"), | ||||
| onClick: onDeleteClick, | onClick: onDeleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error" | |||||
| color: "error", | |||||
| isHidden: !maintainSubsidiary, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [onTaskClick, t], | [onTaskClick, t], | ||||
| @@ -2,15 +2,16 @@ import React from "react"; | |||||
| import SubsidiarySearch from "./SubsidiarySearch"; | import SubsidiarySearch from "./SubsidiarySearch"; | ||||
| import SubsidiarySearchLoading from "./SubsidiarySearchLoading"; | import SubsidiarySearchLoading from "./SubsidiarySearchLoading"; | ||||
| import { fetchAllSubsidiaries } from "@/app/api/subsidiary"; | import { fetchAllSubsidiaries } from "@/app/api/subsidiary"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof SubsidiarySearchLoading; | Loading: typeof SubsidiarySearchLoading; | ||||
| } | } | ||||
| const SubsidiarySearchWrapper: React.FC & SubComponents = async () => { | const SubsidiarySearchWrapper: React.FC & SubComponents = async () => { | ||||
| const subsidiaries = await fetchAllSubsidiaries(); | |||||
| const [subsidiaries, abilities] = await Promise.all([fetchAllSubsidiaries(), getUserAbilities()]); | |||||
| return <SubsidiarySearch subsidiaries={subsidiaries} />; | |||||
| return <SubsidiarySearch subsidiaries={subsidiaries} abilities={abilities} />; | |||||
| }; | }; | ||||
| SubsidiarySearchWrapper.Loading = SubsidiarySearchLoading; | SubsidiarySearchWrapper.Loading = SubsidiarySearchLoading; | ||||
| @@ -10,14 +10,16 @@ import DeleteIcon from "@mui/icons-material/Delete"; | |||||
| import { useRouter } from "next/navigation"; | import { useRouter } from "next/navigation"; | ||||
| import { deleteTeam } from "@/app/api/team/actions"; | import { deleteTeam } from "@/app/api/team/actions"; | ||||
| import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; | ||||
| import { MAINTAIN_TEAM } from "@/middleware"; | |||||
| interface Props { | interface Props { | ||||
| team: TeamResult[]; | team: TeamResult[]; | ||||
| abilities: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<TeamResult, "id">>; | type SearchQuery = Partial<Omit<TeamResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const TeamSearch: React.FC<Props> = ({ team }) => { | |||||
| const TeamSearch: React.FC<Props> = ({ team, abilities }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const [filteredTeam, setFilteredTeam] = useState(team); | const [filteredTeam, setFilteredTeam] = useState(team); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| @@ -29,6 +31,8 @@ const TeamSearch: React.FC<Props> = ({ team }) => { | |||||
| const teamLead = t("teamLead") | const teamLead = t("teamLead") | ||||
| const delete_t = t("delete") | const delete_t = t("delete") | ||||
| const maintainTeam = abilities.includes(MAINTAIN_TEAM) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| { | { | ||||
| @@ -77,6 +81,7 @@ const TeamSearch: React.FC<Props> = ({ team }) => { | |||||
| label: edit, | label: edit, | ||||
| onClick: onTeamClick, | onClick: onTeamClick, | ||||
| buttonIcon: <EditNote />, | buttonIcon: <EditNote />, | ||||
| isHidden: !maintainTeam, | |||||
| }, | }, | ||||
| { name: "name", label: name }, | { name: "name", label: name }, | ||||
| { name: "code", label: code }, | { name: "code", label: code }, | ||||
| @@ -87,7 +92,8 @@ const TeamSearch: React.FC<Props> = ({ team }) => { | |||||
| label: delete_t, | label: delete_t, | ||||
| onClick: onDeleteClick, | onClick: onDeleteClick, | ||||
| buttonIcon: <DeleteIcon />, | buttonIcon: <DeleteIcon />, | ||||
| color: "error" | |||||
| color: "error", | |||||
| isHidden: !maintainTeam, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [t] | [t] | ||||
| @@ -3,6 +3,7 @@ import React from "react"; | |||||
| import TeamSearch from "./TeamSearch"; | import TeamSearch from "./TeamSearch"; | ||||
| import TeamSearchLoading from "./TeamSearchLoading"; | import TeamSearchLoading from "./TeamSearchLoading"; | ||||
| import { fetchTeam } from "@/app/api/team"; | import { fetchTeam } from "@/app/api/team"; | ||||
| import { getUserAbilities } from "@/app/utils/commonUtil"; | |||||
| // import { preloadTeam } from "@/app/api/Team"; | // import { preloadTeam } from "@/app/api/Team"; | ||||
| interface SubComponents { | interface SubComponents { | ||||
| @@ -10,10 +11,10 @@ interface SubComponents { | |||||
| } | } | ||||
| const TeamSearchWrapper: React.FC & SubComponents = async () => { | const TeamSearchWrapper: React.FC & SubComponents = async () => { | ||||
| const Team = await fetchTeam(); | |||||
| const [Team, abilities] = await Promise.all([fetchTeam(), getUserAbilities()]); | |||||
| console.log(Team); | console.log(Team); | ||||
| return <TeamSearch team={Team} />; | |||||
| return <TeamSearch team={Team} abilities={abilities}/>; | |||||
| }; | }; | ||||
| TeamSearchWrapper.Loading = TeamSearchLoading; | TeamSearchWrapper.Loading = TeamSearchLoading; | ||||
| @@ -11,22 +11,41 @@ export const [ | |||||
| NORMAL_STAFF, | NORMAL_STAFF, | ||||
| SUPPORTING_STAFF | SUPPORTING_STAFF | ||||
| ] = [ | ] = [ | ||||
| "Super Admin", | |||||
| "Top Management", | |||||
| "Team Leader", | |||||
| "Normal Staff", | |||||
| "Supporting Staff" | |||||
| ] | |||||
| "Super Admin", | |||||
| "Top Management", | |||||
| "Team Leader", | |||||
| "Normal Staff", | |||||
| "Supporting Staff" | |||||
| ] | |||||
| // abilities | // abilities | ||||
| export const [ | export const [ | ||||
| VIEW_USER, | |||||
| MAINTAIN_USER, | MAINTAIN_USER, | ||||
| MAINTAIN_TIMESHEET, | MAINTAIN_TIMESHEET, | ||||
| VIEW_TASK_TEMPLATE, | VIEW_TASK_TEMPLATE, | ||||
| VIEW_GROUP, | VIEW_GROUP, | ||||
| VIEW_MASTERDATA, | |||||
| MAINTAIN_MASTERDATA, | |||||
| VIEW_CLIENT, | |||||
| VIEW_SUBSIDIARY, | |||||
| VIEW_STAFF, | |||||
| VIEW_COMPANY, | |||||
| VIEW_SKILL, | |||||
| VIEW_DEPARTMENT, | |||||
| VIEW_POSITION, | |||||
| VIEW_SALARY, | |||||
| VIEW_TEAM, | |||||
| VIEW_USER_GROUP, | |||||
| VIEW_HOLIDAY, | |||||
| MAINTAIN_CLIENT, | |||||
| MAINTAIN_SUBSIDIARY, | |||||
| MAINTAIN_STAFF, | |||||
| MAINTAIN_COMPANY, | |||||
| MAINTAIN_SKILL, | |||||
| MAINTAIN_DEPARTMENT, | |||||
| MAINTAIN_POSITION, | |||||
| MAINTAIN_SALARY, | |||||
| MAINTAIN_TEAM, | |||||
| MAINTAIN_USER_GROUP, | |||||
| MAINTAIN_HOLIDAY, | |||||
| VIEW_DASHBOARD_SELF, | VIEW_DASHBOARD_SELF, | ||||
| VIEW_DASHBOARD_ALL, | VIEW_DASHBOARD_ALL, | ||||
| IMPORT_INVOICE, | IMPORT_INVOICE, | ||||
| @@ -41,27 +60,46 @@ export const [ | |||||
| DELETE_PROJECT, | DELETE_PROJECT, | ||||
| MAINTAIN_TIMESHEET_FAST_TIME_ENTRY, | MAINTAIN_TIMESHEET_FAST_TIME_ENTRY, | ||||
| ] = [ | ] = [ | ||||
| 'VIEW_USER', | |||||
| 'MAINTAIN_USER', | |||||
| 'MAINTAIN_TIMESHEET', | |||||
| 'VIEW_TASK_TEMPLATE', | |||||
| 'VIEW_GROUP', | |||||
| 'VIEW_MASTERDATA', | |||||
| 'MAINTAIN_MASTERDATA', | |||||
| 'VIEW_DASHBOARD_SELF', | |||||
| 'VIEW_DASHBOARD_ALL', | |||||
| 'IMPORT_INVOICE', | |||||
| 'MAINTAIN_GROUP', | |||||
| 'GENERATE_REPORTS', | |||||
| 'VIEW_STAFF_PROFILE', | |||||
| 'IMPORT_RECEIPT', | |||||
| 'MAINTAIN_TASK_TEMPLATE', | |||||
| 'MAINTAIN_TIMESHEET_7DAYS', | |||||
| 'VIEW_PROJECT', | |||||
| 'MAINTAIN_PROJECT', | |||||
| 'DELETE_PROJECT', | |||||
| 'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY' | |||||
| ] | |||||
| 'MAINTAIN_USER', | |||||
| 'MAINTAIN_TIMESHEET', | |||||
| 'VIEW_TASK_TEMPLATE', | |||||
| 'VIEW_GROUP', | |||||
| 'VIEW_CLIENT', | |||||
| 'VIEW_SUBSIDIARY', | |||||
| 'VIEW_STAFF', | |||||
| 'VIEW_COMPANY', | |||||
| 'VIEW_SKILL', | |||||
| 'VIEW_DEPARTMENT', | |||||
| 'VIEW_POSITION', | |||||
| 'VIEW_SALARY', | |||||
| 'VIEW_TEAM', | |||||
| 'VIEW_USER_GROUP', | |||||
| 'VIEW_HOLIDAY', | |||||
| 'MAINTAIN_CLIENT', | |||||
| 'MAINTAIN_SUBSIDIARY', | |||||
| 'MAINTAIN_STAFF', | |||||
| 'MAINTAIN_COMPANY', | |||||
| 'MAINTAIN_SKILL', | |||||
| 'MAINTAIN_DEPARTMENT', | |||||
| 'MAINTAIN_POSITION', | |||||
| 'MAINTAIN_SALARY', | |||||
| 'MAINTAIN_TEAM', | |||||
| 'MAINTAIN_USER_GROUP', | |||||
| 'MAINTAIN_HOLIDAY', | |||||
| 'VIEW_DASHBOARD_SELF', | |||||
| 'VIEW_DASHBOARD_ALL', | |||||
| 'IMPORT_INVOICE', | |||||
| 'MAINTAIN_GROUP', | |||||
| 'GENERATE_REPORTS', | |||||
| 'VIEW_STAFF_PROFILE', | |||||
| 'IMPORT_RECEIPT', | |||||
| 'MAINTAIN_TASK_TEMPLATE', | |||||
| 'MAINTAIN_TIMESHEET_7DAYS', | |||||
| 'VIEW_PROJECT', | |||||
| 'MAINTAIN_PROJECT', | |||||
| 'DELETE_PROJECT', | |||||
| 'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY' | |||||
| ] | |||||
| const PRIVATE_ROUTES = [ | const PRIVATE_ROUTES = [ | ||||
| "/analytics", | "/analytics", | ||||
| @@ -93,7 +131,7 @@ export default async function middleware( | |||||
| const authMiddleware = withAuth({ | const authMiddleware = withAuth({ | ||||
| pages: authOptions.pages, | pages: authOptions.pages, | ||||
| callbacks: { | callbacks: { | ||||
| authorized: ({req, token}) => { | |||||
| authorized: ({ req, token }) => { | |||||
| let isAuth = Boolean(token); | let isAuth = Boolean(token); | ||||
| if (!Boolean(token)) { | if (!Boolean(token)) { | ||||
| return Boolean(token) | return Boolean(token) | ||||
| @@ -109,26 +147,96 @@ export default async function middleware( | |||||
| } | } | ||||
| if (req.nextUrl.pathname.startsWith('/settings')) { | if (req.nextUrl.pathname.startsWith('/settings')) { | ||||
| isAuth = [VIEW_MASTERDATA, MAINTAIN_MASTERDATA].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| isAuth = [ | |||||
| VIEW_CLIENT, | |||||
| VIEW_SUBSIDIARY, | |||||
| VIEW_STAFF, | |||||
| VIEW_COMPANY, | |||||
| VIEW_SKILL, | |||||
| VIEW_DEPARTMENT, | |||||
| VIEW_POSITION, | |||||
| VIEW_SALARY, | |||||
| VIEW_TEAM, | |||||
| VIEW_USER_GROUP, | |||||
| VIEW_HOLIDAY, | |||||
| MAINTAIN_CLIENT, | |||||
| MAINTAIN_SUBSIDIARY, | |||||
| MAINTAIN_STAFF, | |||||
| MAINTAIN_COMPANY, | |||||
| MAINTAIN_SKILL, | |||||
| MAINTAIN_DEPARTMENT, | |||||
| MAINTAIN_POSITION, | |||||
| MAINTAIN_SALARY, | |||||
| MAINTAIN_TEAM, | |||||
| MAINTAIN_USER_GROUP, | |||||
| MAINTAIN_HOLIDAY | |||||
| ].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/customer/create') || req.nextUrl.pathname.startsWith('/settings/customer/edit')) { | |||||
| isAuth = [MAINTAIN_CLIENT].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/subsidiary/create') || req.nextUrl.pathname.startsWith('/settings/subsidiary/edit')) { | |||||
| isAuth = [MAINTAIN_SUBSIDIARY].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/staff/create') || req.nextUrl.pathname.startsWith('/settings/staff/edit')) { | |||||
| isAuth = [MAINTAIN_STAFF].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/company/create') || req.nextUrl.pathname.startsWith('/settings/company/edit')) { | |||||
| isAuth = [MAINTAIN_COMPANY].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/skill/create') || req.nextUrl.pathname.startsWith('/settings/skill/edit')) { | |||||
| isAuth = [MAINTAIN_SKILL].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/department/create') || req.nextUrl.pathname.startsWith('/settings/department/edit')) { | |||||
| isAuth = [MAINTAIN_DEPARTMENT].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/position/create') || req.nextUrl.pathname.startsWith('/settings/position/edit')) { | |||||
| isAuth = [MAINTAIN_POSITION].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/team/create') || req.nextUrl.pathname.startsWith('/settings/team/edit')) { | |||||
| isAuth = [MAINTAIN_TEAM].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/group/create') || req.nextUrl.pathname.startsWith('/settings/group/edit')) { | |||||
| isAuth = [MAINTAIN_USER_GROUP].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/holiday/create') || req.nextUrl.pathname.startsWith('/settings/holiday/edit')) { | |||||
| isAuth = [MAINTAIN_HOLIDAY].some((ability) => abilities.includes(ability)); | |||||
| } | |||||
| if (req.nextUrl.pathname.startsWith('/settings/user')) { | if (req.nextUrl.pathname.startsWith('/settings/user')) { | ||||
| isAuth = [MAINTAIN_USER, VIEW_USER].some((ability) => abilities.includes(ability)); | |||||
| isAuth = [MAINTAIN_USER].some((ability) => abilities.includes(ability)); | |||||
| } | } | ||||
| if (req.nextUrl.pathname.startsWith('/settings/staff/user')) { | if (req.nextUrl.pathname.startsWith('/settings/staff/user')) { | ||||
| isAuth = [MAINTAIN_USER, VIEW_USER].some((ability) => abilities.includes(ability)); | |||||
| isAuth = [MAINTAIN_USER].some((ability) => abilities.includes(ability)); | |||||
| } | } | ||||
| if (req.nextUrl.pathname.startsWith('/analytics')) { | if (req.nextUrl.pathname.startsWith('/analytics')) { | ||||
| isAuth = [GENERATE_REPORTS].some((ability) => abilities.includes(ability)); | isAuth = [GENERATE_REPORTS].some((ability) => abilities.includes(ability)); | ||||
| } | } | ||||
| if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) { | if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) { | ||||
| isAuth = [VIEW_STAFF_PROFILE].some((ability) => abilities.includes(ability)); | isAuth = [VIEW_STAFF_PROFILE].some((ability) => abilities.includes(ability)); | ||||
| } | } | ||||
| if (req.nextUrl.pathname.startsWith('/invoice')) { | if (req.nextUrl.pathname.startsWith('/invoice')) { | ||||
| isAuth = [IMPORT_INVOICE, IMPORT_RECEIPT].some((ability) => abilities.includes(ability)); | isAuth = [IMPORT_INVOICE, IMPORT_RECEIPT].some((ability) => abilities.includes(ability)); | ||||
| } | } | ||||
| if (req.nextUrl.pathname.startsWith('/dashboard')) { | if (req.nextUrl.pathname.startsWith('/dashboard')) { | ||||
| isAuth = [VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_SELF].some((ability) => abilities.includes(ability)); | isAuth = [VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_SELF].some((ability) => abilities.includes(ability)); | ||||
| } | } | ||||
| return isAuth | return isAuth | ||||
| } | } | ||||
| } | } | ||||