@@ -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 | ||||
} | } | ||||
} | } | ||||