@olenbetong/synergi-react 2.2.3 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/components/FilePond.d.ts +8 -5
- package/es/components/FilePond.js +47 -20
- package/es/elements/ElementDamage/DamageFormButtons.d.ts +9 -0
- package/es/elements/ElementDamage/DamageFormButtons.js +7 -0
- package/es/elements/ElementDamage/DescriptionInput.d.ts +14 -0
- package/es/elements/ElementDamage/DescriptionInput.js +44 -0
- package/es/elements/{ElementDamageForm/DuplicateDamage/DuplicateElementDamageDialog.d.ts → ElementDamage/DuplicateDamage/DublicateElementDamageDialog.d.ts} +4 -3
- package/es/elements/ElementDamage/DuplicateDamage/DublicateElementDamageDialog.js +12 -0
- package/es/elements/ElementDamage/DuplicateDamage/index.d.ts +36 -0
- package/es/elements/ElementDamage/DuplicateDamage/index.js +40 -0
- package/es/elements/ElementDamage/ElementNumberTextField.d.ts +10 -0
- package/es/elements/ElementDamage/ElementNumberTextField.js +18 -0
- package/es/elements/ElementDamage/HiddenDamageFields.d.ts +6 -0
- package/es/elements/ElementDamage/HiddenDamageFields.js +4 -0
- package/es/elements/ElementDamage/RegisterFix.d.ts +14 -0
- package/es/elements/ElementDamage/RegisterFix.js +45 -0
- package/es/elements/ElementDamage/SubmitButtons.d.ts +20 -0
- package/es/elements/ElementDamage/SubmitButtons.js +7 -0
- package/es/elements/ElementDamage/TransportElementSelect.d.ts +7 -0
- package/es/elements/ElementDamage/TransportElementSelect.js +16 -0
- package/es/elements/ElementLookup.d.ts +20 -0
- package/es/elements/ElementLookup.js +30 -0
- package/es/elements/{AddElementDialog → ElementLookupDialog}/Manual/ManualForm.js +4 -2
- package/es/elements/{AddElementDialog → ElementLookupDialog}/getElement.d.ts +1 -0
- package/es/elements/{AddElementDialog → ElementLookupDialog}/getElement.js +1 -0
- package/es/elements/{AddElementDialog → ElementLookupDialog}/index.d.ts +5 -5
- package/es/elements/{AddElementDialog → ElementLookupDialog}/index.js +4 -1
- package/es/elements/ProjectSelect.js +9 -2
- package/es/elements/data/dsDamages.d.ts +1 -1
- package/es/elements/data/dsDamages.js +2 -2
- package/es/elements/data/dsElements.d.ts +2 -0
- package/es/elements/data/dsElements.js +11 -1
- package/es/elements/data/dsTransportElements.d.ts +10 -0
- package/es/elements/data/dsTransportElements.js +57 -0
- package/es/elements/data/index.d.ts +5 -1
- package/es/elements/data/index.js +5 -1
- package/es/elements/data/procDeleteAttachment.d.ts +2 -0
- package/es/elements/data/procDeleteAttachment.js +18 -0
- package/es/elements/data/procDeleteDamage.d.ts +2 -0
- package/es/elements/data/procDeleteDamage.js +18 -0
- package/es/elements/data/procRemoveDamageFix.d.ts +2 -0
- package/es/elements/data/procRemoveDamageFix.js +18 -0
- package/es/elements/data/procRestoreAttachments.d.ts +2 -0
- package/es/elements/data/procRestoreAttachments.js +18 -0
- package/es/elements/index.d.ts +8 -2
- package/es/elements/index.js +8 -2
- package/es/index.d.ts +1 -1
- package/es/index.js +1 -1
- package/package.json +4 -4
- package/src/components/FilePond.tsx +73 -26
- package/src/elements/ElementDamage/DamageFormButtons.tsx +35 -0
- package/src/elements/ElementDamage/DescriptionInput.tsx +111 -0
- package/src/elements/{ElementDamageForm/DuplicateDamage/DuplicateElementDamageDialog.tsx → ElementDamage/DuplicateDamage/DublicateElementDamageDialog.tsx} +24 -9
- package/src/elements/ElementDamage/DuplicateDamage/index.tsx +105 -0
- package/src/elements/ElementDamage/ElementNumberTextField.tsx +41 -0
- package/src/elements/ElementDamage/HiddenDamageFields.tsx +13 -0
- package/src/elements/ElementDamage/RegisterFix.tsx +100 -0
- package/src/elements/ElementDamage/SubmitButtons.tsx +40 -0
- package/src/elements/ElementDamage/TransportElementSelect.tsx +52 -0
- package/src/elements/ElementLookup.tsx +90 -0
- package/src/elements/{AddElementDialog → ElementLookupDialog}/Manual/ManualForm.tsx +5 -2
- package/src/elements/{AddElementDialog → ElementLookupDialog}/getElement.ts +2 -0
- package/src/elements/{AddElementDialog → ElementLookupDialog}/index.tsx +9 -5
- package/src/elements/ProjectSelect.tsx +13 -3
- package/src/elements/data/dsDamages.ts +3 -3
- package/src/elements/data/dsElements.ts +13 -1
- package/src/elements/data/dsTransportElements.ts +68 -0
- package/src/elements/data/index.ts +5 -1
- package/src/elements/data/procDeleteAttachment.ts +19 -0
- package/src/elements/data/procDeleteDamage.ts +19 -0
- package/src/elements/data/procRemoveDamageFix.ts +19 -0
- package/src/elements/data/procRestoreAttachments.ts +19 -0
- package/src/elements/index.ts +8 -3
- package/src/index.ts +1 -1
- package/es/elements/ElementDamageForm/DamageFormFields.d.ts +0 -12
- package/es/elements/ElementDamageForm/DamageFormFields.js +0 -39
- package/es/elements/ElementDamageForm/DuplicateDamage/DuplicateElementDamageDialog.js +0 -11
- package/es/elements/ElementDamageForm/index.d.ts +0 -13
- package/es/elements/ElementDamageForm/index.js +0 -219
- package/src/elements/ElementDamageForm/DamageFormFields.tsx +0 -149
- package/src/elements/ElementDamageForm/index.tsx +0 -397
- /package/es/elements/{ElementDamageForm → ElementDamage}/DuplicateDamage/DesktopView.d.ts +0 -0
- /package/es/elements/{ElementDamageForm → ElementDamage}/DuplicateDamage/DesktopView.js +0 -0
- /package/es/elements/{ElementDamageForm → ElementDamage}/DuplicateDamage/PhoneView.d.ts +0 -0
- /package/es/elements/{ElementDamageForm → ElementDamage}/DuplicateDamage/PhoneView.js +0 -0
- /package/es/elements/{ElementDamageForm → ElementDamage}/index.css +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/DeadIcon.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/DeadIcon.js +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/Error.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/Error.js +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/index.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/index.js +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/useScanner.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/useScanner.js +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/index.css +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/index.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/index.js +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/useHIDScanner.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/useHIDScanner.js +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/Manual/ManualForm.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/utils/Giai.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/utils/Giai.js +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/utils/beep.d.ts +0 -0
- /package/es/elements/{AddElementDialog → ElementLookupDialog}/utils/beep.js +0 -0
- /package/src/elements/{ElementDamageForm → ElementDamage}/DuplicateDamage/DesktopView.tsx +0 -0
- /package/src/elements/{ElementDamageForm → ElementDamage}/DuplicateDamage/PhoneView.tsx +0 -0
- /package/src/elements/{ElementDamageForm → ElementDamage}/index.css +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/DeadIcon.tsx +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/Error.tsx +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/index.tsx +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/BarcodeScanner/useScanner.ts +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/index.css +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/index.tsx +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/HIDScanner/useHIDScanner.ts +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/utils/Giai.ts +0 -0
- /package/src/elements/{AddElementDialog → ElementLookupDialog}/utils/beep.ts +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Autocomplete, Button, TextField } from "@mui/material";
|
|
2
|
+
import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
export type DescriptionInputType = {
|
|
6
|
+
Description?: string | null;
|
|
7
|
+
Cause?: string | null;
|
|
8
|
+
Action?: string | null;
|
|
9
|
+
Date?: Date;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type DescriptionInputProps = {
|
|
13
|
+
value?: DescriptionInputType | null;
|
|
14
|
+
defaultValue?: DescriptionInputType | null;
|
|
15
|
+
onCopyPrevDamage?: (value: DescriptionInputType) => void;
|
|
16
|
+
onChange?: (value: DescriptionInputType) => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function DescriptionInput({ value, defaultValue, onCopyPrevDamage, onChange }: DescriptionInputProps) {
|
|
20
|
+
let initializedRef = useRef<boolean>(false);
|
|
21
|
+
|
|
22
|
+
let [input, setInput] = useState<DescriptionInputType | null>(defaultValue ?? null);
|
|
23
|
+
let inputValue = value === undefined ? input : value;
|
|
24
|
+
|
|
25
|
+
// biome-ignore lint: intentionally skipping "value" dependency as this should never run when "value" changes
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (initializedRef.current) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (value === undefined && defaultValue) {
|
|
32
|
+
setInput(defaultValue);
|
|
33
|
+
initializedRef.current = true;
|
|
34
|
+
}
|
|
35
|
+
}, [defaultValue]);
|
|
36
|
+
|
|
37
|
+
function handleCopyPrevDamage() {
|
|
38
|
+
let description = localStorage.getItem("ElementDamage_Description") ?? "";
|
|
39
|
+
let cause = localStorage.getItem("ElementDamage_Cause") ?? "";
|
|
40
|
+
let action = localStorage.getItem("ElementDamage_Action") ?? "";
|
|
41
|
+
|
|
42
|
+
onCopyPrevDamage?.({
|
|
43
|
+
Description: description,
|
|
44
|
+
Cause: cause,
|
|
45
|
+
Action: action,
|
|
46
|
+
});
|
|
47
|
+
setInput({
|
|
48
|
+
...inputValue,
|
|
49
|
+
Description: description,
|
|
50
|
+
Cause: cause,
|
|
51
|
+
Action: action,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleOnChange(value: DescriptionInputType) {
|
|
56
|
+
onChange?.(value);
|
|
57
|
+
setInput(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
<TextField
|
|
63
|
+
name="Date"
|
|
64
|
+
type="date"
|
|
65
|
+
value={(inputValue?.Date ?? new Date()).toISOString().split("T")[0]}
|
|
66
|
+
onChange={(event) => handleOnChange({ ...inputValue, Date: new Date(event.target.value) })}
|
|
67
|
+
id="NewElementDamage_Date"
|
|
68
|
+
label={getLocalizedString("Date")}
|
|
69
|
+
fullWidth
|
|
70
|
+
required
|
|
71
|
+
slotProps={{ inputLabel: { shrink: true } }}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<Button variant="contained" onClick={handleCopyPrevDamage}>
|
|
75
|
+
{getLocalizedString("Copy previous damage")}
|
|
76
|
+
</Button>
|
|
77
|
+
|
|
78
|
+
<Autocomplete
|
|
79
|
+
value={inputValue?.Description ?? ""}
|
|
80
|
+
onChange={(_event, value) => handleOnChange({ ...inputValue, Description: value ?? "" })}
|
|
81
|
+
options={[
|
|
82
|
+
getLocalizedString("Tear in element"),
|
|
83
|
+
getLocalizedString("Damage around lifting anchor"),
|
|
84
|
+
getLocalizedString("Damage on underside"),
|
|
85
|
+
]}
|
|
86
|
+
freeSolo
|
|
87
|
+
renderInput={(params) => (
|
|
88
|
+
<TextField name="Description" required {...(params as any)} label={getLocalizedString("Description")} />
|
|
89
|
+
)}
|
|
90
|
+
/>
|
|
91
|
+
|
|
92
|
+
<TextField
|
|
93
|
+
name="Cause"
|
|
94
|
+
id="NewElementDamage_Cause"
|
|
95
|
+
value={inputValue?.Cause ?? ""}
|
|
96
|
+
onChange={(event) => handleOnChange({ ...inputValue, Cause: event.currentTarget.value })}
|
|
97
|
+
label={getLocalizedString("Cause")}
|
|
98
|
+
fullWidth
|
|
99
|
+
/>
|
|
100
|
+
|
|
101
|
+
<TextField
|
|
102
|
+
name="Action"
|
|
103
|
+
id="NewElementDamage_Action"
|
|
104
|
+
value={inputValue?.Action ?? ""}
|
|
105
|
+
onChange={(event) => handleOnChange({ ...inputValue, Action: event.currentTarget.value })}
|
|
106
|
+
label={getLocalizedString("Action")}
|
|
107
|
+
fullWidth
|
|
108
|
+
/>
|
|
109
|
+
</>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -8,20 +8,28 @@ import { DuplicatePhoneViewItem } from "./PhoneView";
|
|
|
8
8
|
|
|
9
9
|
type DuplicateElementDamageDialogProps = {
|
|
10
10
|
open: boolean;
|
|
11
|
-
onClose: (
|
|
11
|
+
onClose: () => void;
|
|
12
12
|
record: DamagesRecord[];
|
|
13
|
-
|
|
13
|
+
onRegisterAnyway: () => void;
|
|
14
|
+
loading: boolean;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
export function DuplicateElementDamageDialog({
|
|
17
|
+
export function DuplicateElementDamageDialog({
|
|
18
|
+
open,
|
|
19
|
+
onClose,
|
|
20
|
+
record,
|
|
21
|
+
onRegisterAnyway,
|
|
22
|
+
loading,
|
|
23
|
+
}: DuplicateElementDamageDialogProps) {
|
|
17
24
|
let matches = useMediaQuery(theme.breakpoints.up("lg"));
|
|
25
|
+
let backgroundColor = "#e9ebee";
|
|
18
26
|
|
|
19
27
|
return (
|
|
20
|
-
<Dialog open={open} onClose={
|
|
21
|
-
<DialogTitle sx={{ backgroundColor:
|
|
28
|
+
<Dialog open={open} onClose={onClose} fullWidth fullScreen={!matches}>
|
|
29
|
+
<DialogTitle sx={{ backgroundColor: backgroundColor }}>
|
|
22
30
|
{getLocalizedString("This element has one or more unfixed registered damage")}
|
|
23
31
|
</DialogTitle>
|
|
24
|
-
<DialogContent sx={{ backgroundColor:
|
|
32
|
+
<DialogContent sx={{ backgroundColor: backgroundColor }}>
|
|
25
33
|
<Grid container>
|
|
26
34
|
{matches ? (
|
|
27
35
|
<>
|
|
@@ -39,9 +47,16 @@ export function DuplicateElementDamageDialog({ open, onClose, record, fallBack }
|
|
|
39
47
|
)}
|
|
40
48
|
</Grid>
|
|
41
49
|
</DialogContent>
|
|
42
|
-
<DialogActions sx={{ backgroundColor:
|
|
43
|
-
<Button onClick={
|
|
44
|
-
<Button
|
|
50
|
+
<DialogActions sx={{ backgroundColor: backgroundColor }}>
|
|
51
|
+
<Button onClick={onClose}>{getLocalizedString("Cancel")}</Button>
|
|
52
|
+
<Button
|
|
53
|
+
startIcon={<Add />}
|
|
54
|
+
variant="contained"
|
|
55
|
+
type="submit"
|
|
56
|
+
onClick={onRegisterAnyway}
|
|
57
|
+
loading={loading}
|
|
58
|
+
loadingPosition="start"
|
|
59
|
+
>
|
|
45
60
|
{getLocalizedString("Register damage anyway")}
|
|
46
61
|
</Button>
|
|
47
62
|
</DialogActions>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useDataWithFilter } from "@olenbetong/appframe-react";
|
|
2
|
+
import { forwardRef, useImperativeHandle, useMemo, useState } from "react";
|
|
3
|
+
import { type DamagesRecord, dsDamagesDuplicateCheck } from "../../data";
|
|
4
|
+
import type { Element } from "../../ElementLookup";
|
|
5
|
+
import { DuplicateElementDamageDialog } from "./DublicateElementDamageDialog";
|
|
6
|
+
|
|
7
|
+
export { DuplicateElementDamageDialog } from "./DublicateElementDamageDialog";
|
|
8
|
+
|
|
9
|
+
export type DuplicateElementDamageHandle = {
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
checkDuplicate: () => Promise<DamagesRecord[] | null>;
|
|
12
|
+
record: DamagesRecord[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type DuplicateElementDamageProps = {
|
|
16
|
+
element: Element | null;
|
|
17
|
+
/**
|
|
18
|
+
* External control of dialog visibility
|
|
19
|
+
*/
|
|
20
|
+
open?: boolean;
|
|
21
|
+
onOpenChange?: (value: boolean) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Skip duplicate checking (used in edit mode)
|
|
24
|
+
*/
|
|
25
|
+
disableCheck?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Loading state for submit button
|
|
28
|
+
*/
|
|
29
|
+
loading?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Called when duplicate or duplicates are found
|
|
32
|
+
* @param record Array with all of the duplicate damages
|
|
33
|
+
* @returns void
|
|
34
|
+
*/
|
|
35
|
+
onDuplicateFound?: (record: DamagesRecord[]) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Called when user presses register anyway in dialog
|
|
38
|
+
* @returns void
|
|
39
|
+
*/
|
|
40
|
+
onRegisterAnyway?: () => void;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const DuplicateElementDamage = forwardRef<DuplicateElementDamageHandle, DuplicateElementDamageProps>(
|
|
44
|
+
function DuplicateElementDamage(
|
|
45
|
+
{
|
|
46
|
+
element,
|
|
47
|
+
open: controlledOpen,
|
|
48
|
+
onOpenChange,
|
|
49
|
+
disableCheck = false,
|
|
50
|
+
loading = false,
|
|
51
|
+
onRegisterAnyway,
|
|
52
|
+
}: DuplicateElementDamageProps,
|
|
53
|
+
ref,
|
|
54
|
+
) {
|
|
55
|
+
let [record, setRecord] = useState<DamagesRecord[]>([]);
|
|
56
|
+
let [uncontrolledOpen, setUncontrolledOpen] = useState<boolean>(false);
|
|
57
|
+
let open = controlledOpen === undefined ? uncontrolledOpen : controlledOpen;
|
|
58
|
+
|
|
59
|
+
let whereClause = useMemo(() => {
|
|
60
|
+
if (!element) {
|
|
61
|
+
return "1=0";
|
|
62
|
+
}
|
|
63
|
+
return `Domain = '${element.Domain}' AND ElementID = '${element.ElementID}' AND ProjectID = '${element.ProjectID}'`;
|
|
64
|
+
}, [element]);
|
|
65
|
+
|
|
66
|
+
let duplicateRecord = useDataWithFilter(dsDamagesDuplicateCheck, whereClause, "whereClause");
|
|
67
|
+
|
|
68
|
+
async function runDuplicateCheck(): Promise<DamagesRecord[] | null> {
|
|
69
|
+
// If we are editing a record, we don't need to check for duplicates
|
|
70
|
+
if (disableCheck) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (duplicateRecord.length > 0) {
|
|
75
|
+
setRecord(duplicateRecord);
|
|
76
|
+
setUncontrolledOpen(true);
|
|
77
|
+
|
|
78
|
+
return duplicateRecord;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function handleOpenChange(value: boolean) {
|
|
85
|
+
setUncontrolledOpen(value);
|
|
86
|
+
onOpenChange?.(value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
useImperativeHandle(ref, () => ({
|
|
90
|
+
onClose: () => setUncontrolledOpen(false),
|
|
91
|
+
checkDuplicate: () => runDuplicateCheck(),
|
|
92
|
+
record: record,
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<DuplicateElementDamageDialog
|
|
97
|
+
open={open}
|
|
98
|
+
onClose={() => handleOpenChange(false)}
|
|
99
|
+
record={record}
|
|
100
|
+
onRegisterAnyway={onRegisterAnyway ?? (() => {})}
|
|
101
|
+
loading={loading ?? false}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
},
|
|
105
|
+
);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type SxProps, TextField, type Theme } from "@mui/material";
|
|
2
|
+
import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
type ElementNumberTextFieldProps = {
|
|
6
|
+
value?: string | null;
|
|
7
|
+
defaultValue?: string | null;
|
|
8
|
+
onChange?: (value: string) => void;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
sx?: SxProps<Theme>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function ElementNumberTextField({ value, defaultValue, onChange, disabled, sx }: ElementNumberTextFieldProps) {
|
|
14
|
+
let [elementNumber, setElementNumber] = useState<string>(defaultValue ?? "");
|
|
15
|
+
let elementNumberValue = value === undefined ? elementNumber : value;
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (value === undefined && defaultValue) {
|
|
19
|
+
setElementNumber(defaultValue);
|
|
20
|
+
}
|
|
21
|
+
}, [defaultValue, value]);
|
|
22
|
+
|
|
23
|
+
function handleChange(value: string) {
|
|
24
|
+
onChange?.(value);
|
|
25
|
+
setElementNumber(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<TextField
|
|
30
|
+
name="ElementNumber"
|
|
31
|
+
id="NewElementDamage_ElementNr"
|
|
32
|
+
value={elementNumberValue}
|
|
33
|
+
onChange={(event) => handleChange(event.currentTarget.value)}
|
|
34
|
+
label={getLocalizedString("Element number")}
|
|
35
|
+
fullWidth
|
|
36
|
+
autoComplete="off"
|
|
37
|
+
disabled={disabled ?? false}
|
|
38
|
+
sx={{ ...sx }}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type HiddenDamageFieldsProps = {
|
|
2
|
+
primKey: string;
|
|
3
|
+
ID: number;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function HiddenDamageFields({ primKey, ID }: HiddenDamageFieldsProps) {
|
|
7
|
+
return (
|
|
8
|
+
<>
|
|
9
|
+
<input type="hidden" name="PrimKey" value={primKey} />
|
|
10
|
+
<input type="hidden" name="ID" value={ID} />
|
|
11
|
+
</>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import CheckIcon from "@mui/icons-material/Check";
|
|
2
|
+
import { Button, Checkbox, FormControlLabel, Typography } from "@mui/material";
|
|
3
|
+
import { getLocalizedString, localize } from "@olenbetong/appframe-core";
|
|
4
|
+
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
|
|
7
|
+
import { procApproveDamage } from "../data";
|
|
8
|
+
|
|
9
|
+
const formatter = new Intl.DateTimeFormat(af.userSession.culture, {
|
|
10
|
+
day: "numeric",
|
|
11
|
+
month: "long",
|
|
12
|
+
year: "numeric",
|
|
13
|
+
hour: "2-digit",
|
|
14
|
+
minute: "2-digit",
|
|
15
|
+
timeZone: "UTC",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
type RegisterFixType = {
|
|
19
|
+
ID: number;
|
|
20
|
+
RegisteredFixFullName: string | null;
|
|
21
|
+
RegisteredFix: Date | null;
|
|
22
|
+
RegisteredFixBy: string | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type ApproveDamageProcedureRecord = {
|
|
26
|
+
Table: Array<Required<RegisterFixType>>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type RegisterFixProps = {
|
|
30
|
+
value?: RegisterFixType | null;
|
|
31
|
+
defaultValue?: RegisterFixType | null;
|
|
32
|
+
onFix?: (ID: number) => void;
|
|
33
|
+
edit?: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function RegisterFix({ value, defaultValue, onFix, edit }: RegisterFixProps) {
|
|
37
|
+
let [loading, setLoading] = useState<boolean>(false);
|
|
38
|
+
let initializedRef = useRef<boolean>(false);
|
|
39
|
+
|
|
40
|
+
let [fix, setFix] = useState<RegisterFixType | null>(defaultValue ?? null);
|
|
41
|
+
let fixValue = value === undefined ? fix : value;
|
|
42
|
+
|
|
43
|
+
// biome-ignore lint: intentionally skipping "value" dependency as this should never run when "value" changes
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (initializedRef.current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (value === undefined && defaultValue) {
|
|
50
|
+
setFix(defaultValue);
|
|
51
|
+
initializedRef.current = true;
|
|
52
|
+
}
|
|
53
|
+
}, [defaultValue]);
|
|
54
|
+
|
|
55
|
+
async function handleRegisterFix() {
|
|
56
|
+
try {
|
|
57
|
+
if (confirm(getLocalizedString("Are you sure you want to register a fix for this damage?"))) {
|
|
58
|
+
setLoading(true);
|
|
59
|
+
let record = (await procApproveDamage.execute({ ID: fixValue?.ID })) as ApproveDamageProcedureRecord;
|
|
60
|
+
|
|
61
|
+
setFix({ ...record.Table[0] });
|
|
62
|
+
|
|
63
|
+
setLoading(false);
|
|
64
|
+
onFix?.(fixValue?.ID ?? -1);
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
alert((error as Error).message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
{edit ? (
|
|
74
|
+
<Button
|
|
75
|
+
loading={loading}
|
|
76
|
+
loadingPosition="end"
|
|
77
|
+
endIcon={<CheckIcon />}
|
|
78
|
+
onClick={handleRegisterFix}
|
|
79
|
+
disabled={!!fixValue?.RegisteredFix}
|
|
80
|
+
variant="contained"
|
|
81
|
+
>
|
|
82
|
+
{getLocalizedString("Register fix")}
|
|
83
|
+
</Button>
|
|
84
|
+
) : (
|
|
85
|
+
<FormControlLabel
|
|
86
|
+
control={<Checkbox />}
|
|
87
|
+
label={getLocalizedString("Register fixed")}
|
|
88
|
+
name="RegisteredFix"
|
|
89
|
+
id="NewElementDamage_RegisteredFix"
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
|
|
93
|
+
{fixValue?.RegisteredFix && (
|
|
94
|
+
<Typography mb={1.5} color="text.secondary">
|
|
95
|
+
{localize`Registered fix by ${fixValue.RegisteredFixFullName ?? fixValue.RegisteredFixBy} on the ${formatter.format(fixValue.RegisteredFix)}`}
|
|
96
|
+
</Typography>
|
|
97
|
+
)}
|
|
98
|
+
</>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Add from "@mui/icons-material/Add";
|
|
2
|
+
import { Box, Button, type SxProps, type Theme } from "@mui/material";
|
|
3
|
+
import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
4
|
+
|
|
5
|
+
type SubmitButtonsProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Whether the form is in edit mode. If true, delete button is visible and submit button's text changes.
|
|
8
|
+
*/
|
|
9
|
+
edit?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Set loading state for submit button
|
|
12
|
+
*/
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Custom styles for Box wrapper around buttons
|
|
16
|
+
*/
|
|
17
|
+
sx?: SxProps<Theme>;
|
|
18
|
+
onDelete?: () => void;
|
|
19
|
+
onCancel?: () => void;
|
|
20
|
+
onSubmit?: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function SubmitButtons({ edit, loading, onDelete, onCancel, onSubmit, sx }: SubmitButtonsProps) {
|
|
24
|
+
return (
|
|
25
|
+
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 2, justifyContent: "flex-end", ...sx }}>
|
|
26
|
+
{edit && <Button onClick={onDelete}>{getLocalizedString("Delete")}</Button>}
|
|
27
|
+
<Button onClick={onCancel}>{getLocalizedString("Cancel")}</Button>
|
|
28
|
+
<Button
|
|
29
|
+
loading={loading}
|
|
30
|
+
loadingPosition="start"
|
|
31
|
+
startIcon={<Add />}
|
|
32
|
+
type="submit"
|
|
33
|
+
variant="contained"
|
|
34
|
+
onClick={onSubmit}
|
|
35
|
+
>
|
|
36
|
+
{edit ? getLocalizedString("Save changes") : getLocalizedString("Register damage")}
|
|
37
|
+
</Button>
|
|
38
|
+
</Box>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import DropDownArrowIcon from "@mui/icons-material/KeyboardArrowDown";
|
|
2
|
+
import { Button, ListItemText, Menu, MenuItem } from "@mui/material";
|
|
3
|
+
import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import type { TransportElementsRecord } from "../data/dsTransportElements.js";
|
|
6
|
+
|
|
7
|
+
type TransportElementSelectProps = {
|
|
8
|
+
elements: TransportElementsRecord[];
|
|
9
|
+
onSelect: (element: TransportElementsRecord) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function TransportElementSelect({ elements, onSelect }: TransportElementSelectProps) {
|
|
13
|
+
let [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
|
14
|
+
let open = !!anchorEl;
|
|
15
|
+
let [selected, setSelected] = useState<TransportElementsRecord | null>(null);
|
|
16
|
+
|
|
17
|
+
function handleChange(element: TransportElementsRecord) {
|
|
18
|
+
setAnchorEl(null);
|
|
19
|
+
onSelect(element);
|
|
20
|
+
setSelected(element);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<Button
|
|
26
|
+
id="select_element"
|
|
27
|
+
variant="contained"
|
|
28
|
+
endIcon={
|
|
29
|
+
<DropDownArrowIcon
|
|
30
|
+
style={{ transform: open ? "rotate(180deg)" : "rotate(0deg)", transition: "transform 0.2s" }}
|
|
31
|
+
/>
|
|
32
|
+
}
|
|
33
|
+
sx={{ width: "165px" }}
|
|
34
|
+
aria-controls={open ? "basic-menu" : undefined}
|
|
35
|
+
aria-haspopup="true"
|
|
36
|
+
aria-expanded={open ? "true" : undefined}
|
|
37
|
+
onClick={(event) => setAnchorEl(event.currentTarget)}
|
|
38
|
+
>
|
|
39
|
+
{selected ? `${selected.ElementID} - ${selected.Name}` : getLocalizedString("Select element")}
|
|
40
|
+
</Button>
|
|
41
|
+
<Menu id="menu_element" anchorEl={anchorEl} open={open} onClose={() => setAnchorEl(null)}>
|
|
42
|
+
{elements.map((element) => (
|
|
43
|
+
<MenuItem key={element.PrimKey} onClick={() => handleChange(element)}>
|
|
44
|
+
<ListItemText>
|
|
45
|
+
{element.ElementID} - {element.Name}
|
|
46
|
+
</ListItemText>
|
|
47
|
+
</MenuItem>
|
|
48
|
+
))}
|
|
49
|
+
</Menu>
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import SearchIcon from "@mui/icons-material/Search";
|
|
2
|
+
import { IconButton, TextField } from "@mui/material";
|
|
3
|
+
import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
4
|
+
import { type RefObject, useEffect, useImperativeHandle, useState } from "react";
|
|
5
|
+
import { type AddElement, ElementLookupDialog } from "./ElementLookupDialog";
|
|
6
|
+
|
|
7
|
+
export type Element = {
|
|
8
|
+
Domain: string;
|
|
9
|
+
PrimKey: string;
|
|
10
|
+
ProjectID: string;
|
|
11
|
+
ProjectName: string;
|
|
12
|
+
ElementID: string | null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type ElementLookupHandle = {
|
|
16
|
+
openDialog: () => void;
|
|
17
|
+
closeDialog: () => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ElementLookupProps = {
|
|
21
|
+
value?: Element | null;
|
|
22
|
+
defaultValue?: Element | null;
|
|
23
|
+
ref?: RefObject<ElementLookupHandle | null>;
|
|
24
|
+
openLookupDialogIcon?: boolean;
|
|
25
|
+
onChange?: (value: Element | null) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function ElementLookup({ value, defaultValue, ref, openLookupDialogIcon, onChange }: ElementLookupProps) {
|
|
29
|
+
let [openElementLookupDialog, setOpenElementLookupDialog] = useState<boolean>(false);
|
|
30
|
+
let [element, setElement] = useState<Element | null>(defaultValue ?? null);
|
|
31
|
+
let elementValue = value === undefined ? element : value;
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (value === undefined && defaultValue) {
|
|
35
|
+
setElement(defaultValue);
|
|
36
|
+
}
|
|
37
|
+
}, [defaultValue, value]);
|
|
38
|
+
|
|
39
|
+
useImperativeHandle(ref, () => ({
|
|
40
|
+
openDialog: () => setOpenElementLookupDialog(true),
|
|
41
|
+
closeDialog: () => setOpenElementLookupDialog(false),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
async function handleAddElement(addElement: AddElement) {
|
|
45
|
+
onChange?.(addElement.element);
|
|
46
|
+
setElement(addElement.element);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
<ElementLookupDialog
|
|
52
|
+
open={openElementLookupDialog}
|
|
53
|
+
mode="single"
|
|
54
|
+
onClose={() => setOpenElementLookupDialog(false)}
|
|
55
|
+
onAdd={handleAddElement}
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
<input type="hidden" name="ProjectID" value={elementValue?.ProjectID ?? ""} />
|
|
59
|
+
<input type="hidden" name="ElementRef" value={elementValue?.PrimKey ?? ""} />
|
|
60
|
+
|
|
61
|
+
<TextField
|
|
62
|
+
id="NewElementDamge_Project"
|
|
63
|
+
value={elementValue ? `${elementValue.ProjectID}: ${elementValue.ProjectName}` : ""}
|
|
64
|
+
label={getLocalizedString("Project")}
|
|
65
|
+
fullWidth
|
|
66
|
+
required
|
|
67
|
+
autoComplete="off"
|
|
68
|
+
slotProps={{ htmlInput: { readOnly: true } }}
|
|
69
|
+
/>
|
|
70
|
+
<TextField
|
|
71
|
+
name="ElementID"
|
|
72
|
+
id="NewElementDamage_ElementID"
|
|
73
|
+
value={elementValue ? elementValue.ElementID : ""}
|
|
74
|
+
label={getLocalizedString("Element ID")}
|
|
75
|
+
fullWidth
|
|
76
|
+
autoComplete="off"
|
|
77
|
+
slotProps={{
|
|
78
|
+
htmlInput: { readOnly: true },
|
|
79
|
+
input: {
|
|
80
|
+
endAdornment: openLookupDialogIcon ? (
|
|
81
|
+
<IconButton onClick={() => setOpenElementLookupDialog(true)}>
|
|
82
|
+
<SearchIcon />
|
|
83
|
+
</IconButton>
|
|
84
|
+
) : undefined,
|
|
85
|
+
},
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
</>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Add from "@mui/icons-material/Add";
|
|
2
2
|
import { Alert, Button, TextField } from "@mui/material";
|
|
3
3
|
import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
4
|
-
import { useRef, useState } from "react";
|
|
4
|
+
import { useId, useRef, useState } from "react";
|
|
5
5
|
import type { ProjectsRecord } from "../../data/dsProjects";
|
|
6
6
|
import { ProjectSelect } from "../../ProjectSelect";
|
|
7
7
|
import { type ElementDetails, getElement } from "../getElement";
|
|
@@ -22,6 +22,7 @@ export type AddElementManualFormProps = {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export function AddElementManualForm({ onAdd, mode, defaultProject }: AddElementManualFormProps) {
|
|
25
|
+
let id = useId();
|
|
25
26
|
let buttonRef = useRef<HTMLButtonElement>(null);
|
|
26
27
|
let [working, setWorking] = useState(false);
|
|
27
28
|
let [elementId, setElementId] = useState<string>("");
|
|
@@ -30,6 +31,7 @@ export function AddElementManualForm({ onAdd, mode, defaultProject }: AddElement
|
|
|
30
31
|
async function handleSubmit(evt: React.FormEvent<HTMLFormElement>) {
|
|
31
32
|
try {
|
|
32
33
|
evt.preventDefault();
|
|
34
|
+
evt.stopPropagation();
|
|
33
35
|
let formData = new FormData(evt.target as HTMLFormElement);
|
|
34
36
|
let elements = formData.get("ElementID");
|
|
35
37
|
let domain = formData.get("Domain");
|
|
@@ -64,7 +66,7 @@ export function AddElementManualForm({ onAdd, mode, defaultProject }: AddElement
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
return (
|
|
67
|
-
<form onSubmit={handleSubmit} method="post">
|
|
69
|
+
<form id={id} onSubmit={handleSubmit} method="post">
|
|
68
70
|
<Alert severity="warning" sx={{ mb: 2 }}>
|
|
69
71
|
{getLocalizedString(
|
|
70
72
|
"Manually entering elements is a major source of errors. Only use this as a last resort. If the camera or scanner is not working, please contact the IT department.",
|
|
@@ -90,6 +92,7 @@ export function AddElementManualForm({ onAdd, mode, defaultProject }: AddElement
|
|
|
90
92
|
/>
|
|
91
93
|
|
|
92
94
|
<Button
|
|
95
|
+
form={id}
|
|
93
96
|
ref={buttonRef}
|
|
94
97
|
color="primary"
|
|
95
98
|
variant="contained"
|
|
@@ -5,6 +5,7 @@ import Giai from "./utils/Giai";
|
|
|
5
5
|
export type AddElementSource = "manual" | "camera-barcode" | "loading-list" | "hid-barcode" | "hid-rfid";
|
|
6
6
|
|
|
7
7
|
export type ElementDetails = {
|
|
8
|
+
Domain: string;
|
|
8
9
|
PrimKey: string;
|
|
9
10
|
Created: Date;
|
|
10
11
|
ProjectID: string;
|
|
@@ -20,6 +21,7 @@ const elementsDataHandler = new DataProviderHandlerAPI<ElementDetails>({
|
|
|
20
21
|
fields: [
|
|
21
22
|
{ name: "PrimKey", type: "string" },
|
|
22
23
|
{ name: "Created", type: "datetime" },
|
|
24
|
+
{ name: "Domain", type: "string" },
|
|
23
25
|
{ name: "ProjectID", type: "string" },
|
|
24
26
|
{ name: "ProjectName", type: "string" },
|
|
25
27
|
{ name: "ProjectDomain", type: "string" },
|