Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MS2: Property Panel Improvements #166

Merged
merged 22 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
76d871b
enable setting colors of elements
LucasMGo Nov 2, 2023
c332ca7
added documentation field in property panel
LucasMGo Nov 2, 2023
95086a8
use resizable card for properties panel
LucasMGo Nov 2, 2023
e2a435f
add process creation button to process overview
LucasMGo Nov 11, 2023
78877a9
make use of quill editor for property panel descriptions
LucasMGo Nov 13, 2023
ba3a127
Added custom properties to property panel
LucasMGo Nov 17, 2023
d4247a1
add milestones to property panel for userTasks
LucasMGo Nov 20, 2023
482ffc1
show name and type of element in general info of property panel
LucasMGo Nov 20, 2023
0ed63ce
remove scrollbar in sider and in main view
LucasMGo Nov 20, 2023
b896007
design property panel as resizable and collapsible card
LucasMGo Nov 21, 2023
96ee1ae
Merge main into branch
LucasMGo Nov 21, 2023
f4267cb
remove quill editor
LucasMGo Nov 21, 2023
c0a7e8f
Merge branch 'main' into ms2/property-panel
LucasMGo Nov 22, 2023
e1f72c9
define metaData and milestones as useMemo instead of using useEffect;…
LucasMGo Nov 24, 2023
f9953d0
remove triggerRerender callback
LucasMGo Nov 24, 2023
b31f71c
store isResizing as variable without using useState
LucasMGo Nov 24, 2023
3411cb3
move milestones logic from properties panel to milestone selection se…
LucasMGo Nov 24, 2023
c319420
Merge main into branch
LucasMGo Nov 24, 2023
7ae1501
create FormSubmitButton component for reuse in form components
LucasMGo Nov 24, 2023
166fc87
add rest endpoints for creating/deleting images in processes
LucasMGo Nov 27, 2023
c291de1
make use of forwardref for resizing element
LucasMGo Dec 1, 2023
8873865
Merge main into branch
LucasMGo Dec 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/management-system-v2/components/ResizableElement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, {
useState,
useEffect,
PropsWithChildren,
CSSProperties,
forwardRef,
useImperativeHandle,
Dispatch,
SetStateAction,
} from 'react';

type ResizableElementProps = PropsWithChildren<{
initialWidth: number;
minWidth: number;
maxWidth: number;
style?: CSSProperties;
}>;

export type ResizableElementRefType = Dispatch<SetStateAction<number>>;

let isResizing = false;
const ResizableElement = forwardRef<ResizableElementRefType, ResizableElementProps>(
function ResizableElement({ children, initialWidth, minWidth, maxWidth, style = {} }, ref) {
const [width, setWidth] = useState(initialWidth);

useImperativeHandle(ref, () => setWidth);

const onMouseDown = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
isResizing = true;
};

const onMouseUp = (e: MouseEvent) => {
isResizing = false;
};

const onMouseMove = (e: MouseEvent) => {
if (isResizing) {
let offsetRight = document.body.offsetWidth - (e.clientX - document.body.offsetLeft);

if (offsetRight > minWidth && offsetRight < maxWidth) {
setWidth(offsetRight);
}
}
};

useEffect(() => {
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);

return () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
});

return (
<div
style={{
...style,
width: width,
}}
>
{/* This is used to resize the element */}
<div
style={{
position: 'absolute',
width: '5px',
padding: '4px 0 0',
top: 0,
left: 0,
bottom: 0,
zIndex: 100,
cursor: 'ew-resize',
}}
onMouseDown={onMouseDown}
/>
{children}
</div>
);
},
);

export default ResizableElement;
71 changes: 71 additions & 0 deletions src/management-system-v2/components/collapsible-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client';

import { Card, Button } from 'antd';
import { DoubleRightOutlined, DoubleLeftOutlined } from '@ant-design/icons';
import React, { FC, PropsWithChildren } from 'react';
import classNames from 'classnames';

type CollapsibleCardProps = PropsWithChildren<{
show: boolean;
collapsedWidth?: string;
onCollapse: () => void;
title: string;
}>;

const CollapsibleCard: FC<CollapsibleCardProps> = ({
title,
show,
collapsedWidth = '30px',
onCollapse,
children,
}) => {
return (
<Card
className={classNames({ 'Hide-Scroll-Bar': !show })}
style={{
scrollBehavior: 'smooth',
overflowY: 'scroll',
height: '100%',
scrollbarWidth: 'none',
width: show ? '100%' : collapsedWidth,
}}
headStyle={{ padding: 0 }}
title={
show ? (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Button
type="text"
style={{
padding: '2px',
marginInline: '6px',
}}
onClick={() => {
onCollapse();
}}
>
<DoubleRightOutlined />
</Button>
<span>{title}</span>
</div>
) : (
<Button
type="text"
style={{
padding: '2px',
width: '100%',
}}
onClick={() => {
onCollapse();
}}
>
<DoubleLeftOutlined />
</Button>
)
}
>
{show && children}
</Card>
);
};

