added permissions and changed to url paths

main
alex 2023-08-27 21:01:35 +02:00
parent 9d76ac0cd8
commit 4289a8290b
6 changed files with 290 additions and 180 deletions

View File

@ -17,6 +17,9 @@ const PageNotFound = lazy(() => import("../../Pages/PageNotFound"));
const EquipmentDocumentationOverview = lazy(() => const EquipmentDocumentationOverview = lazy(() =>
import("../../Pages/EquipmentDocumentation") import("../../Pages/EquipmentDocumentation")
); );
const ViewEquipmentDocumentations = lazy(() =>
import("../../Pages/EquipmentDocumentation/ViewEquipmentDocumentation")
);
function SuspenseFallback({ children }) { function SuspenseFallback({ children }) {
return ( return (
@ -72,6 +75,52 @@ export default function AppRoutes() {
/> />
)} )}
{hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.VIEW
) && (
<Route
path={`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}:paramStockId`}
element={
<SuspenseFallback>
<ViewEquipmentDocumentations />
</SuspenseFallback>
}
/>
)}
{hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.EDIT
) && (
<Route
path={`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}:paramStockId/edit/:paramDocumentationId`}
element={
<SuspenseFallback>
<ViewEquipmentDocumentations
isEditEquipmentDocumentationModalOpen={true}
/>
</SuspenseFallback>
}
/>
)}
{hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.CREATE
) && (
<Route
path={`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}:paramStockId/create`}
element={
<SuspenseFallback>
<ViewEquipmentDocumentations
isCreateEquipmentDocumentationModalOpen={true}
/>
</SuspenseFallback>
}
/>
)}
<Route <Route
path={Constants.ROUTE_PATHS.GROUP_TASKS} path={Constants.ROUTE_PATHS.GROUP_TASKS}
element={ element={

View File

@ -1,35 +1,16 @@
import { Button } from "antd";
import { useState } from "react";
import { PlusOutlined } from "@ant-design/icons";
import { EquipmentDocumentationViewEditComponent } from "."; import { EquipmentDocumentationViewEditComponent } from ".";
import { useTranslation } from "react-i18next";
export default function CreateEquipmentDocumentationModal({ export default function CreateEquipmentDocumentationModal({
isOpen,
stockItemId, stockItemId,
fetchDocumentation, fetchDocumentation,
buttonBlock,
}) { }) {
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
return ( return (
<> <EquipmentDocumentationViewEditComponent
<Button createMode
block={buttonBlock} isOpen={isOpen}
type="primary" stockItemId={stockItemId}
icon={<PlusOutlined />} fetchDocumentation={fetchDocumentation}
onClick={() => setIsOpen(true)} />
>
{t("createEquipmentDocumentationModal.buttonCreateDocumentation")}
</Button>
<EquipmentDocumentationViewEditComponent
createMode
isOpen={isOpen}
onCancel={() => setIsOpen(false)}
stockItemId={stockItemId}
fetchDocumentation={fetchDocumentation}
/>
</>
); );
} }

View File

@ -1,26 +1,18 @@
import { EditOutlined } from "@ant-design/icons";
import { useState } from "react";
import { EquipmentDocumentationViewEditComponent } from "."; import { EquipmentDocumentationViewEditComponent } from ".";
export default function EditEquipmentDocumentationModal({ export default function EditEquipmentDocumentationModal({
isOpen,
stockItemId, stockItemId,
documentationId, documentationId,
fetchDocumentation, fetchDocumentation,
}) { }) {
const [isOpen, setIsOpen] = useState(false);
return ( return (
<> <EquipmentDocumentationViewEditComponent
<EditOutlined style={{ fontSize: 24 }} onClick={() => setIsOpen(true)} /> createMode={false}
isOpen={isOpen}
<EquipmentDocumentationViewEditComponent stockItemId={stockItemId}
createMode={false} documentationId={documentationId}
isOpen={isOpen} fetchDocumentation={fetchDocumentation}
onCancel={() => setIsOpen(false)} />
stockItemId={stockItemId}
documentationId={documentationId}
fetchDocumentation={fetchDocumentation}
/>
</>
); );
} }

View File

@ -1,23 +1,44 @@
import { Card, Col, Popover, Result, Row, Spin, Typography } from "antd"; import {
Button,
Card,
Col,
Popover,
Result,
Row,
Spin,
Typography,
} from "antd";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import CreateEquipmentDocumentationModal from "./CreateEquipmentDocumentationModal"; import CreateEquipmentDocumentationModal from "./CreateEquipmentDocumentationModal";
import { AppStyle, Constants, FormatDatetime, myFetch } from "../../utils"; import {
AppStyle,
Constants,
FormatDatetime,
hasPermission,
myFetch,
} from "../../utils";
import { import {
BookOutlined, BookOutlined,
EditOutlined,
InfoCircleOutlined, InfoCircleOutlined,
PlusOutlined,
ToolOutlined, ToolOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import EditEquipmentDocumentationModal from "./EditEquipmentDocumentationModal"; import EditEquipmentDocumentationModal from "./EditEquipmentDocumentationModal";
import { NoteComponent } from "."; import { NoteComponent, ScannerComponent } from ".";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { MyAvatar } from "../../Components/MyAvatar"; import { MyAvatar } from "../../Components/MyAvatar";
import { useAppContext } from "../../Contexts/AppContext"; import { useAppContext } from "../../Contexts/AppContext";
import { useNavigate, useParams } from "react-router-dom";
export default function ViewEquipmentDocumentations({ scannerResult }) { export default function ViewEquipmentDocumentations({
isEditEquipmentDocumentationModalOpen,
isCreateEquipmentDocumentationModalOpen,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate();
const appContext = useAppContext(); const appContext = useAppContext();
const { paramStockId, paramDocumentationId } = useParams();
console.log("render ViewEquipmentDocumentations");
const [equipmentDocumentationResponse, setEquipmentDocumentationResponse] = const [equipmentDocumentationResponse, setEquipmentDocumentationResponse] =
useState(null); useState(null);
@ -27,73 +48,50 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
const fetchDocumentation = () => { const fetchDocumentation = () => {
setIsEquipmentDocumentationLoading(true); setIsEquipmentDocumentationLoading(true);
myFetch(`/equipment/documentations/${scannerResult}`, "GET").then( myFetch(`/equipment/documentations/${paramStockId}`, "GET").then((data) => {
(data) => { const updatedData = { ...data };
const updatedData = { ...data };
// sort by date // sort by date
// last created will be on top of the list // last created will be on top of the list
updatedData.Documentations = data.Documentations.sort( updatedData.Documentations = data.Documentations.sort(
(a, b) => new Date(b.CreatedAt) - new Date(a.CreatedAt) (a, b) => new Date(b.CreatedAt) - new Date(a.CreatedAt)
); );
setIsEquipmentDocumentationLoading(false); setIsEquipmentDocumentationLoading(false);
setEquipmentDocumentationResponse(updatedData); setEquipmentDocumentationResponse(updatedData);
} });
};
useEffect(() => fetchDocumentation(), [paramStockId]);
const CreateEquipmentDocumentationButton = ({ buttonBlock }) => {
if (
!hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.CREATE
)
)
return null;
return (
<Button
block={buttonBlock}
type="primary"
icon={<PlusOutlined />}
onClick={() => navigate(`create`)}
>
{t("createEquipmentDocumentationModal.buttonCreateDocumentation")}
</Button>
); );
}; };
useEffect(() => fetchDocumentation(), [scannerResult]); const InvexStockItemThumbnail = ({ width }) => {
const CreateDocumentationButton = () => {
const InvexStockItemThumbnail = ({ width }) => {
return (
<img
src={`${Constants.API_ADDRESS}/equipment/thumbnail/${scannerResult}`}
width={width}
style={{ borderRadius: 6 }}
/>
);
};
return ( return (
<Row gutter={AppStyle.grid.row.gutter} style={{ alignItems: "center" }}> <img
<Col src={`${Constants.API_ADDRESS}/equipment/thumbnail/${paramStockId}`}
xs={24} width={width}
sm={{ span: 8 }} style={{ borderRadius: 6 }}
md={{ span: 6 }} />
style={{
display: "flex",
flexDirection: "row",
gap: 10,
alignItems: "center",
}}
>
{equipmentDocumentationResponse.Documentations.length !== 0 && (
<Popover
placement="right"
trigger="hover"
content={<InvexStockItemThumbnail width={256} />}
>
<>
<InvexStockItemThumbnail width={64} />
</>
</Popover>
)}
<Typography.Title level={4} style={{ margin: 0 }}>
{scannerResult}
</Typography.Title>
</Col>
<Col xs={24} sm={{ span: 8, offset: 8 }} md={{ span: 6, offset: 12 }}>
<CreateEquipmentDocumentationModal
stockItemId={scannerResult}
fetchDocumentation={fetchDocumentation}
buttonBlock={true}
/>
</Col>
</Row>
); );
}; };
@ -173,11 +171,15 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
/> />
</Popover> </Popover>
<EditEquipmentDocumentationModal {hasPermission(
stockItemId={scannerResult} appContext.userPermissions,
documentationId={documentation.Id} Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.EDIT
fetchDocumentation={fetchDocumentation} ) && (
/> <EditOutlined
style={{ fontSize: 24 }}
onClick={() => navigate(`edit/${documentation.Id}`)}
/>
)}
</div> </div>
</div> </div>
@ -197,70 +199,129 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
if (isEquipmentDocumentationLoading) { if (isEquipmentDocumentationLoading) {
return ( return (
<div <>
style={{ <ScannerComponent />
display: "flex",
alignItems: "center", <div
justifyContent: "center", style={{
marginTop: 120, display: "flex",
}} alignItems: "center",
> justifyContent: "center",
<Spin size="large" /> marginTop: 120,
</div> }}
>
<Spin size="large" />
</div>
</>
); );
} }
if (equipmentDocumentationResponse === null) return null; if (equipmentDocumentationResponse === null) return <ScannerComponent />;
// backend unauthorized to access invex // backend unauthorized to access invex
if (equipmentDocumentationResponse.Status === 401) { if (equipmentDocumentationResponse.Status === 401) {
return ( return (
<Result <>
status="403" <ScannerComponent />
title={t("viewEquipmentDocumentations.result403.title")}
subTitle={t("viewEquipmentDocumentations.result403.description")} <Result
/> status="403"
title={t("viewEquipmentDocumentations.result403.title")}
subTitle={t("viewEquipmentDocumentations.result403.description")}
/>
</>
); );
} }
// stock item doesn't exists on invex // stock item doesn't exists on invex
if (equipmentDocumentationResponse.Status === 404) { if (equipmentDocumentationResponse.Status === 404) {
return ( return (
<Result <>
status="500" <ScannerComponent />
title={t("viewEquipmentDocumentations.result500.title")}
subTitle={t("viewEquipmentDocumentations.result500.description")} <Result
/> status="500"
title={t("viewEquipmentDocumentations.result500.title")}
subTitle={t("viewEquipmentDocumentations.result500.description")}
/>
</>
); );
} }
if (equipmentDocumentationResponse.Documentations.length === 0) { if (equipmentDocumentationResponse.Documentations.length === 0) {
return ( return (
<Result <>
status="404" <ScannerComponent />
title={t("viewEquipmentDocumentations.result404.title")}
subTitle={t("viewEquipmentDocumentations.result404.description")} <Result
extra={[ status="404"
<CreateEquipmentDocumentationModal title={t("viewEquipmentDocumentations.result404.title")}
key="0" subTitle={t("viewEquipmentDocumentations.result404.description")}
stockItemId={scannerResult} extra={[
fetchDocumentation={fetchDocumentation} <CreateEquipmentDocumentationButton key="0" buttonBlock={false} />,
buttonBlock={false} ]}
/>, />
]} </>
/>
); );
} }
return ( return (
<> <>
<CreateDocumentationButton /> <ScannerComponent />
<Row gutter={AppStyle.grid.row.gutter} style={{ alignItems: "center" }}>
<Col
xs={24}
sm={{ span: 8 }}
md={{ span: 6 }}
style={{
display: "flex",
flexDirection: "row",
gap: 10,
alignItems: "center",
}}
>
{equipmentDocumentationResponse.Documentations.length !== 0 && (
<Popover
placement="right"
trigger="hover"
content={<InvexStockItemThumbnail width={256} />}
>
<>
<InvexStockItemThumbnail width={64} />
</>
</Popover>
)}
<Typography.Title level={4} style={{ margin: 0 }}>
{paramStockId}
</Typography.Title>
</Col>
<Col xs={24} sm={{ span: 8, offset: 8 }} md={{ span: 6, offset: 12 }}>
<CreateEquipmentDocumentationButton buttonBlock={true} />
</Col>
</Row>
{equipmentDocumentationResponse.Documentations.map( {equipmentDocumentationResponse.Documentations.map(
(documentation, index) => ( (documentation, index) => (
<DocumentationContent key={index} documentation={documentation} /> <DocumentationContent key={index} documentation={documentation} />
) )
)} )}
<EditEquipmentDocumentationModal
isOpen={isEditEquipmentDocumentationModalOpen}
stockItemId={paramStockId}
documentationId={paramDocumentationId}
fetchDocumentation={fetchDocumentation}
/>
<CreateEquipmentDocumentationModal
isOpen={isCreateEquipmentDocumentationModalOpen}
stockItemId={paramStockId}
fetchDocumentation={fetchDocumentation}
buttonBlock={true}
/>
</> </>
); );
} }

View File

@ -25,7 +25,6 @@ import {
import { AppStyle, Constants, myFetch } from "../../utils"; import { AppStyle, Constants, myFetch } from "../../utils";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { QrScanner } from "@yudiel/react-qr-scanner"; import { QrScanner } from "@yudiel/react-qr-scanner";
import ViewEquipmentDocumentations from "./ViewEquipmentDocumentation";
import MyModal, { import MyModal, {
MyModalCloseCreateButtonFooter, MyModalCloseCreateButtonFooter,
MyModalCloseSaveButtonFooter, MyModalCloseSaveButtonFooter,
@ -34,14 +33,42 @@ import Webcam from "react-webcam";
import TextArea from "antd/es/input/TextArea"; import TextArea from "antd/es/input/TextArea";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import MyTypography from "../../Components/MyTypography"; import MyTypography from "../../Components/MyTypography";
import { useNavigate } from "react-router-dom";
export default function EquipmentDocumentationOverview() { export default function EquipmentDocumentationOverview() {
const { t } = useTranslation(); const { t } = useTranslation();
return (
<>
<ScannerComponent />
<Result
status="404"
title={t(
"equipmentDocumentationOverview.noEquipmentScannedResult.title"
)}
subTitle={t(
"equipmentDocumentationOverview.noEquipmentScannedResult.description"
)}
/>
</>
);
}
export function ScannerComponent() {
const navigate = useNavigate();
const { t } = useTranslation();
const handleScannedEquipment = (scannedEquipment) => {
navigate(
`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}${scannedEquipment}`
);
};
const [messageApi, messageContextHolder] = message.useMessage({ const [messageApi, messageContextHolder] = message.useMessage({
maxCount: 2, maxCount: 2,
}); });
const [scannerResult, setScannerResult] = useState("");
const [isScannerActive, setIsScannerActive] = useState(false); const [isScannerActive, setIsScannerActive] = useState(false);
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
@ -73,6 +100,7 @@ export default function EquipmentDocumentationOverview() {
return ( return (
<> <>
{messageContextHolder} {messageContextHolder}
<Row style={{ marginBottom: AppStyle.app.margin }}> <Row style={{ marginBottom: AppStyle.app.margin }}>
<Col xs={0} md={7} /> <Col xs={0} md={7} />
<Col xs={24} md={10}> <Col xs={24} md={10}>
@ -94,8 +122,11 @@ export default function EquipmentDocumentationOverview() {
return; return;
} }
setScannerResult(JSON.parse(result).stockitem.toString());
setIsScannerActive(false); setIsScannerActive(false);
handleScannedEquipment(
JSON.parse(result).stockitem.toString()
);
}} }}
onError={(error) => console.error(error?.message)} onError={(error) => console.error(error?.message)}
/> />
@ -143,7 +174,7 @@ export default function EquipmentDocumentationOverview() {
return; return;
} }
setScannerResult(inputValue); handleScannedEquipment(inputValue);
}} }}
> >
{t( {t(
@ -156,20 +187,6 @@ export default function EquipmentDocumentationOverview() {
</Col> </Col>
<Col xs={0} md={7} /> <Col xs={0} md={7} />
</Row> </Row>
{scannerResult === "" ? (
<Result
status="404"
title={t(
"equipmentDocumentationOverview.noEquipmentScannedResult.title"
)}
subTitle={t(
"equipmentDocumentationOverview.noEquipmentScannedResult.description"
)}
/>
) : (
<ViewEquipmentDocumentations scannerResult={scannerResult} />
)}
</> </>
); );
} }
@ -179,11 +196,11 @@ export const EmptyNote = { Image: null, Description: "" };
export function EquipmentDocumentationViewEditComponent({ export function EquipmentDocumentationViewEditComponent({
createMode, createMode,
isOpen, isOpen,
onCancel,
fetchDocumentation, fetchDocumentation,
stockItemId, stockItemId,
documentationId, documentationId,
}) { }) {
const navigate = useNavigate();
const { t } = useTranslation(); const { t } = useTranslation();
const [title, setTitle] = useState( const [title, setTitle] = useState(
createMode createMode
@ -201,6 +218,12 @@ export function EquipmentDocumentationViewEditComponent({
useState(false); useState(false);
const documentationResponse = useRef(); const documentationResponse = useRef();
const handleCancel = () => {
navigate(
`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}${stockItemId}`
);
};
const handleCreate = () => { const handleCreate = () => {
setIsDocumentationUploading(true); setIsDocumentationUploading(true);
@ -219,12 +242,14 @@ export function EquipmentDocumentationViewEditComponent({
notes: updatedNotes, notes: updatedNotes,
}; };
myFetch(`/equipment/documentation/create`, "POST", body, {}).then( myFetch(`/equipment/documentation/create`, "POST", body, {}).then(() => {
(data) => { setIsDocumentationUploading(false);
setIsDocumentationUploading(false); fetchDocumentation();
fetchDocumentation(); handleCancel();
onCancel(); });
}
navigate(
`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}${stockItemId}`
); );
}; };
@ -248,10 +273,10 @@ export function EquipmentDocumentationViewEditComponent({
notes: updatedNotes, notes: updatedNotes,
}; };
myFetch(`/equipment/documentation/edit`, "POST", body, {}).then((data) => { myFetch(`/equipment/documentation/edit`, "POST", body, {}).then(() => {
setIsDocumentationUploading(false); setIsDocumentationUploading(false);
fetchDocumentation(); fetchDocumentation();
onCancel(); handleCancel();
}); });
}; };
@ -329,7 +354,7 @@ export function EquipmentDocumentationViewEditComponent({
return ( return (
<MyModal <MyModal
isOpen={isOpen} isOpen={isOpen}
onCancel={onCancel} onCancel={handleCancel}
title={ title={
<MyTypography <MyTypography
value={title} value={title}
@ -341,14 +366,14 @@ export function EquipmentDocumentationViewEditComponent({
createMode ? ( createMode ? (
<MyModalCloseCreateButtonFooter <MyModalCloseCreateButtonFooter
onCreate={handleCreate} onCreate={handleCreate}
onCancel={onCancel} onCancel={handleCancel}
isCreateButtonDisabled={isCreateButtonDisabled()} isCreateButtonDisabled={isCreateButtonDisabled()}
isCreateButtonLoading={isDocumentationUploading} isCreateButtonLoading={isDocumentationUploading}
/> />
) : ( ) : (
<MyModalCloseSaveButtonFooter <MyModalCloseSaveButtonFooter
onSave={handleEdit} onSave={handleEdit}
onCancel={onCancel} onCancel={handleCancel}
isSaveButtonLoading={isDocumentationUploading} isSaveButtonLoading={isDocumentationUploading}
/> />
) )

View File

@ -46,7 +46,7 @@ export const Constants = {
WS_ADDRESS: wsAddress, WS_ADDRESS: wsAddress,
ROUTE_PATHS: { ROUTE_PATHS: {
EQUIPMENT_DOCUMENTATION: "/equipment-documentation", EQUIPMENT_DOCUMENTATION: "/equipment-documentation",
//EQUIPMENT_DOCUMENTATION_VIEW: "/equipment-documentation/", EQUIPMENT_DOCUMENTATION_VIEW: "/equipment-documentation/",
EQUIPMENT_DOCUMENTATION_CREATE: "/equipment-documentation/create/", EQUIPMENT_DOCUMENTATION_CREATE: "/equipment-documentation/create/",
GROUP_TASKS: "/group-tasks", GROUP_TASKS: "/group-tasks",
GROUP_TASKS_VIEW: "/group-tasks/", GROUP_TASKS_VIEW: "/group-tasks/",
@ -86,6 +86,8 @@ export const Constants = {
PERMISSIONS: { PERMISSIONS: {
EQUIPMENT_DOCUMENTATION: { EQUIPMENT_DOCUMENTATION: {
VIEW: "equipment_documentation.view", VIEW: "equipment_documentation.view",
CREATE: "equipment_documentation.create",
EDIT: "equipment_documentation.edit",
}, },
GROUP_TASKS: { GROUP_TASKS: {
OVERVIEW: { OVERVIEW: {