Pārlūkot izejas kodu

Add timehseet API

tags/Baseline_30082024_FRONTEND_UAT
Wayne pirms 1 gada
vecāks
revīzija
795b67d686
13 mainītis faili ar 251 papildinājumiem un 102 dzēšanām
  1. +10
    -1
      src/app/(main)/home/page.tsx
  2. +4
    -4
      src/app/(main)/layout.tsx
  3. +22
    -14
      src/app/api/projects/actions.ts
  4. +22
    -0
      src/app/api/timesheets/actions.ts
  5. +10
    -0
      src/app/api/timesheets/index.ts
  6. +100
    -52
      src/components/CreateProject/CreateProject.tsx
  7. +1
    -1
      src/components/CreateProject/CreateProjectWrapper.tsx
  8. +39
    -12
      src/components/TimesheetModal/TimesheetModal.tsx
  9. +6
    -7
      src/components/TimesheetTable/EntryInputTable.tsx
  10. +5
    -3
      src/components/UserWorkspacePage/AssignedProjects.tsx
  11. +11
    -1
      src/components/UserWorkspacePage/UserWorkspacePage.tsx
  12. +18
    -3
      src/components/UserWorkspacePage/UserWorkspaceWrapper.tsx
  13. +3
    -4
      src/config/authConfig.ts

+ 10
- 1
src/app/(main)/home/page.tsx Parādīt failu

@@ -1,15 +1,24 @@
import { Metadata } from "next"; import { Metadata } from "next";
import { I18nProvider } from "@/i18n"; import { I18nProvider } from "@/i18n";
import UserWorkspacePage from "@/components/UserWorkspacePage"; import UserWorkspacePage from "@/components/UserWorkspacePage";
import { fetchTimesheets } from "@/app/api/timesheets";
import { authOptions } from "@/config/authConfig";
import { getServerSession } from "next-auth";


export const metadata: Metadata = { export const metadata: Metadata = {
title: "User Workspace", title: "User Workspace",
}; };


const Home: React.FC = async () => { const Home: React.FC = async () => {
const session = await getServerSession(authOptions);
// Get name for caching
const username = session!.user!.name!;

await fetchTimesheets(username);

return ( return (
<I18nProvider namespaces={["home"]}> <I18nProvider namespaces={["home"]}>
<UserWorkspacePage />
<UserWorkspacePage username={username} />
</I18nProvider> </I18nProvider>
); );
}; };


+ 4
- 4
src/app/(main)/layout.tsx Parādīt failu

@@ -31,10 +31,10 @@ export default async function MainLayout({
padding: { xs: "1rem", sm: "1.5rem", lg: "3rem" }, padding: { xs: "1rem", sm: "1.5rem", lg: "3rem" },
}} }}
> >
<Stack spacing={2}>
<Breadcrumb />
{children}
</Stack>
<Stack spacing={2}>
<Breadcrumb />
{children}
</Stack>
</Box> </Box>
</> </>
); );


+ 22
- 14
src/app/api/projects/actions.ts Parādīt failu

@@ -1,13 +1,16 @@
"use server"; "use server";


import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import {
serverFetchJson,
serverFetchWithNoContent,
} from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api"; import { BASE_API_URL } from "@/config/api";
import { Task, TaskGroup } from "../tasks"; import { Task, TaskGroup } from "../tasks";
import { Customer } from "../customer"; import { Customer } from "../customer";
import { revalidateTag } from "next/cache"; import { revalidateTag } from "next/cache";


