Преглед изворни кода

1. Department Search Page

2. Create Department Page
3. Postion Search Page
4. Create Position Page
tags/Baseline_30082024_FRONTEND_UAT
MSI\2Fi пре 1 година
родитељ
комит
ffaf2e764b
26 измењених фајлова са 930 додато и 1 уклоњено
  1. +25
    -0
      src/app/(main)/settings/department/new/page.tsx
  2. +50
    -0
      src/app/(main)/settings/department/page.tsx
  3. +25
    -0
      src/app/(main)/settings/position/new/page.tsx
  4. +50
    -0
      src/app/(main)/settings/position/page.tsx
  5. +18
    -0
      src/app/api/departments/actions.ts
  6. +21
    -0
      src/app/api/departments/index.ts
  7. +18
    -0
      src/app/api/positions/actions.ts
  8. +21
    -0
      src/app/api/positions/index.ts
  9. +5
    -0
      src/components/Breadcrumb/Breadcrumb.tsx
  10. +102
    -0
      src/components/CreateDepartment/CreateDepartment.tsx
  11. +20
    -0
      src/components/CreateDepartment/CreateDepartmentWrapper.tsx
  12. +81
    -0
      src/components/CreateDepartment/DepartmentDetails.tsx
  13. +1
    -0
      src/components/CreateDepartment/index.ts
  14. +102
    -0
      src/components/CreatePosition/CreatePosition.tsx
  15. +18
    -0
      src/components/CreatePosition/CreatePositionWrapper.tsx
  16. +81
    -0
      src/components/CreatePosition/PositionDetails.tsx
  17. +1
    -0
      src/components/CreatePosition/index.ts
  18. +82
    -0
      src/components/DepartmentSearch/DepartmentSearch.tsx
  19. +40
    -0
      src/components/DepartmentSearch/DepartmentSearchLoading.tsx
  20. +20
    -0
      src/components/DepartmentSearch/DepartmentSearchWrapper.tsx
  21. +1
    -0
      src/components/DepartmentSearch/index.ts
  22. +6
    -1
      src/components/NavigationContent/NavigationContent.tsx
  23. +81
    -0
      src/components/PositionSearch/PositionSearch.tsx
  24. +40
    -0
      src/components/PositionSearch/PositionSearchLoading.tsx
  25. +20
    -0
      src/components/PositionSearch/PositionSearchWrapper.tsx
  26. +1
    -0
      src/components/PositionSearch/index.ts

+ 25
- 0
src/app/(main)/settings/department/new/page.tsx Прегледај датотеку

@@ -0,0 +1,25 @@
import CreateDepartment from "@/components/CreateDepartment";
import { I18nProvider, getServerI18n } from "@/i18n";
import Typography from "@mui/material/Typography";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Create Department",
};

const Department: React.FC = async () => {
const { t } = await getServerI18n("departments");

// Preload necessary dependencies

return (
<>
<Typography variant="h4">{t("Create Department")}</Typography>
<I18nProvider namespaces={["departments"]}>
<CreateDepartment />
</I18nProvider>
</>
);
};

export default Department;

+ 50
- 0
src/app/(main)/settings/department/page.tsx Прегледај датотеку

@@ -0,0 +1,50 @@
import DepartmentSearch from "@/components/DepartmentSearch";
import { Metadata } from "next";
import { getServerI18n } from "@/i18n";
import Add from "@mui/icons-material/Add";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import Link from "next/link";
import { Suspense } from "react";
import { fetchDepartments, preloadDepartments } from "@/app/api/departments";

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

const Department: React.FC = async () => {
const { t } = await getServerI18n("department");
// Preload necessary dependencies
// fetchDepartments();
// preloadDepartments();
return (
<>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Typography variant="h4" marginInlineEnd={2}>
{t("Department")}
</Typography>
<Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/settings/department/new"
>
{t("Create Department")}
</Button>
</Stack>
<Suspense fallback={<DepartmentSearch.Loading />}>
<DepartmentSearch/>
</Suspense>
</>
)
};

export default Department;

+ 25
- 0
src/app/(main)/settings/position/new/page.tsx Прегледај датотеку

