IO-3624 Polish config empty states and admin cards

This commit is contained in:
Dave
2026-03-25 10:16:48 -04:00
parent 3aa19ec09f
commit b8246e03c1
22 changed files with 4976 additions and 4967 deletions

View File

@@ -0,0 +1,11 @@
import { useTranslation } from "react-i18next";
export default function ConfigListEmptyState({ actionLabel, minHeight = 96 }) {
const { t } = useTranslation();
return (
<div className="imex-form-row-empty-state" style={{ minHeight }}>
{t("general.labels.click_to_begin", { action: actionLabel })}
</div>
);
}

View File

@@ -1,15 +1,19 @@
export const inlineFormRowTitleStyles = Object.freeze({ export const inlineFormRowTitleStyles = Object.freeze({
input: Object.freeze({ input: Object.freeze({
background: "var(--imex-form-title-input-bg)", background: "transparent",
border: "1px solid var(--imex-form-title-input-border)", border: "none",
borderRadius: 6, borderRadius: 0,
paddingInline: 10, boxShadow: "none",
paddingBlock: 4, paddingInline: 0,
lineHeight: 1.3 paddingBlock: 0,
lineHeight: 1.35,
flex: "1 1 auto",
minWidth: 0,
width: "100%"
}), }),
row: Object.freeze({ row: Object.freeze({
display: "flex", display: "flex",
gap: 4, gap: 6,
flexWrap: "wrap", flexWrap: "wrap",
alignItems: "center", alignItems: "center",
width: "100%", width: "100%",
@@ -18,25 +22,37 @@ export const inlineFormRowTitleStyles = Object.freeze({
group: Object.freeze({ group: Object.freeze({
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
gap: 6, gap: 8,
minWidth: 0 paddingInline: 8,
paddingBlock: 4,
borderRadius: 10,
border: "1px solid var(--imex-form-title-group-border)",
background: "var(--imex-form-title-group-bg)",
minWidth: 0,
flex: "1 1 0"
}), }),
label: Object.freeze({ label: Object.freeze({
color: "var(--ant-color-text)", color: "var(--ant-color-text-secondary)",
fontSize: "var(--ant-font-size)", fontSize: 12,
fontWeight: 500, fontWeight: 600,
whiteSpace: "nowrap" lineHeight: 1,
whiteSpace: "nowrap",
paddingInline: 6,
paddingBlock: 3,
borderRadius: 999,
border: "1px solid var(--imex-form-title-label-border)",
background: "var(--imex-form-title-label-bg)"
}), }),
handle: Object.freeze({ handle: Object.freeze({
color: "var(--ant-color-text-tertiary)", color: "var(--ant-color-text-tertiary)",
fontSize: 14, fontSize: 14,
flex: "0 0 auto", flex: "0 0 auto",
marginRight: 4 marginRight: 2
}), }),
separator: Object.freeze({ separator: Object.freeze({
width: 1, width: 1,
height: 18, height: 16,
background: "color-mix(in srgb, var(--imex-form-surface-border) 78%, transparent)", background: "color-mix(in srgb, var(--imex-form-surface-border) 58%, transparent)",
borderRadius: 999, borderRadius: 999,
flex: "0 0 auto", flex: "0 0 auto",
marginInline: 2 marginInline: 2
@@ -52,6 +68,10 @@ export const inlineFormRowTitleStyles = Object.freeze({
export const INLINE_TITLE_INPUT_STYLE = inlineFormRowTitleStyles.input; export const INLINE_TITLE_INPUT_STYLE = inlineFormRowTitleStyles.input;
export const INLINE_TITLE_ROW_STYLE = inlineFormRowTitleStyles.row; export const INLINE_TITLE_ROW_STYLE = inlineFormRowTitleStyles.row;
export const INLINE_TITLE_GROUP_STYLE = inlineFormRowTitleStyles.group; export const INLINE_TITLE_GROUP_STYLE = inlineFormRowTitleStyles.group;
export const INLINE_TITLE_SWITCH_GROUP_STYLE = Object.freeze({
...inlineFormRowTitleStyles.group,
flex: "0 0 auto"
});
export const INLINE_TITLE_LABEL_STYLE = inlineFormRowTitleStyles.label; export const INLINE_TITLE_LABEL_STYLE = inlineFormRowTitleStyles.label;
export const INLINE_TITLE_HANDLE_STYLE = inlineFormRowTitleStyles.handle; export const INLINE_TITLE_HANDLE_STYLE = inlineFormRowTitleStyles.handle;
export const INLINE_TITLE_SEPARATOR_STYLE = inlineFormRowTitleStyles.separator; export const INLINE_TITLE_SEPARATOR_STYLE = inlineFormRowTitleStyles.separator;

View File

@@ -15,6 +15,10 @@
--imex-form-surface-border: #d9d9d9; /* matches AntD-ish border */ --imex-form-surface-border: #d9d9d9; /* matches AntD-ish border */
--imex-form-title-input-bg: rgba(255, 255, 255, 0.96); --imex-form-title-input-bg: rgba(255, 255, 255, 0.96);
--imex-form-title-input-border: rgba(0, 0, 0, 0.08); --imex-form-title-input-border: rgba(0, 0, 0, 0.08);
--imex-form-title-group-bg: rgba(255, 255, 255, 0.72);
--imex-form-title-group-border: rgba(0, 0, 0, 0.08);
--imex-form-title-label-bg: rgba(0, 0, 0, 0.04);
--imex-form-title-label-border: rgba(0, 0, 0, 0.06);
} }
/* Pick the selector that matches your app and remove the rest */ /* Pick the selector that matches your app and remove the rest */
@@ -24,6 +28,10 @@ html[data-theme="dark"] {
--imex-form-surface-border: rgba(5, 5, 5, 0.12); --imex-form-surface-border: rgba(5, 5, 5, 0.12);
--imex-form-title-input-bg: rgba(255, 255, 255, 0.12); --imex-form-title-input-bg: rgba(255, 255, 255, 0.12);
--imex-form-title-input-border: rgba(255, 255, 255, 0.2); --imex-form-title-input-border: rgba(255, 255, 255, 0.2);
--imex-form-title-group-bg: rgba(255, 255, 255, 0.08);
--imex-form-title-group-border: rgba(255, 255, 255, 0.16);
--imex-form-title-label-bg: rgba(255, 255, 255, 0.06);
--imex-form-title-label-border: rgba(255, 255, 255, 0.12);
} }
.imex-form-row { .imex-form-row {
@@ -114,6 +122,20 @@ html[data-theme="dark"] {
background: var(--imex-form-surface); background: var(--imex-form-surface);
} }
.ant-card-actions {
background: var(--imex-form-surface-head);
border-top-color: var(--imex-form-surface-border);
}
.ant-card-actions > li {
margin: 10px 0;
padding-inline: 12px;
}
.ant-card-actions .ant-btn {
width: 100%;
}
.ant-form-item:last-child { .ant-form-item:last-child {
margin-bottom: 4px; margin-bottom: 4px;
} }
@@ -156,3 +178,14 @@ html[data-theme="dark"] {
} }
} }
} }
.imex-form-row-empty-state {
display: flex;
align-items: center;
justify-content: center;
padding: 24px 16px;
text-align: center;
color: var(--ant-color-text-description);
font-size: var(--ant-font-size);
line-height: 1.5;
}

View File

@@ -27,6 +27,7 @@ import dayjs from "../../utils/day";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx"; import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -35,6 +36,7 @@ import {
INLINE_TITLE_LABEL_STYLE, INLINE_TITLE_LABEL_STYLE,
INLINE_TITLE_ROW_STYLE, INLINE_TITLE_ROW_STYLE,
INLINE_TITLE_SEPARATOR_STYLE, INLINE_TITLE_SEPARATOR_STYLE,
INLINE_TITLE_SWITCH_GROUP_STYLE,
INLINE_TITLE_TEXT_STYLE INLINE_TITLE_TEXT_STYLE
} from "../layout-form-row/inline-form-row-title.utils.js"; } from "../layout-form-row/inline-form-row-title.utils.js";
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component"; import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
@@ -166,6 +168,8 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => (
<Button <Button
type="text"
danger
onClick={async () => { onClick={async () => {
await deleteVacation({ await deleteVacation({
variables: { id: record.id }, variables: { id: record.id },
@@ -195,8 +199,8 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
<Card <Card
title={employeeCardTitle} title={employeeCardTitle}
extra={ extra={
<Button type="primary" onClick={() => form.submit()}> <Button type="primary" onClick={() => form.submit()} style={{ minWidth: 170 }}>
{t("general.actions.save")} {t("employees.actions.save_employee")}
</Button> </Button>
} }
> >
@@ -229,8 +233,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div
style={{ style={{
...INLINE_TITLE_GROUP_STYLE, ...INLINE_TITLE_SWITCH_GROUP_STYLE
flex: "0 0 auto"
}} }}
> >
<div style={INLINE_TITLE_LABEL_STYLE}>{t("employees.labels.active")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("employees.labels.active")}</div>
@@ -241,8 +244,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div
style={{ style={{
...INLINE_TITLE_GROUP_STYLE, ...INLINE_TITLE_SWITCH_GROUP_STYLE
flex: "0 0 auto"
}} }}
> >
<div style={INLINE_TITLE_LABEL_STYLE}>{t("employees.fields.flat_rate")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("employees.fields.flat_rate")}</div>
@@ -396,26 +398,38 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
</Col> </Col>
</Row> </Row>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow title={t("bodyshop.labels.employee_rates")}>
<Form.List name={["rates"]}> <Form.List name={["rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
title={t("bodyshop.labels.employee_rates")}
actions={[
<Button
key="add-rate"
type="primary"
block
onClick={() => {
add();
}}
id="add-employee-rate-button"
>
<span id="new-employee-rate">{t("employees.actions.addrate")}</span>
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("employees.actions.addrate")} />
) : (
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}> <Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
<LayoutFormRow <LayoutFormRow
noDivider noDivider
titleOnly
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 260px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("employees.fields.cost_center")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("employees.fields.cost_center")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -447,34 +461,6 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 190px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("employees.fields.rate")}</div>
<Form.Item
noStyle
name={[field.name, "rate"]}
rules={[
{
required: true
}
]}
>
<InputNumber
min={0}
precision={2}
size="small"
style={{
...INLINE_TITLE_INPUT_STYLE,
width: "100%"
}}
/>
</Form.Item>
</div>
</div> </div>
} }
wrapTitle wrapTitle
@@ -482,6 +468,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -495,30 +482,47 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
/> />
</Space> </Space>
} }
/> >
<Form.Item
label={t("employees.fields.rate")}
name={[field.name, "rate"]}
rules={[
{
required: true
}
]}
style={{ marginBottom: 0 }}
>
<InputNumber min={0} precision={2} style={{ width: "100%" }} />
</Form.Item>
</LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
id="add-employee-rate-button"
>
<span id="new-employee-rate">{t("employees.actions.newrate")}</span>
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</Form> </Form>
<LayoutFormRow title={t("bodyshop.labels.employee_vacation")}> <LayoutFormRow
title={t("bodyshop.labels.employee_vacation")}
actions={[
<ShopEmployeeAddVacation
key="add-vacation"
employee={data && data.employees_by_pk}
buttonProps={{
type: "primary",
block: true
}}
/>
]}
>
{(data?.employees_by_pk?.employee_vacations ?? []).length === 0 ? (
<ConfigListEmptyState actionLabel={t("employees.actions.addvacation")} />
) : (
<div> <div>
<ResponsiveTable <ResponsiveTable
columns={columns} columns={columns}
@@ -527,16 +531,8 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
dataSource={data?.employees_by_pk?.employee_vacations ?? []} dataSource={data?.employees_by_pk?.employee_vacations ?? []}
pagination={false} pagination={false}
/> />
<div style={{ marginTop: 12 }}>
<ShopEmployeeAddVacation
employee={data && data.employees_by_pk}
buttonProps={{
type: "dashed",
block: true
}}
/>
</div>
</div> </div>
)}
</LayoutFormRow> </LayoutFormRow>
</Card> </Card>
); );

View File

@@ -4,6 +4,8 @@ import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import ResponsiveTable from "../responsive-table/responsive-table.component"; import ResponsiveTable from "../responsive-table/responsive-table.component";
export default function ShopEmployeesListComponent({ loading, employees }) { export default function ShopEmployeesListComponent({ loading, employees }) {
@@ -16,13 +18,28 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
filteredInfo: { text: "" } filteredInfo: { text: "" }
}); });
const navigateToEmployee = (employeeId) => {
history({
search: queryString.stringify({
...search,
employeeId
})
});
};
const clearEmployeeSelection = () => {
const { employeeId, ...nextSearch } = search;
void employeeId;
history({
search: queryString.stringify(nextSearch)
});
};
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
if (record) { if (record) {
search.employeeId = record.id; navigateToEmployee(record.id);
history({ search: queryString.stringify(search) });
} else { } else {
delete search.employeeId; clearEmployeeSelection();
history({ search: queryString.stringify(search) });
} }
}; };
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
@@ -89,21 +106,18 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
} }
]; ];
return ( return (
<div> <LayoutFormRow
<ResponsiveTable title={t("bodyshop.labels.employees")}
title={() => { actions={[
return ( <Button key="new-employee" type="primary" block onClick={() => navigateToEmployee("new")}>
<Button
type="primary"
onClick={() => {
search.employeeId = "new";
history({ search: queryString.stringify(search) });
}}
>
{t("employees.actions.new")} {t("employees.actions.new")}
</Button> </Button>
); ]}
}} >
{employees.length === 0 ? (
<ConfigListEmptyState actionLabel={t("employees.actions.new")} />
) : (
<ResponsiveTable
loading={loading} loading={loading}
pagination={{ placement: "top" }} pagination={{ placement: "top" }}
columns={columns} columns={columns}
@@ -111,10 +125,7 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
rowKey="id" rowKey="id"
dataSource={employees} dataSource={employees}
rowSelection={{ rowSelection={{
onSelect: (props) => { onSelect: (props) => navigateToEmployee(props.id),
search.employeeId = props.id;
history({ search: queryString.stringify(search) });
},
type: "radio", type: "radio",
selectedRowKeys: [search.employeeId] selectedRowKeys: [search.employeeId]
}} }}
@@ -127,6 +138,7 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
}; };
}} }}
/> />
</div> )}
</LayoutFormRow>
); );
} }