export interface CreateProjectInputs { export interface CreateProjectInputs {
// Project
// Project
projectId: number | null; projectId: number | null;
projectDeleted: boolean | null; projectDeleted: boolean | null;
projectCode: string; projectCode: string;
@@ -67,19 +70,22 @@ export interface PaymentInputs {
} }


export interface CreateProjectResponse { export interface CreateProjectResponse {
id: number,
name: string,
code: string,
category: string,
team: string,
client: string,
id: number;
name: string;
code: string;
category: string;
team: string;
client: string;
} }
export const saveProject = async (data: CreateProjectInputs) => { export const saveProject = async (data: CreateProjectInputs) => {
const newProject = await serverFetchJson<CreateProjectResponse>(`${BASE_API_URL}/projects/new`, {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
const newProject = await serverFetchJson<CreateProjectResponse>(
`${BASE_API_URL}/projects/new`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);


revalidateTag("projects"); revalidateTag("projects");
return newProject; return newProject;
@@ -94,5 +100,7 @@ export const deleteProject = async (id: number) => {
}, },
); );


return project
revalidateTag("projects");
revalidateTag("assignedProjects");
return project;
}; };

+ 22
- 0
src/app/api/timesheets/actions.ts Parādīt failu

@@ -1,9 +1,13 @@
"use server"; "use server";


import { serverFetchJson } from "@/app/utils/fetchUtil";
import { ProjectResult } from "../projects"; import { ProjectResult } from "../projects";
import { Task, TaskGroup } from "../tasks"; import { Task, TaskGroup } from "../tasks";
import { BASE_API_URL } from "@/config/api";
import { revalidateTag } from "next/cache";