@@ -0,0 +1,25 @@
import CreatePosition from "@/components/CreatePosition";
import { I18nProvider, getServerI18n } from "@/i18n";
import Typography from "@mui/material/Typography";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Create Position",
};

const Positions: React.FC = async () => {
const { t } = await getServerI18n("positions");

// Preload necessary dependencies

return (
<>
<Typography variant="h4">{t("Create Position")}</Typography>
<I18nProvider namespaces={["positions"]}>
<CreatePosition />
</I18nProvider>
</>
);
};

export default Positions;

+ 50
- 0
src/app/(main)/settings/position/page.tsx Прегледај датотеку

@@ -0,0 +1,50 @@
import PositionSearch from "@/components/PositionSearch";
import { Metadata } from "next";
import { getServerI18n } from "@/i18n";
import Add from "@mui/icons-material/Add";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import Link from "next/link";
import { Suspense } from "react";
import { fetchPositions, preloadPositions } from "@/app/api/positions";

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

const Position: React.FC = async () => {
const { t } = await getServerI18n("Position");
// Preload necessary dependencies
// fetchPositions();
// preloadPositions();
return (
<>
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
>
<Typography variant="h4" marginInlineEnd={2}>
{t("Position")}
</Typography>
<Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/settings/position/new"
>
{t("Create Position")}
</Button>
</Stack>
<Suspense fallback={<PositionSearch.Loading />}>
<PositionSearch/>
</Suspense>
</>
)
};

export default Position;

+ 18
- 0
src/app/api/departments/actions.ts Прегледај датотеку

@@ -0,0 +1,18 @@
"use server"

import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";

export interface CreateDepartmentInputs {
departmentCode: string;
departmentName: string;
description: string;
}

