Переглянути джерело

Add draft projects

tags/Baseline_180220205_Frontend
Wayne 8 місяці тому
джерело
коміт
cb76ceb602
8 змінених файлів з 361 додано та 180 видалено
  1. +16
    -3
      src/app/(main)/projects/create/page.tsx
  2. +48
    -39
      src/app/(main)/projects/page.tsx
  3. +55
    -0
      src/app/utils/draftUtils.ts
  4. +144
    -115
      src/components/CreateProject/CreateProject.tsx
  5. +7
    -4
      src/components/CreateProject/CreateProjectWrapper.tsx
  6. +2
    -2
      src/components/CreateProject/ProjectClientDetails.tsx
  7. +68
    -9
      src/components/ProjectSearch/ProjectSearch.tsx
  8. +21
    -8
      src/components/ProjectSearch/ProjectSearchWrapper.tsx

+ 16
- 3
src/app/(main)/projects/create/page.tsx Переглянути файл

@@ -1,4 +1,8 @@
import { fetchAllCustomers, fetchAllSubsidiaries, fetchCustomerTypes } from "@/app/api/customer";
import {
fetchAllCustomers,
fetchAllSubsidiaries,
fetchCustomerTypes,
} from "@/app/api/customer";
import { fetchGrades } from "@/app/api/grades";
import {
fetchProjectBuildingTypes,
@@ -16,6 +20,7 @@ import CreateProject from "@/components/CreateProject";
import { I18nProvider, getServerI18n } from "@/i18n";
import { MAINTAIN_PROJECT } from "@/middleware";
import Typography from "@mui/material/Typography";
import isString from "lodash/isString";
import { Metadata } from "next";
import { notFound } from "next/navigation";

@@ -23,7 +28,11 @@ export const metadata: Metadata = {
title: "Create Project",
};

const Projects: React.FC = async () => {
interface Props {
searchParams: { [key: string]: string | string[] | undefined };
}

const Projects: React.FC<Props> = async ({ searchParams }) => {
const { t } = await getServerI18n("projects");

const abilities = await fetchUserAbilities();
@@ -32,6 +41,10 @@ const Projects: React.FC = async () => {
notFound();
}

const draftId = isString(searchParams["draftId"])
? parseInt(searchParams["draftId"])
: undefined;

// Preload necessary dependencies
fetchAllTasks();
fetchTaskTemplates();
@@ -53,7 +66,7 @@ const Projects: React.FC = async () => {
<>
<Typography variant="h4">{t("Create Project")}</Typography>
<I18nProvider namespaces={["projects"]}>
<CreateProject isEditMode={false} />
<CreateProject isEditMode={false} draftId={draftId} />
</I18nProvider>
</>
);


+ 48
- 39
src/app/(main)/projects/page.tsx Переглянути файл

@@ -1,8 +1,10 @@
import { fetchProjectCategories, fetchProjects, preloadProjects } from "@/app/api/projects";
import { fetchAllCustomers } from "@/app/api/customer";
import { fetchProjectCategories, fetchProjects } from "@/app/api/projects";
import { fetchTeam } from "@/app/api/team";
import { fetchUserAbilities } from "@/app/utils/fetchUtil";
import ProjectSearch from "@/components/ProjectSearch";
import { getServerI18n, I18nProvider } from "@/i18n";
import { MAINTAIN_PROJECT, VIEW_PROJECT } from "@/middleware";
import { MAINTAIN_PROJECT } from "@/middleware";
import Add from "@mui/icons-material/Add";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
@@ -18,55 +20,62 @@ export const metadata: Metadata = {

const Projects: React.FC = async () => {
const { t } = await getServerI18n("projects");
// preloadProjects();
fetchProjectCategories();
fetchTeam();
fetchAllCustomers();
const projects = await fetchProjects();
const abilities = await fetchUserAbilities()
if (![MAINTAIN_PROJECT].some(ability => abilities.includes(ability))) {
const abilities = await fetchUserAbilities();
if (![MAINTAIN_PROJECT].some((ability) => abilities.includes(ability))) {
notFound();
}

return (
<>
<I18nProvider namespaces={["projects","common"]}>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Typography variant="h4" marginInlineEnd={2}>
{t("Project Management")}
</Typography>
{abilities.includes(MAINTAIN_PROJECT) && <Stack
<I18nProvider namespaces={["projects", "common"]}>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
spacing={1}
>
{projects.filter(project => project.status.toLowerCase() !== "deleted").length > 0 && <Button
variant="contained"
color="secondary"
startIcon={<Add />}
LinkComponent={Link}
href="/projects/createSub"
>
{t("Create Sub Project")}
</Button>}
<Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/projects/create"
>
{t("Create Project")}
</Button>
</Stack >}
</Stack>
<Suspense fallback={<ProjectSearch.Loading />}>
<ProjectSearch />
</Suspense>
<Typography variant="h4" marginInlineEnd={2}>
{t("Project Management")}
</Typography>
{abilities.includes(MAINTAIN_PROJECT) && (
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
spacing={1}
>
{projects.filter(
(project) => project.status.toLowerCase() !== "deleted",
).length > 0 && (
<Button
variant="contained"
color="secondary"
startIcon={<Add />}
LinkComponent={Link}
href="/projects/createSub"
>
{t("Create Sub Project")}
</Button>
)}
<Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/projects/create"
>
{t("Create Project")}
</Button>
</Stack>
)}
</Stack>
<Suspense fallback={<ProjectSearch.Loading />}>
<ProjectSearch />
</Suspense>
</I18nProvider>
</>
);


+ 55
- 0
src/app/utils/draftUtils.ts Переглянути файл

@@ -0,0 +1,55 @@
import { CreateProjectInputs } from "../api/projects/actions";

const STORAGE_KEY = "draftProjects";

const getStorage = (): {
[draftId: string]: CreateProjectInputs;
} => {
if (typeof window === "undefined") {
return {};
}

const storageString = localStorage.getItem(STORAGE_KEY);
if (!storageString) {
return {};
}

try {
return JSON.parse(storageString);
} catch {
return {};
}
};

export const loadDrafts = (): [id: string, CreateProjectInputs][] => {
return Object.entries(getStorage());
};

export const saveToLocalStorage = (
draftId: number,
data: CreateProjectInputs,
) => {
const storage = getStorage();
localStorage.setItem(
STORAGE_KEY,
JSON.stringify({
...storage,
[draftId]: data,
}),
);
};

export const loadDraft = (draftId: number): CreateProjectInputs | undefined => {
const storage = getStorage();
const draft = storage[draftId];

return draft;
};

export const deleteDraft = (draftId: number) => {
const storage = getStorage();

delete storage[draftId];

localStorage.setItem(STORAGE_KEY, JSON.stringify(storage));
};

+ 144
- 115
src/components/CreateProject/CreateProject.tsx Переглянути файл

@@ -4,12 +4,18 @@ import AutorenewIcon from "@mui/icons-material/Autorenew";
import DoneIcon from "@mui/icons-material/Done";
import Check from "@mui/icons-material/Check";
import Close from "@mui/icons-material/Close";
import Button from "@mui/material/Button";
import Button, { ButtonProps } from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Tab from "@mui/material/Tab";
import Tabs, { TabsProps } from "@mui/material/Tabs";
import { useRouter } from "next/navigation";
import React, { useCallback, useEffect, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useTranslation } from "react-i18next";
import ProjectClientDetails from "./ProjectClientDetails";
import TaskSetup from "./TaskSetup";
@@ -28,7 +34,7 @@ import {
deleteProject,
saveProject,
} from "@/app/api/projects/actions";
import { Delete, Error, PlayArrow } from "@mui/icons-material";
import { Delete, EditNote, Error, PlayArrow } from "@mui/icons-material";
import {
BuildingType,
ContractType,
@@ -40,7 +46,7 @@ import {
WorkNature,
} from "@/app/api/projects";
import { StaffResult } from "@/app/api/staff";
import { Grid, Typography } from "@mui/material";
import { Box, Grid, Typography } from "@mui/material";
import { Grade } from "@/app/api/grades";
import { Customer, CustomerType, Subsidiary } from "@/app/api/customer";
import { isEmpty } from "lodash";
@@ -54,9 +60,11 @@ import {
import dayjs from "dayjs";
import { DELETE_PROJECT } from "@/middleware";
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { deleteDraft, loadDraft, saveToLocalStorage } from "@/app/utils/draftUtils";

export interface Props {
isEditMode: boolean;
draftId?: number;
isSubProject: boolean;
mainProjects?: MainProject[];
defaultInputs?: CreateProjectInputs;
@@ -106,6 +114,7 @@ const hasErrorsInTab = (

const CreateProject: React.FC<Props> = ({
isEditMode,
draftId,
isSubProject,
mainProjects,
defaultInputs,
@@ -127,11 +136,46 @@ const CreateProject: React.FC<Props> = ({
abilities,
}) => {
const [serverError, setServerError] = useState("");
const [loading, setLoading] = useState(true);
const [tabIndex, setTabIndex] = useState(0);
const { t } = useTranslation();
const router = useRouter();

const formProps = useForm<CreateProjectInputs>({
defaultValues: {
taskGroups: {},
allocatedStaffIds: [],
milestones: {},
totalManhour: 0,
taskTemplateId: "All",
projectName:
mainProjects !== undefined ? mainProjects[0].projectName : undefined,
projectDescription:
mainProjects !== undefined
? mainProjects[0].projectDescription
: undefined,
expectedProjectFee:
mainProjects !== undefined
? mainProjects[0].expectedProjectFee
: undefined,
subContractFee:
mainProjects !== undefined ? mainProjects[0].subContractFee : undefined,
clientId: allCustomers !== undefined ? allCustomers[0].id : undefined,
ratePerManhour: 250,
...defaultInputs,

// manhourPercentageByGrade should have a sensible default
manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade)
? grades.reduce((acc, grade) => {
return { ...acc, [grade.id]: 100 / grades.length };
}, {})
: defaultInputs?.manhourPercentageByGrade,
},
});

const projectName = formProps.watch("projectName");
const projectDeleted = formProps.watch("projectDeleted");
const projectStatus = formProps.watch("projectStatus") || "";

const defaultBtn = {
buttonName: "submit",
title: t("Do you want to submit?"),
@@ -139,31 +183,64 @@ const CreateProject: React.FC<Props> = ({
successTitle: t("Submit Success"),
errorTitle: t("Submit Fail"),
};
const [buttonData, setButtonData] = useState<{
const buttonData = useMemo<{
buttonName: string;
title: string;
confirmButtonText: string;
successTitle: string;
errorTitle: string;
buttonText: string;
buttonIcon: any;
buttonColor: any;
}>({
...defaultBtn,
buttonText: t("Submit Project"),
buttonIcon: <Check />,
buttonColor: "success",
});

const disableChecking = () => {
return (
loading ||
formProps.getValues("projectDeleted") === true ||
formProps.getValues("projectStatus")?.toLowerCase() === "deleted" ||
// !!formProps.getValues("projectActualStart") &&
!!(formProps.getValues("projectStatus")?.toLowerCase() === "completed")
);
};
buttonIcon: React.ReactNode;
buttonColor: ButtonProps["color"];
}>(() => {
//Button Parameters//
switch (projectStatus) {
case "pending to start":
return {
buttonName: "start",
title: t("Do you want to start?"),
confirmButtonText: t("Start"),
successTitle: t("Start Success"),
errorTitle: t("Start Fail"),
buttonText: t("Start Project"),
buttonIcon: <PlayArrow />,
buttonColor: "success",
};
case "on-going":
return {
buttonName: "complete",
title: t("Do you want to complete?"),
confirmButtonText: t("Complete"),
successTitle: t("Complete Success"),
errorTitle: t("Complete Fail"),
buttonText: t("Complete Project"),
buttonIcon: <DoneIcon />,
buttonColor: "info",
};
case "completed":
return {
buttonName: "reopen",
title: t("Do you want to reopen?"),
confirmButtonText: t("Reopen"),
successTitle: t("Reopen Success"),
errorTitle: t("Reopen Fail"),
buttonText: t("Reopen Project"),
buttonIcon: <AutorenewIcon />,
buttonColor: "secondary",
};
default:
return {
buttonName: "submit",
title: t("Do you want to submit?"),
confirmButtonText: t("Submit"),
successTitle: t("Submit Success"),
errorTitle: t("Submit Fail"),
buttonText: t("Submit Project"),
buttonIcon: <Check />,
buttonColor: "success",
};
}
}, [projectStatus, t]);

const handleCancel = () => {
router.replace("/projects");
@@ -333,6 +410,9 @@ const CreateProject: React.FC<Props> = ({
: buttonData.successTitle,
t,
).then(() => {
if (draftId) {
deleteDraft(draftId);
}
router.replace("/projects");
});
} else {
@@ -408,58 +488,29 @@ const CreateProject: React.FC<Props> = ({
[],
);

const formProps = useForm<CreateProjectInputs>({
defaultValues: {
taskGroups: {},
allocatedStaffIds: [],
milestones: {},
totalManhour: 0,
taskTemplateId: "All",
projectName:
mainProjects !== undefined ? mainProjects[0].projectName : undefined,
projectDescription:
mainProjects !== undefined
? mainProjects[0].projectDescription
: undefined,
expectedProjectFee:
mainProjects !== undefined
? mainProjects[0].expectedProjectFee
: undefined,
subContractFee:
mainProjects !== undefined ? mainProjects[0].subContractFee : undefined,
clientId: allCustomers !== undefined ? allCustomers[0].id : undefined,
ratePerManhour: 250,
...defaultInputs,

// manhourPercentageByGrade should have a sensible default
manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade)
? grades.reduce((acc, grade) => {
return { ...acc, [grade.id]: 100 / grades.length };
}, {})
: defaultInputs?.manhourPercentageByGrade,
},
});

const errors = formProps.formState.errors;

// auto calculate the total project manhour
const expectedProjectFee = formProps.watch("expectedProjectFee");
const ratePerManhour = formProps.watch("ratePerManhour");
const totalManhour = formProps.watch("totalManhour");
const [firstLoaded, setFirstLoaded] = useState(false);
React.useMemo(() => {
if (firstLoaded && expectedProjectFee > 0 && ratePerManhour > 0) {
console.log(ratePerManhour, formProps.watch("totalManhour"));
const firstLoadedRef = useRef(false);
useEffect(() => {
if (
firstLoadedRef.current &&
expectedProjectFee > 0 &&
ratePerManhour > 0
) {
formProps.setValue(
"totalManhour",
Math.ceil(expectedProjectFee / ratePerManhour),
);
} else {
setFirstLoaded(true);
firstLoadedRef.current = true;
}
}, [expectedProjectFee, ratePerManhour]);

React.useMemo(() => {
useEffect(() => {
if (
expectedProjectFee > 0 &&
ratePerManhour > 0 &&
@@ -472,57 +523,24 @@ const CreateProject: React.FC<Props> = ({
}
}, [totalManhour]);

const updateButtonData = () => {
const status = formProps.getValues("projectStatus")?.toLowerCase();
const loading = isEditMode ? !Boolean(projectName) : false;

//Button Parameters//
switch (status) {
case "pending to start":
setButtonData({
buttonName: "start",
title: t("Do you want to start?"),
confirmButtonText: t("Start"),
successTitle: t("Start Success"),
errorTitle: t("Start Fail"),
buttonText: t("Start Project"),
buttonIcon: <PlayArrow />,
buttonColor: "success",
});
break;
case "on-going":
setButtonData({
buttonName: "complete",
title: t("Do you want to complete?"),
confirmButtonText: t("Complete"),
successTitle: t("Complete Success"),
errorTitle: t("Complete Fail"),
buttonText: t("Complete Project"),
buttonIcon: <DoneIcon />,
buttonColor: "info",
});
break;
case "completed":
setButtonData({
buttonName: "reopen",
title: t("Do you want to reopen?"),
confirmButtonText: t("Reopen"),
successTitle: t("Reopen Success"),
errorTitle: t("Reopen Fail"),
buttonText: t("Reopen Project"),
buttonIcon: <AutorenewIcon />,
buttonColor: "secondary",
});
}
};
const submitDisabled =
loading ||
projectDeleted === true ||
projectStatus.toLowerCase() === "deleted" ||
// !!formProps.getValues("projectActualStart") &&
!!(projectStatus.toLowerCase() === "completed");

useEffect(() => {
if (!isEditMode) {
setLoading(false);
} else if (formProps?.getValues("projectName")) {
setLoading(false);
updateButtonData();
}
}, [formProps]);
const draftInputs = draftId ? loadDraft(draftId) : undefined;
formProps.reset(draftInputs);
}, [draftId, formProps]);

const saveDraft = useCallback(() => {
saveToLocalStorage(draftId || Date.now(), formProps.getValues());
router.replace("/projects");
}, [draftId, formProps, router]);

return (
<>
@@ -577,10 +595,8 @@ const CreateProject: React.FC<Props> = ({
// formProps.getValues("projectActualStart") &&
// formProps.getValues("projectActualEnd")
(
formProps.getValues("projectStatus")?.toLowerCase() ===
"completed" ||
formProps.getValues("projectStatus")?.toLowerCase() ===
"deleted"
projectStatus.toLowerCase() === "completed" ||
projectStatus.toLowerCase() === "deleted"
)
) &&
abilities.includes(DELETE_PROJECT) && (
@@ -694,6 +710,19 @@ const CreateProject: React.FC<Props> = ({
</Typography>
)}
<Stack direction="row" justifyContent="flex-end" gap={1}>
{!isEditMode && (
<>
<Button
variant="outlined"
color="secondary"
startIcon={<EditNote />}
onClick={saveDraft}
>
{t("Save Draft")}
</Button>
<Box sx={{ flex: 1, pointerEvents: "none" }} />
</>
)}
<Button
variant="outlined"
startIcon={<Close />}
@@ -706,7 +735,7 @@ const CreateProject: React.FC<Props> = ({
variant="contained"
startIcon={<Check />}
type="submit"
disabled={disableChecking()}
disabled={submitDisabled}
>
{isEditMode ? t("Save") : t("Confirm")}
</Button>


+ 7
- 4
src/components/CreateProject/CreateProjectWrapper.tsx Переглянути файл

@@ -23,6 +23,7 @@ import { fetchUserAbilities, fetchUserStaff } from "@/app/utils/fetchUtil";
type CreateProjectProps = {
isEditMode: false;
isSubProject?: boolean;
draftId?: number;
};
interface EditProjectProps {
isEditMode: true;
@@ -68,9 +69,11 @@ const CreateProjectWrapper: React.FC<Props> = async (props) => {
fetchCustomerTypes(),
fetchUserAbilities(),
]);
const userStaff = await fetchUserStaff()
const teamId = userStaff?.teamId
const filteredTeamLeads = teamLeads.filter(teamLead => teamLead.teamId === teamId)
const userStaff = await fetchUserStaff();
const teamId = userStaff?.teamId;
const filteredTeamLeads = teamLeads.filter(
(teamLead) => teamLead.teamId === teamId,
);
const projectInfo = props.isEditMode
? await fetchProjectDetails(props.projectId!)
: undefined;
@@ -79,10 +82,10 @@ const CreateProjectWrapper: React.FC<Props> = async (props) => {
? await fetchMainProjects()
: undefined;

console.log(projectInfo)
return (
<CreateProject
isEditMode={props.isEditMode}
draftId={props.isEditMode ? undefined : props.draftId}
isSubProject={Boolean(props.isSubProject)}
defaultInputs={projectInfo}
allTasks={tasks}


+ 2
- 2
src/components/CreateProject/ProjectClientDetails.tsx Переглянути файл

@@ -121,8 +121,8 @@ const ProjectClientDetails: React.FC<Props> = ({
if (selectedCustomerId !== undefined) {
fetchCustomer(selectedCustomerId).then(
({ contacts, subsidiaryIds, customer }) => {
console.log(contacts)
console.log(subsidiaryIds)
// console.log(contacts)
// console.log(subsidiaryIds)
setCustomerContacts(contacts);
setCustomerSubsidiaryIds(subsidiaryIds);
setValue(


+ 68
- 9
src/components/ProjectSearch/ProjectSearch.tsx Переглянути файл

@@ -1,7 +1,7 @@
"use client";

import { ProjectCategory, ProjectResult } from "@/app/api/projects";
import React, { useCallback, useMemo, useState } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
@@ -9,23 +9,73 @@ import EditNote from "@mui/icons-material/EditNote";
import uniq from "lodash/uniq";
import { useRouter } from "next/navigation";
import { MAINTAIN_PROJECT } from "@/middleware";
import { uniqBy } from "lodash";
import { reverse, uniqBy } from "lodash";
import { loadDrafts } from "@/app/utils/draftUtils";
import { TeamResult } from "@/app/api/team";
import { Customer } from "@/app/api/customer";

type ProjectResultOrDraft = ProjectResult & { isDraft?: boolean };

interface Props {
projects: ProjectResult[];
projectCategories: ProjectCategory[];
abilities: string[]
abilities: string[];
teams: TeamResult[];
customers: Customer[];
}

type SearchQuery = Partial<Omit<ProjectResult, "id">>;
type SearchParamNames = keyof SearchQuery;

const ProjectSearch: React.FC<Props> = ({ projects, projectCategories, abilities }) => {
const ProjectSearch: React.FC<Props> = ({
projects,
projectCategories,
abilities,
teams,
customers,
}) => {
const router = useRouter();
const { t } = useTranslation("projects");

const [draftProjects, setDraftProjects] = useState<ProjectResultOrDraft[]>(
[],
);

useEffect(() => {
const drafts = reverse(loadDrafts());
setDraftProjects(
drafts.map(([id, inputs]) => {
const team = teams.find(
(team) => team.teamLead === inputs.projectLeadId,
);
return {
isDraft: true,
id: parseInt(id),
code: inputs.projectCode || "",
name: inputs.projectName || t("Draft Project"),
category:
projectCategories.find((cat) => cat.id === inputs.projectCategoryId)
?.name || "",
team: team?.code || "",
client:
customers.find((customer) => customer.id === inputs.clientId)
?.name || "",
status: t("Draft"),
teamCodeName: team?.code || "",
teamId: team?.teamId || 0,
mainProject: "",
};
}),
);
}, [projectCategories, t, teams, customers]);

const [filteredProjects, setFilteredProjects] = useState(projects);

const draftAndFilterdProjects = useMemo<ProjectResultOrDraft[]>(
() => [...draftProjects, ...filteredProjects],
[draftProjects, filteredProjects],
);

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Project Code"), paramName: "code", type: "text" },
@@ -34,7 +84,13 @@ const ProjectSearch: React.FC<Props> = ({ projects, projectCategories, abilities
label: t("Client Name"),
paramName: "client",
type: "autocomplete",
options: uniqBy(projects.map((project) => ({value: project.client, label: project.client})), "value").sort((a, b) => a.value >= b.value ? 1 : -1),
options: uniqBy(
projects.map((project) => ({
value: project.client,
label: project.client,
})),
"value",
).sort((a, b) => (a.value >= b.value ? 1 : -1)),
},
{
label: t("Project Category"),
@@ -63,8 +119,10 @@ const ProjectSearch: React.FC<Props> = ({ projects, projectCategories, abilities
}, [projects]);

const onProjectClick = useCallback(
(project: ProjectResult) => {
if (Boolean(project.mainProject)) {
(project: ProjectResultOrDraft) => {
if (project.isDraft && project.id) {
router.push(`/projects/create?draftId=${project.id}`);
} else if (Boolean(project.mainProject)) {
router.push(`/projects/editSub?id=${project.id}`);
} else router.push(`/projects/edit?id=${project.id}`);
},
@@ -103,7 +161,8 @@ const ProjectSearch: React.FC<Props> = ({ projects, projectCategories, abilities
(query.client === "All" || p.client === query.client) &&
(query.category === "All" || p.category === query.category) &&
// (query.team === "All" || p.team === query.team) &&
(query.team === "All" || query.team.toLowerCase().includes(p.team.toLowerCase())) &&
(query.team === "All" ||
query.team.toLowerCase().includes(p.team.toLowerCase())) &&
(query.status === "All" || p.status === query.status),
),
);
@@ -111,7 +170,7 @@ const ProjectSearch: React.FC<Props> = ({ projects, projectCategories, abilities
onReset={onReset}
/>
<SearchResults<ProjectResult>
items={filteredProjects}
items={draftAndFilterdProjects}
columns={columns}
/>
</>


+ 21
- 8
src/components/ProjectSearch/ProjectSearchWrapper.tsx Переглянути файл

@@ -6,6 +6,8 @@ import { fetchUserAbilities, fetchUserStaff } from "@/app/utils/fetchUtil";
import { authOptions } from "@/config/authConfig";
import { getServerSession } from "next-auth";
import { VIEW_ALL_PROJECTS } from "@/middleware";
import { fetchTeam } from "@/app/api/team";
import { fetchAllCustomers } from "@/app/api/customer";

interface SubComponents {
Loading: typeof ProjectSearchLoading;
@@ -13,20 +15,31 @@ interface SubComponents {

const ProjectSearchWrapper: React.FC & SubComponents = async () => {
const projectCategories = await fetchProjectCategories();
const userStaff = await fetchUserStaff()
const teamId = userStaff?.teamId
const userStaff = await fetchUserStaff();
const teamId = userStaff?.teamId;
const projects = await fetchProjects();
const teams = await fetchTeam();
const customers = await fetchAllCustomers();

const abilities = await fetchUserAbilities()
const isViewAllProjectRight = [VIEW_ALL_PROJECTS].some((ability) => abilities.includes(ability))
const abilities = await fetchUserAbilities();
const isViewAllProjectRight = [VIEW_ALL_PROJECTS].some((ability) =>
abilities.includes(ability),
);


let filteredProjects = projects
let filteredProjects = projects;
if (!isViewAllProjectRight) {
filteredProjects = projects.filter(project => project.teamId === teamId)
filteredProjects = projects.filter((project) => project.teamId === teamId);
}

return <ProjectSearch projects={filteredProjects} projectCategories={projectCategories} abilities={abilities}/>;
return (
<ProjectSearch
projects={filteredProjects}
projectCategories={projectCategories}
abilities={abilities}
teams={teams}
customers={customers}
/>
);
};

ProjectSearchWrapper.Loading = ProjectSearchLoading;


Завантаження…
Відмінити
Зберегти