equipment documentation
parent
f056e645f4
commit
c125751f17
|
@ -14,7 +14,15 @@
|
|||
"confirm": "Bestätigen",
|
||||
"create": "Erstellen"
|
||||
},
|
||||
"contactAdmin": "Bitte kontaktieren Sie einen Administrator"
|
||||
"contactAdmin": "Bitte kontaktieren Sie einen Administrator",
|
||||
"text": {
|
||||
"id": "ID:",
|
||||
"createdAt": "Erstellt am:",
|
||||
"updatedAt": "Aktualisiert am:",
|
||||
"createdBy": "Erstellt von:",
|
||||
"endedAt": "Beendet am:",
|
||||
"type": "Typ:"
|
||||
}
|
||||
},
|
||||
"sideMenu": {
|
||||
"dashboard": "Dashboard",
|
||||
|
@ -335,8 +343,54 @@
|
|||
},
|
||||
"header": { "scanners": "Scanner" }
|
||||
},
|
||||
"equipmentDocumentation": {},
|
||||
"equipmentViewModal": {
|
||||
"equipmentDocumentationNotFound": "Gerätedokumentation nicht gefunden"
|
||||
"equipmentDocumentationOverview": {
|
||||
"messageErrorInvalidStockItem": "Ungültiges Stock Item",
|
||||
"buttonCloseCamera": "Kamera schließen",
|
||||
"scanEquipment": {
|
||||
"title": "Ausrüstung scannen",
|
||||
"inputPlaceholder": "Ausrüstungs ID",
|
||||
"buttonSearch": "Suchen"
|
||||
},
|
||||
"noEquipmentScannedResult": {
|
||||
"title": "Keine Ausrüstung gescannt",
|
||||
"description": "Bitte scannen Sie eine Ausrüstung"
|
||||
}
|
||||
},
|
||||
"equipmentDocumentationViewEditComponent": {
|
||||
"selectDocumentationTypeOptions": [
|
||||
{ "value": 1, "label": "Reparaturprotokoll" },
|
||||
{ "value": 2, "label": "Dokumentation" }
|
||||
],
|
||||
"titleNewDocumentation": "Neue Dokumentation",
|
||||
"textDocumentationType": "Dokumentationstyp",
|
||||
"buttonAddNote": "Notiz hinzufügen",
|
||||
"buttonTakePicture": "Foto aufnehmen",
|
||||
"buttonMoveUp": "Nach oben verschieben",
|
||||
"buttonMoveDown": "Nach unten verschieben",
|
||||
"buttonDelete": "Löschen",
|
||||
"textImageNoImage": "Kein Bild",
|
||||
"textImageNoImageSelected": "Kein Bild ausgewählt",
|
||||
"textareaPlaceholder": "Notiz",
|
||||
"modalImageFullscreenTitle": "Vorschau"
|
||||
},
|
||||
"viewEquipmentDocumentations": {
|
||||
"detailsPopover": {
|
||||
"title": "Details"
|
||||
},
|
||||
"result403": {
|
||||
"title": "Keine Berechtigung",
|
||||
"description": "Der Backend-Server ist nicht berechtigt, auf Invex zuzugreifen. Bitte kontaktieren Sie einen Administrator."
|
||||
},
|
||||
"result500": {
|
||||
"title": "Keine Ausrüstung gefunden",
|
||||
"description": "Die gescannte Ausrüstung existiert nicht in Invex."
|
||||
},
|
||||
"result404": {
|
||||
"title": "Keine Dokumentation gefunden",
|
||||
"description": "Für die gescannte Ausrüstung existiert keine Dokumentation."
|
||||
}
|
||||
},
|
||||
"createEquipmentDocumentationModal": {
|
||||
"buttonCreateDocumentation": "Dokumentation erstellen"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,15 @@
|
|||
"confirm": "Confirm",
|
||||
"create": "Create"
|
||||
},
|
||||
"contactAdmin": "Please contact an administrator"
|
||||
"contactAdmin": "Please contact an administrator",
|
||||
"text": {
|
||||
"id": "ID:",
|
||||
"createdAt": "Created at:",
|
||||
"updatedAt": "Updated at:",
|
||||
"createdBy": "Created by:",
|
||||
"endedAt": "Ended at:",
|
||||
"type": "Type:"
|
||||
}
|
||||
},
|
||||
"sideMenu": {
|
||||
"dashboard": "Dashboard",
|
||||
|
@ -334,8 +342,54 @@
|
|||
},
|
||||
"header": { "scanners": "Scanners" }
|
||||
},
|
||||
"equipmentDocumentation": {},
|
||||
"equipmentViewModal": {
|
||||
"equipmentDocumentationNotFound": "Equipment documentation not found"
|
||||
"equipmentDocumentationOverview": {
|
||||
"messageErrorInvalidStockItem": "Invalid Stock Item",
|
||||
"buttonCloseCamera": "Close Camera",
|
||||
"scanEquipment": {
|
||||
"title": "Scan Equipment",
|
||||
"inputPlaceholder": "Equipment ID",
|
||||
"buttonSearch": "Search"
|
||||
},
|
||||
"noEquipmentScannedResult": {
|
||||
"title": "No Equipment Scanned",
|
||||
"description": "Please scan an equipment"
|
||||
}
|
||||
},
|
||||
"equipmentDocumentationViewEditComponent": {
|
||||
"selectDocumentationTypeOptions": [
|
||||
{ "value": 1, "label": "Repair protocol" },
|
||||
{ "value": 2, "label": "Documentation" }
|
||||
],
|
||||
"titleNewDocumentation": "New Documentation",
|
||||
"textDocumentationType": "Documentation Type",
|
||||
"buttonAddNote": "Add Note",
|
||||
"buttonTakePicture": "Take Photo",
|
||||
"buttonMoveUp": "Move Up",
|
||||
"buttonMoveDown": "Move Down",
|
||||
"buttonDelete": "Delete",
|
||||
"textImageNoImage": "No Image",
|
||||
"textImageNoImageSelected": "No Image Selected",
|
||||
"textareaPlaceholder": "Note",
|
||||
"modalImageFullscreenTitle": "Preview"
|
||||
},
|
||||
"viewEquipmentDocumentations": {
|
||||
"detailsPopover": {
|
||||
"title": "Details"
|
||||
},
|
||||
"result403": {
|
||||
"title": "Unauthorized Access",
|
||||
"description": "The backend server is not authorized to access Invex. Please contact an administrator."
|
||||
},
|
||||
"result500": {
|
||||
"title": "Equipment Not Found",
|
||||
"description": "The scanned equipment does not exist in Invex."
|
||||
},
|
||||
"result404": {
|
||||
"title": "Documentation Not Found",
|
||||
"description": "No documentation found for the scanned equipment."
|
||||
}
|
||||
},
|
||||
"createEquipmentDocumentationModal": {
|
||||
"buttonCreateDocumentation": "Create Documentation"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useState } from "react";
|
|||
import "antd/dist/reset.css";
|
||||
import "./App.css";
|
||||
import Login from "./Pages/Login";
|
||||
import { Layout, notification } from "antd";
|
||||
import { Layout, message, notification } from "antd";
|
||||
import { UseUserSession, WebSocketProvider } from "./utils";
|
||||
import DashboardLayout from "./Components/DashboardLayout";
|
||||
|
||||
|
|
|
@ -16,8 +16,83 @@ import GroupTasks from "../../Pages/GroupTasks/Overview";
|
|||
import GroupTasksHistory from "../../Pages/GroupTasks/History";
|
||||
import PageNotFound from "../../Pages/PageNotFound";
|
||||
import EquipmentDocumentationOverview from "../../Pages/EquipmentDocumentation";
|
||||
import ViewEquipmentDocumentations from "../../Pages/EquipmentDocumentation/ViewEquipmentDocumentation";
|
||||
|
||||
export default function AppRoutes() {
|
||||
// const webSocketContext = useContext(WebSocketContext);
|
||||
|
||||
console.log("appRoutes");
|
||||
|
||||
/*
|
||||
TODO: move down
|
||||
{hasPermission(
|
||||
webSocketContext.User.Permissions,
|
||||
Constants.PERMISSIONS.EQUIPMENT_DOCUMENTATION.VIEW
|
||||
) && (
|
||||
<Route
|
||||
path="/equipment-documentation"
|
||||
element={<EquipmentDocumentation />}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
<Route
|
||||
path={
|
||||
Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW +
|
||||
":paramEquipmentId"
|
||||
}
|
||||
element={<EquipmentDocumentation isEquipmentViewModalOpen={true} />}
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<Route
|
||||
path={
|
||||
Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION + "/:paramStockItemId"
|
||||
}
|
||||
element={<ViewEquipmentDocumentations />}
|
||||
/>
|
||||
*/
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<Dashboard />} />
|
||||
|
||||
<Route
|
||||
path={Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION}
|
||||
element={<EquipmentDocumentationOverview />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Constants.ROUTE_PATHS.GROUP_TASKS}
|
||||
element={<GroupTasks isGroupTasksViewModalOpen={false} />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Constants.ROUTE_PATHS.GROUP_TASKS_VIEW + ":paramGroupTaskId"}
|
||||
element={<GroupTasks isGroupTasksViewModalOpen={true} />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={Constants.ROUTE_PATHS.GROUP_TASKS + "-history"}
|
||||
element={<GroupTasksHistory />}
|
||||
/>
|
||||
|
||||
<Route path="/scanners" element={<Scanners />} />
|
||||
|
||||
<Route path="/users" element={<AllUsers />} />
|
||||
|
||||
<Route path="/user-profile" element={<UserProfile />} />
|
||||
|
||||
<Route path="/admin-area/roles" element={<AdminAreaRoles />} />
|
||||
|
||||
<Route path="/admin-area/logs" element={<AdminAreaLogs />} />
|
||||
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
</Routes>
|
||||
);
|
||||
} /*
|
||||
|
||||
/*
|
||||
export default function AppRoutes() {
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
|
||||
|
@ -50,7 +125,7 @@ export default function AppRoutes() {
|
|||
}
|
||||
element={<ViewEquipmentDocumentations />}
|
||||
/>
|
||||
*/
|
||||
*/ /*
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
|
@ -107,3 +182,4 @@ export default function AppRoutes() {
|
|||
</Routes>
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,7 @@ export default function MyModal({
|
|||
isOpen,
|
||||
onCancel,
|
||||
footer = <MyModalOnlyCloseButtonFooter onCancel={onCancel} />,
|
||||
title,
|
||||
}) {
|
||||
const screenBreakpoint = useBreakpoint();
|
||||
|
||||
|
@ -21,6 +22,7 @@ export default function MyModal({
|
|||
onCancel={onCancel}
|
||||
footer={footer}
|
||||
centered={screenBreakpoint.xs}
|
||||
title={title}
|
||||
>
|
||||
{children}
|
||||
</Modal>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import { EditOutlined } from "@ant-design/icons";
|
||||
import { Input, Typography } from "antd";
|
||||
import { useState } from "react";
|
||||
import { Constants } from "../../utils";
|
||||
|
||||
export default function MyTypography({ value, setValue, maxLength }) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
marginRight: 26,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{editing ? (
|
||||
<Input
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
maxLength={maxLength}
|
||||
showCount
|
||||
/>
|
||||
) : (
|
||||
<Typography.Title level={3} style={{ margin: 0 }}>
|
||||
{value}
|
||||
</Typography.Title>
|
||||
)}
|
||||
|
||||
<EditOutlined
|
||||
style={{ fontSize: 24 }}
|
||||
onClick={() => setEditing(!editing)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -2,12 +2,14 @@ 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({
|
||||
stockItemId,
|
||||
fetchDocumentation,
|
||||
buttonBlock,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
|
@ -18,7 +20,7 @@ export default function CreateEquipmentDocumentationModal({
|
|||
icon={<PlusOutlined />}
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
Create documentation
|
||||
{t("createEquipmentDocumentationModal.buttonCreateDocumentation")}
|
||||
</Button>
|
||||
|
||||
<EquipmentDocumentationViewEditComponent
|
||||
|
@ -31,161 +33,3 @@ export default function CreateEquipmentDocumentationModal({
|
|||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
export default function CreateEquipmentDocumentationModal({
|
||||
scannerResult,
|
||||
fetchDocumentation,
|
||||
buttonBlock,
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [title, setTitle] = useState("New documentation");
|
||||
const [selectedDocumentationType, setSelectedDocumentationType] = useState(
|
||||
selectDocumentationTypeOptions[0].value
|
||||
);
|
||||
const [notes, setNotes] = useState([emptyNote]);
|
||||
const [isDocumentationUploading, setIsDocumentationUploading] =
|
||||
useState(false);
|
||||
|
||||
const handleCancel = () => setIsOpen(false);
|
||||
|
||||
const handleCreate = () => {
|
||||
setIsDocumentationUploading(true);
|
||||
|
||||
const updatedNotes = [...notes];
|
||||
|
||||
updatedNotes.forEach((note, index) => {
|
||||
if (note.image === null && note.description === "") {
|
||||
updatedNotes.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
let body = {
|
||||
stockItemId: scannerResult,
|
||||
type: selectedDocumentationType,
|
||||
title: title,
|
||||
notes: updatedNotes,
|
||||
};
|
||||
|
||||
console.log("body", body);
|
||||
|
||||
myFetch(`/equipment/documentation/create`, "POST", body, {}).then(
|
||||
(data) => {
|
||||
console.log("data", data);
|
||||
|
||||
setIsDocumentationUploading(false);
|
||||
fetchDocumentation();
|
||||
handleCancel();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (index) => (e) => {
|
||||
const updatedNotes = [...notes];
|
||||
|
||||
updatedNotes[index] = {
|
||||
...updatedNotes[index],
|
||||
description: e.target.value,
|
||||
};
|
||||
|
||||
setNotes(updatedNotes);
|
||||
};
|
||||
|
||||
const handleImageChange = (index) => (newImage) => {
|
||||
const updatedNotes = [...notes];
|
||||
|
||||
updatedNotes[index] = {
|
||||
...updatedNotes[index],
|
||||
image: newImage,
|
||||
};
|
||||
|
||||
setNotes(updatedNotes);
|
||||
};
|
||||
|
||||
const handleAddNote = () => setNotes([...notes, emptyNote]);
|
||||
|
||||
const isAddNoteButtonDisabled = () => {
|
||||
const lastNote = notes[notes.length - 1];
|
||||
|
||||
return lastNote.image === null && lastNote.description === "";
|
||||
};
|
||||
|
||||
const isCreateButtonDisabled = () => {
|
||||
if (notes.length === 0) return true;
|
||||
|
||||
if (notes.length === 1) {
|
||||
return notes[0].image === null && notes[0].description === "";
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
block={buttonBlock}
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
Create documentation
|
||||
</Button>
|
||||
|
||||
<MyModal
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
footer={
|
||||
<MyModalCloseCreateButtonFooter
|
||||
onCreate={handleCreate}
|
||||
isCreateButtonDisabled={isCreateButtonDisabled()}
|
||||
isCreateButtonLoading={isDocumentationUploading}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Typography.Title
|
||||
editable={{ text: title, onChange: setTitle }}
|
||||
level={1}
|
||||
>
|
||||
{title}
|
||||
</Typography.Title>
|
||||
|
||||
<div style={{ marginBottom: AppStyle.typography.text.marginBottom }}>
|
||||
<Typography.Text>Documentation type</Typography.Text>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
//defaultValue={selectedDocumentationType}
|
||||
value={selectedDocumentationType}
|
||||
style={{ width: "100%", marginBottom: AppStyle.app.margin }}
|
||||
onChange={(value) => setSelectedDocumentationType(value)}
|
||||
options={selectDocumentationTypeOptions}
|
||||
/>
|
||||
|
||||
{notes.map((note, index) => (
|
||||
<NoteComponent
|
||||
key={index}
|
||||
index={index}
|
||||
image={note.image}
|
||||
onImageChange={handleImageChange(index)}
|
||||
description={note.description}
|
||||
onDescriptionChange={handleDescriptionChange(index)}
|
||||
onDeleteImage={() => handleImageChange(index)(null)}
|
||||
/>
|
||||
))}
|
||||
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={isAddNoteButtonDisabled()}
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAddNote}
|
||||
>
|
||||
Add note
|
||||
</Button>
|
||||
</div>
|
||||
</MyModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -8,10 +8,16 @@ import {
|
|||
Spin,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import CreateEquipmentDocumentationModal from "./CreateEquipmentDocumentationModal";
|
||||
import { AppStyle, Constants, FormatDatetime, myFetch } from "../../utils";
|
||||
import {
|
||||
AppStyle,
|
||||
Constants,
|
||||
FormatDatetime,
|
||||
WebSocketContext,
|
||||
myFetch,
|
||||
} from "../../utils";
|
||||
import {
|
||||
BookOutlined,
|
||||
InfoCircleOutlined,
|
||||
|
@ -19,8 +25,15 @@ import {
|
|||
} from "@ant-design/icons";
|
||||
import EditEquipmentDocumentationModal from "./EditEquipmentDocumentationModal";
|
||||
import { NoteComponent } from ".";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MyAvatar } from "../../Components/MyAvatar";
|
||||
|
||||
export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||
const { t } = useTranslation();
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
|
||||
console.log("render ViewEquipmentDocumentations");
|
||||
|
||||
const [equipmentDocumentationResponse, setEquipmentDocumentationResponse] =
|
||||
useState(null);
|
||||
const [isEquipmentDocumentationLoading, setIsEquipmentDocumentationLoading] =
|
||||
|
@ -31,8 +44,6 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
|
||||
myFetch(`/equipment/documentations/${scannerResult}`, "GET").then(
|
||||
(data) => {
|
||||
console.log("data", data);
|
||||
|
||||
const updatedData = { ...data };
|
||||
|
||||
// sort by date
|
||||
|
@ -47,11 +58,7 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log("scannerResult", scannerResult);
|
||||
|
||||
fetchDocumentation();
|
||||
}, [scannerResult]);
|
||||
useEffect(() => fetchDocumentation(), [scannerResult]);
|
||||
|
||||
const CreateDocumentationButton = () => {
|
||||
const InvexStockItemThumbnail = ({ width }) => {
|
||||
|
@ -65,7 +72,7 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
};
|
||||
|
||||
return (
|
||||
<Row style={{ alignItems: "center" }}>
|
||||
<Row gutter={AppStyle.grid.row.gutter} style={{ alignItems: "center" }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={{ span: 8 }}
|
||||
|
@ -139,32 +146,45 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<MyAvatar
|
||||
allUsers={webSocketContext.AllUsers}
|
||||
userId={documentation.CreatedByUserId}
|
||||
tooltip
|
||||
/>
|
||||
|
||||
<Popover
|
||||
title={
|
||||
<Typography.Title
|
||||
level={4}
|
||||
style={{ color: Constants.COLORS.SECONDARY }}
|
||||
>
|
||||
Details
|
||||
{t("viewEquipmentDocumentations.detailsPopover.title")}
|
||||
</Typography.Title>
|
||||
}
|
||||
trigger="click"
|
||||
placement="left"
|
||||
content={
|
||||
<p style={{ color: "#000", margin: 0 }}>
|
||||
<b>ID:</b> {documentation.Id}
|
||||
<b>{t("common.text.id")}</b> {documentation.Id}
|
||||
<br />
|
||||
<b>Type:</b> {documentation.Type}
|
||||
<b>{t("common.text.type")}</b>{" "}
|
||||
{
|
||||
t(
|
||||
"equipmentDocumentationViewEditComponent.selectDocumentationTypeOptions",
|
||||
{ returnObjects: true }
|
||||
)[documentation.Type - 1]?.label
|
||||
}
|
||||
<br />
|
||||
<b>Created at:</b> {FormatDatetime(documentation.CreatedAt)}
|
||||
<b>{t("common.text.createdAt")}</b>{" "}
|
||||
{FormatDatetime(documentation.CreatedAt)}
|
||||
<br />
|
||||
<b>Updated at:</b> {FormatDatetime(documentation.UpdatedAt)}
|
||||
<b>{t("common.text.updatedAt")}</b>{" "}
|
||||
{FormatDatetime(documentation.UpdatedAt)}
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<InfoCircleOutlined
|
||||
style={{ fontSize: 24, color: Constants.COLORS.ICON_INFO }}
|
||||
onClick={() => console.log("info")}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
|
@ -177,10 +197,7 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
</div>
|
||||
|
||||
{documentation.Notes !== "" &&
|
||||
JSON.parse(documentation.Notes).map((note, index) => {
|
||||
console.log("map doc");
|
||||
|
||||
return (
|
||||
JSON.parse(documentation.Notes).map((note, index) => (
|
||||
<NoteComponent
|
||||
key={index}
|
||||
viewMode
|
||||
|
@ -188,8 +205,7 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
documentationId={documentation.Id}
|
||||
description={note.Description}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
@ -216,8 +232,8 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
return (
|
||||
<Result
|
||||
status="403"
|
||||
title="401"
|
||||
subTitle="The backend server is not authorized to access invex."
|
||||
title={t("viewEquipmentDocumentations.result403.title")}
|
||||
subTitle={t("viewEquipmentDocumentations.result403.description")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -227,8 +243,8 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
return (
|
||||
<Result
|
||||
status="500"
|
||||
title="500"
|
||||
subTitle="The scanned item doesn't exists on invex"
|
||||
title={t("viewEquipmentDocumentations.result500.title")}
|
||||
subTitle={t("viewEquipmentDocumentations.result500.description")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -237,8 +253,8 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
|||
return (
|
||||
<Result
|
||||
status="404"
|
||||
title="404"
|
||||
subTitle="Sorry, for the equipment does not exist an documentation."
|
||||
title={t("viewEquipmentDocumentations.result404.title")}
|
||||
subTitle={t("viewEquipmentDocumentations.result404.description")}
|
||||
extra={[
|
||||
<CreateEquipmentDocumentationModal
|
||||
key="0"
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
ArrowUpOutlined,
|
||||
CameraOutlined,
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
EllipsisOutlined,
|
||||
FullscreenOutlined,
|
||||
PlusOutlined,
|
||||
|
@ -12,7 +13,6 @@ import {
|
|||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Input,
|
||||
Result,
|
||||
|
@ -33,18 +33,19 @@ import MyModal, {
|
|||
} from "../../Components/MyModal";
|
||||
import Webcam from "react-webcam";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
|
||||
message.config({
|
||||
maxCount: 2,
|
||||
});
|
||||
import { useTranslation } from "react-i18next";
|
||||
import MyTypography from "../../Components/MyTypography";
|
||||
|
||||
export default function EquipmentDocumentationOverview() {
|
||||
const { t } = useTranslation();
|
||||
const [messageApi, messageContextHolder] = message.useMessage({
|
||||
maxCount: 2,
|
||||
});
|
||||
|
||||
const [scannerResult, setScannerResult] = useState("");
|
||||
const [isScannerActive, setIsScannerActive] = useState(false);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
console.log("scan", scannerResult);
|
||||
|
||||
const isScannedQrCodeValid = (result) => {
|
||||
// {"stockitem": 11} or 11 is valid
|
||||
|
||||
|
@ -72,6 +73,7 @@ export default function EquipmentDocumentationOverview() {
|
|||
|
||||
return (
|
||||
<>
|
||||
{messageContextHolder}
|
||||
<Row style={{ marginBottom: AppStyle.app.margin }}>
|
||||
<Col xs={0} md={7} />
|
||||
<Col xs={24} md={10}>
|
||||
|
@ -87,7 +89,11 @@ export default function EquipmentDocumentationOverview() {
|
|||
console.log(result);
|
||||
|
||||
if (!isScannedQrCodeValid(result)) {
|
||||
message.error("Invalid stock item QR code");
|
||||
messageApi.error(
|
||||
t(
|
||||
"equipmentDocumentationOverview.messageErrorInvalidStockItem"
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -104,20 +110,24 @@ export default function EquipmentDocumentationOverview() {
|
|||
marginBottom: AppStyle.app.margin,
|
||||
}}
|
||||
>
|
||||
Close camera
|
||||
{t("equipmentDocumentationOverview.buttonCloseCamera")}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<div onClick={() => setIsScannerActive(true)}>
|
||||
<CameraOutlined style={{ fontSize: 64 }} />
|
||||
<Typography.Title level={5}>Scan equipment</Typography.Title>
|
||||
<Typography.Title level={5}>
|
||||
{t("equipmentDocumentationOverview.scanEquipment.title")}
|
||||
</Typography.Title>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Row gutter={AppStyle.grid.row.gutter}>
|
||||
<Col xs={24} xl={16}>
|
||||
<Input
|
||||
placeholder="Equipment id"
|
||||
placeholder={t(
|
||||
"equipmentDocumentationOverview.scanEquipment.inputPlaceholder"
|
||||
)}
|
||||
value={inputValue}
|
||||
onInput={(e) => setInputValue(e.target.value)}
|
||||
/>
|
||||
|
@ -128,14 +138,20 @@ export default function EquipmentDocumentationOverview() {
|
|||
icon={<SearchOutlined />}
|
||||
onClick={() => {
|
||||
if (!isScannedQrCodeValid(inputValue)) {
|
||||
message.error("Invalid stock item code");
|
||||
messageApi.error(
|
||||
t(
|
||||
"equipmentDocumentationOverview.messageErrorInvalidStockItem"
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setScannerResult(inputValue);
|
||||
}}
|
||||
>
|
||||
Search
|
||||
{t(
|
||||
"equipmentDocumentationOverview.scanEquipment.buttonSearch"
|
||||
)}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -147,8 +163,12 @@ export default function EquipmentDocumentationOverview() {
|
|||
{scannerResult === "" ? (
|
||||
<Result
|
||||
status="404"
|
||||
title="No equipment scanned"
|
||||
subTitle="Scan a equipment to see the documentation."
|
||||
title={t(
|
||||
"equipmentDocumentationOverview.noEquipmentScannedResult.title"
|
||||
)}
|
||||
subTitle={t(
|
||||
"equipmentDocumentationOverview.noEquipmentScannedResult.description"
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<ViewEquipmentDocumentations scannerResult={scannerResult} />
|
||||
|
@ -159,11 +179,6 @@ export default function EquipmentDocumentationOverview() {
|
|||
|
||||
export const EmptyNote = { Image: null, Description: "" };
|
||||
|
||||
const selectDocumentationTypeOptions = [
|
||||
{ value: 1, label: "Repair protocol" },
|
||||
{ value: 2, label: "Documentation" },
|
||||
];
|
||||
|
||||
export function EquipmentDocumentationViewEditComponent({
|
||||
createMode,
|
||||
isOpen,
|
||||
|
@ -172,9 +187,17 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
stockItemId,
|
||||
documentationId,
|
||||
}) {
|
||||
const [title, setTitle] = useState(createMode ? "New documentation" : "");
|
||||
const { t } = useTranslation();
|
||||
const [title, setTitle] = useState(
|
||||
createMode
|
||||
? t("equipmentDocumentationViewEditComponent.titleNewDocumentation")
|
||||
: ""
|
||||
);
|
||||
const [selectedDocumentationType, setSelectedDocumentationType] = useState(
|
||||
selectDocumentationTypeOptions[0].value
|
||||
t(
|
||||
"equipmentDocumentationViewEditComponent.selectDocumentationTypeOptions",
|
||||
{ returnObjects: true }
|
||||
)[0].value
|
||||
);
|
||||
const [notes, setNotes] = useState([EmptyNote]);
|
||||
const [isDocumentationUploading, setIsDocumentationUploading] =
|
||||
|
@ -199,12 +222,8 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
notes: updatedNotes,
|
||||
};
|
||||
|
||||
console.log("body", body);
|
||||
|
||||
myFetch(`/equipment/documentation/create`, "POST", body, {}).then(
|
||||
(data) => {
|
||||
console.log("data", data);
|
||||
|
||||
setIsDocumentationUploading(false);
|
||||
fetchDocumentation();
|
||||
onCancel();
|
||||
|
@ -217,8 +236,6 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
|
||||
const updatedNotes = [...notes];
|
||||
|
||||
console.log("documentationResponse.current", documentationResponse.current);
|
||||
|
||||
updatedNotes.forEach((note, index) => {
|
||||
if (note.Image?.startsWith("http")) {
|
||||
updatedNotes[index].Image = JSON.parse(
|
||||
|
@ -234,11 +251,7 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
notes: updatedNotes,
|
||||
};
|
||||
|
||||
console.log("body", body);
|
||||
|
||||
myFetch(`/equipment/documentation/edit`, "POST", body, {}).then((data) => {
|
||||
console.log("data", data);
|
||||
|
||||
setIsDocumentationUploading(false);
|
||||
fetchDocumentation();
|
||||
onCancel();
|
||||
|
@ -279,7 +292,9 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
if (notes.length === 0) return true;
|
||||
|
||||
if (notes.length === 1) {
|
||||
return notes[0].Image === null && notes[0].Description === "";
|
||||
return (
|
||||
(notes[0].Image === null && notes[0].Description === "") || title === ""
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -294,8 +309,6 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
`/equipment/documentation/${stockItemId}/${documentationId}`,
|
||||
"GET"
|
||||
).then((data) => {
|
||||
console.log("data", data);
|
||||
|
||||
documentationResponse.current = data;
|
||||
|
||||
setTitle(data.Title);
|
||||
|
@ -320,6 +333,13 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
<MyModal
|
||||
isOpen={isOpen}
|
||||
onCancel={onCancel}
|
||||
title={
|
||||
<MyTypography
|
||||
value={title}
|
||||
setValue={setTitle}
|
||||
maxLength={Constants.GLOBALS.MAX_EQUIPMENT_DOCUMENTATION_TITLE_LENGTH}
|
||||
/>
|
||||
}
|
||||
footer={
|
||||
createMode ? (
|
||||
<MyModalCloseCreateButtonFooter
|
||||
|
@ -337,22 +357,20 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
)
|
||||
}
|
||||
>
|
||||
<Typography.Title
|
||||
editable={{ text: title, onChange: setTitle }}
|
||||
level={1}
|
||||
>
|
||||
{title}
|
||||
</Typography.Title>
|
||||
|
||||
<div style={{ marginBottom: AppStyle.typography.text.marginBottom }}>
|
||||
<Typography.Text>Documentation type</Typography.Text>
|
||||
<Typography.Text>
|
||||
{t("equipmentDocumentationViewEditComponent.textDocumentationType")}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
value={selectedDocumentationType}
|
||||
style={{ width: "100%", marginBottom: AppStyle.app.margin }}
|
||||
onChange={(value) => setSelectedDocumentationType(value)}
|
||||
options={selectDocumentationTypeOptions}
|
||||
options={t(
|
||||
"equipmentDocumentationViewEditComponent.selectDocumentationTypeOptions",
|
||||
{ returnObjects: true }
|
||||
)}
|
||||
/>
|
||||
|
||||
{notes.map((note, index) => (
|
||||
|
@ -440,19 +458,19 @@ export function EquipmentDocumentationViewEditComponent({
|
|||
icon={<PlusOutlined />}
|
||||
onClick={handleAddNote}
|
||||
>
|
||||
Add note
|
||||
{t("equipmentDocumentationViewEditComponent.buttonAddNote")}
|
||||
</Button>
|
||||
</div>
|
||||
</MyModal>
|
||||
);
|
||||
}
|
||||
|
||||
function UploadComponent({ setImagePreview }) {
|
||||
function UploadComponent({ setImagePreview, messageApi }) {
|
||||
const handleBeforeUpload = (file) => {
|
||||
if (
|
||||
!Constants.ACCEPTED_EQUIPMENT_DOCUMENTATION_FILE_TYPES.includes(file.type)
|
||||
) {
|
||||
message.error(`${file.name} is not valid file type`);
|
||||
messageApi.error(`${file.name} is not valid file type`);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -476,6 +494,7 @@ function UploadComponent({ setImagePreview }) {
|
|||
}
|
||||
|
||||
function CameraComponent({ setImagePreview }) {
|
||||
const { t } = useTranslation();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [cameraVisible, setCameraVisible] = useState(false);
|
||||
const webcamRef = useRef(null);
|
||||
|
@ -509,7 +528,7 @@ function CameraComponent({ setImagePreview }) {
|
|||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key={0} block type="primary" onClick={handleCapture}>
|
||||
Take picture
|
||||
{t("equipmentDocumentationViewEditComponent.buttonTakePicture")}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
|
@ -532,6 +551,8 @@ function CameraComponent({ setImagePreview }) {
|
|||
}
|
||||
|
||||
function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={["click"]}
|
||||
|
@ -542,7 +563,9 @@ function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
|||
<div onClick={onMoveUp}>
|
||||
<Space>
|
||||
<ArrowUpOutlined />
|
||||
<span>Move up</span>
|
||||
<span>
|
||||
{t("equipmentDocumentationViewEditComponent.buttonMoveUp")}
|
||||
</span>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
|
@ -553,7 +576,11 @@ function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
|||
<div onClick={onMoveDown}>
|
||||
<Space>
|
||||
<ArrowDownOutlined />
|
||||
<span>Move down</span>
|
||||
<span>
|
||||
{t(
|
||||
"equipmentDocumentationViewEditComponent.buttonMoveDown"
|
||||
)}
|
||||
</span>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
|
@ -564,7 +591,9 @@ function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
|||
<div onClick={onDelete}>
|
||||
<Space>
|
||||
<DeleteOutlined />
|
||||
<span>Delete note</span>
|
||||
<span>
|
||||
{t("equipmentDocumentationViewEditComponent.buttonDelete")}
|
||||
</span>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
|
@ -590,6 +619,8 @@ export function NoteComponent({
|
|||
onMoveDown,
|
||||
onDelete,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [messageApi, messageContextHolder] = message.useMessage();
|
||||
const [isImageFullScreenModalOpen, setIsImageFullScreenModalOpen] =
|
||||
useState(false);
|
||||
|
||||
|
@ -610,6 +641,8 @@ export function NoteComponent({
|
|||
|
||||
return (
|
||||
<div style={{ marginBottom: AppStyle.app.margin }}>
|
||||
{messageContextHolder}
|
||||
|
||||
<Row gutter={[AppStyle.grid.row.gutter[0]]}>
|
||||
<Col xs={24} md={8}>
|
||||
<Card
|
||||
|
@ -620,7 +653,10 @@ export function NoteComponent({
|
|||
? [<FullscreenOutlinedIcon />]
|
||||
: null
|
||||
: [
|
||||
<UploadComponent setImagePreview={onImageChange} />,
|
||||
<UploadComponent
|
||||
setImagePreview={onImageChange}
|
||||
messageApi={messageApi}
|
||||
/>,
|
||||
<CameraComponent setImagePreview={onImageChange} />,
|
||||
<DeleteOutlined onClick={onDeleteImage} />,
|
||||
<FullscreenOutlinedIcon
|
||||
|
@ -643,7 +679,13 @@ export function NoteComponent({
|
|||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{viewMode ? "No image" : "No image selected"}
|
||||
{viewMode
|
||||
? t(
|
||||
"equipmentDocumentationViewEditComponent.textImageNoImage"
|
||||
)
|
||||
: t(
|
||||
"equipmentDocumentationViewEditComponent.textImageNoImageSelected"
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<img
|
||||
|
@ -664,10 +706,16 @@ export function NoteComponent({
|
|||
<Typography.Text>{description}</Typography.Text>
|
||||
) : (
|
||||
<TextArea
|
||||
rows={8}
|
||||
placeholder="Description"
|
||||
autoSize={{ minRows: 8, maxRows: 13 }}
|
||||
placeholder={t(
|
||||
"equipmentDocumentationViewEditComponent.textareaPlaceholder"
|
||||
)}
|
||||
value={description}
|
||||
onChange={onDescriptionChange}
|
||||
showCount
|
||||
maxLength={
|
||||
Constants.GLOBALS.MAX_EQUIPMENT_DOCUMENTATION_NOTE_LENGTH
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
|
@ -683,8 +731,16 @@ export function NoteComponent({
|
|||
}
|
||||
|
||||
function ImageFullscreenModal({ isOpen, onCancel, image }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<MyModal isOpen={isOpen} onCancel={onCancel}>
|
||||
<MyModal
|
||||
isOpen={isOpen}
|
||||
onCancel={onCancel}
|
||||
title={t(
|
||||
"equipmentDocumentationViewEditComponent.modalImageFullscreenTitle"
|
||||
)}
|
||||
>
|
||||
<img width="100%" src={image} alt="Fullscreen preview" />
|
||||
</MyModal>
|
||||
);
|
||||
|
|
|
@ -69,6 +69,8 @@ export const Constants = {
|
|||
MIN_ROLE_DISPLAY_NAME: 3,
|
||||
MAX_ROLE_DISPLAY_NAME: 30,
|
||||
MAX_ROLE_DESCRIPTION: 80,
|
||||
MAX_EQUIPMENT_DOCUMENTATION_TITLE_LENGTH: 60,
|
||||
MAX_EQUIPMENT_DOCUMENTATION_NOTE_LENGTH: 2000,
|
||||
},
|
||||
MAX_AVATAR_SIZE: 5 * 1024 * 1024,
|
||||
ACCEPTED_AVATAR_FILE_TYPES: [
|
||||
|
|
Loading…
Reference in New Issue