export const saveDepartment = async (data: CreateDepartmentInputs) => {
return serverFetchJson(`${BASE_API_URL}/departments/new`, {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
};

+ 21
- 0
src/app/api/departments/index.ts Прегледај датотеку

@@ -0,0 +1,21 @@
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import "server-only";

export interface DepartmentResult {
id: number;
code: string;
name: string;
description: string;
}

export const preloadDepartments = () => {
fetchDepartments();
};

export const fetchDepartments = cache(async () => {
return serverFetchJson<DepartmentResult[]>(`${BASE_API_URL}/departments`, {
next: { tags: ["departments"] },
});
});

+ 18
- 0
src/app/api/positions/actions.ts Прегледај датотеку

@@ -0,0 +1,18 @@
"use server"

import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";

export interface CreatePositionInputs {
positionCode: string;
positionName: string;
description: string;
}

export const savePosition = async (data: CreatePositionInputs) => {
return serverFetchJson(`${BASE_API_URL}/positions/new`, {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
};

+ 21
- 0
src/app/api/positions/index.ts Прегледај датотеку

@@ -0,0 +1,21 @@
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import "server-only";

export interface PositionResult {
id: number;
code: string;
name: string;
description: string;
}

export const preloadPositions = () => {
fetchPositions();
};

export const fetchPositions = cache(async () => {
return serverFetchJson<PositionResult[]>(`${BASE_API_URL}/positions`, {
next: { tags: ["positions"] },
});
});

+ 5
- 0
src/components/Breadcrumb/Breadcrumb.tsx Прегледај датотеку

@@ -15,7 +15,12 @@ const pathToLabelMap: { [path: string]: string } = {
"/tasks/create": "Create Task Template",
"/customer": "Customer",
"/customer/create": "Create Customer",
"/settings": "Settings",
"/company": "Company",
"/settings/department": "Department",
"/settings/department/new": "Create Department",
"/settings/position": "Position",
"/settings/position/new": "Create Position",
};

const Breadcrumb = () => {


+ 102
- 0
src/components/CreateDepartment/CreateDepartment.tsx Прегледај датотеку

@@ -0,0 +1,102 @@
"use client";

import Check from "@mui/icons-material/Check";
import Close from "@mui/icons-material/Close";
import Button 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, useState } from "react";
import { useTranslation } from "react-i18next";
import { Task, TaskTemplate } from "@/app/api/tasks";
import {
FieldErrors,
FormProvider,
SubmitErrorHandler,
SubmitHandler,
useForm,
} from "react-hook-form";
import { CreateDepartmentInputs, saveDepartment } from "@/app/api/departments/actions";
import { Error } from "@mui/icons-material";
import { ProjectCategory } from "@/app/api/projects";
import { Staff } from "@/app/api/staff";
import { Typography } from "@mui/material";
import DepartmentDetails from "./DepartmentDetails";


const CreateDepartment: React.FC = ({
// allTasks,
// projectCategories,
// taskTemplates,
// teamLeads,
}) => {
const [serverError, setServerError] = useState("");
const { t } = useTranslation();
const router = useRouter();

const handleCancel = () => {
router.back();
};

const onSubmit = useCallback<SubmitHandler<CreateDepartmentInputs>>(
async (data) => {
try {
console.log(data);
setServerError("");
// console.log(JSON.stringify(data));
await saveDepartment(data)
router.replace("/settings/department");
} catch (e) {
setServerError(t("An error has occurred. Please try again later."));
}
},
[router, t],
);

const onSubmitError = useCallback<SubmitErrorHandler<CreateDepartmentInputs>>(
(errors) => {
console.log(errors)
},
[],
);

const formProps = useForm<CreateDepartmentInputs>({
defaultValues: {
departmentCode: "",
departmentName: "",
description: "",
},
});

const errors = formProps.formState.errors;

return (
<FormProvider {...formProps}>
<Stack
spacing={2}
component="form"
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
>
{
<DepartmentDetails />
}

<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
variant="outlined"
startIcon={<Close />}
onClick={handleCancel}
>
{t("Cancel")}
</Button>
<Button variant="contained" startIcon={<Check />} type="submit">
{t("Confirm")}
</Button>
</Stack>
</Stack>
</FormProvider>
);
};

export default CreateDepartment;

+ 20
- 0
src/components/CreateDepartment/CreateDepartmentWrapper.tsx Прегледај датотеку

@@ -0,0 +1,20 @@
import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks";
import CreateDepartment from "./CreateDepartment";
import { fetchTeamLeads } from "@/app/api/staff";

const CreateDepartmentWrapper: React.FC = async () => {
// const [tasks, taskTemplates, DepartmentCategories, teamLeads] =
// await Promise.all([
// fetchAllTasks(),
// fetchTaskTemplates(),
// fetchDepartmentCategories(),
// fetchTeamLeads(),
// ]);

return (
<CreateDepartment
/>
);
};

export default CreateDepartmentWrapper;

+ 81
- 0
src/components/CreateDepartment/DepartmentDetails.tsx Прегледај датотеку

@@ -0,0 +1,81 @@
"use client";

import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import FormControl from "@mui/material/FormControl";
import Grid from "@mui/material/Grid";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import Select from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import CardActions from "@mui/material/CardActions";
import RestartAlt from "@mui/icons-material/RestartAlt";
import Button from "@mui/material/Button";
import { Controller, useFormContext } from "react-hook-form";
import { CreateDepartmentInputs } from "@/app/api/departments/actions";
import { Staff } from "@/app/api/staff";

const DepartmentDetails: React.FC = ({
}) => {
const { t } = useTranslation();
const {
register,
formState: { errors },
control,
} = useFormContext<CreateDepartmentInputs>();

return (
<Card>
<CardContent component={Stack} spacing={4}>
<Box>
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Department Details")}
</Typography>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
label={t("Department Code")}
fullWidth
{...register("departmentCode", {
required: "Department code required!",
})}
error={Boolean(errors.departmentCode)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Department Name")}
fullWidth
{...register("departmentName", {
required: "Department name required!",
})}
error={Boolean(errors.departmentName)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Department Description")}
fullWidth
{...register("description", {
required: "Please enter a description",
})}
error={Boolean(errors.description)}
/>
</Grid>
</Grid>
</Box>
{/* <CardActions sx={{ justifyContent: "flex-end" }}>
<Button variant="text" startIcon={<RestartAlt />}>
{t("Reset")}
</Button>
</CardActions> */}
</CardContent>
</Card>
);
};

export default DepartmentDetails;

+ 1
- 0
src/components/CreateDepartment/index.ts Прегледај датотеку

@@ -0,0 +1 @@
export { default } from "./CreateDepartmentWrapper"

+ 102
- 0
src/components/CreatePosition/CreatePosition.tsx Прегледај датотеку

@@ -0,0 +1,102 @@
"use client";

import Check from "@mui/icons-material/Check";
import Close from "@mui/icons-material/Close";
import Button 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, useState } from "react";
import { useTranslation } from "react-i18next";
import { Task, TaskTemplate } from "@/app/api/tasks";
import {
FieldErrors,
FormProvider,
SubmitErrorHandler,
SubmitHandler,
useForm,
} from "react-hook-form";
import { CreatePositionInputs, savePosition } from "@/app/api/positions/actions";
import { Error } from "@mui/icons-material";
import { ProjectCategory } from "@/app/api/projects";
import { Staff } from "@/app/api/staff";
import { Typography } from "@mui/material";
import PositionDetails from "./PositionDetails";


const CreatePosition: React.FC = ({
// allTasks,
// projectCategories,
// taskTemplates,
// teamLeads,
}) => {
const [serverError, setServerError] = useState("");
const { t } = useTranslation();
const router = useRouter();

const handleCancel = () => {
router.back();
};

const onSubmit = useCallback<SubmitHandler<CreatePositionInputs>>(
async (data) => {
try {
console.log(data);
setServerError("");
// console.log(JSON.stringify(data));
await savePosition(data)
router.replace("/settings/position");
} catch (e) {
setServerError(t("An error has occurred. Please try again later."));
}
},
[router, t],
);

const onSubmitError = useCallback<SubmitErrorHandler<CreatePositionInputs>>(
(errors) => {
console.log(errors)
},
[],
);

const formProps = useForm<CreatePositionInputs>({
defaultValues: {
positionCode: "",
positionName: "",
description: "",
},
});

const errors = formProps.formState.errors;

return (
<FormProvider {...formProps}>
<Stack
spacing={2}
component="form"
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
>
{
<PositionDetails />
}

<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
variant="outlined"
startIcon={<Close />}
onClick={handleCancel}
>
{t("Cancel")}
</Button>
<Button variant="contained" startIcon={<Check />} type="submit">
{t("Confirm")}
</Button>
</Stack>
</Stack>
</FormProvider>
);
};

export default CreatePosition;

+ 18
- 0
src/components/CreatePosition/CreatePositionWrapper.tsx Прегледај датотеку

@@ -0,0 +1,18 @@
import CreatePosition from "./CreatePosition";

const CreatePositionWrapper: React.FC = async () => {
// const [tasks, taskTemplates, PositionCategories, teamLeads] =
// await Promise.all([
// fetchAllTasks(),
// fetchTaskTemplates(),
// fetchPositionCategories(),
// fetchTeamLeads(),
// ]);

return (
<CreatePosition
/>
);
};

export default CreatePositionWrapper;

+ 81
- 0
src/components/CreatePosition/PositionDetails.tsx Прегледај датотеку

@@ -0,0 +1,81 @@
"use client";

import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import FormControl from "@mui/material/FormControl";
import Grid from "@mui/material/Grid";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import Select from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import CardActions from "@mui/material/CardActions";
import RestartAlt from "@mui/icons-material/RestartAlt";
import Button from "@mui/material/Button";
import { Controller, useFormContext } from "react-hook-form";
import { CreatePositionInputs } from "@/app/api/positions/actions";
import { Staff } from "@/app/api/staff";

const PositionDetails: React.FC = ({
}) => {
const { t } = useTranslation();
const {
register,
formState: { errors },
control,
} = useFormContext<CreatePositionInputs>();

return (
<Card>
<CardContent component={Stack} spacing={4}>
<Box>
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Position Details")}
</Typography>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
label={t("Position Code")}
fullWidth
{...register("positionCode", {
required: "Position code required!",
})}
error={Boolean(errors.positionCode)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Position Name")}
fullWidth
{...register("positionName", {
required: "Position name required!",
})}
error={Boolean(errors.positionName)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Position Description")}
fullWidth
{...register("description", {
required: "Please enter a description",
})}
error={Boolean(errors.description)}
/>
</Grid>
</Grid>
</Box>
{/* <CardActions sx={{ justifyContent: "flex-end" }}>
<Button variant="text" startIcon={<RestartAlt />}>
{t("Reset")}
</Button>
</CardActions> */}
</CardContent>
</Card>
);
};

export default PositionDetails;

+ 1
- 0
src/components/CreatePosition/index.ts Прегледај датотеку

@@ -0,0 +1 @@
export { default } from "./CreatePositionWrapper"

+ 82
- 0
src/components/DepartmentSearch/DepartmentSearch.tsx Прегледај датотеку

@@ -0,0 +1,82 @@
"use client";

import React, { useCallback, useMemo, useState } from "react";
import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import EditNote from "@mui/icons-material/EditNote";
import uniq from "lodash/uniq";
import { DepartmentResult } from "@/app/api/departments";

interface Props {
departments: DepartmentResult[];
}

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

const DepartmentSearch: React.FC<Props> = ({ departments }) => {
const { t } = useTranslation("departments");

const [filteredDepartments, setFilteredDepartments] = useState(departments);

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Department code"), paramName: "code", type: "text" },
{ label: t("Department name"), paramName: "name", type: "text" },
{ label: t("Department Description"), paramName: "description", type: "text" },
],
[t, departments],
);

const onReset = useCallback(() => {
setFilteredDepartments(departments);
}, [departments]);

const onProjectClick = useCallback((project: DepartmentResult) => {
console.log(project);
}, []);

const columns = useMemo<Column<DepartmentResult>[]>(
() => [
{
name: "id",
label: t("Details"),
onClick: onProjectClick,
buttonIcon: <EditNote />,
},
{ name: "code", label: t("Department Code") },
{ name: "name", label: t("Department Name") },
{ name: "description", label: t("Department Description") },
],
[t, onProjectClick],
);

return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
setFilteredDepartments(
departments.filter(
(d) =>
d.code.toLowerCase().includes(query.code.toLowerCase()) &&
d.name.toLowerCase().includes(query.name.toLowerCase()) &&
d.description.toLowerCase().includes(query.description.toLowerCase()) &&
{/*(query.client === "All" || p.client === query.client) &&
(query.category === "All" || p.category === query.category) &&
(query.team === "All" || p.team === query.team), **/}
),
);
}}
onReset={onReset}
/>
<SearchResults<DepartmentResult>
items={filteredDepartments}
columns={columns}
/>
</>
);
};

export default DepartmentSearch;

+ 40
- 0
src/components/DepartmentSearch/DepartmentSearchLoading.tsx Прегледај датотеку

@@ -0,0 +1,40 @@
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Skeleton from "@mui/material/Skeleton";
import Stack from "@mui/material/Stack";
import React from "react";

// Can make this nicer
export const DepartmentSearchLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>Department
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default DepartmentSearchLoading;

+ 20
- 0
src/components/DepartmentSearch/DepartmentSearchWrapper.tsx Прегледај датотеку

@@ -0,0 +1,20 @@
// import { fetchDepartmentCategories, fetchDepartments } from "@/app/api/companys";
import React from "react";
import DepartmentSearch from "./DepartmentSearch";
import DepartmentSearchLoading from "./DepartmentSearchLoading";
import { fetchDepartments } from "@/app/api/departments";

interface SubComponents {
Loading: typeof DepartmentSearchLoading;
}

const DepartmentSearchWrapper: React.FC & SubComponents = async () => {
const Departments = await fetchDepartments();
// const Departments:any[] = []

return <DepartmentSearch departments={Departments} />;
};

DepartmentSearchWrapper.Loading = DepartmentSearchLoading;

export default DepartmentSearchWrapper;

+ 1
- 0
src/components/DepartmentSearch/index.ts Прегледај датотеку

@@ -0,0 +1 @@
export { default } from "./DepartmentSearchWrapper";

+ 6
- 1
src/components/NavigationContent/NavigationContent.tsx Прегледај датотеку

@@ -18,6 +18,9 @@ import Settings from "@mui/icons-material/Settings";
import Analytics from "@mui/icons-material/Analytics";
import Payments from "@mui/icons-material/Payments";
import Staff from "@mui/icons-material/PeopleAlt";
import Company from '@mui/icons-material/Store';
import Department from '@mui/icons-material/Diversity3';
import Position from '@mui/icons-material/Paragliding';
import { useTranslation } from "react-i18next";
import Typography from "@mui/material/Typography";
import { usePathname } from "next/navigation";
@@ -92,7 +95,9 @@ const navigationItems: NavigationItem[] = [
children: [
{ icon: <GroupIcon />, label: "Customer", path: "/customer" },
{ icon: <Staff />, label: "Staff", path: "/staff" },
{ icon: <Staff />, label: "Company", path: "/settings/company" }
{ icon: <Company />, label: "Company", path: "/settings/company" },
{ icon: <Department />, label: "Department", path: "/settings/department" },
{ icon: <Position />, label: "Position", path: "/settings/position" },
],
},
];


+ 81
- 0
src/components/PositionSearch/PositionSearch.tsx Прегледај датотеку

@@ -0,0 +1,81 @@
"use client";

import React, { useCallback, useMemo, useState } from "react";
import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import EditNote from "@mui/icons-material/EditNote";
import { PositionResult } from "@/app/api/positions";

interface Props {
positions: PositionResult[];
}

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

const PositionSearch: React.FC<Props> = ({ positions }) => {
const { t } = useTranslation("positions");

const [filteredPositions, setFilteredPositions] = useState(positions);

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Position code"), paramName: "code", type: "text" },
{ label: t("Position name"), paramName: "name", type: "text" },
{ label: t("Position Description"), paramName: "description", type: "text" },
],
[t, positions],
);