export interface TimeEntry { export interface TimeEntry {
id: number;
projectId: ProjectResult["id"]; projectId: ProjectResult["id"];
taskGroupId: TaskGroup["id"]; taskGroupId: TaskGroup["id"];
taskId: Task["id"]; taskId: Task["id"];
@@ -13,3 +17,21 @@ export interface TimeEntry {
export interface RecordTimesheetInput { export interface RecordTimesheetInput {
[date: string]: TimeEntry[]; [date: string]: TimeEntry[];
} }

export const saveTimesheet = async (
data: RecordTimesheetInput,
username: string,
) => {
const savedRecords = await serverFetchJson<RecordTimesheetInput>(
`${BASE_API_URL}/timesheets/save`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);

revalidateTag(`timesheets_${username}`);

return savedRecords;
};

+ 10
- 0
src/app/api/timesheets/index.ts Parādīt failu

@@ -0,0 +1,10 @@
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import { RecordTimesheetInput } from "./actions";

export const fetchTimesheets = cache(async (username: string) => {
return serverFetchJson<RecordTimesheetInput>(`${BASE_API_URL}/timesheets`, {
next: { tags: [`timesheets_${username}`] },
});
});

+ 100
- 52
src/components/CreateProject/CreateProject.tsx Parādīt failu

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


import DoneIcon from '@mui/icons-material/Done'
import DoneIcon from "@mui/icons-material/Done";
import Check from "@mui/icons-material/Check"; import Check from "@mui/icons-material/Check";
import Close from "@mui/icons-material/Close"; import Close from "@mui/icons-material/Close";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@@ -22,7 +22,11 @@ import {
SubmitHandler, SubmitHandler,
useForm, useForm,
} from "react-hook-form"; } from "react-hook-form";
import { CreateProjectInputs, deleteProject, saveProject } from "@/app/api/projects/actions";
import {
CreateProjectInputs,
deleteProject,
saveProject,
} from "@/app/api/projects/actions";
import { Delete, Error, PlayArrow } from "@mui/icons-material"; import { Delete, Error, PlayArrow } from "@mui/icons-material";
import { import {
BuildingType, BuildingType,
@@ -38,7 +42,12 @@ import { Typography } from "@mui/material";
import { Grade } from "@/app/api/grades"; import { Grade } from "@/app/api/grades";
import { Customer, Subsidiary } from "@/app/api/customer"; import { Customer, Subsidiary } from "@/app/api/customer";
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import { deleteDialog, errorDialog, submitDialog, successDialog } from "../Swal/CustomAlerts";
import {
deleteDialog,
errorDialog,
submitDialog,
successDialog,
} from "../Swal/CustomAlerts";
import dayjs from "dayjs"; import dayjs from "dayjs";


export interface Props { export interface Props {
@@ -103,16 +112,15 @@ const CreateProject: React.FC<Props> = ({


const handleDelete = () => { const handleDelete = () => {
deleteDialog(async () => { deleteDialog(async () => {
await deleteProject(formProps.getValues("projectId")!!)
await deleteProject(formProps.getValues("projectId")!);


const clickSuccessDialog = await successDialog("Delete Success", t)
const clickSuccessDialog = await successDialog("Delete Success", t);


if (clickSuccessDialog) { if (clickSuccessDialog) {
router.replace("/projects"); router.replace("/projects");
} }

}, t)
}
}, t);
};


const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => { (_e, newValue) => {
@@ -124,51 +132,55 @@ const CreateProject: React.FC<Props> = ({
const onSubmit = useCallback<SubmitHandler<CreateProjectInputs>>( const onSubmit = useCallback<SubmitHandler<CreateProjectInputs>>(
async (data, event) => { async (data, event) => {
try { try {
console.log("first")
console.log("first");
setServerError(""); setServerError("");


let title = t("Do you want to submit?")
let confirmButtonText = t("Submit")
let successTitle = t("Submit Success")
let errorTitle = t("Submit Fail")
const buttonName = (event?.nativeEvent as any).submitter.name
let title = t("Do you want to submit?");
let confirmButtonText = t("Submit");
let successTitle = t("Submit Success");
let errorTitle = t("Submit Fail");
const buttonName = (event?.nativeEvent as any).submitter.name;


if (buttonName === "start") { if (buttonName === "start") {
title = t("Do you want to start?")
confirmButtonText = t("Start")
successTitle = t("Start Success")
errorTitle = t("Start Fail")
title = t("Do you want to start?");
confirmButtonText = t("Start");
successTitle = t("Start Success");
errorTitle = t("Start Fail");
} else if (buttonName === "complete") { } else if (buttonName === "complete") {
title = t("Do you want to complete?")
confirmButtonText = t("Complete")
successTitle = t("Complete Success")
errorTitle = t("Complete Fail")
title = t("Do you want to complete?");
confirmButtonText = t("Complete");
successTitle = t("Complete Success");
errorTitle = t("Complete Fail");
} }


submitDialog(async () => {
if (buttonName === "start") {
data.projectActualStart = dayjs().format("YYYY-MM-DD")
} else if (buttonName === "complete") {
data.projectActualEnd = dayjs().format("YYYY-MM-DD")
}
submitDialog(
async () => {
if (buttonName === "start") {
data.projectActualStart = dayjs().format("YYYY-MM-DD");
} else if (buttonName === "complete") {
data.projectActualEnd = dayjs().format("YYYY-MM-DD");
}


const response = await saveProject(data);
const response = await saveProject(data);


if (response.id > 0) {
successDialog(successTitle, t).then(() => {
router.replace("/projects");
})
} else {
errorDialog(errorTitle, t).then(() => {
return false
})
}
}, t, { title: title, confirmButtonText: confirmButtonText })
if (response.id > 0) {
successDialog(successTitle, t).then(() => {
router.replace("/projects");
});
} else {
errorDialog(errorTitle, t).then(() => {
return false;
});
}
},
t,
{ title: title, confirmButtonText: confirmButtonText },
);
} catch (e) { } catch (e) {
setServerError(t("An error has occurred. Please try again later.")); setServerError(t("An error has occurred. Please try again later."));
} }
}, },
[router, t, isEditMode],
[router, t],
); );


const onSubmitError = useCallback<SubmitErrorHandler<CreateProjectInputs>>( const onSubmitError = useCallback<SubmitErrorHandler<CreateProjectInputs>>(
@@ -196,8 +208,8 @@ const CreateProject: React.FC<Props> = ({
// manhourPercentageByGrade should have a sensible default // manhourPercentageByGrade should have a sensible default
manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade) manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade)
? grades.reduce((acc, grade) => { ? grades.reduce((acc, grade) => {
return { ...acc, [grade.id]: 1 / grades.length };
}, {})
return { ...acc, [grade.id]: 1 / grades.length };
}, {})
: defaultInputs?.manhourPercentageByGrade, : defaultInputs?.manhourPercentageByGrade,
}, },
}); });
@@ -214,15 +226,42 @@ const CreateProject: React.FC<Props> = ({
> >
{isEditMode && !(formProps.getValues("projectDeleted") === true) && ( {isEditMode && !(formProps.getValues("projectDeleted") === true) && (
<Stack direction="row" gap={1}> <Stack direction="row" gap={1}>
{!formProps.getValues("projectActualStart") && <Button name="start" type="submit" variant="contained" startIcon={<PlayArrow />} color="success">
{t("Start Project")}
</Button>}
{formProps.getValues("projectActualStart") && !formProps.getValues("projectActualEnd") && <Button name="complete" type="submit" variant="contained" startIcon={<DoneIcon />} color="info">
{t("Complete Project")}
</Button>}
{!(formProps.getValues("projectActualStart") && formProps.getValues("projectActualEnd")) && <Button variant="outlined" startIcon={<Delete />} color="error" onClick={handleDelete}>
{t("Delete Project")}
</Button>}
{!formProps.getValues("projectActualStart") && (
<Button
name="start"
type="submit"
variant="contained"
startIcon={<PlayArrow />}
color="success"
>
{t("Start Project")}
</Button>
)}
{formProps.getValues("projectActualStart") &&
!formProps.getValues("projectActualEnd") && (
<Button
name="complete"
type="submit"
variant="contained"
startIcon={<DoneIcon />}
color="info"
>
{t("Complete Project")}
</Button>
)}
{!(
formProps.getValues("projectActualStart") &&
formProps.getValues("projectActualEnd")
) && (
<Button
variant="outlined"
startIcon={<Delete />}
color="error"
onClick={handleDelete}
>
{t("Delete Project")}
</Button>
)}
</Stack> </Stack>
)} )}
<Tabs <Tabs
@@ -290,7 +329,16 @@ const CreateProject: React.FC<Props> = ({
> >
{t("Cancel")} {t("Cancel")}
</Button> </Button>
<Button variant="contained" startIcon={<Check />} type="submit" disabled={formProps.getValues("projectDeleted") === true || (!!formProps.getValues("projectActualStart") && !!formProps.getValues("projectActualEnd"))}>
<Button
variant="contained"
startIcon={<Check />}
type="submit"
disabled={
formProps.getValues("projectDeleted") === true ||
(!!formProps.getValues("projectActualStart") &&
!!formProps.getValues("projectActualEnd"))
}
>
{isEditMode ? t("Save") : t("Confirm")} {isEditMode ? t("Save") : t("Confirm")}
</Button> </Button>
</Stack> </Stack>


+ 1
- 1
src/components/CreateProject/CreateProjectWrapper.tsx Parādīt failu

@@ -56,7 +56,7 @@ const CreateProjectWrapper: React.FC<Props> = async (props) => {
]); ]);


const projectInfo = props.isEditMode const projectInfo = props.isEditMode
? await fetchProjectDetails(props.projectId!!)
? await fetchProjectDetails(props.projectId!)
: undefined; : undefined;


return ( return (


+ 39
- 12
src/components/TimesheetModal/TimesheetModal.tsx Parādīt failu

@@ -12,8 +12,11 @@ import {
import TimesheetTable from "../TimesheetTable"; import TimesheetTable from "../TimesheetTable";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Check, Close } from "@mui/icons-material"; import { Check, Close } from "@mui/icons-material";
import { FormProvider, useForm } from "react-hook-form";
import { RecordTimesheetInput } from "@/app/api/timesheets/actions";
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import {
RecordTimesheetInput,
saveTimesheet,
} from "@/app/api/timesheets/actions";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { AssignedProject } from "@/app/api/projects"; import { AssignedProject } from "@/app/api/projects";
@@ -23,6 +26,8 @@ interface Props {
onClose: () => void; onClose: () => void;
timesheetType: "time" | "leave"; timesheetType: "time" | "leave";
assignedProjects: AssignedProject[]; assignedProjects: AssignedProject[];
username: string;
defaultTimesheets?: RecordTimesheetInput;
} }


const modalSx: SxProps = { const modalSx: SxProps = {
@@ -40,6 +45,8 @@ const TimesheetModal: React.FC<Props> = ({
onClose, onClose,
timesheetType, timesheetType,
assignedProjects, assignedProjects,
username,
defaultTimesheets,
}) => { }) => {
const { t } = useTranslation("home"); const { t } = useTranslation("home");


@@ -48,15 +55,37 @@ const TimesheetModal: React.FC<Props> = ({
return Array(7) return Array(7)
.fill(undefined) .fill(undefined)
.reduce<RecordTimesheetInput>((acc, _, index) => { .reduce<RecordTimesheetInput>((acc, _, index) => {
const date = today.subtract(index, "day").format(INPUT_DATE_FORMAT);
return { return {
...acc, ...acc,
[today.subtract(index, "day").format(INPUT_DATE_FORMAT)]: [],
[date]: defaultTimesheets?.[date] ?? [],
}; };
}, {}); }, {});
}, []);
}, [defaultTimesheets]);


const formProps = useForm<RecordTimesheetInput>({ defaultValues }); const formProps = useForm<RecordTimesheetInput>({ defaultValues });


const onSubmit = useCallback<SubmitHandler<RecordTimesheetInput>>(
async (data) => {
const savedRecords = await saveTimesheet(data, username);

const today = dayjs();
const newFormValues = Array(7)
.fill(undefined)
.reduce<RecordTimesheetInput>((acc, _, index) => {
const date = today.subtract(index, "day").format(INPUT_DATE_FORMAT);
return {
...acc,
[date]: savedRecords[date] ?? [],
};
}, {});

formProps.reset(newFormValues);
onClose();
},
[formProps, onClose, username],
);

const onCancel = useCallback(() => { const onCancel = useCallback(() => {
formProps.reset(defaultValues); formProps.reset(defaultValues);
onClose(); onClose();
@@ -66,7 +95,10 @@ const TimesheetModal: React.FC<Props> = ({
<Modal open={isOpen} onClose={onClose}> <Modal open={isOpen} onClose={onClose}>
<Card sx={modalSx}> <Card sx={modalSx}>
<FormProvider {...formProps}> <FormProvider {...formProps}>
<CardContent>
<CardContent
component="form"
onSubmit={formProps.handleSubmit(onSubmit)}
>
<Typography variant="overline" display="block" marginBlockEnd={1}> <Typography variant="overline" display="block" marginBlockEnd={1}>
{t(timesheetType === "time" ? "Timesheet Input" : "Record Leave")} {t(timesheetType === "time" ? "Timesheet Input" : "Record Leave")}
</Typography> </Typography>
@@ -86,13 +118,8 @@ const TimesheetModal: React.FC<Props> = ({
> >
{t("Cancel")} {t("Cancel")}
</Button> </Button>
<Button
onClick={onClose}
variant="contained"
startIcon={<Check />}
type="submit"
>
{t("Confirm")}
<Button variant="contained" startIcon={<Check />} type="submit">
{t("Save")}
</Button> </Button>
</CardActions> </CardActions>
</CardContent> </CardContent>


+ 6
- 7
src/components/TimesheetTable/EntryInputTable.tsx Parādīt failu

@@ -36,7 +36,6 @@ type TimeEntryRow = Partial<
_isNew: boolean; _isNew: boolean;
_error: string; _error: string;
isPlanned: boolean; isPlanned: boolean;
id: string;
} }
>; >;


@@ -74,21 +73,19 @@ const EntryInputTable: React.FC<Props> = ({ day, assignedProjects }) => {
const { getValues, setValue } = useFormContext<RecordTimesheetInput>(); const { getValues, setValue } = useFormContext<RecordTimesheetInput>();
const currentEntries = getValues(day); const currentEntries = getValues(day);


const [entries, setEntries] = useState<TimeEntryRow[]>(
currentEntries.map((e, index) => ({ ...e, id: `${day}-${index}` })) || [],
);
const [entries, setEntries] = useState<TimeEntryRow[]>(currentEntries || []);


const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});


const apiRef = useGridApiRef(); const apiRef = useGridApiRef();
const addRow = useCallback(() => { const addRow = useCallback(() => {
const id = `${day}-${Date.now()}`;
const id = Date.now();
setEntries((e) => [...e, { id, _isNew: true }]); setEntries((e) => [...e, { id, _isNew: true }]);
setRowModesModel((model) => ({ setRowModesModel((model) => ({
...model, ...model,
[id]: { mode: GridRowModes.Edit, fieldToFocus: "projectId" }, [id]: { mode: GridRowModes.Edit, fieldToFocus: "projectId" },
})); }));
}, [day]);
}, []);


const validateRow = useCallback( const validateRow = useCallback(
(id: GridRowId) => { (id: GridRowId) => {
@@ -318,9 +315,11 @@ const EntryInputTable: React.FC<Props> = ({ day, assignedProjects }) => {
e.inputHours && e.inputHours &&
e.projectId && e.projectId &&
e.taskId && e.taskId &&
e.taskGroupId,
e.taskGroupId &&
e.id,
) )
.map((e) => ({ .map((e) => ({
id: e.id!,
inputHours: e.inputHours!, inputHours: e.inputHours!,
projectId: e.projectId!, projectId: e.projectId!,
taskId: e.taskId!, taskId: e.taskId!,


+ 5
- 3
src/components/UserWorkspacePage/AssignedProjects.tsx Parādīt failu

@@ -14,9 +14,11 @@ import { Clear, Search } from "@mui/icons-material";
import ProjectGrid from "./ProjectGrid"; import ProjectGrid from "./ProjectGrid";
import { Props as UserWorkspaceProps } from "./UserWorkspacePage"; import { Props as UserWorkspaceProps } from "./UserWorkspacePage";


const AssignedProjects: React.FC<UserWorkspaceProps> = ({
assignedProjects,
}) => {
interface Props {
assignedProjects: UserWorkspaceProps["assignedProjects"];
}

const AssignedProjects: React.FC<Props> = ({ assignedProjects }) => {
const { t } = useTranslation("home"); const { t } = useTranslation("home");


// Projects // Projects


+ 11
- 1
src/components/UserWorkspacePage/UserWorkspacePage.tsx Parādīt failu

@@ -10,12 +10,19 @@ import ButtonGroup from "@mui/material/ButtonGroup";
import AssignedProjects from "./AssignedProjects"; import AssignedProjects from "./AssignedProjects";
import TimesheetModal from "../TimesheetModal"; import TimesheetModal from "../TimesheetModal";
import { AssignedProject } from "@/app/api/projects"; import { AssignedProject } from "@/app/api/projects";
import { RecordTimesheetInput } from "@/app/api/timesheets/actions";


export interface Props { export interface Props {
assignedProjects: AssignedProject[]; assignedProjects: AssignedProject[];
username: string;
defaultTimesheets: RecordTimesheetInput;
} }


const UserWorkspacePage: React.FC<Props> = ({ assignedProjects }) => {
const UserWorkspacePage: React.FC<Props> = ({
assignedProjects,
username,
defaultTimesheets,
}) => {
const [isTimeheetModalVisible, setTimeheetModalVisible] = useState(false); const [isTimeheetModalVisible, setTimeheetModalVisible] = useState(false);
const [isLeaveModalVisible, setLeaveModalVisible] = useState(false); const [isLeaveModalVisible, setLeaveModalVisible] = useState(false);
const { t } = useTranslation("home"); const { t } = useTranslation("home");
@@ -75,12 +82,15 @@ const UserWorkspacePage: React.FC<Props> = ({ assignedProjects }) => {
isOpen={isTimeheetModalVisible} isOpen={isTimeheetModalVisible}
onClose={handleCloseTimesheetModal} onClose={handleCloseTimesheetModal}
assignedProjects={assignedProjects} assignedProjects={assignedProjects}
username={username}
defaultTimesheets={defaultTimesheets}
/> />
<TimesheetModal <TimesheetModal
timesheetType="leave" timesheetType="leave"
isOpen={isLeaveModalVisible} isOpen={isLeaveModalVisible}
onClose={handleCloseLeaveModal} onClose={handleCloseLeaveModal}
assignedProjects={assignedProjects} assignedProjects={assignedProjects}
username={username}
/> />
<AssignedProjects assignedProjects={assignedProjects} /> <AssignedProjects assignedProjects={assignedProjects} />
</> </>


+ 18
- 3
src/components/UserWorkspacePage/UserWorkspaceWrapper.tsx Parādīt failu

@@ -1,9 +1,24 @@
import { fetchAssignedProjects } from "@/app/api/projects"; import { fetchAssignedProjects } from "@/app/api/projects";
import UserWorkspacePage from "./UserWorkspacePage"; import UserWorkspacePage from "./UserWorkspacePage";
import { fetchTimesheets } from "@/app/api/timesheets";


const UserWorkspaceWrapper: React.FC = async () => {
const assignedProjects = await fetchAssignedProjects();
return <UserWorkspacePage assignedProjects={assignedProjects} />;
interface Props {
username: string;
}

const UserWorkspaceWrapper: React.FC<Props> = async ({ username }) => {
const [assignedProjects, timesheets] = await Promise.all([
fetchAssignedProjects(),
fetchTimesheets(username),
]);

return (
<UserWorkspacePage
assignedProjects={assignedProjects}
username={username}
defaultTimesheets={timesheets}
/>
);
}; };


export default UserWorkspaceWrapper; export default UserWorkspaceWrapper;

+ 3
- 4
src/config/authConfig.ts Parādīt failu

@@ -8,12 +8,10 @@ export interface SessionWithTokens extends Session {
refreshToken?: string; refreshToken?: string;
} }



export interface ability { export interface ability {
actionSubjectCombo: string; actionSubjectCombo: string;
} }



export const authOptions: AuthOptions = { export const authOptions: AuthOptions = {
debug: process.env.NODE_ENV === "development", debug: process.env.NODE_ENV === "development",
providers: [ providers: [
@@ -55,11 +53,12 @@ export const authOptions: AuthOptions = {
const sessionWithToken: SessionWithTokens = { const sessionWithToken: SessionWithTokens = {
...session, ...session,
// Add the data from the token to the session // Add the data from the token to the session
abilities: (token.abilities as ability[]).map((item: ability) => item.actionSubjectCombo) as string[],
abilities: (token.abilities as ability[]).map(
(item: ability) => item.actionSubjectCombo,
) as string[],
accessToken: token.accessToken as string | undefined, accessToken: token.accessToken as string | undefined,
refreshToken: token.refreshToken as string | undefined, refreshToken: token.refreshToken as string | undefined,
}; };
// console.log(sessionWithToken) // console.log(sessionWithToken)
return sessionWithToken; return sessionWithToken;
}, },


Notiek ielāde…
Atcelt
Saglabāt