documentation equipment

main
alex 2023-08-22 23:11:43 +00:00
parent fdae12fc88
commit 5116def685
8 changed files with 792 additions and 358 deletions

View File

@ -15,7 +15,8 @@ import { useContext } from "react";
import GroupTasks from "../../Pages/GroupTasks/Overview";
import GroupTasksHistory from "../../Pages/GroupTasks/History";
import PageNotFound from "../../Pages/PageNotFound";
import EquipmentDocumentation from "../../Pages/EquipmentDocumentation";
import EquipmentDocumentationOverview from "../../Pages/EquipmentDocumentation";
import ViewEquipmentDocumentations from "../../Pages/EquipmentDocumentation/ViewEquipmentDocumentation";
export default function AppRoutes() {
const webSocketContext = useContext(WebSocketContext);
@ -48,15 +49,14 @@ export default function AppRoutes() {
<Route
path={Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION}
element={<EquipmentDocumentation isEquipmentCreateModalOpen={false} />}
element={<EquipmentDocumentationOverview />}
/>
<Route
path={
Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_CREATE +
":paramStockItemId"
Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION + "/:paramStockItemId"
}
element={<EquipmentDocumentation isEquipmentCreateModalOpen={true} />}
element={<ViewEquipmentDocumentations />}
/>
<Route

View File

@ -0,0 +1,31 @@
import { Spin } from "antd";
import { useState } from "react";
export default function MyImage({ src, width, style }) {
const [loaded, setLoaded] = useState(false);
return (
<div>
{loaded ? null : (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: 200,
}}
>
<Spin />
</div>
)}
<img
src={src}
width={width}
style={loaded ? style : { display: "none" }}
alt="Image"
onLoad={() => setLoaded(true)}
/>
</div>
);
}

View File