const onReset = useCallback(() => {
setFilteredPositions(positions);
}, [positions]);

const onProjectClick = useCallback((project: PositionResult) => {
console.log(project);
}, []);

const columns = useMemo<Column<PositionResult>[]>(
() => [
{
name: "id",
label: t("Details"),
onClick: onProjectClick,
buttonIcon: <EditNote />,
},
{ name: "code", label: t("Position Code") },
{ name: "name", label: t("Position Name") },
{ name: "description", label: t("Position Description") },
],
[t, onProjectClick],
);

return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
setFilteredPositions(
positions.filter(
(d) =>
d.code.toLowerCase().includes(query.code.toLowerCase()) &&
d.name.toLowerCase().includes(query.name.toLowerCase()) &&
d.description.toLowerCase().includes(query.description.toLowerCase()) &&
{/*(query.client === "All" || p.client === query.client) &&
(query.category === "All" || p.category === query.category) &&
(query.team === "All" || p.team === query.team), **/}
),
);
}}
onReset={onReset}
/>
<SearchResults<PositionResult>
items={filteredPositions}
columns={columns}
/>
</>
);
};

export default PositionSearch;

+ 40
- 0
src/components/PositionSearch/PositionSearchLoading.tsx Прегледај датотеку

@@ -0,0 +1,40 @@
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Skeleton from "@mui/material/Skeleton";
import Stack from "@mui/material/Stack";
import React from "react";

// Can make this nicer
export const PositionSearchLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>Position
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default PositionSearchLoading;

+ 20
- 0
src/components/PositionSearch/PositionSearchWrapper.tsx Прегледај датотеку

@@ -0,0 +1,20 @@
// import { fetchPositionCategories, fetchPositions } from "@/app/api/companys";
import React from "react";
import PositionSearch from "./PositionSearch";
import PositionSearchLoading from "./PositionSearchLoading";
import { fetchPositions } from "@/app/api/positions";

interface SubComponents {
Loading: typeof PositionSearchLoading;
}

const PositionSearchWrapper: React.FC & SubComponents = async () => {
const Positions = await fetchPositions();
// const Positions:any[] = []

return <PositionSearch positions={Positions} />;
};

PositionSearchWrapper.Loading = PositionSearchLoading;

export default PositionSearchWrapper;

+ 1
- 0
src/components/PositionSearch/index.ts Прегледај датотеку

@@ -0,0 +1 @@
export { default } from "./PositionSearchWrapper";

Loading…
Откажи
Сачувај