diff --git a/src/app/(main)/projects/create/not-found.tsx b/src/app/(main)/projects/create/not-found.tsx new file mode 100644 index 0000000..002e9f5 --- /dev/null +++ b/src/app/(main)/projects/create/not-found.tsx @@ -0,0 +1,17 @@ +import { getServerI18n } from "@/i18n"; +import { Stack, Typography, Link } from "@mui/material"; +import NextLink from "next/link"; + +export default async function NotFound() { + const { t } = await getServerI18n("projects", "common"); + + return ( + + {t("Not Found")} + {t("The create project page was not found!")} + + {t("Return to all projects")} + + + ); +} diff --git a/src/app/(main)/projects/create/page.tsx b/src/app/(main)/projects/create/page.tsx index 1671262..62f2492 100644 --- a/src/app/(main)/projects/create/page.tsx +++ b/src/app/(main)/projects/create/page.tsx @@ -11,10 +11,13 @@ import { } from "@/app/api/projects"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; +import { getUserAbilities } from "@/app/utils/commonUtil"; import CreateProject from "@/components/CreateProject"; import { I18nProvider, getServerI18n } from "@/i18n"; +import { MAINTAIN_PROJECT } from "@/middleware"; import Typography from "@mui/material/Typography"; import { Metadata } from "next"; +import { notFound } from "next/navigation"; export const metadata: Metadata = { title: "Create Project", @@ -23,6 +26,12 @@ export const metadata: Metadata = { const Projects: React.FC = async () => { const { t } = await getServerI18n("projects"); + const abilities = await getUserAbilities() + + if (!abilities.includes(MAINTAIN_PROJECT)) { + notFound(); + } + // Preload necessary dependencies fetchAllTasks(); fetchTaskTemplates(); diff --git a/src/app/(main)/projects/createSub/page.tsx b/src/app/(main)/projects/createSub/page.tsx index 3f474de..cc5be9c 100644 --- a/src/app/(main)/projects/createSub/page.tsx +++ b/src/app/(main)/projects/createSub/page.tsx @@ -13,9 +13,11 @@ import { } from "@/app/api/projects"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; +import { getUserAbilities } from "@/app/utils/commonUtil"; import { ServerFetchError } from "@/app/utils/fetchUtil"; import CreateProject from "@/components/CreateProject"; import { I18nProvider, getServerI18n } from "@/i18n"; +import { MAINTAIN_PROJECT } from "@/middleware"; import Typography from "@mui/material/Typography"; import { Metadata } from "next"; import { notFound } from "next/navigation"; @@ -26,6 +28,11 @@ export const metadata: Metadata = { const Projects: React.FC = async () => { const { t } = await getServerI18n("projects"); + const abilities = await getUserAbilities() + + if (!abilities.includes(MAINTAIN_PROJECT)) { + notFound(); + } // Preload necessary dependencies fetchAllTasks(); diff --git a/src/app/(main)/projects/edit/page.tsx b/src/app/(main)/projects/edit/page.tsx index 78e0ed1..55f401a 100644 --- a/src/app/(main)/projects/edit/page.tsx +++ b/src/app/(main)/projects/edit/page.tsx @@ -12,9 +12,11 @@ import { } from "@/app/api/projects"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; +import { getUserAbilities } from "@/app/utils/commonUtil"; import { ServerFetchError } from "@/app/utils/fetchUtil"; import CreateProject from "@/components/CreateProject"; import { I18nProvider, getServerI18n } from "@/i18n"; +import { MAINTAIN_PROJECT } from "@/middleware"; import Typography from "@mui/material/Typography"; import { isArray } from "lodash"; import { Metadata } from "next"; @@ -32,8 +34,9 @@ const Projects: React.FC = async ({ searchParams }) => { const { t } = await getServerI18n("projects"); // Assume projectId is string here const projectId = searchParams["id"]; + const abilities = await getUserAbilities() - if (!projectId || isArray(projectId)) { + if (!projectId || isArray(projectId) || abilities.includes(MAINTAIN_PROJECT)) { notFound(); } diff --git a/src/app/(main)/projects/editSub/page.tsx b/src/app/(main)/projects/editSub/page.tsx index eb4f5c6..37279a1 100644 --- a/src/app/(main)/projects/editSub/page.tsx +++ b/src/app/(main)/projects/editSub/page.tsx @@ -13,8 +13,10 @@ import { } from "@/app/api/projects"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; +import { getUserAbilities } from "@/app/utils/commonUtil"; import CreateProject from "@/components/CreateProject"; import { I18nProvider, getServerI18n } from "@/i18n"; +import { MAINTAIN_PROJECT } from "@/middleware"; import Typography from "@mui/material/Typography"; import { isArray } from "lodash"; import { Metadata } from "next"; @@ -32,7 +34,8 @@ const Projects: React.FC = async ({ searchParams }) => { const { t } = await getServerI18n("projects"); const projectId = searchParams["id"]; - if (!projectId || isArray(projectId)) { + const abilities = await getUserAbilities() + if (!projectId || isArray(projectId) || !abilities.includes(MAINTAIN_PROJECT)) { notFound(); } diff --git a/src/app/(main)/projects/not-found.tsx b/src/app/(main)/projects/not-found.tsx new file mode 100644 index 0000000..6464b10 --- /dev/null +++ b/src/app/(main)/projects/not-found.tsx @@ -0,0 +1,17 @@ +import { getServerI18n } from "@/i18n"; +import { Stack, Typography, Link } from "@mui/material"; +import NextLink from "next/link"; + +export default async function NotFound() { + const { t } = await getServerI18n("projects", "common"); + + return ( + + {t("Not Found")} + {t("The project page was not found!")} + + {t("Return to home")} + + + ); +} diff --git a/src/app/(main)/projects/page.tsx b/src/app/(main)/projects/page.tsx index 7cf44d0..faaa2d9 100644 --- a/src/app/(main)/projects/page.tsx +++ b/src/app/(main)/projects/page.tsx @@ -1,13 +1,15 @@ import { fetchProjectCategories, fetchProjects, preloadProjects } from "@/app/api/projects"; +import { getUserAbilities } from "@/app/utils/commonUtil"; import ProjectSearch from "@/components/ProjectSearch"; import { getServerI18n } from "@/i18n"; +import { MAINTAIN_PROJECT, VIEW_PROJECT } from "@/middleware"; import Add from "@mui/icons-material/Add"; -import { ButtonGroup } from "@mui/material"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; import { Metadata } from "next"; import Link from "next/link"; +import { notFound } from "next/navigation"; import { Suspense } from "react"; export const metadata: Metadata = { @@ -19,6 +21,10 @@ const Projects: React.FC = async () => { // preloadProjects(); fetchProjectCategories(); const projects = await fetchProjects(); + const abilities = await getUserAbilities() + if (!abilities.includes(VIEW_PROJECT)) { + notFound(); + } return ( <> @@ -31,7 +37,7 @@ const Projects: React.FC = async () => { {t("Projects")} - { > {t("Create Project")} - + } }> diff --git a/src/app/utils/commonUtil.ts b/src/app/utils/commonUtil.ts index d942bec..9356047 100644 --- a/src/app/utils/commonUtil.ts +++ b/src/app/utils/commonUtil.ts @@ -1,8 +1,9 @@ +import { SessionWithTokens, authOptions } from "@/config/authConfig" +import { getServerSession } from "next-auth" export interface WildCard { [key: string]: any; } - export const dateInRange = (currentDate: string, startDate: string, endDate: string) => { if (currentDate === undefined) { @@ -46,6 +47,11 @@ export function readIntFromString(input: string): [string, number | null] | stri const stringPart = parts.slice(0, parts.length - 1).join("-"); const intPartStr = parts[parts.length - 1]; const intPart = intPartStr ? parseInt(intPartStr, 10) : null; - + return [stringPart, intPart]; - } \ No newline at end of file +} + +export const getUserAbilities = async () => { + const session = await getServerSession(authOptions) as SessionWithTokens; + return session?.abilities ?? [] +} \ No newline at end of file diff --git a/src/app/utils/fetchUtil.ts b/src/app/utils/fetchUtil.ts index a97a09c..9d9a63d 100644 --- a/src/app/utils/fetchUtil.ts +++ b/src/app/utils/fetchUtil.ts @@ -22,7 +22,7 @@ export const serverFetch: typeof fetch = async (input, init) => { const session = await getServerSession(authOptions); const accessToken = session?.accessToken; - console.log(accessToken); + // console.log(accessToken); return fetch(input, { ...init, headers: { diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index 57c190a..10cf3fe 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -16,7 +16,7 @@ export interface AppBarProps { const AppBar: React.FC = async ({ avatarImageSrc, profileName }) => { const session = await getServerSession(authOptions) as any; const abilities: string[] = session.abilities - console.log(abilities) + // console.log(abilities) return ( diff --git a/src/components/CreateProject/CreateProject.tsx b/src/components/CreateProject/CreateProject.tsx index 0161188..31ea689 100644 --- a/src/components/CreateProject/CreateProject.tsx +++ b/src/components/CreateProject/CreateProject.tsx @@ -50,6 +50,7 @@ import { successDialog, } from "../Swal/CustomAlerts"; import dayjs from "dayjs"; +import { DELETE_PROJECT } from "@/middleware"; export interface Props { isEditMode: boolean; @@ -70,6 +71,7 @@ export interface Props { workNatures: WorkNature[]; allStaffs: StaffResult[]; grades: Grade[]; + abilities: string[]; } const hasErrorsInTab = ( @@ -113,6 +115,7 @@ const CreateProject: React.FC = ({ buildingTypes, workNatures, allStaffs, + abilities, }) => { const [serverError, setServerError] = useState(""); const [tabIndex, setTabIndex] = useState(0); @@ -302,7 +305,7 @@ const CreateProject: React.FC = ({ {isEditMode && !(formProps.getValues("projectDeleted") === true) && ( {/* {!formProps.getValues("projectActualStart") && ( */} - {formProps.getValues("projectStatus").toLowerCase() === "pending to start" && ( + {formProps.getValues("projectStatus")?.toLowerCase() === "pending to start" && (