@ -117,13 +117,23 @@ export function MyModalCloseSaveButtonFooter({ onCancel, onSave }) {
);
}
export function MyModalCloseCreateButtonFooter({ onCancel, onCreate }) {
export function MyModalCloseCreateButtonFooter({
onCancel,
onCreate,
isCreateButtonDisabled,
isCreateButtonLoading,
}) {
const { t } = useTranslation();
return (
<>
<Button onClick={onCancel}>{t("common.button.close")}</Button>
<Button onClick={onCreate} type="primary">
<Button
onClick={onCreate}
type="primary"
disabled={isCreateButtonDisabled}
loading={isCreateButtonLoading}
>
{t("common.button.create")}
</Button>
</>

View File

@ -1,11 +1,10 @@
import { useNavigate, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
import MyModal, {
MyModalCloseCreateButtonFooter,
MyModalCloseSaveButtonFooter,
} from "../../Components/MyModal";
import { AppStyle, Constants, myFetch, myFetchContentType } from "../../utils";
import { AppStyle, Constants, myFetch } from "../../utils";
import { Button, Card, Col, Row, Select, Typography } from "antd";
import { createRef, useRef, useState } from "react";
import { useRef, useState } from "react";
import TextArea from "antd/es/input/TextArea";
import Webcam from "react-webcam";
import {
@ -14,20 +13,17 @@ import {
FullscreenOutlined,
PlusOutlined,
} from "@ant-design/icons";
import { DocumentationImage } from ".";
import MyImage from "../../Components/MyImage";
function UploadComponent({ index, setImagePreview }) {
const handleFileChange = (event) => {
const file = event.target.files[0];
console.log("file", event.target.files);
if (file) {
const reader = new FileReader();
reader.onload = () => {
//setImagePreview(URL.createObjectURL(file));
setImagePreview(reader.result);
// base 64 string
// console.log(reader.result);
};
reader.readAsDataURL(file);
}
@ -116,49 +112,101 @@ export function NoteComponent({
description,
onDescriptionChange,
onDeleteImage,
imageDocumentationId,
}) {
return (
<Row
gutter={AppStyle.grid.row.glutter}
style={{ marginBottom: AppStyle.app.marginBottom }}
>
<Col xs={24} md={8}>
<Card
bodyStyle={{ padding: 0 }}
actions={
viewMode
? [<FullscreenOutlined />]
: [
<UploadComponent
index={index}
setImagePreview={onImageChange}
/>,
<CameraComponent setImagePreview={onImageChange} />,
<DeleteOutlined onClick={onDeleteImage} />,
<FullscreenOutlined />,
]
}
>
<DocumentationImage
image={image}
imgStyle={{ borderTopLeftRadius: 6, borderTopRightRadius: 6 }}
/>
</Card>
</Col>
const [isImageFullScreenModalOpen, setIsImageFullScreenModalOpen] =
useState(false);
<Col xs={24} md={16}>
{viewMode ? (
<Typography.Text>{description}</Typography.Text>
) : (
<TextArea
rows={8}
placeholder="Description"
value={description}
onChange={onDescriptionChange}
/>
)}
</Col>
</Row>
const imageSrc = imageDocumentationId
? `${Constants.STATIC_CONTENT_ADDRESS}equipmentdocumentation/${imageDocumentationId}/${image}`
: image;
const FullscreenOutlinedIcon = ({ disabled }) => (
<FullscreenOutlined
disabled={true}
onClick={() => {
if (disabled) return;
setIsImageFullScreenModalOpen(true);
}}
/>
);
return (
<div style={{ marginBottom: AppStyle.app.margin }}>
<Row gutter={[AppStyle.grid.row.gutter[0]]}>
<Col xs={24} md={8}>
<Card
bodyStyle={{ padding: 0 }}
actions={
viewMode
? image
? [<FullscreenOutlinedIcon />]
: null
: [
<UploadComponent
index={index}
setImagePreview={onImageChange}
/>,
<CameraComponent setImagePreview={onImageChange} />,
<DeleteOutlined onClick={onDeleteImage} />,
<FullscreenOutlinedIcon disabled={image === null} />,
]
}
>
{!image ? (
<div
style={{
height: 250,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
{viewMode ? "No image" : "No image selected"}
</div>
) : (
<MyImage
src={imageSrc}
width="100%"
style={{
borderTopLeftRadius: 6,
borderTopRightRadius: 6,
}}
alt="Preview"
/>
)}
</Card>
</Col>
<Col xs={24} md={16}>
{viewMode ? (
<Typography.Text>{description}</Typography.Text>
) : (
<TextArea
rows={8}
placeholder="Description"
value={description}
onChange={onDescriptionChange}
/>
)}
</Col>
</Row>
<ImageFullscreenModal
isOpen={isImageFullScreenModalOpen}
onCancel={() => setIsImageFullScreenModalOpen(false)}
image={imageSrc}
/>
</div>
);
}
function ImageFullscreenModal({ isOpen, onCancel, image }) {
return (
<MyModal isOpen={isOpen} onCancel={onCancel}>
<img width="100%" src={image} alt="Fullscreen preview" />
</MyModal>
);
}
@ -169,30 +217,52 @@ const selectDocumentationTypeOptions = [
{ value: 1, label: "Documentation" },
];
export default function CreateEquipmentDocumentationModal({ isOpen }) {
const navigate = useNavigate();
export default function CreateEquipmentDocumentationModal({
fetchDocumentation,
buttonBlock,
}) {
let { paramStockItemId } = useParams();
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 = () =>
navigate(Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION);
const handleCancel = () => setIsOpen(false);
const handleCreate = () => {
let obj = {
setIsDocumentationUploading(true);
const updatedNotes = [...notes];
updatedNotes.forEach((note, index) => {
if (note.image === null && note.description === "") {
updatedNotes.splice(index, 1);
}
});
let body = {
stockItemId: paramStockItemId,
type: selectedDocumentationType,
title: title,
notes: notes,
notes: updatedNotes,
};
myFetch(`/equipment/documentation/create`, "POST", obj, {}).then((data) => {
console.log("data", data);
});
console.log("body", body);
myFetch(`/equipment/documentation/create`, "POST", body, {}).then(
(data) => {
console.log("data", data);
setIsDocumentationUploading(false);
fetchDocumentation();
handleCancel();
}
);
};
const handleDescriptionChange = (index) => (e) => {
@ -225,57 +295,83 @@ export default function CreateEquipmentDocumentationModal({ isOpen }) {
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 (
<MyModal
isOpen={isOpen}
onCancel={handleCancel}
footer={
<MyModalCloseCreateButtonFooter
onCreate={handleCreate}
onCancel={handleCancel}
/>
}
>
<Typography.Title
editable={{ text: title, onChange: setTitle }}
level={1}
<>
<Button
block={buttonBlock}
type="primary"
icon={<PlusOutlined />}
onClick={() => setIsOpen(true)}
>
{title}
</Typography.Title>
Create documentation
</Button>
<div style={{ marginBottom: AppStyle.typography.text.marginBottom }}>
<Typography.Text>Documentation type</Typography.Text>
</div>
<Select
defaultValue={selectedDocumentationType}
style={{ width: "100%", marginBottom: AppStyle.app.marginBottom }}
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}
<MyModal
isOpen={isOpen}
onCancel={handleCancel}
footer={
<MyModalCloseCreateButtonFooter
onCreate={handleCreate}
isCreateButtonDisabled={isCreateButtonDisabled()}
isCreateButtonLoading={isDocumentationUploading}
onCancel={handleCancel}
/>
}
>
<Typography.Title
editable={{ text: title, onChange: setTitle }}
level={1}
>
Add note
</Button>
</div>
</MyModal>
{title}
</Typography.Title>
<div style={{ marginBottom: AppStyle.typography.text.marginBottom }}>
<Typography.Text>Documentation type</Typography.Text>
</div>
<Select
defaultValue={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)}
onImageFullscreen={() => {
console.log("onImageFullscreen");
}}
/>
))}
<div style={{ textAlign: "center" }}>
<Button
type="primary"
disabled={isAddNoteButtonDisabled()}
icon={<PlusOutlined />}
onClick={handleAddNote}
>
Add note
</Button>
</div>
</MyModal>
</>
);
}

View File

@ -1,68 +0,0 @@
import { Constants } from "../../utils";
import { useNavigate, useParams } from "react-router-dom";
import { MyLazyLoadingModal } from "../../Components/MyModal";
import { useState } from "react";
import { Steps } from "antd";
import MyAttachments from "../../Components/MyAttachments";
export default function EquipmentViewModal({ isOpen }) {
//const { t } = useTranslation();
const navigate = useNavigate();
let { paramEquipmentId } = useParams();
const [equipment, setEquipment] = useState([]);
const handleCancel = () =>
navigate(Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION);
console.log("equipment", equipment);
return (
<MyLazyLoadingModal
isOpen={isOpen}
onCancel={handleCancel}
resultTitleNoDataFound={"Not found"}
setFoundData={setEquipment}
fetchUrl={`/equipment/documentation/${paramEquipmentId}`}
fetchType={"GET"}
>
<h1>This is my children</h1>
<Steps
direction="vertical"
current={1}
items={[
{
title: "Finished",
description: (
<>
<p style={{ color: "#000" }}>
<b>Started at:</b> 18.8.2023, 00:59:10
<br />
<b>Endet at:</b> 18.8.2023, 00:59:13
<br />
<b>Duration:</b> 3s 516ms
</p>
<MyAttachments
attachments={[
{
OriginalFileName: "test.png",
SystemFileName:
"e74fc0c4-4114-4d48-8ca2-5adb77d98ebe.jpg",
},
]}
downloadUrl={`${Constants.STATIC_CONTENT_ADDRESS}grouptasks/df1fc270-485c-4d9a-8439-dc7fbc151f4c/`}
/>
</>
),
},
{
title: "In Progress",
description: "This is a description.",
},
]}
/>
</MyLazyLoadingModal>
);
}

View File

@ -0,0 +1,295 @@
import {
Breadcrumb,
Button,
Card,
Col,
Popover,
Result,
Row,
Spin,
Typography,
} from "antd";
import { useEffect, useState } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
import MyImage from "../../Components/MyImage";
import CreateEquipmentDocumentationModal, {
NoteComponent,
} from "./CreateEquipmentDocumentationModal";
import { AppStyle, Constants, FormatDatetime, myFetch } from "../../utils";
import {
BookOutlined,
EditOutlined,
InfoCircleOutlined,
ToolOutlined,
} from "@ant-design/icons";
export default function ViewEquipmentDocumentations() {
let { paramStockItemId } = useParams();
const navigate = useNavigate();
const [equipmentDocumentationResponse, setEquipmentDocumentationResponse] =
useState(null);
const [isEquipmentDocumentationLoading, setIsEquipmentDocumentationLoading] =
useState(false);
const fetchDocumentation = () => {
setIsEquipmentDocumentationLoading(true);
myFetch(`/equipment/documentation/${paramStockItemId}`, "GET").then(
(data) => {
console.log("data", data);
const updatedData = { ...data };
// sort by date
// last created will be on top of the list
updatedData.Documentations = data.Documentations.sort(
(a, b) => new Date(b.CreatedAt) - new Date(a.CreatedAt)
);
setIsEquipmentDocumentationLoading(false);
setEquipmentDocumentationResponse(updatedData);
}
);
};
useEffect(() => {
console.log("paramStockItemId", paramStockItemId);
fetchDocumentation();
}, [paramStockItemId]);
const CreateDocumentationButton = () => {
return (
<Row style={{ alignItems: "center" }}>
<Col xs={24} sm={{ span: 8 }} md={{ span: 6 }}>
<MyImage
src={
"http://localhost:50050/v1/equipment/thumbnail/media/part_images/part_153_image.thumbnail.png"
}
width={64}
/>
</Col>
<Col xs={24} sm={{ span: 8, offset: 8 }} md={{ span: 6, offset: 12 }}>
<CreateEquipmentDocumentationModal
fetchDocumentation={fetchDocumentation}
buttonBlock={true}
/>
</Col>
</Row>
);
};
const DocumentationContent = ({ documentation }) => {
return (
<Card style={{ marginTop: AppStyle.app.margin }}>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
justifyContent: "space-between",
marginBottom: AppStyle.typography.text.marginBottom,
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
alignItems: "center",
}}
>
<DocumentationTypeIcon type={documentation.Type} />
<Typography.Title level={4} style={{ margin: 0 }}>
{documentation.Title}
</Typography.Title>
</div>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
alignItems: "center",
}}
>
<Popover
title={
<Typography.Title
level={4}
style={{ color: Constants.COLORS.SECONDARY }}
>
Details
</Typography.Title>
}
trigger="click"
placement="left"
content={
<p style={{ color: "#000", margin: 0 }}>
<b>ID:</b> {documentation.Id}
<br />
<b>Type:</b> {documentation.Type}
<br />
<b>Created at:</b> {FormatDatetime(documentation.CreatedAt)}
<br />
<b>Updated at:</b> {FormatDatetime(documentation.UpdatedAt)}
</p>
}
>
<InfoCircleOutlined
style={{ fontSize: 24, color: Constants.COLORS.ICON_INFO }}
onClick={() => console.log("info")}
/>
</Popover>
<EditOutlined
style={{ fontSize: 24 }}
onClick={() => console.log("edit")}
/>
</div>
</div>
{documentation.Notes !== "" &&
JSON.parse(documentation.Notes).map((note, index) => {
console.log("map doc");
return (
<NoteComponent
key={index}
viewMode
image={note.Image}
imageDocumentationId={documentation.Id}
description={note.Description}
/>
);
})}
</Card>
);
};
if (isEquipmentDocumentationLoading) {
return (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
marginTop: 120,
}}
>
<Spin size="large" />
</div>
);
}
if (equipmentDocumentationResponse === null) return null;
// backend unauthorized to access invex
if (equipmentDocumentationResponse.Status === 401) {
return (
<Result
status="403"
title="401"
subTitle="The backend server is not authorized to access invex."
extra={
<>
<Button
onClick={() =>
navigate(Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION)
}
>
Back to Overview
</Button>
</>
}
/>
);
}
// stock item doesn't exists on invex
if (equipmentDocumentationResponse.Status === 404) {
return (
<Result
status="500"
title="500"
subTitle="The scanned item doesn't exists on invex"
extra={
<>
<Button
onClick={() =>
navigate(Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION)
}
>
Back to Overview
</Button>
</>
}
/>
);
}
if (equipmentDocumentationResponse.Documentations.length === 0) {
return (
<Result
status="404"
title="404"
subTitle="Sorry, for the equipment does not exist an documentation."
extra={[
<Button
key="0"
onClick={() =>
navigate(Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION)
}
>
Back to Overview
</Button>,
<CreateEquipmentDocumentationModal
key="1"
fetchDocumentation={fetchDocumentation}
buttonBlock={false}
/>,
]}
/>
);
}
return (
<>
<Breadcrumb
style={{ marginBottom: AppStyle.app.margin }}
items={[
{
title: (
<Link to={Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION}>
Overview
</Link>
),
},
{ title: `Stock Item ${paramStockItemId}` },
]}
/>
<CreateDocumentationButton />
{equipmentDocumentationResponse.Documentations.map(
(documentation, index) => (
<DocumentationContent key={index} documentation={documentation} />
)
)}
</>
);
}
function DocumentationTypeIcon({ type }) {
switch (type) {
case 0:
return <ToolOutlined style={{ fontSize: 24 }} />;
case 1:
return <BookOutlined style={{ fontSize: 24 }} />;
default:
console.log("DocumentationTypeIcon type not found:", type);
return null;
}
}

View File

@ -1,185 +1,207 @@
import { CameraOutlined } from "@ant-design/icons";
import { CameraOutlined, SearchOutlined } from "@ant-design/icons";
import { Button, Card, Col, Input, Result, Row, Typography } from "antd";
import { AppStyle, Constants, myFetch } from "../../utils";
import { useEffect, useState } from "react";
import { AppStyle, Constants } from "../../utils";
import { useState } from "react";
import { QrScanner } from "@yudiel/react-qr-scanner";
import EquipmentViewModal from "./EquipmentViewModal";
import CreateEquipmentDocumentationModal, {
NoteComponent,
} from "./CreateEquipmentDocumentationModal";
import { Link } from "react-router-dom";
import { useNavigate } from "react-router-dom";
export default function EquipmentDocumentationOverview() {
return (
<>
<ScanEquipmentComponent />
<Result
status="404"
title="No equipment scanned"
subTitle="Scan a equipment to see the documentation."
/>
</>
);
}
/*
function DocumentationTypeIcon({ type }) {
switch (type) {
case 0:
return <ToolOutlined style={{ fontSize: 24 }} />;
case 1:
return <BookOutlined style={{ fontSize: 24 }} />;
default:
console.log("DocumentationTypeIcon type not found:", type);
return null;
}
} */
/*
export function EquipmentDocumentation() {
let { paramStockItemId } = useParams();
const navigate = useNavigate();
export default function EquipmentDocumentation({ isEquipmentCreateModalOpen }) {
const [isScannerActive, setIsScannerActive] = useState(false);
// const [fetchingEquipment, setFetchingEquipment] = useState(false);
const [scannerResult, setScannerResult] = useState("");
const [equipmentDocumentation, setEquipmentDocumentation] = useState([]);
const [isEquipmentDocumentationLoading, setIsEquipmentDocumentationLoading] =
useState(false);
const [isEquipmentCreateModalOpen, setIsEquipmentCreateModalOpen] =
useState(false);
useEffect(() => {
console.log("scannerResult", scannerResult);
const scannerResult = paramStockItemId;
if (scannerResult === "") return;
const setScannerResult = (v) => {
console.log("set scanne result", v);
navigate(`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION}/${v}`);
};
const fetchDocumentation = () => {
if (!scannerResult) return;
setIsEquipmentDocumentationLoading(true);
myFetch(`/equipment/documentation/${scannerResult}`, "GET").then((data) => {
console.log("data", data);
setEquipmentDocumentation(data);
// sort by date
// last created will be on top of the list
const sortedData = data.sort(
(a, b) => new Date(b.CreatedAt) - new Date(a.CreatedAt)
);
//setEquipment(data);
//setFetchingEquipment(false);
setIsEquipmentDocumentationLoading(false);
setEquipmentDocumentation(sortedData);
});
}, [scannerResult]);
// const [equipment, setEquipment] = useState([]);
/*useEffect(() => {
console.log("eq");
setFetchingEquipment(true);
myFetch("/equipment", "GET").then((data) => {
setEquipment(data);
setFetchingEquipment(false);
});
}, []);
const getTableColumns = () => {
return [
{
title: "Name",
dataIndex: "name",
key: "name",
},
{
title: "Last modification",
dataIndex: "lastModification",
key: "lastModification",
},
{
title: "Last modified at",
dataIndex: "lastModifiedAt",
key: "lastModifiedAt",
},
{
title: "Action",
dataIndex: "action",
key: "action",
render: (_, record) => {
return (
<Link
to={
Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_VIEW + record.key
}
>
View
</Link>
);
},
},
];
};
const getTableItems = () => {
return equipment.map((eq) => ({
key: eq.Id,
name: (
<>
<Popover
placement="right"
trigger="hover"
content={
<Avatar
src={`${Constants.API_ADDRESS}/equipment/thumbnail${eq.Thumbnail}`}
size={256}
shape="square"
/>
}
>
<>
<Avatar
src={`${Constants.API_ADDRESS}/equipment/thumbnail${eq.Thumbnail}`}
shape="square"
/>
</>
</Popover>
useEffect(() => {
console.log("scannerResult", scannerResult);
{eq.Name}
</>
),
}));
}; */
fetchDocumentation();
}, [scannerResult]);
const CreateDocumentationButton = () => {
return (
<Link
to={`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION_CREATE}${scannerResult}`}
>
<Button type="primary">Create documentation</Button>
</Link>
<Row style={{ alignItems: "center" }}>
<Col xs={24} sm={{ span: 8 }} md={{ span: 6 }}>
<MyImage
src={
"http://localhost:50050/v1/equipment/thumbnail/media/part_images/part_153_image.thumbnail.png"
}
width={64}
/>
</Col>
<Col xs={24} sm={{ span: 8, offset: 8 }} md={{ span: 6, offset: 12 }}>
<CreateEquipmentDocumentationModal />
</Col>
</Row>
);
};
const DocumentationContent = ({ documentation }) => {
console.log("doc", documentation);
return (
<Card>
<Typography.Title level={4}>{documentation.Title}</Typography.Title>
<Card style={{ marginTop: AppStyle.app.margin }}>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
justifyContent: "space-between",
marginBottom: AppStyle.typography.text.marginBottom,
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
alignItems: "center",
}}
>
<DocumentationTypeIcon type={documentation.Type} />
<Typography.Title level={4} style={{ margin: 0 }}>
{documentation.Title}
</Typography.Title>
</div>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
alignItems: "center",
}}
>
<Popover
title={
<Typography.Title
level={4}
style={{ color: Constants.COLORS.SECONDARY }}
>
Details
</Typography.Title>
}
trigger="click"
placement="left"
content={
<p style={{ color: "#000", margin: 0 }}>
<b>ID:</b> {documentation.Id}
<br />
<b>Type:</b> {documentation.Type}
<br />
<b>Created at:</b> {FormatDatetime(documentation.CreatedAt)}
<br />
<b>Updated at:</b> {FormatDatetime(documentation.UpdatedAt)}
</p>
}
>
<InfoCircleOutlined
style={{ fontSize: 24, color: Constants.COLORS.ICON_INFO }}
onClick={() => console.log("info")}
/>
</Popover>
<EditOutlined
style={{ fontSize: 24 }}
onClick={() => console.log("edit")}
/>
</div>
</div>
{documentation.Notes !== "" &&
JSON.parse(documentation.Notes).map((note, index) => (
<NoteComponent
key={index}
viewMode
image={`${Constants.STATIC_CONTENT_ADDRESS}equipmentdocumentation/${documentation.Id}/${note.Image}`}
description={note.Description}
/>
))}
JSON.parse(documentation.Notes).map((note, index) => {
console.log("map doc");
return (
<NoteComponent
key={index}
viewMode
image={note.Image}
imageDocumentationId={documentation.Id}
description={note.Description}
/>
);
})}
</Card>
);
};
return (
<>
<Row style={{ marginBottom: AppStyle.app.marginBottom }}>
<Col xs={0} sm={7} />
<Col xs={24} sm={10}>
<Card
style={{
textAlign: "center",
}}
>
{isScannerActive ? (
<>
<QrScanner
onDecode={(result) => {
console.log(result);
setScannerResult(result);
setIsScannerActive(false);
}}
onError={(error) => console.log(error?.message)}
/>
<Button onClick={() => setIsScannerActive(false)}>
Close camera
</Button>
</>
) : (
<div onClick={() => setIsScannerActive(true)}>
<CameraOutlined style={{ fontSize: 64 }} />
<Typography.Title level={5}>Scan equipment</Typography.Title>
</div>
)}
Result: {scannerResult}
<Input placeholder="Equipment id" />
<Button onClick={() => setScannerResult("169")}>Search</Button>
</Card>
</Col>
<Col xs={0} sm={7} />
</Row>
<h1>ScannerResult: {scannerResult}</h1>
{scannerResult !== "" && equipmentDocumentation.length === 0 ? (
{isEquipmentDocumentationLoading ? (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
marginTop: 120,
}}
>
<Spin size="large" />
</div>
) : scannerResult === undefined ? (
<Result
status="404"
title="No equipment scanned"
subTitle="Scan a equipment to see the documentation."
/>
) : scannerResult !== undefined && equipmentDocumentation.length === 0 ? (
<Result
status="404"
title="404"
@ -204,32 +226,80 @@ export default function EquipmentDocumentation({ isEquipmentCreateModalOpen }) {
</>
)}
<CreateEquipmentDocumentationModal isOpen={isEquipmentCreateModalOpen} />
<CreateEquipmentDocumentationModal
isOpen={isEquipmentCreateModalOpen}
onCancel={() => setIsEquipmentCreateModalOpen(false)}
fetchDocumentation={() => fetchDocumentation()}
/>
</>
);
}
} */
export function DocumentationImage({ image, imgStyle }) {
return image ? (
<img
src={image}
style={{
width: "100%",
...imgStyle,
}}
alt="Preview"
/>
) : (
<div
style={{
height: 250,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
No image selected
</div>
export function ScanEquipmentComponent() {
const navigate = useNavigate();
const [isScannerActive, setIsScannerActive] = useState(false);
const setScannerResult = (v) => {
console.log("set scanne result", v);
navigate(`${Constants.ROUTE_PATHS.EQUIPMENT_DOCUMENTATION}/${v}`);
};
return (
<Row style={{ marginBottom: AppStyle.app.margin }}>
<Col xs={0} sm={7} />
<Col xs={24} sm={10}>
<Card
style={{
textAlign: "center",
}}
>
{isScannerActive ? (
<>
<QrScanner
onDecode={(result) => {
console.log(result);
setScannerResult(result);
setIsScannerActive(false);
}}
onError={(error) => console.log(error?.message)}
/>
<Button
block
onClick={() => setIsScannerActive(false)}
style={{
marginTop: AppStyle.app.margin,
marginBottom: AppStyle.app.margin,
}}
>
Close camera
</Button>
</>
) : (
<div onClick={() => setIsScannerActive(true)}>
<CameraOutlined style={{ fontSize: 64 }} />
<Typography.Title level={5}>Scan equipment</Typography.Title>
</div>
)}
<Row gutter={AppStyle.grid.row.gutter}>
<Col xs={24} md={16}>
<Input placeholder="Equipment id" />
</Col>
<Col xs={24} md={8}>
<Button
block
icon={<SearchOutlined />}
onClick={() => setScannerResult("169")}
>
Search
</Button>
</Col>
</Row>
</Card>
</Col>
<Col xs={0} sm={7} />
</Row>
);
}

View File

@ -127,7 +127,7 @@ export const Constants = {
export const AppStyle = {
app: {
marginBottom: 12,
margin: 12,
borderRadius: 12,
},
typography: {
@ -137,7 +137,7 @@ export const AppStyle = {
},
grid: {
row: {
glutter: [16, 16],
gutter: [16, 16],
},
},
};