View File

@@ -163,8 +163,14 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
<Card <Card
title={<ShopInfoSectionNavigator tabsRef={tabsRef} activeTabKey={activeTabKey} />} title={<ShopInfoSectionNavigator tabsRef={tabsRef} activeTabKey={activeTabKey} />}
extra={ extra={
<Button type="primary" loading={saveLoading} onClick={() => form.submit()} id="shop-info-save-button"> <Button
{t("general.actions.save")} type="primary"
loading={saveLoading}
onClick={() => form.submit()}
id="shop-info-save-button"
style={{ minWidth: 210 }}
>
{t("bodyshop.actions.save_shop_information")}
</Button> </Button>
} }
> >

View File

@@ -8,6 +8,7 @@ import CurrencyInput from "../form-items-formatted/currency-form-item.component"
import FormItemEmail from "../form-items-formatted/email-form-item.component"; import FormItemEmail from "../form-items-formatted/email-form-item.component";
import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component"; import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -15,7 +16,8 @@ import {
INLINE_TITLE_INPUT_STYLE, INLINE_TITLE_INPUT_STYLE,
INLINE_TITLE_LABEL_STYLE, INLINE_TITLE_LABEL_STYLE,
INLINE_TITLE_ROW_STYLE, INLINE_TITLE_ROW_STYLE,
INLINE_TITLE_SEPARATOR_STYLE INLINE_TITLE_SEPARATOR_STYLE,
INLINE_TITLE_SWITCH_GROUP_STYLE
} from "../layout-form-row/inline-form-row-title.utils.js"; } from "../layout-form-row/inline-form-row-title.utils.js";
const timeZonesList = Intl.supportedValuesOf("timeZone"); const timeZonesList = Intl.supportedValuesOf("timeZone");
@@ -28,6 +30,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
export function ShopInfoGeneral({ form }) { export function ShopInfoGeneral({ form }) {
const { t } = useTranslation(); const { t } = useTranslation();
const buildSectionActionButton = (key, label, onClick, id) => (
<Button key={key} type="primary" block id={id} onClick={onClick}>
{label}
</Button>
);
const renderListOrEmpty = (fields, actionLabel, renderItems) =>
fields.length === 0 ? <ConfigListEmptyState actionLabel={actionLabel} /> : renderItems();
return ( return (
<div> <div>
@@ -449,26 +458,30 @@ export function ShopInfoGeneral({ form }) {
<Switch /> <Switch />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")} id="messagingpresets">
<Form.List name={["md_messaging_presets"]}> <Form.List name={["md_messaging_presets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.labels.messagingpresets")}
id="messagingpresets"
actions={[
buildSectionActionButton("add-messaging-preset", t("bodyshop.actions.add_messaging_preset"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_messaging_preset"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
noDivider noDivider
titleOnly
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 190px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.messaginglabel_short")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.messaginglabel_short")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -490,41 +503,13 @@ export function ShopInfoGeneral({ form }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 260px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.messagingtext_short")}</div>
<Form.Item
noStyle
name={[field.name, "text"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input.TextArea
autoSize={{ minRows: 1, maxRows: 3 }}
placeholder={t("bodyshop.fields.messagingtext_short")}
style={{
...INLINE_TITLE_INPUT_STYLE,
width: "100%",
paddingBlock: 5
}}
/>
</Form.Item>
</div>
</div> </div>
} }
extra={ extra={
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -538,46 +523,57 @@ export function ShopInfoGeneral({ form }) {
/> />
</Space> </Space>
} }
>
<Form.Item
label={t("bodyshop.fields.messagingtext_short")}
name={[field.name, "text"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
style={{ marginBottom: 0 }}
>
<Input.TextArea
autoSize={{ minRows: 1, maxRows: 3 }}
placeholder={t("bodyshop.fields.messagingtext_short")}
/> />
</Form.Item> </Form.Item>
); </LayoutFormRow>
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item> </Form.Item>
);
})
)}
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.notespresets")} id="notespresets">
<Form.List name={["md_notes_presets"]}> <Form.List name={["md_notes_presets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.labels.notespresets")}
id="notespresets"
actions={[
buildSectionActionButton("add-note-preset", t("bodyshop.actions.add_note_preset"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_note_preset"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
noDivider noDivider
titleOnly
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 190px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.noteslabel_short")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.noteslabel_short")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -599,41 +595,13 @@ export function ShopInfoGeneral({ form }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 260px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.notestext_short")}</div>
<Form.Item
noStyle
name={[field.name, "text"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Input.TextArea
autoSize={{ minRows: 1, maxRows: 3 }}
placeholder={t("bodyshop.fields.notestext_short")}
style={{
...INLINE_TITLE_INPUT_STYLE,
width: "100%",
paddingBlock: 5
}}
/>
</Form.Item>
</div>
</div> </div>
} }
extra={ extra={
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -647,46 +615,57 @@ export function ShopInfoGeneral({ form }) {
/> />
</Space> </Space>
} }
>
<Form.Item
label={t("bodyshop.fields.notestext_short")}
name={[field.name, "text"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
style={{ marginBottom: 0 }}
>
<Input.TextArea
autoSize={{ minRows: 1, maxRows: 3 }}
placeholder={t("bodyshop.fields.notestext_short")}
/> />
</Form.Item> </Form.Item>
); </LayoutFormRow>
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item> </Form.Item>
);
})
)}
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.partslocations")} id="partslocations">
<Form.List name={["md_parts_locations"]}> <Form.List name={["md_parts_locations"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.labels.partslocations")}
id="partslocations"
actions={[
buildSectionActionButton("add-parts-location", t("bodyshop.actions.addpartslocation"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.addpartslocation"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
noDivider noDivider
titleOnly
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.partslocation")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.partslocation")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -714,6 +693,7 @@ export function ShopInfoGeneral({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -730,35 +710,36 @@ export function ShopInfoGeneral({ form }) {
/> />
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addpartslocation")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
{/*Start Insurance Provider Row */} {/*Start Insurance Provider Row */}
<Form.List name={["md_ins_cos"]}>
{(fields, { add, remove, move }) => {
return (
<LayoutFormRow <LayoutFormRow
grow grow
header={<span id="insurancecos-header">{t("bodyshop.labels.insurancecos")}</span>} header={<span id="insurancecos-header">{t("bodyshop.labels.insurancecos")}</span>}
id="insurancecos" id="insurancecos"
actions={[
buildSectionActionButton(
"add-insurance-company",
t("bodyshop.actions.add_insurance_company"),
() => {
add();
},
"insurancecos-add-button"
)
]}
> >
<Form.List name={["md_ins_cos"]}>
{(fields, { add, remove, move }) => {
return (
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_insurance_company"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -766,12 +747,7 @@ export function ShopInfoGeneral({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_ins_co.name")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_ins_co.name")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -814,12 +790,7 @@ export function ShopInfoGeneral({ form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 0 auto"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_ins_co.private")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_ins_co.private")}</div>
<Form.Item noStyle name={[field.name, "private"]} valuePropName="checked"> <Form.Item noStyle name={[field.name, "private"]} valuePropName="checked">
<Switch size="small" /> <Switch size="small" />
@@ -832,6 +803,7 @@ export function ShopInfoGeneral({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -884,32 +856,31 @@ export function ShopInfoGeneral({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
id="insurancecos-add-button"
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
{/*End Insurance Provider Row */} {/*End Insurance Provider Row */}
<LayoutFormRow grow header={t("bodyshop.labels.estimators")} id="estimators">
<Form.List name={["md_estimators"]}> <Form.List name={["md_estimators"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.labels.estimators")}
id="estimators"
actions={[
buildSectionActionButton("add-estimator", t("bodyshop.actions.add_estimator"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_estimator"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -917,12 +888,7 @@ export function ShopInfoGeneral({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 255px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_ct_fn_short")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_ct_fn_short")}</div>
<Form.Item noStyle name={[field.name, "est_ct_fn"]}> <Form.Item noStyle name={[field.name, "est_ct_fn"]}>
<Input <Input
@@ -936,12 +902,7 @@ export function ShopInfoGeneral({ form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 255px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_ct_ln_short")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_ct_ln_short")}</div>
<Form.Item noStyle name={[field.name, "est_ct_ln"]}> <Form.Item noStyle name={[field.name, "est_ct_ln"]}>
<Input <Input
@@ -955,12 +916,7 @@ export function ShopInfoGeneral({ form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 240px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_co_nm_short")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.est_co_nm_short")}</div>
<Form.Item noStyle name={[field.name, "est_co_nm"]}> <Form.Item noStyle name={[field.name, "est_co_nm"]}>
<Input <Input
@@ -980,6 +936,7 @@ export function ShopInfoGeneral({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -1020,29 +977,29 @@ export function ShopInfoGeneral({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.filehandlers")} id="filehandlers">
<Form.List name={["md_filehandlers"]}> <Form.List name={["md_filehandlers"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.labels.filehandlers")}
id="filehandlers"
actions={[
buildSectionActionButton("add-adjuster", t("bodyshop.actions.add_adjuster"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_adjuster"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -1050,12 +1007,7 @@ export function ShopInfoGeneral({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 255px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.ins_ct_fn_short")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.ins_ct_fn_short")}</div>
<Form.Item noStyle name={[field.name, "ins_ct_fn"]}> <Form.Item noStyle name={[field.name, "ins_ct_fn"]}>
<Input <Input
@@ -1069,12 +1021,7 @@ export function ShopInfoGeneral({ form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 255px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.ins_ct_ln_short")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.ins_ct_ln_short")}</div>
<Form.Item noStyle name={[field.name, "ins_ct_ln"]}> <Form.Item noStyle name={[field.name, "ins_ct_ln"]}>
<Input <Input
@@ -1094,6 +1041,7 @@ export function ShopInfoGeneral({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -1134,30 +1082,34 @@ export function ShopInfoGeneral({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<FeatureWrapper featureName="courtesycars" noauth={() => null}> <FeatureWrapper featureName="courtesycars" noauth={() => null}>
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates">
<Form.List name={["md_ccc_rates"]}> <Form.List name={["md_ccc_rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.fields.md_ccc_rates")}
id="md_ccc_rates"
actions={[
buildSectionActionButton(
"add-courtesy-car-rate-preset",
t("bodyshop.actions.add_courtesy_car_rate_preset"),
() => {
add();
}
)
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_courtesy_car_rate_preset"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -1165,12 +1117,7 @@ export function ShopInfoGeneral({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -1199,6 +1146,7 @@ export function ShopInfoGeneral({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -1286,31 +1234,31 @@ export function ShopInfoGeneral({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</FeatureWrapper> </FeatureWrapper>
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")} id="md_jobline_presets">
<Form.List name={["md_jobline_presets"]}> <Form.List name={["md_jobline_presets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.fields.md_jobline_presets")}
id="md_jobline_presets"
actions={[
buildSectionActionButton("add-jobline-preset", t("bodyshop.actions.add_jobline_preset"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_jobline_preset"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -1318,12 +1266,7 @@ export function ShopInfoGeneral({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -1346,12 +1289,7 @@ export function ShopInfoGeneral({ form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("joblines.fields.line_desc")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("joblines.fields.line_desc")}</div>
<Form.Item noStyle name={[field.name, "line_desc"]}> <Form.Item noStyle name={[field.name, "line_desc"]}>
<Input.TextArea <Input.TextArea
@@ -1372,6 +1310,7 @@ export function ShopInfoGeneral({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -1477,29 +1416,33 @@ export function ShopInfoGeneral({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.fields.md_parts_order_comment")} id="md_parts_order_comment">
<Form.List name={["md_parts_order_comment"]}> <Form.List name={["md_parts_order_comment"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.fields.md_parts_order_comment")}
id="md_parts_order_comment"
actions={[
buildSectionActionButton(
"add-parts-order-comment",
t("bodyshop.actions.add_parts_order_comment"),
() => {
add();
}
)
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_parts_order_comment"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -1508,12 +1451,7 @@ export function ShopInfoGeneral({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -1535,16 +1473,30 @@ export function ShopInfoGeneral({ form }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> </div>
<div }
style={{ wrapTitle
...INLINE_TITLE_GROUP_STYLE, extra={
flex: "1 1 320px" <Space align="center" size="small">
<Button
type="text"
danger
icon={<DeleteFilled />}
onClick={() => {
remove(field.name);
}} }}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
orientation="horizontal"
/>
</Space>
}
> >
<div style={INLINE_TITLE_LABEL_STYLE}>{t("parts_orders.fields.comments")}</div>
<Form.Item <Form.Item
noStyle label={t("parts_orders.fields.comments")}
name={[field.name, "comment"]} name={[field.name, "comment"]}
rules={[ rules={[
{ {
@@ -1552,78 +1504,47 @@ export function ShopInfoGeneral({ form }) {
//message: t("general.validation.required"), //message: t("general.validation.required"),
} }
]} ]}
style={{ marginBottom: 0 }}
> >
<Input.TextArea <Input.TextArea
autoSize={{ minRows: 1, maxRows: 3 }} autoSize={{ minRows: 1, maxRows: 3 }}
placeholder={t("parts_orders.fields.comments")} placeholder={t("parts_orders.fields.comments")}
style={{
...INLINE_TITLE_INPUT_STYLE,
width: "100%",
paddingBlock: 5
}}
/> />
</Form.Item> </Form.Item>
</div> </LayoutFormRow>
</div>
}
wrapTitle
extra={
<Space align="center" size="small">
<Button
type="text"
icon={<DeleteFilled />}
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
orientation="horizontal"
/>
</Space>
}
/>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.md_to_emails")} id="md_to_emails">
<Form.List name={["md_to_emails"]}> <Form.List name={["md_to_emails"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.labels.md_to_emails")}
id="md_to_emails"
actions={[
buildSectionActionButton("add-to-email-preset", t("bodyshop.actions.add_to_email_preset"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_to_email_preset"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
noDivider noDivider
titleOnly
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("general.labels.label")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -1645,27 +1566,6 @@ export function ShopInfoGeneral({ form }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.labels.md_to_emails_emails")}</div>
<Form.Item
noStyle
name={[field.name, "emails"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<FormItemEmail email={form.getFieldValue([field.name, "emails"])} />
</Form.Item>
</div>
</div> </div>
} }
wrapTitle wrapTitle
@@ -1673,6 +1573,7 @@ export function ShopInfoGeneral({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -1686,26 +1587,30 @@ export function ShopInfoGeneral({ form }) {
/> />
</Space> </Space>
} }
/> >
<Form.Item
label={t("bodyshop.labels.md_to_emails_emails")}
name={[field.name, "emails"]}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
style={{ marginBottom: 0 }}
>
<FormItemEmail email={form.getFieldValue([field.name, "emails"])} />
</Form.Item>
</LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</div> </div>
); );
} }

View File

@@ -5,6 +5,7 @@ import styled from "styled-components";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import ConfigFormTypes from "../config-form-components/config-form-types"; import ConfigFormTypes from "../config-form-components/config-form-types";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -12,7 +13,8 @@ import {
INLINE_TITLE_INPUT_STYLE, INLINE_TITLE_INPUT_STYLE,
INLINE_TITLE_LABEL_STYLE, INLINE_TITLE_LABEL_STYLE,
INLINE_TITLE_ROW_STYLE, INLINE_TITLE_ROW_STYLE,
INLINE_TITLE_SEPARATOR_STYLE INLINE_TITLE_SEPARATOR_STYLE,
INLINE_TITLE_SWITCH_GROUP_STYLE
} from "../layout-form-row/inline-form-row-title.utils.js"; } from "../layout-form-row/inline-form-row-title.utils.js";
const SelectorDiv = styled.div` const SelectorDiv = styled.div`
@@ -87,12 +89,30 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
</SelectorDiv> </SelectorDiv>
<LayoutFormRow header={t("bodyshop.labels.intakechecklist")} id="intakechecklist">
<Form.List name={["intakechecklist", "form"]}> <Form.List name={["intakechecklist", "form"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.intakechecklist")}
id="intakechecklist"
actions={[
<Button
key="add-intake-checklist-item"
type="primary"
block
onClick={() => {
add();
}}
>
{t("bodyshop.actions.add_intake_checklist_item")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.add_intake_checklist_item")} />
) : (
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -100,12 +120,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.name")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.name")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -127,12 +142,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 0 auto"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.required")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.required")}</div>
<Form.Item noStyle name={[field.name, "required"]} valuePropName="checked"> <Form.Item noStyle name={[field.name, "required"]} valuePropName="checked">
<Switch /> <Switch />
@@ -145,6 +155,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -226,29 +237,37 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.deliverchecklist")} id="deliverchecklist">
<Form.List name={["deliverchecklist", "form"]}> <Form.List name={["deliverchecklist", "form"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.deliverchecklist")}
id="deliverchecklist"
actions={[
<Button
key="add-delivery-checklist-item"
type="primary"
block
onClick={() => {
add();
}}
>
{t("bodyshop.actions.add_delivery_checklist_item")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.add_delivery_checklist_item")} />
) : (
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -256,12 +275,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.name")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.name")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -283,12 +297,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 0 auto"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.required")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.intake.required")}</div>
<Form.Item noStyle name={[field.name, "required"]} valuePropName="checked"> <Form.Item noStyle name={[field.name, "required"]} valuePropName="checked">
<Switch /> <Switch />
@@ -301,6 +310,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -384,23 +394,13 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</div> </div>
); );
} }

View File

@@ -3,6 +3,7 @@ import { Button, Form, Input, Space } from "antd";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -25,12 +26,29 @@ export default function ShopInfoLaborRates() {
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.laborrates")}>
<Form.List name={["md_labor_rates"]}> <Form.List name={["md_labor_rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.laborrates")}
actions={[
<Button
key="add-labor-rate"
type="primary"
block
onClick={() => {
add();
}}
>
{t("bodyshop.actions.newlaborrate")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.newlaborrate")} />
) : (
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -38,12 +56,7 @@ export default function ShopInfoLaborRates() {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 340px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.labor_rate_desc")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("jobs.fields.labor_rate_desc")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -71,6 +84,7 @@ export default function ShopInfoLaborRates() {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -363,23 +377,13 @@ export default function ShopInfoLaborRates() {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.newlaborrate")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</> </>
); );
} }

View File

@@ -3,6 +3,7 @@ import { Button, Col, Form, Input, Row, Select, Space, Switch } from "antd";
import { useMemo } from "react"; import { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -10,7 +11,8 @@ import {
INLINE_TITLE_INPUT_STYLE, INLINE_TITLE_INPUT_STYLE,
INLINE_TITLE_LABEL_STYLE, INLINE_TITLE_LABEL_STYLE,
INLINE_TITLE_ROW_STYLE, INLINE_TITLE_ROW_STYLE,
INLINE_TITLE_SEPARATOR_STYLE INLINE_TITLE_SEPARATOR_STYLE,
INLINE_TITLE_SWITCH_GROUP_STYLE
} from "../layout-form-row/inline-form-row-title.utils.js"; } from "../layout-form-row/inline-form-row-title.utils.js";
import i18n from "i18next"; import i18n from "i18next";
@@ -76,11 +78,33 @@ export default function ShopInfoPartsScan({ form }) {
return ( return (
<div> <div>
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
<Form.List name={["md_parts_scan"]}> <Form.List name={["md_parts_scan"]}>
{(fields, { add, remove, move }) => ( {(fields, { add, remove, move }) => (
<LayoutFormRow
header={t("bodyshop.labels.md_parts_scan")}
actions={[
<Button
key="add-parts-scan-rule"
type="primary"
block
onClick={() =>
add({
field: "line_desc",
operation: "contains",
mark_critical: true,
caseInsensitive: true
})
}
>
{t("bodyshop.actions.addpartsrule")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.addpartsrule")} />
) : (
fields.map((field, index) => {
const selectedField = watchedFields?.[index]?.field || "line_desc"; const selectedField = watchedFields?.[index]?.field || "line_desc";
const fieldType = getFieldType(selectedField); const fieldType = getFieldType(selectedField);
@@ -91,12 +115,7 @@ export default function ShopInfoPartsScan({ form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 260px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_parts_scan.field")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.md_parts_scan.field")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -131,12 +150,7 @@ export default function ShopInfoPartsScan({ form }) {
{fieldType === "string" && ( {fieldType === "string" && (
<> <>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 0 auto"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.md_parts_scan.caseInsensitive")} {t("bodyshop.fields.md_parts_scan.caseInsensitive")}
</div> </div>
@@ -147,12 +161,7 @@ export default function ShopInfoPartsScan({ form }) {
</> </>
)} )}
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_SWITCH_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 0 auto"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.md_parts_scan.mark_critical")} {t("bodyshop.fields.md_parts_scan.mark_critical")}
</div> </div>
@@ -167,6 +176,7 @@ export default function ShopInfoPartsScan({ form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -277,28 +287,12 @@ export default function ShopInfoPartsScan({ form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})}
<Form.Item>
<Button
type="dashed"
onClick={() =>
add({
field: "line_desc",
operation: "contains",
mark_critical: true,
caseInsensitive: true
}) })
} )}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addpartsrule")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
)} )}
</Form.List> </Form.List>
</LayoutFormRow>
</div> </div>
); );
} }

View File

@@ -12,6 +12,7 @@ import DataLabel from "../data-label/data-label.component";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import { getFormListItemTitle } from "../form-list-move-arrows/form-list-item-title.utils"; import { getFormListItemTitle } from "../form-list-move-arrows/form-list-item-title.utils";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -42,6 +43,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoResponsibili
export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
const { t } = useTranslation(); const { t } = useTranslation();
const dmsPayers = Form.useWatch(["cdk_configuration", "payers"], form) || []; const dmsPayers = Form.useWatch(["cdk_configuration", "payers"], form) || [];
const buildSectionActionButton = (key, label, onClick) => (
<Button key={key} type="primary" block onClick={onClick}>
{label}
</Button>
);
const renderListOrEmpty = (fields, actionLabel, renderItems) =>
fields.length === 0 ? <ConfigListEmptyState actionLabel={actionLabel} /> : renderItems();
const hasDMSKey = bodyshopHasDmsKey(bodyshop); const hasDMSKey = bodyshopHasDmsKey(bodyshop);
@@ -513,12 +521,20 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
{!bodyshop.rr_dealerid && ( {!bodyshop.rr_dealerid && (
<> <>
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}>
<Form.List name={["cdk_configuration", "payers"]}> <Form.List name={["cdk_configuration", "payers"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.dms.cdk.payers")}
actions={[
buildSectionActionButton("add-payer", t("jobs.actions.addpayer"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("jobs.actions.addpayer"), () =>
fields.map((field, index) => {
const dmsPayer = dmsPayers[field.name] || {}; const dmsPayer = dmsPayers[field.name] || {};
return ( return (
@@ -535,6 +551,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -587,29 +604,31 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.controllist")}>
<Form.List name={["cdk_configuration", "controllist"]}> <Form.List name={["cdk_configuration", "controllist"]}>
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.dms.cdk.controllist")}
actions={[
buildSectionActionButton(
"add-control-number",
t("bodyshop.actions.add_control_number"),
() => {
add();
}
)
]}
>
<div> <div>
{fields.map((field, index) => ( {renderListOrEmpty(fields, t("bodyshop.actions.add_control_number"), () =>
fields.map((field, index) => (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow noDivider> <LayoutFormRow noDivider>
<Form.Item <Form.Item
@@ -628,30 +647,23 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<DeleteFilled <Button
type="text"
danger
icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
}} }}
/> />
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
))} ))
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</> </>
)} )}
</div> </div>
@@ -659,30 +671,29 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
)} )}
{HasFeatureAccess({ featureName: "export", bodyshop }) && ( {HasFeatureAccess({ featureName: "export", bodyshop }) && (
<> <>
<LayoutFormRow header={t("bodyshop.labels.responsibilitycenters.costs")} id="costs">
<Form.List name={["md_responsibility_centers", "costs"]}> <Form.List name={["md_responsibility_centers", "costs"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.responsibilitycenters.costs")}
id="costs"
actions={[
buildSectionActionButton("add-cost-center", t("bodyshop.actions.add_cost_center"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_cost_center"), () =>
const hasProfitCenterBodyFields = fields.map((field, index) => {
(hasDMSKey && !bodyshop.rr_dealerid) || bodyshop.cdk_dealerid || bodyshop.rr_dealerid;
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
noDivider noDivider
titleOnly={!hasProfitCenterBodyFields}
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 340px",
maxWidth: 390
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenter")} {t("bodyshop.fields.responsibilitycenter")}
</div> </div>
@@ -699,38 +710,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenter_accountname")}
</div>
<Form.Item noStyle name={[field.name, "accountname"]} rules={[{ required: true }]}>
<Input
placeholder={t("bodyshop.fields.responsibilitycenter_accountname")}
onBlur={handleBlur}
size="small"
style={{
...INLINE_TITLE_INPUT_STYLE,
width: "100%"
}}
/>
</Form.Item>
</div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenter_accountdesc")} {t("bodyshop.fields.responsibilitycenter_accountdesc")}
</div> </div>
<Form.Item noStyle name={[field.name, "accountdesc"]} rules={[{ required: true }]}> <Form.Item
noStyle
name={[field.name, "accountdesc"]}
rules={[{ required: true }]}
>
<Input <Input
placeholder={t("bodyshop.fields.responsibilitycenter_accountdesc")} placeholder={t("bodyshop.fields.responsibilitycenter_accountdesc")}
onBlur={handleBlur} onBlur={handleBlur}
@@ -742,36 +730,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
{!hasDMSKey && (
<>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0.9 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenter_accountitem")}
</div>
<Form.Item
noStyle
name={[field.name, "accountitem"]}
rules={[{ required: true }]}
>
<Input
placeholder={t("bodyshop.fields.responsibilitycenter_accountitem")}
onBlur={handleBlur}
size="small"
style={{
...INLINE_TITLE_INPUT_STYLE,
width: "100%"
}}
/>
</Form.Item>
</div>
</>
)}
</div> </div>
} }
wrapTitle wrapTitle
@@ -779,6 +737,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -793,6 +752,24 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Space> </Space>
} }
> >
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
key={`${index}accountname`}
name={[field.name, "accountname"]}
rules={[{ required: true }]}
>
<Input onBlur={handleBlur} />
</Form.Item>
{!hasDMSKey && (
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
key={`${index}accountitem`}
name={[field.name, "accountitem"]}
rules={[{ required: true }]}
>
<Input onBlur={handleBlur} />
</Form.Item>
)}
{hasDMSKey && !bodyshop.rr_dealerid && ( {hasDMSKey && !bodyshop.rr_dealerid && (
<> <>
<Form.Item <Form.Item
@@ -830,46 +807,36 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.responsibilitycenters.profits")} id="profits">
<Form.List name={["md_responsibility_centers", "profits"]}> <Form.List name={["md_responsibility_centers", "profits"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.responsibilitycenters.profits")}
id="profits"
actions={[
buildSectionActionButton("add-profit-center", t("bodyshop.actions.add_profit_center"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_profit_center"), () =>
const hasProfitCenterBodyFields = fields.map((field, index) => {
(hasDMSKey && !bodyshop.rr_dealerid) || bodyshop.cdk_dealerid || bodyshop.rr_dealerid;
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
noDivider noDivider
titleOnly={!hasProfitCenterBodyFields}
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1.4 1 440px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenter")} {t("bodyshop.fields.responsibilitycenter")}
</div> </div>
@@ -886,16 +853,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 260px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenter_accountdesc")} {t("bodyshop.fields.responsibilitycenter_accountdesc")}
</div> </div>
<Form.Item noStyle name={[field.name, "accountdesc"]} rules={[{ required: true }]}> <Form.Item
noStyle
name={[field.name, "accountdesc"]}
rules={[{ required: true }]}
>
<Input <Input
placeholder={t("bodyshop.fields.responsibilitycenter_accountdesc")} placeholder={t("bodyshop.fields.responsibilitycenter_accountdesc")}
onBlur={handleBlur} onBlur={handleBlur}
@@ -907,59 +873,46 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
/> />
</Form.Item> </Form.Item>
</div> </div>
{!hasDMSKey && (
<>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0.9 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenter_accountitem")}
</div> </div>
}
wrapTitle
extra={
<Space align="center" size="small">
<Button
type="text"
danger
icon={<DeleteFilled />}
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
orientation="horizontal"
/>
</Space>
}
>
{!hasDMSKey && (
<Form.Item <Form.Item
noStyle label={t("bodyshop.fields.responsibilitycenter_accountitem")}
key={`${index}accountitem`}
name={[field.name, "accountitem"]} name={[field.name, "accountitem"]}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Input <Input onBlur={handleBlur} />
placeholder={t("bodyshop.fields.responsibilitycenter_accountitem")}
onBlur={handleBlur}
size="small"
style={{
...INLINE_TITLE_INPUT_STYLE,
width: "100%"
}}
/>
</Form.Item> </Form.Item>
</div>
</>
)} )}
{bodyshop.rr_dealerid && ( {bodyshop.rr_dealerid && (
<>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0.9 1 220px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenters.item_type")}
</div>
<Form.Item <Form.Item
noStyle label={t("bodyshop.fields.responsibilitycenters.item_type")}
key={`${index}rr_item_type`}
name={[field.name, "rr_item_type"]} name={[field.name, "rr_item_type"]}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Select <Select
size="small"
style={{ width: "100%" }}
styles={{
selector: INLINE_TITLE_INPUT_STYLE
}}
options={[ options={[
{ {
value: "G", value: "G",
@@ -976,30 +929,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
]} ]}
/> />
</Form.Item> </Form.Item>
</div>
</>
)} )}
</div>
}
wrapTitle
extra={
<Space align="center" size="small">
<Button
type="text"
icon={<DeleteFilled />}
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
orientation="horizontal"
/>
</Space>
}
>
{hasDMSKey && !bodyshop.rr_dealerid && ( {hasDMSKey && !bodyshop.rr_dealerid && (
<Form.Item <Form.Item
label={t("bodyshop.fields.dms.dms_acctnumber")} label={t("bodyshop.fields.dms.dms_acctnumber")}
@@ -1047,31 +977,34 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
<SelectorDiv> <SelectorDiv>
{hasDMSKey && ( {hasDMSKey && (
<LayoutFormRow id="mappingname" header={t("bodyshop.labels.dms.dms_allocations")}>
<Form.List name={["md_responsibility_centers", "dms_defaults"]}> <Form.List name={["md_responsibility_centers", "dms_defaults"]}>
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
return ( return (
<LayoutFormRow
id="mappingname"
header={t("bodyshop.labels.dms.dms_allocations")}
actions={[
buildSectionActionButton(
"add-dms-allocation",
t("bodyshop.actions.add_dms_allocation"),
() => {
add();
}
)
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.add_dms_allocation"), () =>
fields.map((field, index) => {
const dmsDefault = form.getFieldValue([ const dmsDefault = form.getFieldValue([
"md_responsibility_centers", "md_responsibility_centers",
"dms_defaults", "dms_defaults",
@@ -1089,6 +1022,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
extra={ extra={
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -2339,23 +2273,13 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
)} )}
<LayoutFormRow header={t("bodyshop.labels.defaultcostsmapping")}> <LayoutFormRow header={t("bodyshop.labels.defaultcostsmapping")}>
@@ -3601,12 +3525,21 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
)} )}
<LayoutFormRow id="mappingname" header={t("bodyshop.labels.responsibilitycenters.sales_tax_codes")}>
<Form.List name={["md_responsibility_centers", "sales_tax_codes"]}> <Form.List name={["md_responsibility_centers", "sales_tax_codes"]}>
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
return ( return (
<LayoutFormRow
id="mappingname"
header={t("bodyshop.labels.responsibilitycenters.sales_tax_codes")}
actions={[
buildSectionActionButton("add-sales-tax-code", t("bodyshop.actions.newsalestaxcode"), () => {
add();
})
]}
>
<div> <div>
{fields.map((field, index) => { {renderListOrEmpty(fields, t("bodyshop.actions.newsalestaxcode"), () =>
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow <LayoutFormRow
@@ -3615,16 +3548,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1.2 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenters.sales_tax_codes.description")} {t("bodyshop.fields.responsibilitycenters.sales_tax_codes.description")}
</div> </div>
<Form.Item noStyle name={[field.name, "description"]} rules={[{ required: true }]}> <Form.Item
noStyle
name={[field.name, "description"]}
rules={[{ required: true }]}
>
<Input <Input
size="small" size="small"
placeholder={t( placeholder={t(
@@ -3638,13 +3570,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 140px",
maxWidth: 170
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}> <div style={INLINE_TITLE_LABEL_STYLE}>
{t("bodyshop.fields.responsibilitycenters.sales_tax_codes.code")} {t("bodyshop.fields.responsibilitycenters.sales_tax_codes.code")}
</div> </div>
@@ -3665,6 +3591,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
extra={ extra={
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -3699,23 +3626,13 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.newsalestaxcode")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</> </>
)} )}
</RbacWrapper> </RbacWrapper>

View File

@@ -7,6 +7,7 @@ import { ChromePicker } from "react-color";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styled from "styled-components"; import styled from "styled-components";
import { getFormListItemTitle } from "../form-list-move-arrows/form-list-item-title.utils"; import { getFormListItemTitle } from "../form-list-move-arrows/form-list-item-title.utils";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { DEFAULT_TRANSLUCENT_CARD_COLOR, getTintedCardSurfaceStyles } from "./shop-info.color.utils"; import { DEFAULT_TRANSLUCENT_CARD_COLOR, getTintedCardSurfaceStyles } from "./shop-info.color.utils";
@@ -405,11 +406,32 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
{Production_List_Status_Colors.treatment === "on" && ( {Production_List_Status_Colors.treatment === "on" && (
<LayoutFormRow grow header={t("bodyshop.fields.statuses.production_colors")} id="production_colors">
<Form.List name={["md_ro_statuses", "production_colors"]}> <Form.List name={["md_ro_statuses", "production_colors"]}>
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
return ( return (
<LayoutFormRow
grow
header={t("bodyshop.fields.statuses.production_colors")}
id="production_colors"
actions={[
<Button
key="add-production-status-color"
type="primary"
block
onClick={() => {
add({
color: { ...DEFAULT_TRANSLUCENT_CARD_COLOR }
});
}}
>
{t("bodyshop.actions.add_production_status_color")}
</Button>
]}
>
<div> <div>
{fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.add_production_status_color")} />
) : (
<Space size="large" wrap align="start"> <Space size="large" wrap align="start">
{fields.map((field, index) => { {fields.map((field, index) => {
const productionColor = productionColors[field.name] || {}; const productionColor = productionColors[field.name] || {};
@@ -460,6 +482,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
extra={ extra={
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -487,24 +510,12 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
); );
})} })}
</Space> </Space>
<Form.Item style={{ marginTop: 8 }}> )}
<Button
type="dashed"
onClick={() => {
add({
color: { ...DEFAULT_TRANSLUCENT_CARD_COLOR }
});
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
)} )}
</SelectorDiv> </SelectorDiv>
); );

View File

@@ -7,6 +7,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import ColorpickerFormItemComponent from "../form-items-formatted/colorpicker-form-item.component"; import ColorpickerFormItemComponent from "../form-items-formatted/colorpicker-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { ColorPicker } from "./shop-info.rostatus.component"; import { ColorPicker } from "./shop-info.rostatus.component";
import { import {
@@ -227,12 +228,35 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
))} ))}
</Space> </Space>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.apptcolors")} id="apptcolors">
<Form.List name={["appt_colors"]}> <Form.List name={["appt_colors"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.apptcolors")}
id="apptcolors"
actions={[
<Button
key="add-appointment-color"
type="primary"
block
onClick={() => {
add({
color: {
...DEFAULT_TRANSLUCENT_PICKER_COLOR,
rgb: { ...DEFAULT_TRANSLUCENT_PICKER_COLOR.rgb }
}
});
}}
>
{t("bodyshop.actions.addapptcolor")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.addapptcolor")} />
) : (
fields.map((field, index) => {
const appointmentColor = const appointmentColor =
appointmentColors[field.name] || form.getFieldValue(["appt_colors", field.name]) || {}; appointmentColors[field.name] || form.getFieldValue(["appt_colors", field.name]) || {};
const appointmentColorSurfaceStyles = getTintedCardSurfaceStyles(appointmentColor.color); const appointmentColorSurfaceStyles = getTintedCardSurfaceStyles(appointmentColor.color);
@@ -266,6 +290,7 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -296,35 +321,40 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add({
color: {
...DEFAULT_TRANSLUCENT_PICKER_COLOR,
rgb: { ...DEFAULT_TRANSLUCENT_PICKER_COLOR.rgb }
}
});
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addapptcolor")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
{HasFeatureAccess({ featureName: "smartscheduling", bodyshop }) && ( {HasFeatureAccess({ featureName: "smartscheduling", bodyshop }) && (
<LayoutFormRow header={t("bodyshop.labels.ssbuckets")} id="ssbuckets">
<Form.List name={["ssbuckets"]}> <Form.List name={["ssbuckets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.ssbuckets")}
id="ssbuckets"
actions={[
<Button
key="add-job-size-definition"
type="primary"
block
onClick={() => {
add({
color: { ...DEFAULT_TRANSLUCENT_CARD_COLOR }
});
}}
>
{t("bodyshop.actions.addbucket")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.addbucket")} />
) : (
fields.map((field, index) => {
const schedulingBucket = const schedulingBucket =
schedulingBuckets[field.name] || form.getFieldValue(["ssbuckets", field.name]) || {}; schedulingBuckets[field.name] || form.getFieldValue(["ssbuckets", field.name]) || {};
const schedulingBucketSurfaceStyles = getTintedCardSurfaceStyles(schedulingBucket.color); const schedulingBucketSurfaceStyles = getTintedCardSurfaceStyles(schedulingBucket.color);
@@ -395,6 +425,7 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -473,25 +504,13 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add({
color: { ...DEFAULT_TRANSLUCENT_CARD_COLOR }
});
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addbucket")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
)} )}
</div> </div>
); );

View File

@@ -3,6 +3,7 @@ import { Button, Form, Input, Select, Space } from "antd";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -23,12 +24,29 @@ export default function ShopInfoSpeedPrint() {
}); });
return ( return (
<LayoutFormRow header={t("bodyshop.labels.speedprint_configurations")}>
<Form.List name={["speedprint"]}> <Form.List name={["speedprint"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.speedprint_configurations")}
actions={[
<Button
key="add-speedprint"
type="primary"
block
onClick={() => {
add();
}}
>
{t("bodyshop.actions.addspeedprint")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.addspeedprint")} />
) : (
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}> <Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
<LayoutFormRow <LayoutFormRow
@@ -36,12 +54,7 @@ export default function ShopInfoSpeedPrint() {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 180px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.speedprint.id")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.speedprint.id")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -64,12 +77,7 @@ export default function ShopInfoSpeedPrint() {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 280px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.speedprint.label")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("bodyshop.fields.speedprint.label")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -98,6 +106,7 @@ export default function ShopInfoSpeedPrint() {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -134,22 +143,12 @@ export default function ShopInfoSpeedPrint() {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addspeedprint")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
); );
} }

View File

@@ -3,6 +3,7 @@ import { Button, Checkbox, Col, Form, Input, InputNumber, Row, Select, Space, Sw
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import { getFormListItemTitle } from "../form-list-move-arrows/form-list-item-title.utils"; import { getFormListItemTitle } from "../form-list-move-arrows/form-list-item-title.utils";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -78,7 +79,6 @@ export function ShopInfoTaskPresets({ bodyshop }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.md_tasks_presets")}>
<Form.List <Form.List
name={["md_tasks_presets", "presets"]} name={["md_tasks_presets", "presets"]}
rules={[ rules={[
@@ -95,8 +95,26 @@ export function ShopInfoTaskPresets({ bodyshop }) {
> >
{(fields, { add, remove, move }, { errors }) => { {(fields, { add, remove, move }, { errors }) => {
return ( return (
<LayoutFormRow
header={t("bodyshop.labels.md_tasks_presets")}
actions={[
<Button
key="add-task-preset"
type="primary"
block
onClick={() => {
add();
}}
>
{t("bodyshop.actions.add_task_preset")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("bodyshop.actions.add_task_preset")} />
) : (
fields.map((field, index) => {
const taskPreset = taskPresets[field.name] || {}; const taskPreset = taskPresets[field.name] || {};
return ( return (
@@ -113,6 +131,7 @@ export function ShopInfoTaskPresets({ bodyshop }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -262,24 +281,14 @@ export function ShopInfoTaskPresets({ bodyshop }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
)}
<Form.ErrorList errors={errors} /> <Form.ErrorList errors={errors} />
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.add_task_preset")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</> </>
); );
} }

View File

@@ -13,6 +13,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { import {
INLINE_TITLE_GROUP_STYLE, INLINE_TITLE_GROUP_STYLE,
@@ -20,7 +21,8 @@ import {
INLINE_TITLE_INPUT_STYLE, INLINE_TITLE_INPUT_STYLE,
INLINE_TITLE_LABEL_STYLE, INLINE_TITLE_LABEL_STYLE,
INLINE_TITLE_ROW_STYLE, INLINE_TITLE_ROW_STYLE,
INLINE_TITLE_SEPARATOR_STYLE INLINE_TITLE_SEPARATOR_STYLE,
INLINE_TITLE_SWITCH_GROUP_STYLE
} from "../layout-form-row/inline-form-row-title.utils.js"; } from "../layout-form-row/inline-form-row-title.utils.js";
import { import {
@@ -209,8 +211,8 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
<Card <Card
title={isTeamHydrating ? undefined : teamCardTitle} title={isTeamHydrating ? undefined : teamCardTitle}
extra={ extra={
<Button type="primary" onClick={() => form.submit()} disabled={isTeamHydrating}> <Button type="primary" onClick={() => form.submit()} disabled={isTeamHydrating} style={{ minWidth: 190 }}>
{t("general.actions.save")} {t("employee_teams.actions.save_team")}
</Button> </Button>
} }
> >
@@ -239,8 +241,7 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
</div> </div>
<div <div
style={{ style={{
...INLINE_TITLE_GROUP_STYLE, ...INLINE_TITLE_SWITCH_GROUP_STYLE,
flex: "0 0 auto",
marginLeft: "auto" marginLeft: "auto"
}} }}
> >
@@ -277,12 +278,34 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
<InputNumber min={0} precision={1} suffix="%" /> <InputNumber min={0} precision={1} suffix="%" />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow title={t("employee_teams.labels.members")}>
<Form.List name={["employee_team_members"]}> <Form.List name={["employee_team_members"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<LayoutFormRow
title={t("employee_teams.labels.members")}
actions={[
<Button
key="add-team-member"
type="primary"
block
onClick={() => {
add({
percentage: 0,
payout_method: "hourly",
labor_rates: {},
commission_rates: {}
});
}}
>
{t("employee_teams.actions.newmember")}
</Button>
]}
>
<div> <div>
{fields.map((field, index) => { {fields.length === 0 ? (
<ConfigListEmptyState actionLabel={t("employee_teams.actions.newmember")} />
) : (
fields.map((field, index) => {
return ( return (
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}> <Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
<Form.Item name={[field.name, "id"]} hidden> <Form.Item name={[field.name, "id"]} hidden>
@@ -293,12 +316,7 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
title={ title={
<div style={INLINE_TITLE_ROW_STYLE}> <div style={INLINE_TITLE_ROW_STYLE}>
<HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} /> <HolderOutlined style={INLINE_TITLE_HANDLE_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "1 1 320px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.employeeid")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.employeeid")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -313,12 +331,7 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 170px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.allocation")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.allocation")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -344,12 +357,7 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
</Form.Item> </Form.Item>
</div> </div>
<div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} /> <div aria-hidden style={INLINE_TITLE_SEPARATOR_STYLE} />
<div <div style={INLINE_TITLE_GROUP_STYLE}>
style={{
...INLINE_TITLE_GROUP_STYLE,
flex: "0 1 260px"
}}
>
<div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.payout_method")}</div> <div style={INLINE_TITLE_LABEL_STYLE}>{t("employee_teams.fields.payout_method")}</div>
<Form.Item <Form.Item
noStyle noStyle
@@ -380,6 +388,7 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
<Space align="center" size="small"> <Space align="center" size="small">
<Button <Button
type="text" type="text"
danger
icon={<DeleteFilled />} icon={<DeleteFilled />}
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
@@ -437,28 +446,13 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
); );
})} })
<Form.Item> )}
<Button
type="dashed"
onClick={() => {
add({
percentage: 0,
payout_method: "hourly",
labor_rates: {},
commission_rates: {}
});
}}
style={{ width: "100%" }}
>
{t("employee_teams.actions.newmember")}
</Button>
</Form.Item>
</div> </div>
</LayoutFormRow>
); );
}} }}
</Form.List> </Form.List>
</LayoutFormRow>
</Form> </Form>
)} )}
</Card> </Card>

View File

@@ -42,9 +42,11 @@ vi.mock("react-i18next", () => ({
"employee_teams.options.commission": "Commission", "employee_teams.options.commission": "Commission",
"employee_teams.options.commission_percentage": "Commission", "employee_teams.options.commission_percentage": "Commission",
"employee_teams.actions.newmember": "New Team Member", "employee_teams.actions.newmember": "New Team Member",
"employee_teams.actions.save_team": "Save Employee Team",
"employee_teams.errors.minimum_one_member": "Add at least one team member.", "employee_teams.errors.minimum_one_member": "Add at least one team member.",
"employee_teams.errors.duplicate_member": "Team members must be unique.", "employee_teams.errors.duplicate_member": "Team members must be unique.",
"employee_teams.errors.allocation_total_exact": "Allocation must total exactly 100%.", "employee_teams.errors.allocation_total_exact": "Allocation must total exactly 100%.",
"general.labels.click_to_begin": `Click ${values.action ?? ""} to begin`,
"general.actions.save": "Save", "general.actions.save": "Save",
"employees.successes.save": "Saved" "employees.successes.save": "Saved"
}; };
@@ -101,11 +103,12 @@ vi.mock("../form-items-formatted/currency-form-item.component", () => ({
})); }));
vi.mock("../layout-form-row/layout-form-row.component", () => ({ vi.mock("../layout-form-row/layout-form-row.component", () => ({
default: ({ title, extra, children }) => ( default: ({ title, extra, actions, children }) => (
<div> <div>
{title} {title}
{extra} {extra}
{children} {children}
{actions}
</div> </div>
) )
})); }));
@@ -211,7 +214,7 @@ describe("ShopEmployeeTeamsFormComponent", () => {
rate: 27.5 rate: 27.5
}); });
fireEvent.click(screen.getByRole("button", { name: "Save" })); fireEvent.click(screen.getByRole("button", { name: "Save Employee Team" }));
await waitFor(() => { await waitFor(() => {
expect(insertEmployeeTeamMock).toHaveBeenCalledWith({ expect(insertEmployeeTeamMock).toHaveBeenCalledWith({

View File

@@ -2,6 +2,8 @@ import { Button } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import ConfigListEmptyState from "../layout-form-row/config-list-empty-state.component.jsx";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import ResponsiveTable from "../responsive-table/responsive-table.component"; import ResponsiveTable from "../responsive-table/responsive-table.component";
export default function ShopEmployeeTeamsListComponent({ loading, employee_teams }) { export default function ShopEmployeeTeamsListComponent({ loading, employee_teams }) {
@@ -9,13 +11,28 @@ export default function ShopEmployeeTeamsListComponent({ loading, employee_teams
const history = useNavigate(); const history = useNavigate();
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const navigateToTeam = (employeeTeamId) => {
history({
search: queryString.stringify({
...search,
employeeTeamId
})
});
};
const clearTeamSelection = () => {
const { employeeTeamId, ...nextSearch } = search;
void employeeTeamId;
history({
search: queryString.stringify(nextSearch)
});
};
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
if (record) { if (record) {
search.employeeTeamId = record.id; navigateToTeam(record.id);
history({ search: queryString.stringify(search) });
} else { } else {
delete search.employeeTeamId; clearTeamSelection();
history({ search: queryString.stringify(search) });
} }
}; };
const columns = [ const columns = [
@@ -27,21 +44,18 @@ export default function ShopEmployeeTeamsListComponent({ loading, employee_teams
]; ];
return ( return (
<div> <LayoutFormRow
<ResponsiveTable title={t("bodyshop.labels.employee_teams")}
title={() => { actions={[
return ( <Button key="new-team" type="primary" block onClick={() => navigateToTeam("new")}>
<Button
type="primary"
onClick={() => {
search.employeeTeamId = "new";
history({ search: queryString.stringify(search) });
}}
>
{t("employee_teams.actions.new")} {t("employee_teams.actions.new")}
</Button> </Button>
); ]}
}} >
{employee_teams.length === 0 ? (
<ConfigListEmptyState actionLabel={t("employee_teams.actions.new")} />
) : (
<ResponsiveTable
loading={loading} loading={loading}
pagination={{ placement: "top" }} pagination={{ placement: "top" }}
columns={columns} columns={columns}
@@ -49,10 +63,7 @@ export default function ShopEmployeeTeamsListComponent({ loading, employee_teams
rowKey="id" rowKey="id"
dataSource={employee_teams} dataSource={employee_teams}
rowSelection={{ rowSelection={{
onSelect: (props) => { onSelect: (props) => navigateToTeam(props.id),
search.employeeTeamId = props.id;
history({ search: queryString.stringify(search) });
},
type: "radio", type: "radio",
selectedRowKeys: [search.employeeTeamId] selectedRowKeys: [search.employeeTeamId]
}} }}
@@ -64,6 +75,7 @@ export default function ShopEmployeeTeamsListComponent({ loading, employee_teams
}; };
}} }}
/> />
</div> )}
</LayoutFormRow>
); );
} }

View File

@@ -6,6 +6,7 @@ import { createStructuredSelector } from "reselect";
import { QUERY_SHOP_ASSOCIATIONS } from "../../graphql/user.queries"; import { QUERY_SHOP_ASSOCIATIONS } from "../../graphql/user.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
import ResponsiveTable from "../responsive-table/responsive-table.component"; import ResponsiveTable from "../responsive-table/responsive-table.component";
import ShopUsersAuthEdit from "../shop-users-auth-edit/shop-users-auth-edit.component"; import ShopUsersAuthEdit from "../shop-users-auth-edit/shop-users-auth-edit.component";
@@ -66,7 +67,7 @@ export function ShopInfoUsersComponent({ bodyshop }) {
return <AlertComponent type="error" title={JSON.stringify(error)} />; return <AlertComponent type="error" title={JSON.stringify(error)} />;
} }
return ( return (
<div> <LayoutFormRow title={t("bodyshop.labels.licensing")}>
<ResponsiveTable <ResponsiveTable
loading={loading} loading={loading}
pagination={{ placement: "top" }} pagination={{ placement: "top" }}
@@ -75,6 +76,6 @@ export function ShopInfoUsersComponent({ bodyshop }) {
rowKey="id" rowKey="id"
dataSource={data && data.associations} dataSource={data && data.associations}
/> />
</div> </LayoutFormRow>
); );
} }

View File

@@ -292,7 +292,23 @@
}, },
"bodyshop": { "bodyshop": {
"actions": { "actions": {
"add_adjuster": "Add Adjuster",
"add_control_number": "Add Control Number",
"add_cost_center": "Add Cost Center",
"add_courtesy_car_rate_preset": "Add Courtesy Car Contract Rate Preset",
"add_delivery_checklist_item": "Add Delivery Checklist Item",
"add_dms_allocation": "Add DMS Allocation",
"add_estimator": "Add Estimator",
"add_insurance_company": "Add Insurance Company",
"add_intake_checklist_item": "Add Intake Checklist Item",
"add_jobline_preset": "Add Jobline Preset",
"add_messaging_preset": "Add Messaging Preset",
"add_note_preset": "Add Note Preset",
"add_parts_order_comment": "Add Parts Order Comment",
"add_production_status_color": "Add Production Status Color",
"add_profit_center": "Add Profit Center",
"add_task_preset": "Add Task Preset", "add_task_preset": "Add Task Preset",
"add_to_email_preset": "Add To Email Preset",
"addapptcolor": "Add Appointment Color", "addapptcolor": "Add Appointment Color",
"addbucket": "Add Definition", "addbucket": "Add Definition",
"addpartslocation": "Add Parts Location", "addpartslocation": "Add Parts Location",
@@ -301,6 +317,7 @@
"addtemplate": "Add Template", "addtemplate": "Add Template",
"newlaborrate": "New Labor Rate", "newlaborrate": "New Labor Rate",
"newsalestaxcode": "New Sales Tax Code", "newsalestaxcode": "New Sales Tax Code",
"save_shop_information": "Save Shop Information",
"newstatus": "Add Status", "newstatus": "Add Status",
"testrender": "Test Render" "testrender": "Test Render"
}, },
@@ -1206,7 +1223,8 @@
"employee_teams": { "employee_teams": {
"actions": { "actions": {
"new": "New Team", "new": "New Team",
"newmember": "New Team Member" "newmember": "New Team Member",
"save_team": "Save Employee Team"
}, },
"errors": { "errors": {
"allocation_total_exact": "Team allocation must total exactly 100%.", "allocation_total_exact": "Team allocation must total exactly 100%.",
@@ -1236,9 +1254,11 @@
}, },
"employees": { "employees": {
"actions": { "actions": {
"addrate": "Add Rate",
"addvacation": "Add Vacation", "addvacation": "Add Vacation",
"new": "New Employee", "new": "New Employee",
"newrate": "New Rate", "newrate": "New Rate",
"save_employee": "Save Employee",
"select": "Select Employee" "select": "Select Employee"
}, },
"errors": { "errors": {
@@ -1403,6 +1423,7 @@
"beta": "BETA", "beta": "BETA",
"cancel": "Are you sure you want to cancel? Your changes will not be saved.", "cancel": "Are you sure you want to cancel? Your changes will not be saved.",
"changelog": "Change Log", "changelog": "Change Log",
"click_to_begin": "Click {{action}} to begin",
"clear": "Clear", "clear": "Clear",
"confirmpassword": "Confirm Password", "confirmpassword": "Confirm Password",
"created_at": "Created At", "created_at": "Created At",

View File

@@ -292,7 +292,23 @@
}, },
"bodyshop": { "bodyshop": {
"actions": { "actions": {
"add_adjuster": "",
"add_control_number": "",
"add_cost_center": "",
"add_courtesy_car_rate_preset": "",
"add_delivery_checklist_item": "",
"add_dms_allocation": "",
"add_estimator": "",
"add_insurance_company": "",
"add_intake_checklist_item": "",
"add_jobline_preset": "",
"add_messaging_preset": "",
"add_note_preset": "",
"add_parts_order_comment": "",
"add_production_status_color": "",
"add_profit_center": "",
"add_task_preset": "", "add_task_preset": "",
"add_to_email_preset": "",
"addapptcolor": "", "addapptcolor": "",
"addbucket": "", "addbucket": "",
"addpartslocation": "", "addpartslocation": "",
@@ -301,6 +317,7 @@
"addtemplate": "", "addtemplate": "",
"newlaborrate": "", "newlaborrate": "",
"newsalestaxcode": "", "newsalestaxcode": "",
"save_shop_information": "",
"newstatus": "", "newstatus": "",
"testrender": "" "testrender": ""
}, },
@@ -1206,7 +1223,8 @@
"employee_teams": { "employee_teams": {
"actions": { "actions": {
"new": "", "new": "",
"newmember": "" "newmember": "",
"save_team": ""
}, },
"errors": { "errors": {
"allocation_total_exact": "", "allocation_total_exact": "",
@@ -1236,9 +1254,11 @@
}, },
"employees": { "employees": {
"actions": { "actions": {
"addrate": "",
"addvacation": "", "addvacation": "",
"new": "Nuevo empleado", "new": "Nuevo empleado",
"newrate": "", "newrate": "",
"save_employee": "",
"select": "" "select": ""
}, },
"errors": { "errors": {
@@ -1403,6 +1423,7 @@
"beta": "", "beta": "",
"cancel": "", "cancel": "",
"changelog": "", "changelog": "",
"click_to_begin": "",
"clear": "", "clear": "",
"confirmpassword": "", "confirmpassword": "",
"created_at": "", "created_at": "",

View File

@@ -292,7 +292,23 @@
}, },
"bodyshop": { "bodyshop": {
"actions": { "actions": {
"add_adjuster": "",
"add_control_number": "",
"add_cost_center": "",
"add_courtesy_car_rate_preset": "",
"add_delivery_checklist_item": "",
"add_dms_allocation": "",
"add_estimator": "",
"add_insurance_company": "",
"add_intake_checklist_item": "",
"add_jobline_preset": "",
"add_messaging_preset": "",
"add_note_preset": "",
"add_parts_order_comment": "",
"add_production_status_color": "",
"add_profit_center": "",
"add_task_preset": "", "add_task_preset": "",
"add_to_email_preset": "",
"addapptcolor": "", "addapptcolor": "",
"addbucket": "", "addbucket": "",
"addpartslocation": "", "addpartslocation": "",
@@ -301,6 +317,7 @@
"addtemplate": "", "addtemplate": "",
"newlaborrate": "", "newlaborrate": "",
"newsalestaxcode": "", "newsalestaxcode": "",
"save_shop_information": "",
"newstatus": "", "newstatus": "",
"testrender": "" "testrender": ""
}, },
@@ -1206,7 +1223,8 @@
"employee_teams": { "employee_teams": {
"actions": { "actions": {
"new": "", "new": "",
"newmember": "" "newmember": "",
"save_team": ""
}, },
"errors": { "errors": {
"allocation_total_exact": "", "allocation_total_exact": "",
@@ -1236,9 +1254,11 @@
}, },
"employees": { "employees": {
"actions": { "actions": {
"addrate": "",
"addvacation": "", "addvacation": "",
"new": "Nouvel employé", "new": "Nouvel employé",
"newrate": "", "newrate": "",
"save_employee": "",
"select": "" "select": ""
}, },
"errors": { "errors": {
@@ -1403,6 +1423,7 @@
"beta": "", "beta": "",
"cancel": "", "cancel": "",
"changelog": "", "changelog": "",
"click_to_begin": "",
"clear": "", "clear": "",
"confirmpassword": "", "confirmpassword": "",
"created_at": "", "created_at": "",