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(() =>
import("../../Pages/EquipmentDocumentation")
);
const ViewEquipmentDocumentations = lazy(() =>
import("../../Pages/EquipmentDocumentation/ViewEquipmentDocumentation")
);
function SuspenseFallback({ children }) {
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
path={Constants.ROUTE_PATHS.GROUP_TASKS}
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 { useTranslation } from "react-i18next";
export default function CreateEquipmentDocumentationModal({
isOpen,
stockItemId,
fetchDocumentation,
buttonBlock,
}) {
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button
block={buttonBlock}
type="primary"
icon={<PlusOutlined />}
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 ".";
export default function EditEquipmentDocumentationModal({
isOpen,
stockItemId,
documentationId,
fetchDocumentation,
}) {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<EditOutlined style={{ fontSize: 24 }} onClick={() => setIsOpen(true)} />
<EquipmentDocumentationViewEditComponent
createMode={false}
isOpen={isOpen}
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 CreateEquipmentDocumentationModal from "./CreateEquipmentDocumentationModal";
import { AppStyle, Constants, FormatDatetime, myFetch } from "../../utils";
import {
AppStyle,
Constants,
FormatDatetime,
hasPermission,
myFetch,
} from "../../utils";
import {
BookOutlined,
EditOutlined,
InfoCircleOutlined,
PlusOutlined,
ToolOutlined,
} from "@ant-design/icons";
import EditEquipmentDocumentationModal from "./EditEquipmentDocumentationModal";
import { NoteComponent } from ".";
import { NoteComponent, ScannerComponent } from ".";
import { useTranslation } from "react-i18next";
import { MyAvatar } from "../../Components/MyAvatar";
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 navigate = useNavigate();
const appContext = useAppContext();
console.log("render ViewEquipmentDocumentations");
const { paramStockId, paramDocumentationId } = useParams();
const [equipmentDocumentationResponse, setEquipmentDocumentationResponse] =
useState(null);
@ -27,8 +48,7 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
const fetchDocumentation = () => {
setIsEquipmentDocumentationLoading(true);
myFetch(`/equipment/documentations/${scannerResult}`, "GET").then(
(data) => {
myFetch(`/equipment/documentations/${paramStockId}`, "GET").then((data) => {
const updatedData = { ...data };
// sort by date
@ -39,64 +59,42 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
setIsEquipmentDocumentationLoading(false);
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 CreateDocumentationButton = () => {
const InvexStockItemThumbnail = ({ width }) => {
return (
<img
src={`${Constants.API_ADDRESS}/equipment/thumbnail/${scannerResult}`}
src={`${Constants.API_ADDRESS}/equipment/thumbnail/${paramStockId}`}
width={width}
style={{ borderRadius: 6 }}
/>
);
};
return (
<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 }}>
{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>
);
};
const DocumentationContent = ({ documentation }) => {
return (
<Card style={{ marginTop: AppStyle.app.margin }}>
@ -173,11 +171,15 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
/>
</Popover>
<EditEquipmentDocumentationModal
stockItemId={scannerResult}
documentationId={documentation.Id}
fetchDocumentation={fetchDocumentation}
{hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.EDIT
) && (
<EditOutlined
style={{ fontSize: 24 }}
onClick={() => navigate(`edit/${documentation.Id}`)}
/>
)}
</div>
</div>
@ -197,6 +199,9 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
if (isEquipmentDocumentationLoading) {
return (
<>
<ScannerComponent />
<div
style={{
display: "flex",
@ -207,60 +212,116 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
>
<Spin size="large" />
</div>
</>
);
}
if (equipmentDocumentationResponse === null) return null;
if (equipmentDocumentationResponse === null) return <ScannerComponent />;
// backend unauthorized to access invex
if (equipmentDocumentationResponse.Status === 401) {
return (
<>
<ScannerComponent />
<Result
status="403"
title={t("viewEquipmentDocumentations.result403.title")}
subTitle={t("viewEquipmentDocumentations.result403.description")}
/>
</>
);
}
// stock item doesn't exists on invex
if (equipmentDocumentationResponse.Status === 404) {
return (
<>
<ScannerComponent />
<Result
status="500"
title={t("viewEquipmentDocumentations.result500.title")}
subTitle={t("viewEquipmentDocumentations.result500.description")}
/>
</>
);
}
if (equipmentDocumentationResponse.Documentations.length === 0) {
return (
<>
<ScannerComponent />
<Result
status="404"
title={t("viewEquipmentDocumentations.result404.title")}
subTitle={t("viewEquipmentDocumentations.result404.description")}
extra={[
<CreateEquipmentDocumentationModal
key="0"
stockItemId={scannerResult}
fetchDocumentation={fetchDocumentation}
buttonBlock={false}
/>,
<CreateEquipmentDocumentationButton key="0" buttonBlock={false} />,
]}
/>
</>
);
}
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(
(documentation, index) => (
<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 { useEffect, useRef, useState } from "react";
import { QrScanner } from "@yudiel/react-qr-scanner";
import ViewEquipmentDocumentations from "./ViewEquipmentDocumentation";
import MyModal, {
MyModalCloseCreateButtonFooter,
MyModalCloseSaveButtonFooter,
@ -34,14 +33,42 @@ import Webcam from "react-webcam";
import TextArea from "antd/es/input/TextArea";
import { useTranslation } from "react-i18next";
import MyTypography from "../../Components/MyTypography";
import { useNavigate } from "react-router-dom";
export default function EquipmentDocumentationOverview() {
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({
maxCount: 2,
});
const [scannerResult, setScannerResult] = useState("");
const [isScannerActive, setIsScannerActive] = useState(false);
const [inputValue, setInputValue] = useState("");
@ -73,6 +100,7 @@ export default function EquipmentDocumentationOverview() {
return (
<>
{messageContextHolder}
<Row style={{ marginBottom: AppStyle.app.margin }}>
<Col xs={0} md={7} />
<Col xs={24} md={10}>
@ -94,8 +122,11 @@ export default function EquipmentDocumentationOverview() {
return;
}
setScannerResult(JSON.parse(result).stockitem.toString());
setIsScannerActive(false);
handleScannedEquipment(
JSON.parse(result).stockitem.toString()
);
}}
onError={(error) => console.error(error?.message)}
/>
@ -143,7 +174,7 @@ export default function EquipmentDocumentationOverview() {
return;
}
setScannerResult(inputValue);
handleScannedEquipment(inputValue);
}}
>
{t(
@ -156,20 +187,6 @@ export default function EquipmentDocumentationOverview() {
</Col>
<Col xs={0} md={7} />
</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({
createMode,
isOpen,
onCancel,
fetchDocumentation,
stockItemId,
documentationId,
}) {
const navigate = useNavigate();
const { t } = useTranslation();
const [title, setTitle] = useState(
createMode
@ -201,6 +218,12 @@ export function EquipmentDocumentationViewEditComponent({
useState(false);
const documentationResponse = useRef();
const handleCancel = () => {
navigate(
`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}${stockItemId}`
);
};
const handleCreate = () => {
setIsDocumentationUploading(true);
@ -219,12 +242,14 @@ export function EquipmentDocumentationViewEditComponent({
notes: updatedNotes,
};
myFetch(`/equipment/documentation/create`, "POST", body, {}).then(
(data) => {
myFetch(`/equipment/documentation/create`, "POST", body, {}).then(() => {
setIsDocumentationUploading(false);
fetchDocumentation();
onCancel();
}
handleCancel();
});
navigate(
`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW}${stockItemId}`
);
};
@ -248,10 +273,10 @@ export function EquipmentDocumentationViewEditComponent({
notes: updatedNotes,
};
myFetch(`/equipment/documentation/edit`, "POST", body, {}).then((data) => {
myFetch(`/equipment/documentation/edit`, "POST", body, {}).then(() => {
setIsDocumentationUploading(false);
fetchDocumentation();
onCancel();
handleCancel();
});
};
@ -329,7 +354,7 @@ export function EquipmentDocumentationViewEditComponent({
return (
<MyModal
isOpen={isOpen}
onCancel={onCancel}
onCancel={handleCancel}
title={
<MyTypography
value={title}
@ -341,14 +366,14 @@ export function EquipmentDocumentationViewEditComponent({
createMode ? (
<MyModalCloseCreateButtonFooter
onCreate={handleCreate}
onCancel={onCancel}
onCancel={handleCancel}
isCreateButtonDisabled={isCreateButtonDisabled()}
isCreateButtonLoading={isDocumentationUploading}
/>
) : (
<MyModalCloseSaveButtonFooter
onSave={handleEdit}
onCancel={onCancel}
onCancel={handleCancel}
isSaveButtonLoading={isDocumentationUploading}
/>
)

View File

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