export default CollapsibleCard;
3 changes: 1 addition & 2 deletions src/management-system-v2/components/content.module.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.Main {
max-height: 100vh !important;
min-height: calc(100vh - 70px) !important;
height: 100%;

.Header {
display: flex;
Expand Down
185 changes: 185 additions & 0 deletions src/management-system-v2/components/custom-property-section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
'use client';

import React, { useEffect, useMemo, useState } from 'react';

import { Button, Col, Form, Input, Row, Space } from 'antd';

import { DeleteOutlined } from '@ant-design/icons';

type CustomPropertyFormProperties = {
customMetaData: { [key: string]: any };
initialValues: { name: string; value: string };
onChange: (name: string, value: any) => void;
};
const CustomPropertyForm: React.FC<CustomPropertyFormProperties> = ({
customMetaData,
onChange,
initialValues,
}) => {
const [form] = Form.useForm<{ name: string; value: any }>();
const [submittable, setSubmittable] = useState(false);

const values = Form.useWatch([], form);

React.useEffect(() => {
form.validateFields({ validateOnly: true }).then(
() => {
setSubmittable(true);
},
() => {
setSubmittable(false);
},
);
}, [form, values]);

useEffect(() => {
form.setFieldsValue(initialValues);
}, [form, initialValues]);

const validateName = async (name: any) => {
// entered name already exists in other custom property
if (initialValues.name !== name && customMetaData[name]) {
throw new Error('Name already exists');
}
return true;
};

return (
<Form
form={form}
layout="inline"
autoComplete="off"
initialValues={initialValues}
style={{ flexGrow: 1 }}
>
<Row style={{ flexGrow: 1 }}>
<Col span={12}>
<Form.Item
name="name"
rules={[{ required: true }, { validator: (_, value) => validateName(value) }]}
>
<Input
size="large"
placeholder="Name"
onBlur={() => {
if (submittable) {
// delete custom property with old name
if (initialValues.name && initialValues.name !== values.name) {
onChange(initialValues.name, null);
}
onChange(values.name, values.value);
}
}}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="value" rules={[{ required: true }]}>
<Input
size="large"
placeholder="Value"
onBlur={() => {
if (submittable) {
onChange(values.name, values.value);
}
}}
/>
</Form.Item>
</Col>
</Row>
</Form>
);
};
type CustomPropertySectionProperties = {
metaData: { [key: string]: any };
onChange: (name: string, value: any) => void;
};

const CustomPropertySection: React.FC<CustomPropertySectionProperties> = ({
metaData,
onChange,
}) => {
const [customProperties, setCustomProperties] = useState([{ name: '', value: '' }]);

const customMetaData = useMemo(() => {
const {
costsPlanned,
timePlannedDuration,
timePlannedOccurrence,
timePlannedEnd,
occurrenceProbability,
orderNumber,
orderName,
orderCode,
customerName,
customerId,
isUsing5i,
defaultPriority,
mqttServer,
'_5i-Inspection-Plan-ID': inspectionPlanId,
'_5i-Inspection-Plan-Title': inspectionPlanTitle,
'_5i-API-Address': apiAddress,
'_5i-Application-Address': applicationAddress,
'_5i-Inspection-Order-ID': inspectionOrderId,
'_5i-Inspection-Order-Code': inspectionOrderCode,
'_5i-Inspection-Order-Shortdescription': inspectionOrderDescription,
'_5i-Assembly-Group-ID': assemblyId,
'_5i-Assembly-Group-Name': assemblyName,
'_5i-Manufacturing-Step-ID': stepId,
'_5i-Manufacturing-Step-Name': stepName,
'_5i-Inspection-Plan-Template-ID': templateId,
...customMetaData
} = metaData;
return { ...customMetaData };
}, [metaData]);

useEffect(() => {
const newCustomProperties = Object.entries(customMetaData).map(
([key, value]: [string, any]) => {
return { name: key, value };
},
);
setCustomProperties([...newCustomProperties, { name: '', value: '' }]);
}, [customMetaData]);

const updateProperty = (newCustomPropertyName: string, newCustomPropertyValue: any) => {
onChange(newCustomPropertyName, newCustomPropertyValue);
};

const deleteProperty = (customPropertyName: string) => {
onChange(customPropertyName, null);
};

return (
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<b>Custom Properties</b>
{customProperties.map((element: { name: string; value: any }, index) => (
<div
key={element.name || 'newCustomProperty'}
style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}
>
<CustomPropertyForm
customMetaData={customMetaData}
initialValues={{ name: element.name, value: element.value }}
onChange={(name, value) => {
if (!value) {
deleteProperty(name);
} else {
updateProperty(name, value);
}
}}
></CustomPropertyForm>
<Button
style={{ visibility: index !== customProperties.length - 1 ? 'visible' : 'hidden' }}
size="large"
type="text"
icon={<DeleteOutlined />}
onClick={() => deleteProperty(element.name)}
></Button>
</div>
))}
</Space>
);
};

export default CustomPropertySection;
44 changes: 44 additions & 0 deletions src/management-system-v2/components/form-submit-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { FormInstance, Form, Button } from 'antd';
import React, { useState } from 'react';

const FormSubmitButton = ({
form,
onSubmit,
submitText,
}: {
form: FormInstance;
onSubmit: Function;
submitText: string;
}) => {
const [submittable, setSubmittable] = useState(false);

// Watch all values
const values = Form.useWatch([], form);

React.useEffect(() => {
form.validateFields({ validateOnly: true }).then(
() => {
setSubmittable(true);
},
() => {
setSubmittable(false);
},
);
}, [form, values]);

return (
<Button
type="primary"
htmlType="submit"
disabled={!submittable}
onClick={() => {
onSubmit(values);
form.resetFields();
}}
>
{submitText}
</Button>
);
};

export default FormSubmitButton;
Loading
Loading