equipment documentation
parent
f056e645f4
commit
c125751f17
|
@ -14,7 +14,15 @@
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"create": "Erstellen"
|
"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": {
|
"sideMenu": {
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
|
@ -335,8 +343,54 @@
|
||||||
},
|
},
|
||||||
"header": { "scanners": "Scanner" }
|
"header": { "scanners": "Scanner" }
|
||||||
},
|
},
|
||||||
"equipmentDocumentation": {},
|
"equipmentDocumentationOverview": {
|
||||||
"equipmentViewModal": {
|
"messageErrorInvalidStockItem": "Ungültiges Stock Item",
|
||||||
"equipmentDocumentationNotFound": "Gerätedokumentation nicht gefunden"
|
"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",
|
"confirm": "Confirm",
|
||||||
"create": "Create"
|
"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": {
|
"sideMenu": {
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
|
@ -334,8 +342,54 @@
|
||||||
},
|
},
|
||||||
"header": { "scanners": "Scanners" }
|
"header": { "scanners": "Scanners" }
|
||||||
},
|
},
|
||||||
"equipmentDocumentation": {},
|
"equipmentDocumentationOverview": {
|
||||||
"equipmentViewModal": {
|
"messageErrorInvalidStockItem": "Invalid Stock Item",
|
||||||
"equipmentDocumentationNotFound": "Equipment documentation not found"
|
"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 "antd/dist/reset.css";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import Login from "./Pages/Login";
|
import Login from "./Pages/Login";
|
||||||
import { Layout, notification } from "antd";
|
import { Layout, message, notification } from "antd";
|
||||||
import { UseUserSession, WebSocketProvider } from "./utils";
|
import { UseUserSession, WebSocketProvider } from "./utils";
|
||||||
import DashboardLayout from "./Components/DashboardLayout";
|
import DashboardLayout from "./Components/DashboardLayout";
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,83 @@ import GroupTasks from "../../Pages/GroupTasks/Overview";
|
||||||
import GroupTasksHistory from "../../Pages/GroupTasks/History";
|
import GroupTasksHistory from "../../Pages/GroupTasks/History";
|
||||||
import PageNotFound from "../../Pages/PageNotFound";
|
import PageNotFound from "../../Pages/PageNotFound";
|
||||||
import EquipmentDocumentationOverview from "../../Pages/EquipmentDocumentation";
|
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() {
|
export default function AppRoutes() {
|
||||||
const webSocketContext = useContext(WebSocketContext);
|
const webSocketContext = useContext(WebSocketContext);
|
||||||
|
|
||||||
|
@ -50,7 +125,7 @@ export default function AppRoutes() {
|
||||||
}
|
}
|
||||||
element={<ViewEquipmentDocumentations />}
|
element={<ViewEquipmentDocumentations />}
|
||||||
/>
|
/>
|
||||||
*/
|
*/ /*
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
|
@ -107,3 +182,4 @@ export default function AppRoutes() {
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -10,6 +10,7 @@ export default function MyModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onCancel,
|
onCancel,
|
||||||
footer = <MyModalOnlyCloseButtonFooter onCancel={onCancel} />,
|
footer = <MyModalOnlyCloseButtonFooter onCancel={onCancel} />,
|
||||||
|
title,
|
||||||
}) {
|
}) {
|
||||||
const screenBreakpoint = useBreakpoint();
|
const screenBreakpoint = useBreakpoint();
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ export default function MyModal({
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
footer={footer}
|
footer={footer}
|
||||||
centered={screenBreakpoint.xs}
|
centered={screenBreakpoint.xs}
|
||||||
|
title={title}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Modal>
|
</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 { useState } from "react";
|
||||||
import { PlusOutlined } from "@ant-design/icons";
|
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({
|
||||||
stockItemId,
|
stockItemId,
|
||||||
fetchDocumentation,
|
fetchDocumentation,
|
||||||
buttonBlock,
|
buttonBlock,
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -18,7 +20,7 @@ export default function CreateEquipmentDocumentationModal({
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => setIsOpen(true)}
|
||||||
>
|
>
|
||||||
Create documentation
|
{t("createEquipmentDocumentationModal.buttonCreateDocumentation")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<EquipmentDocumentationViewEditComponent
|
<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,
|
Spin,
|
||||||
Typography,
|
Typography,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import { useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import CreateEquipmentDocumentationModal from "./CreateEquipmentDocumentationModal";
|
import CreateEquipmentDocumentationModal from "./CreateEquipmentDocumentationModal";
|
||||||
import { AppStyle, Constants, FormatDatetime, myFetch } from "../../utils";
|
import {
|
||||||
|
AppStyle,
|
||||||
|
Constants,
|
||||||
|
FormatDatetime,
|
||||||
|
WebSocketContext,
|
||||||
|
myFetch,
|
||||||
|
} from "../../utils";
|
||||||
import {
|
import {
|
||||||
BookOutlined,
|
BookOutlined,
|
||||||
InfoCircleOutlined,
|
InfoCircleOutlined,
|
||||||
|
@ -19,8 +25,15 @@ import {
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import EditEquipmentDocumentationModal from "./EditEquipmentDocumentationModal";
|
import EditEquipmentDocumentationModal from "./EditEquipmentDocumentationModal";
|
||||||
import { NoteComponent } from ".";
|
import { NoteComponent } from ".";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { MyAvatar } from "../../Components/MyAvatar";
|
||||||
|
|
||||||
export default function ViewEquipmentDocumentations({ scannerResult }) {
|
export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const webSocketContext = useContext(WebSocketContext);
|
||||||
|
|
||||||
|
console.log("render ViewEquipmentDocumentations");
|
||||||
|
|
||||||
const [equipmentDocumentationResponse, setEquipmentDocumentationResponse] =
|
const [equipmentDocumentationResponse, setEquipmentDocumentationResponse] =
|
||||||
useState(null);
|
useState(null);
|
||||||
const [isEquipmentDocumentationLoading, setIsEquipmentDocumentationLoading] =
|
const [isEquipmentDocumentationLoading, setIsEquipmentDocumentationLoading] =
|
||||||
|
@ -31,8 +44,6 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
|
|
||||||
myFetch(`/equipment/documentations/${scannerResult}`, "GET").then(
|
myFetch(`/equipment/documentations/${scannerResult}`, "GET").then(
|
||||||
(data) => {
|
(data) => {
|
||||||
console.log("data", data);
|
|
||||||
|
|
||||||
const updatedData = { ...data };
|
const updatedData = { ...data };
|
||||||
|
|
||||||
// sort by date
|
// sort by date
|
||||||
|
@ -47,11 +58,7 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => fetchDocumentation(), [scannerResult]);
|
||||||
console.log("scannerResult", scannerResult);
|
|
||||||
|
|
||||||
fetchDocumentation();
|
|
||||||
}, [scannerResult]);
|
|
||||||
|
|
||||||
const CreateDocumentationButton = () => {
|
const CreateDocumentationButton = () => {
|
||||||
const InvexStockItemThumbnail = ({ width }) => {
|
const InvexStockItemThumbnail = ({ width }) => {
|
||||||
|
@ -65,7 +72,7 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row style={{ alignItems: "center" }}>
|
<Row gutter={AppStyle.grid.row.gutter} style={{ alignItems: "center" }}>
|
||||||
<Col
|
<Col
|
||||||
xs={24}
|
xs={24}
|
||||||
sm={{ span: 8 }}
|
sm={{ span: 8 }}
|
||||||
|
@ -139,32 +146,45 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<MyAvatar
|
||||||
|
allUsers={webSocketContext.AllUsers}
|
||||||
|
userId={documentation.CreatedByUserId}
|
||||||
|
tooltip
|
||||||
|
/>
|
||||||
|
|
||||||
<Popover
|
<Popover
|
||||||
title={
|
title={
|
||||||
<Typography.Title
|
<Typography.Title
|
||||||
level={4}
|
level={4}
|
||||||
style={{ color: Constants.COLORS.SECONDARY }}
|
style={{ color: Constants.COLORS.SECONDARY }}
|
||||||
>
|
>
|
||||||
Details
|
{t("viewEquipmentDocumentations.detailsPopover.title")}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
}
|
}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
placement="left"
|
placement="left"
|
||||||
content={
|
content={
|
||||||
<p style={{ color: "#000", margin: 0 }}>
|
<p style={{ color: "#000", margin: 0 }}>
|
||||||
<b>ID:</b> {documentation.Id}
|
<b>{t("common.text.id")}</b> {documentation.Id}
|
||||||
<br />
|
<br />
|
||||||
<b>Type:</b> {documentation.Type}
|
<b>{t("common.text.type")}</b>{" "}
|
||||||
|
{
|
||||||
|
t(
|
||||||
|
"equipmentDocumentationViewEditComponent.selectDocumentationTypeOptions",
|
||||||
|
{ returnObjects: true }
|
||||||
|
)[documentation.Type - 1]?.label
|
||||||
|
}
|
||||||
<br />
|
<br />
|
||||||
<b>Created at:</b> {FormatDatetime(documentation.CreatedAt)}
|
<b>{t("common.text.createdAt")}</b>{" "}
|
||||||
|
{FormatDatetime(documentation.CreatedAt)}
|
||||||
<br />
|
<br />
|
||||||
<b>Updated at:</b> {FormatDatetime(documentation.UpdatedAt)}
|
<b>{t("common.text.updatedAt")}</b>{" "}
|
||||||
|
{FormatDatetime(documentation.UpdatedAt)}
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<InfoCircleOutlined
|
<InfoCircleOutlined
|
||||||
style={{ fontSize: 24, color: Constants.COLORS.ICON_INFO }}
|
style={{ fontSize: 24, color: Constants.COLORS.ICON_INFO }}
|
||||||
onClick={() => console.log("info")}
|
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
|
@ -177,19 +197,15 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{documentation.Notes !== "" &&
|
{documentation.Notes !== "" &&
|
||||||
JSON.parse(documentation.Notes).map((note, index) => {
|
JSON.parse(documentation.Notes).map((note, index) => (
|
||||||
console.log("map doc");
|
<NoteComponent
|
||||||
|
key={index}
|
||||||
return (
|
viewMode
|
||||||
<NoteComponent
|
image={note.Image}
|
||||||
key={index}
|
documentationId={documentation.Id}
|
||||||
viewMode
|
description={note.Description}
|
||||||
image={note.Image}
|
/>
|
||||||
documentationId={documentation.Id}
|
))}
|
||||||
description={note.Description}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -216,8 +232,8 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
return (
|
return (
|
||||||
<Result
|
<Result
|
||||||
status="403"
|
status="403"
|
||||||
title="401"
|
title={t("viewEquipmentDocumentations.result403.title")}
|
||||||
subTitle="The backend server is not authorized to access invex."
|
subTitle={t("viewEquipmentDocumentations.result403.description")}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -227,8 +243,8 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
return (
|
return (
|
||||||
<Result
|
<Result
|
||||||
status="500"
|
status="500"
|
||||||
title="500"
|
title={t("viewEquipmentDocumentations.result500.title")}
|
||||||
subTitle="The scanned item doesn't exists on invex"
|
subTitle={t("viewEquipmentDocumentations.result500.description")}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -237,8 +253,8 @@ export default function ViewEquipmentDocumentations({ scannerResult }) {
|
||||||
return (
|
return (
|
||||||
<Result
|
<Result
|
||||||
status="404"
|
status="404"
|
||||||
title="404"
|
title={t("viewEquipmentDocumentations.result404.title")}
|
||||||
subTitle="Sorry, for the equipment does not exist an documentation."
|
subTitle={t("viewEquipmentDocumentations.result404.description")}
|
||||||
extra={[
|
extra={[
|
||||||
<CreateEquipmentDocumentationModal
|
<CreateEquipmentDocumentationModal
|
||||||
key="0"
|
key="0"
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
ArrowUpOutlined,
|
ArrowUpOutlined,
|
||||||
CameraOutlined,
|
CameraOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
|
EditOutlined,
|
||||||
EllipsisOutlined,
|
EllipsisOutlined,
|
||||||
FullscreenOutlined,
|
FullscreenOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
|
@ -12,7 +13,6 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Col,
|
Col,
|
||||||
Divider,
|
|
||||||
Dropdown,
|
Dropdown,
|
||||||
Input,
|
Input,
|
||||||
Result,
|
Result,
|
||||||
|
@ -33,18 +33,19 @@ import MyModal, {
|
||||||
} from "../../Components/MyModal";
|
} from "../../Components/MyModal";
|
||||||
import Webcam from "react-webcam";
|
import Webcam from "react-webcam";
|
||||||
import TextArea from "antd/es/input/TextArea";
|
import TextArea from "antd/es/input/TextArea";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
message.config({
|
import MyTypography from "../../Components/MyTypography";
|
||||||
maxCount: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function EquipmentDocumentationOverview() {
|
export default function EquipmentDocumentationOverview() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [messageApi, messageContextHolder] = message.useMessage({
|
||||||
|
maxCount: 2,
|
||||||
|
});
|
||||||
|
|
||||||
const [scannerResult, setScannerResult] = useState("");
|
const [scannerResult, setScannerResult] = useState("");
|
||||||
const [isScannerActive, setIsScannerActive] = useState(false);
|
const [isScannerActive, setIsScannerActive] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
|
||||||
console.log("scan", scannerResult);
|
|
||||||
|
|
||||||
const isScannedQrCodeValid = (result) => {
|
const isScannedQrCodeValid = (result) => {
|
||||||
// {"stockitem": 11} or 11 is valid
|
// {"stockitem": 11} or 11 is valid
|
||||||
|
|
||||||
|
@ -72,6 +73,7 @@ export default function EquipmentDocumentationOverview() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{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}>
|
||||||
|
@ -87,7 +89,11 @@ export default function EquipmentDocumentationOverview() {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
|
|
||||||
if (!isScannedQrCodeValid(result)) {
|
if (!isScannedQrCodeValid(result)) {
|
||||||
message.error("Invalid stock item QR code");
|
messageApi.error(
|
||||||
|
t(
|
||||||
|
"equipmentDocumentationOverview.messageErrorInvalidStockItem"
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,20 +110,24 @@ export default function EquipmentDocumentationOverview() {
|
||||||
marginBottom: AppStyle.app.margin,
|
marginBottom: AppStyle.app.margin,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Close camera
|
{t("equipmentDocumentationOverview.buttonCloseCamera")}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div onClick={() => setIsScannerActive(true)}>
|
<div onClick={() => setIsScannerActive(true)}>
|
||||||
<CameraOutlined style={{ fontSize: 64 }} />
|
<CameraOutlined style={{ fontSize: 64 }} />
|
||||||
<Typography.Title level={5}>Scan equipment</Typography.Title>
|
<Typography.Title level={5}>
|
||||||
|
{t("equipmentDocumentationOverview.scanEquipment.title")}
|
||||||
|
</Typography.Title>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Row gutter={AppStyle.grid.row.gutter}>
|
<Row gutter={AppStyle.grid.row.gutter}>
|
||||||
<Col xs={24} xl={16}>
|
<Col xs={24} xl={16}>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Equipment id"
|
placeholder={t(
|
||||||
|
"equipmentDocumentationOverview.scanEquipment.inputPlaceholder"
|
||||||
|
)}
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onInput={(e) => setInputValue(e.target.value)}
|
onInput={(e) => setInputValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -128,14 +138,20 @@ export default function EquipmentDocumentationOverview() {
|
||||||
icon={<SearchOutlined />}
|
icon={<SearchOutlined />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!isScannedQrCodeValid(inputValue)) {
|
if (!isScannedQrCodeValid(inputValue)) {
|
||||||
message.error("Invalid stock item code");
|
messageApi.error(
|
||||||
|
t(
|
||||||
|
"equipmentDocumentationOverview.messageErrorInvalidStockItem"
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setScannerResult(inputValue);
|
setScannerResult(inputValue);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Search
|
{t(
|
||||||
|
"equipmentDocumentationOverview.scanEquipment.buttonSearch"
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -147,8 +163,12 @@ export default function EquipmentDocumentationOverview() {
|
||||||
{scannerResult === "" ? (
|
{scannerResult === "" ? (
|
||||||
<Result
|
<Result
|
||||||
status="404"
|
status="404"
|
||||||
title="No equipment scanned"
|
title={t(
|
||||||
subTitle="Scan a equipment to see the documentation."
|
"equipmentDocumentationOverview.noEquipmentScannedResult.title"
|
||||||
|
)}
|
||||||
|
subTitle={t(
|
||||||
|
"equipmentDocumentationOverview.noEquipmentScannedResult.description"
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ViewEquipmentDocumentations scannerResult={scannerResult} />
|
<ViewEquipmentDocumentations scannerResult={scannerResult} />
|
||||||
|
@ -159,11 +179,6 @@ export default function EquipmentDocumentationOverview() {
|
||||||
|
|
||||||
export const EmptyNote = { Image: null, Description: "" };
|
export const EmptyNote = { Image: null, Description: "" };
|
||||||
|
|
||||||
const selectDocumentationTypeOptions = [
|
|
||||||
{ value: 1, label: "Repair protocol" },
|
|
||||||
{ value: 2, label: "Documentation" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function EquipmentDocumentationViewEditComponent({
|
export function EquipmentDocumentationViewEditComponent({
|
||||||
createMode,
|
createMode,
|
||||||
isOpen,
|
isOpen,
|
||||||
|
@ -172,9 +187,17 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
stockItemId,
|
stockItemId,
|
||||||
documentationId,
|
documentationId,
|
||||||
}) {
|
}) {
|
||||||
const [title, setTitle] = useState(createMode ? "New documentation" : "");
|
const { t } = useTranslation();
|
||||||
|
const [title, setTitle] = useState(
|
||||||
|
createMode
|
||||||
|
? t("equipmentDocumentationViewEditComponent.titleNewDocumentation")
|
||||||
|
: ""
|
||||||
|
);
|
||||||
const [selectedDocumentationType, setSelectedDocumentationType] = useState(
|
const [selectedDocumentationType, setSelectedDocumentationType] = useState(
|
||||||
selectDocumentationTypeOptions[0].value
|
t(
|
||||||
|
"equipmentDocumentationViewEditComponent.selectDocumentationTypeOptions",
|
||||||
|
{ returnObjects: true }
|
||||||
|
)[0].value
|
||||||
);
|
);
|
||||||
const [notes, setNotes] = useState([EmptyNote]);
|
const [notes, setNotes] = useState([EmptyNote]);
|
||||||
const [isDocumentationUploading, setIsDocumentationUploading] =
|
const [isDocumentationUploading, setIsDocumentationUploading] =
|
||||||
|
@ -199,12 +222,8 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
notes: updatedNotes,
|
notes: updatedNotes,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("body", body);
|
|
||||||
|
|
||||||
myFetch(`/equipment/documentation/create`, "POST", body, {}).then(
|
myFetch(`/equipment/documentation/create`, "POST", body, {}).then(
|
||||||
(data) => {
|
(data) => {
|
||||||
console.log("data", data);
|
|
||||||
|
|
||||||
setIsDocumentationUploading(false);
|
setIsDocumentationUploading(false);
|
||||||
fetchDocumentation();
|
fetchDocumentation();
|
||||||
onCancel();
|
onCancel();
|
||||||
|
@ -217,8 +236,6 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
|
|
||||||
const updatedNotes = [...notes];
|
const updatedNotes = [...notes];
|
||||||
|
|
||||||
console.log("documentationResponse.current", documentationResponse.current);
|
|
||||||
|
|
||||||
updatedNotes.forEach((note, index) => {
|
updatedNotes.forEach((note, index) => {
|
||||||
if (note.Image?.startsWith("http")) {
|
if (note.Image?.startsWith("http")) {
|
||||||
updatedNotes[index].Image = JSON.parse(
|
updatedNotes[index].Image = JSON.parse(
|
||||||
|
@ -234,11 +251,7 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
notes: updatedNotes,
|
notes: updatedNotes,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("body", body);
|
|
||||||
|
|
||||||
myFetch(`/equipment/documentation/edit`, "POST", body, {}).then((data) => {
|
myFetch(`/equipment/documentation/edit`, "POST", body, {}).then((data) => {
|
||||||
console.log("data", data);
|
|
||||||
|
|
||||||
setIsDocumentationUploading(false);
|
setIsDocumentationUploading(false);
|
||||||
fetchDocumentation();
|
fetchDocumentation();
|
||||||
onCancel();
|
onCancel();
|
||||||
|
@ -279,7 +292,9 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
if (notes.length === 0) return true;
|
if (notes.length === 0) return true;
|
||||||
|
|
||||||
if (notes.length === 1) {
|
if (notes.length === 1) {
|
||||||
return notes[0].Image === null && notes[0].Description === "";
|
return (
|
||||||
|
(notes[0].Image === null && notes[0].Description === "") || title === ""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -294,8 +309,6 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
`/equipment/documentation/${stockItemId}/${documentationId}`,
|
`/equipment/documentation/${stockItemId}/${documentationId}`,
|
||||||
"GET"
|
"GET"
|
||||||
).then((data) => {
|
).then((data) => {
|
||||||
console.log("data", data);
|
|
||||||
|
|
||||||
documentationResponse.current = data;
|
documentationResponse.current = data;
|
||||||
|
|
||||||
setTitle(data.Title);
|
setTitle(data.Title);
|
||||||
|
@ -320,6 +333,13 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
<MyModal
|
<MyModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
|
title={
|
||||||
|
<MyTypography
|
||||||
|
value={title}
|
||||||
|
setValue={setTitle}
|
||||||
|
maxLength={Constants.GLOBALS.MAX_EQUIPMENT_DOCUMENTATION_TITLE_LENGTH}
|
||||||
|
/>
|
||||||
|
}
|
||||||
footer={
|
footer={
|
||||||
createMode ? (
|
createMode ? (
|
||||||
<MyModalCloseCreateButtonFooter
|
<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 }}>
|
<div style={{ marginBottom: AppStyle.typography.text.marginBottom }}>
|
||||||
<Typography.Text>Documentation type</Typography.Text>
|
<Typography.Text>
|
||||||
|
{t("equipmentDocumentationViewEditComponent.textDocumentationType")}
|
||||||
|
</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={selectedDocumentationType}
|
value={selectedDocumentationType}
|
||||||
style={{ width: "100%", marginBottom: AppStyle.app.margin }}
|
style={{ width: "100%", marginBottom: AppStyle.app.margin }}
|
||||||
onChange={(value) => setSelectedDocumentationType(value)}
|
onChange={(value) => setSelectedDocumentationType(value)}
|
||||||
options={selectDocumentationTypeOptions}
|
options={t(
|
||||||
|
"equipmentDocumentationViewEditComponent.selectDocumentationTypeOptions",
|
||||||
|
{ returnObjects: true }
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{notes.map((note, index) => (
|
{notes.map((note, index) => (
|
||||||
|
@ -440,19 +458,19 @@ export function EquipmentDocumentationViewEditComponent({
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={handleAddNote}
|
onClick={handleAddNote}
|
||||||
>
|
>
|
||||||
Add note
|
{t("equipmentDocumentationViewEditComponent.buttonAddNote")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UploadComponent({ setImagePreview }) {
|
function UploadComponent({ setImagePreview, messageApi }) {
|
||||||
const handleBeforeUpload = (file) => {
|
const handleBeforeUpload = (file) => {
|
||||||
if (
|
if (
|
||||||
!Constants.ACCEPTED_EQUIPMENT_DOCUMENTATION_FILE_TYPES.includes(file.type)
|
!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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,6 +494,7 @@ function UploadComponent({ setImagePreview }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function CameraComponent({ setImagePreview }) {
|
function CameraComponent({ setImagePreview }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
const [cameraVisible, setCameraVisible] = useState(false);
|
const [cameraVisible, setCameraVisible] = useState(false);
|
||||||
const webcamRef = useRef(null);
|
const webcamRef = useRef(null);
|
||||||
|
@ -509,7 +528,7 @@ function CameraComponent({ setImagePreview }) {
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
footer={[
|
footer={[
|
||||||
<Button key={0} block type="primary" onClick={handleCapture}>
|
<Button key={0} block type="primary" onClick={handleCapture}>
|
||||||
Take picture
|
{t("equipmentDocumentationViewEditComponent.buttonTakePicture")}
|
||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
@ -532,6 +551,8 @@ function CameraComponent({ setImagePreview }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
trigger={["click"]}
|
trigger={["click"]}
|
||||||
|
@ -542,7 +563,9 @@ function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
||||||
<div onClick={onMoveUp}>
|
<div onClick={onMoveUp}>
|
||||||
<Space>
|
<Space>
|
||||||
<ArrowUpOutlined />
|
<ArrowUpOutlined />
|
||||||
<span>Move up</span>
|
<span>
|
||||||
|
{t("equipmentDocumentationViewEditComponent.buttonMoveUp")}
|
||||||
|
</span>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@ -553,7 +576,11 @@ function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
||||||
<div onClick={onMoveDown}>
|
<div onClick={onMoveDown}>
|
||||||
<Space>
|
<Space>
|
||||||
<ArrowDownOutlined />
|
<ArrowDownOutlined />
|
||||||
<span>Move down</span>
|
<span>
|
||||||
|
{t(
|
||||||
|
"equipmentDocumentationViewEditComponent.buttonMoveDown"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@ -564,7 +591,9 @@ function MoreComponent({ onMoveUp, onMoveDown, onDelete }) {
|
||||||
<div onClick={onDelete}>
|
<div onClick={onDelete}>
|
||||||
<Space>
|
<Space>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
<span>Delete note</span>
|
<span>
|
||||||
|
{t("equipmentDocumentationViewEditComponent.buttonDelete")}
|
||||||
|
</span>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@ -590,6 +619,8 @@ export function NoteComponent({
|
||||||
onMoveDown,
|
onMoveDown,
|
||||||
onDelete,
|
onDelete,
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [messageApi, messageContextHolder] = message.useMessage();
|
||||||
const [isImageFullScreenModalOpen, setIsImageFullScreenModalOpen] =
|
const [isImageFullScreenModalOpen, setIsImageFullScreenModalOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
|
@ -610,6 +641,8 @@ export function NoteComponent({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: AppStyle.app.margin }}>
|
<div style={{ marginBottom: AppStyle.app.margin }}>
|
||||||
|
{messageContextHolder}
|
||||||
|
|
||||||
<Row gutter={[AppStyle.grid.row.gutter[0]]}>
|
<Row gutter={[AppStyle.grid.row.gutter[0]]}>
|
||||||
<Col xs={24} md={8}>
|
<Col xs={24} md={8}>
|
||||||
<Card
|
<Card
|
||||||
|
@ -620,7 +653,10 @@ export function NoteComponent({
|
||||||
? [<FullscreenOutlinedIcon />]
|
? [<FullscreenOutlinedIcon />]
|
||||||
: null
|
: null
|
||||||
: [
|
: [
|
||||||
<UploadComponent setImagePreview={onImageChange} />,
|
<UploadComponent
|
||||||
|
setImagePreview={onImageChange}
|
||||||
|
messageApi={messageApi}
|
||||||
|
/>,
|
||||||
<CameraComponent setImagePreview={onImageChange} />,
|
<CameraComponent setImagePreview={onImageChange} />,
|
||||||
<DeleteOutlined onClick={onDeleteImage} />,
|
<DeleteOutlined onClick={onDeleteImage} />,
|
||||||
<FullscreenOutlinedIcon
|
<FullscreenOutlinedIcon
|
||||||
|
@ -643,7 +679,13 @@ export function NoteComponent({
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{viewMode ? "No image" : "No image selected"}
|
{viewMode
|
||||||
|
? t(
|
||||||
|
"equipmentDocumentationViewEditComponent.textImageNoImage"
|
||||||
|
)
|
||||||
|
: t(
|
||||||
|
"equipmentDocumentationViewEditComponent.textImageNoImageSelected"
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
|
@ -664,10 +706,16 @@ export function NoteComponent({
|
||||||
<Typography.Text>{description}</Typography.Text>
|
<Typography.Text>{description}</Typography.Text>
|
||||||
) : (
|
) : (
|
||||||
<TextArea
|
<TextArea
|
||||||
rows={8}
|
autoSize={{ minRows: 8, maxRows: 13 }}
|
||||||
placeholder="Description"
|
placeholder={t(
|
||||||
|
"equipmentDocumentationViewEditComponent.textareaPlaceholder"
|
||||||
|
)}
|
||||||
value={description}
|
value={description}
|
||||||
onChange={onDescriptionChange}
|
onChange={onDescriptionChange}
|
||||||
|
showCount
|
||||||
|
maxLength={
|
||||||
|
Constants.GLOBALS.MAX_EQUIPMENT_DOCUMENTATION_NOTE_LENGTH
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -683,8 +731,16 @@ export function NoteComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImageFullscreenModal({ isOpen, onCancel, image }) {
|
function ImageFullscreenModal({ isOpen, onCancel, image }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal isOpen={isOpen} onCancel={onCancel}>
|
<MyModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onCancel={onCancel}
|
||||||
|
title={t(
|
||||||
|
"equipmentDocumentationViewEditComponent.modalImageFullscreenTitle"
|
||||||
|
)}
|
||||||
|
>
|
||||||
<img width="100%" src={image} alt="Fullscreen preview" />
|
<img width="100%" src={image} alt="Fullscreen preview" />
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -69,6 +69,8 @@ export const Constants = {
|
||||||
MIN_ROLE_DISPLAY_NAME: 3,
|
MIN_ROLE_DISPLAY_NAME: 3,
|
||||||
MAX_ROLE_DISPLAY_NAME: 30,
|
MAX_ROLE_DISPLAY_NAME: 30,
|
||||||
MAX_ROLE_DESCRIPTION: 80,
|
MAX_ROLE_DESCRIPTION: 80,
|
||||||
|
MAX_EQUIPMENT_DOCUMENTATION_TITLE_LENGTH: 60,
|
||||||
|
MAX_EQUIPMENT_DOCUMENTATION_NOTE_LENGTH: 2000,
|
||||||
},
|
},
|
||||||
MAX_AVATAR_SIZE: 5 * 1024 * 1024,
|
MAX_AVATAR_SIZE: 5 * 1024 * 1024,
|
||||||
ACCEPTED_AVATAR_FILE_TYPES: [
|
ACCEPTED_AVATAR_FILE_TYPES: [
|
||||||
|
|
Loading…
Reference in New Issue