- Finish adjusting the Board Settings component.

- Extract some components out of Lane.jsx into their own files
- Fix misc bugs around preserving lane height in Vertical mode
- Add missing non english translation strings

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-07-15 11:51:34 -04:00
parent adab6ef0c6
commit f8fbbd2323
7 changed files with 143 additions and 123 deletions

View File

@@ -4,23 +4,26 @@ import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UPDATE_KANBAN_SETTINGS } from "../../graphql/user.queries"; import { UPDATE_KANBAN_SETTINGS } from "../../graphql/user.queries";
export default function ProductionBoardKanbanSettings({ associationSettings, parentLoading, onSettingsChange }) { export default function ProductionBoardKanbanSettings({ associationSettings, parentLoading }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [hasChanges, setHasChanges] = useState(false); const [hasChanges, setHasChanges] = useState(false);
const [orientation, setOrientation] = useState(true); // Default to vertical const [orientation, setOrientation] = useState(true);
const [compact, setCompact] = useState(false);
const [colored, setColored] = useState(false);
const [updateKbSettings] = useMutation(UPDATE_KANBAN_SETTINGS); const [updateKbSettings] = useMutation(UPDATE_KANBAN_SETTINGS);
const { t } = useTranslation();
useEffect(() => { useEffect(() => {
if (associationSettings?.kanban_settings) { if (associationSettings?.kanban_settings) {
const { orientation = true, compact = true, cardcolor = true } = associationSettings.kanban_settings;
form.setFieldsValue(associationSettings.kanban_settings); form.setFieldsValue(associationSettings.kanban_settings);
setOrientation(associationSettings.kanban_settings?.orientation ?? true); setOrientation(orientation);
setCompact(compact);
setColored(cardcolor);
} }
}, [form, associationSettings, open]); }, [form, associationSettings]);
const { t } = useTranslation();
const handleFinish = async (values) => { const handleFinish = async (values) => {
setLoading(true); setLoading(true);
@@ -29,9 +32,10 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
const result = await updateKbSettings({ const result = await updateKbSettings({
variables: { variables: {
id: associationSettings?.id, id: associationSettings?.id,
ks: { ...values, orientation } ks: { ...values, orientation, compact, cardcolor: colored }
} }
}); });
if (result.errors) { if (result.errors) {
notification.open({ notification.open({
type: "error", type: "error",
@@ -40,40 +44,56 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
}) })
}); });
} }
setOpen(false); setOpen(false);
setLoading(false); setLoading(false);
parentLoading(false); parentLoading(false);
setHasChanges(false); setHasChanges(false);
}; };
const handleValuesChange = () => { const handleValuesChange = () => setHasChanges(true);
setHasChanges(true);
};
const handleOrientationChange = (checked) => { const handleCheckedChanges = (checked, callback) => {
setOrientation(checked); callback(checked);
setHasChanges(true); setHasChanges(true);
}; };
const cardStyle = { minWidth: "50vw", marginTop: 10 }; const cardStyle = { minWidth: "50vw", marginTop: 10 };
const renderSwitchItem = (name, checked, callback, labelKey, checkedChildrenKey, unCheckedChildrenKey) => (
<Col span={4} key={name}>
<Form.Item name={name} valuePropName="checked" label={t(labelKey)}>
<Switch
checkedChildren={t(checkedChildrenKey)}
unCheckedChildren={t(unCheckedChildrenKey)}
checked={checked}
onChange={(checked) => handleCheckedChanges(checked, callback)}
/>
</Form.Item>
</Col>
);
const renderCheckboxItem = (name, labelKey) => (
<Col span={4} key={name}>
<Form.Item name={name} valuePropName="checked">
<Checkbox>{t(labelKey)}</Checkbox>
</Form.Item>
</Col>
);
const renderCardSettings = () => ( const renderCardSettings = () => (
<> <>
<Card title={t("production.settings.layout")} style={cardStyle}> <Card title={t("production.settings.layout")} style={cardStyle}>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
{renderSwitchItem(
"orientation",
orientation,
setOrientation,
"production.labels.orientation",
"production.labels.vertical",
"production.labels.horizontal"
)}
<Col span={4}> <Col span={4}>
<Form.Item valuePropName="checked" label={t("production.labels.orientation")}>
<Switch
checkedChildren="Vertical"
unCheckedChildren="Horizontal"
checked={orientation}
onChange={handleOrientationChange}
/>
</Form.Item>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={24}>
<Form.Item name="cardSize" label={t("production.labels.card_size")}> <Form.Item name="cardSize" label={t("production.labels.card_size")}>
<Radio.Group> <Radio.Group>
<Radio.Button value="compact">{t("production.options.small")}</Radio.Button> <Radio.Button value="compact">{t("production.options.small")}</Radio.Button>
@@ -82,77 +102,39 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
</Col> </Col>
</Row> {renderSwitchItem(
<Row gutter={[16, 16]}> "compact",
<Col span={4}> compact,
<Form.Item name="compact" valuePropName="checked"> setCompact,
<Checkbox>{t("production.labels.compact")}</Checkbox> "production.labels.compact",
</Form.Item> "production.labels.tall",
</Col> "production.labels.wide"
<Col span={4}> )}
<Form.Item name="cardcolor" valuePropName="checked"> {renderSwitchItem(
<Checkbox>{t("production.labels.cardcolor")}</Checkbox> "cardcolor",
</Form.Item> colored,
</Col> setColored,
"production.labels.cardcolor",
"production.labels.on",
"production.labels.off"
)}
</Row> </Row>
</Card> </Card>
<Card title={t("production.settings.information")} style={cardStyle}> <Card title={t("production.settings.information")} style={cardStyle}>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={4}> {[
<Form.Item name="model_info" valuePropName="checked"> "model_info",
<Checkbox>{t("production.labels.model_info")}</Checkbox> "ownr_nm",
</Form.Item> "clm_no",
</Col> "ins_co_nm",
<Col span={4}> "employeeassignments",
<Form.Item name="ownr_nm" valuePropName="checked"> "actual_in",
<Checkbox>{t("production.labels.ownr_nm")}</Checkbox> "scheduled_completion",
</Form.Item> "ats",
</Col> "production_note",
<Col span={4}> "sublets",
<Form.Item name="clm_no" valuePropName="checked"> "partsstatus"
<Checkbox>{t("production.labels.clm_no")}</Checkbox> ].map((item) => renderCheckboxItem(item, `production.labels.${item}`))}
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="ins_co_nm" valuePropName="checked">
<Checkbox>{t("production.labels.ins_co_nm")}</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="employeeassignments" valuePropName="checked">
<Checkbox>{t("production.labels.employeeassignments")}</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="actual_in" valuePropName="checked">
<Checkbox>{t("production.labels.actual_in")}</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="scheduled_completion" valuePropName="checked">
<Checkbox>{t("production.labels.scheduled_completion")}</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="ats" valuePropName="checked">
<Checkbox>{t("production.labels.ats")}</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="production_note" valuePropName="checked">
<Checkbox>{t("production.labels.production_note")}</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="sublets" valuePropName="checked">
<Checkbox>{t("production.labels.sublets")}</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="partsstatus" valuePropName="checked">
<Checkbox>{t("production.labels.partsstatus")}</Checkbox>
</Form.Item>
</Col>
</Row> </Row>
</Card> </Card>
</> </>
@@ -172,7 +154,7 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
</Button> </Button>
</Col> </Col>
<Col span={8}> <Col span={8}>
<Button block onClick={() => form.submit()} loading={loading} type="primary" disabled={!hasChanges}> <Button block onClick={form.submit} loading={loading} type="primary" disabled={!hasChanges}>
{t("general.actions.save")} {t("general.actions.save")}
</Button> </Button>
</Col> </Col>

View File

@@ -0,0 +1,9 @@
import React from "react";
const ItemComponent = ({ children, maxCardHeight, maxCardWidth, ...props }) => (
<div style={{ minWidth: maxCardWidth, minHeight: maxCardHeight }} {...props}>
{children}
</div>
);
export default ItemComponent;

View File

@@ -0,0 +1,9 @@
import React from "react";
const ItemWrapper = React.memo(({ children, ...props }) => (
<div {...props} className="item-wrapper">
{children}
</div>
));
export default ItemWrapper;

View File

@@ -0,0 +1,9 @@
import React, { forwardRef } from "react";
const ListComponent = forwardRef(({ style, children, ...props }, ref) => (
<div ref={ref} {...props} style={{ ...style }}>
{children}
</div>
));
export default ListComponent;

View File

@@ -1,4 +1,4 @@
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { bindActionCreators } from "redux"; import { bindActionCreators } from "redux";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -15,24 +15,9 @@ import { selectTechnician } from "../../../../redux/tech/tech.selectors.js";
import ProductionBoardCard from "../../../production-board-kanban-card/production-board-kanban-card.component.jsx"; import ProductionBoardCard from "../../../production-board-kanban-card/production-board-kanban-card.component.jsx";
import HeightMemoryWrapper from "../components/Lane/HeightMemoryWrapper.jsx"; import HeightMemoryWrapper from "../components/Lane/HeightMemoryWrapper.jsx";
import SizeMemoryWrapper from "../components/Lane/SizeMemoryWrapper.jsx"; import SizeMemoryWrapper from "../components/Lane/SizeMemoryWrapper.jsx";
import ListComponent from "../components/Lane/ListComponent.jsx";
const ListComponent = forwardRef(({ style, children, ...props }, ref) => ( import ItemComponent from "../components/Lane/ItemComponent.jsx";
<div ref={ref} {...props} style={{ ...style }}> import ItemWrapper from "../components/Lane/ItemWrapper.jsx";
{children}
</div>
));
const ItemComponent = ({ children, maxCardHeight, maxCardWidth, ...props }) => (
<div style={{ minWidth: maxCardWidth, minHeight: maxCardHeight }} {...props}>
{children}
</div>
);
const ItemWrapper = React.memo(({ children, ...props }) => (
<div {...props} className="item-wrapper">
{children}
</div>
));
/** /**
* Lane is a React component that represents a lane in a Trello-like board. * Lane is a React component that represents a lane in a Trello-like board.
@@ -170,6 +155,8 @@ const Lane = ({
const componentProps = orientation === "vertical" ? verticalProps : horizontalProps; const componentProps = orientation === "vertical" ? verticalProps : horizontalProps;
// If the lane is collapsed, we want to render a div instead of the virtualized list, and we want to set the height to the max height of the lane so that
// the lane doesn't shrink when collapsed (in horizontal mode)
const finalComponentProps = collapsed const finalComponentProps = collapsed
? orientation === "horizontal" ? orientation === "horizontal"
? { ? {
@@ -180,19 +167,19 @@ const Lane = ({
: {} : {}
: componentProps; : componentProps;
// If the lane is horizontal and collapsed, we want to override the minHeight style so that the lane doesn't shrink to 0 height
const shouldOverride = orientation !== "horizontal" && (collapsed || !renderedCards.length); const shouldOverride = orientation !== "horizontal" && (collapsed || !renderedCards.length);
// If the lane is horizontal and collapsed, we want to render a placeholder so that the lane doesn't shrink to 0 height and grows when
// a card is dragged over it
const shouldRenderPlaceholder = orientation !== "horizontal" && (collapsed || renderedCards.length === 0); const shouldRenderPlaceholder = orientation !== "horizontal" && (collapsed || renderedCards.length === 0);
const hasKey = // Super magic key to maintain max height on the lane when cards are added / removed / resized / etc
orientation === "vertical" const itemKey = `${id}-${orientation}-${cardSettings?.compact}-${renderedCards.length}-${cardSettings?.cardSize}`;
? {
key: id
}
: {};
return ( return (
<HeightMemoryWrapper <HeightMemoryWrapper
{...hasKey} itemKey={itemKey}
maxHeight={maxLaneHeight} maxHeight={maxLaneHeight}
setMaxHeight={setMaxLaneHeight} setMaxHeight={setMaxLaneHeight}
override={shouldOverride} override={shouldOverride}
@@ -209,7 +196,17 @@ const Lane = ({
</HeightMemoryWrapper> </HeightMemoryWrapper>
); );
}, },
[orientation, collapsed, isVisible, renderDraggable, maxLaneHeight, setMaxLaneHeight, maxCardWidth, id] [
orientation,
collapsed,
isVisible,
renderDraggable,
maxLaneHeight,
setMaxLaneHeight,
maxCardWidth,
id,
cardSettings
]
); );
const renderDragContainer = useCallback( const renderDragContainer = useCallback(

View File

@@ -2759,7 +2759,13 @@
"settings": "Error saving board settings: {{error}}" "settings": "Error saving board settings: {{error}}"
}, },
"labels": { "labels": {
"orientation": "Orientation", "on": "On",
"off": "Off",
"wide": "Wide",
"tall": "Tall",
"vertical": "Vertical",
"horizontal": "Horizontal",
"orientation": "Board Orientation",
"card_size": "Card Size", "card_size": "Card Size",
"model_info": "Model Info", "model_info": "Model Info",
"actual_in": "Actual In", "actual_in": "Actual In",
@@ -2775,11 +2781,11 @@
"qbo_usa": "QBO USA" "qbo_usa": "QBO USA"
} }
}, },
"cardcolor": "Card Colors", "cardcolor": "Colored Cards",
"cardsettings": "Card Settings", "cardsettings": "Card Settings",
"clm_no": "Claim Number", "clm_no": "Claim Number",
"comment": "Comment", "comment": "Comment",
"compact": "Compact", "compact": "Compact Cards",
"detailpriority": "D/P", "detailpriority": "D/P",
"employeeassignments": "Employee Assignments", "employeeassignments": "Employee Assignments",
"employeesearch": "Employee Search", "employeesearch": "Employee Search",

View File

@@ -2742,6 +2742,14 @@
"settings": "" "settings": ""
}, },
"labels": { "labels": {
"on": "",
"off": "",
"wide": "",
"tall": "",
"vertical": "",
"horizontal": "",
"orientation": "",
"card_size": "",
"model_info": "", "model_info": "",
"actual_in": "", "actual_in": "",
"alert": "", "alert": "",