IO-3624 Polish employee and team config layouts
This commit is contained in:
@@ -8,7 +8,7 @@ import { INSERT_VACATION } from "../../graphql/employees.queries";
|
|||||||
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 { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
|
|
||||||
export default function ShopEmployeeAddVacation({ employee }) {
|
export default function ShopEmployeeAddVacation({ employee, buttonProps }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [insertVacation] = useMutation(INSERT_VACATION);
|
const [insertVacation] = useMutation(INSERT_VACATION);
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ export default function ShopEmployeeAddVacation({ employee }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover content={overlay} open={visibility}>
|
<Popover content={overlay} open={visibility}>
|
||||||
<Button loading={loading} disabled={!employee?.active} onClick={handleClick}>
|
<Button loading={loading} disabled={!employee?.active} onClick={handleClick} {...buttonProps}>
|
||||||
{t("employees.actions.addvacation")}
|
{t("employees.actions.addvacation")}
|
||||||
</Button>
|
</Button>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient, useMutation, useQuery } from "@apollo/client/react";
|
import { useApolloClient, useMutation, useQuery } from "@apollo/client/react";
|
||||||
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
|
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Form, Input, InputNumber, Select, Space, Switch } from "antd";
|
import { Button, Card, Col, Form, Input, InputNumber, Row, Select, Space, Switch } from "antd";
|
||||||
import ResponsiveTable from "../responsive-table/responsive-table.component";
|
import ResponsiveTable from "../responsive-table/responsive-table.component";
|
||||||
import { useForm } from "antd/es/form/Form";
|
import { useForm } from "antd/es/form/Form";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
@@ -30,6 +30,7 @@ import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.c
|
|||||||
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 LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
||||||
|
import FormItemEmail from "../form-items-formatted/email-form-item.component.jsx";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -42,6 +43,14 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = useForm();
|
const [form] = useForm();
|
||||||
const employeeRates = Form.useWatch(["rates"], form) || [];
|
const employeeRates = Form.useWatch(["rates"], form) || [];
|
||||||
|
const employeeOptionsColProps = {
|
||||||
|
xs: 24,
|
||||||
|
sm: 12,
|
||||||
|
md: 8,
|
||||||
|
lg: { flex: "0 0 320px" },
|
||||||
|
xl: { flex: "0 0 280px" },
|
||||||
|
xxl: { flex: "0 0 380px" }
|
||||||
|
};
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const [deleteVacation] = useMutation(DELETE_VACATION);
|
const [deleteVacation] = useMutation(DELETE_VACATION);
|
||||||
@@ -177,237 +186,280 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Form onFinish={handleFinish} autoComplete={"off"} layout="vertical" form={form}>
|
<Form onFinish={handleFinish} autoComplete={"off"} layout="vertical" form={form}>
|
||||||
<LayoutFormRow>
|
<LayoutFormRow title={t("bodyshop.labels.employee_options")}>
|
||||||
<Form.Item
|
<div style={{ display: "grid", rowGap: 16 }}>
|
||||||
name="first_name"
|
<Row gutter={[16, 16]} wrap>
|
||||||
label={t("employees.fields.first_name")}
|
<Col {...employeeOptionsColProps}>
|
||||||
rules={[
|
<Form.Item
|
||||||
{
|
name="first_name"
|
||||||
required: true
|
label={t("employees.fields.first_name")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("employees.fields.last_name")}
|
|
||||||
name="last_name"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="employee_number"
|
|
||||||
label={t("employees.fields.employee_number")}
|
|
||||||
validateTrigger="onBlur"
|
|
||||||
hasFeedback
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
() => ({
|
|
||||||
async validator(rule, value) {
|
|
||||||
if (value) {
|
|
||||||
const response = await client.query({
|
|
||||||
query: CHECK_EMPLOYEE_NUMBER,
|
|
||||||
variables: {
|
|
||||||
employeenumber: value
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.employees_aggregate.aggregate.count === 0) {
|
|
||||||
return Promise.resolve();
|
|
||||||
} else if (
|
|
||||||
response.data.employees_aggregate.nodes.length === 1 &&
|
|
||||||
response.data.employees_aggregate.nodes[0].id === form.getFieldValue("id")
|
|
||||||
) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
return Promise.reject(t("employees.validation.unique_employee_number"));
|
]}
|
||||||
} else {
|
>
|
||||||
return Promise.resolve();
|
<Input />
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("employees.fields.pin")}
|
|
||||||
name="pin"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
<LayoutFormRow>
|
|
||||||
<Form.Item label={t("employees.fields.active")} valuePropName="checked" name="active">
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("employees.fields.flat_rate")} name="flat_rate" valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="hire_date"
|
|
||||||
label={t("employees.fields.hire_date")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<DateTimePicker isDateOnly />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("employees.fields.termination_date")} name="termination_date">
|
|
||||||
<DateTimePicker isDateOnly />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("employees.fields.user_email")}
|
|
||||||
name="user_email"
|
|
||||||
validateTrigger="onBlur"
|
|
||||||
rules={[
|
|
||||||
({ getFieldValue }) => ({
|
|
||||||
async validator(rule, value) {
|
|
||||||
const user_email = getFieldValue("user_email");
|
|
||||||
|
|
||||||
if (user_email && value) {
|
|
||||||
const response = await client.query({
|
|
||||||
query: QUERY_USERS_BY_EMAIL,
|
|
||||||
variables: {
|
|
||||||
email: user_email
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.users.length === 1) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
return Promise.reject(t("bodyshop.validation.useremailmustexist"));
|
|
||||||
} else {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label={t("employees.fields.external_id")} name="external_id">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
<Form.List name={["rates"]}>
|
|
||||||
{(fields, { add, remove, move }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{fields.map((field, index) => {
|
|
||||||
const employeeRate = employeeRates[field.name] || {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
|
|
||||||
<LayoutFormRow
|
|
||||||
grow
|
|
||||||
title={getFormListItemTitle(t("employees.fields.cost_center"), index, employeeRate.cost_center)}
|
|
||||||
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
|
|
||||||
label={t("employees.fields.cost_center")}
|
|
||||||
key={`${index}`}
|
|
||||||
name={[field.name, "cost_center"]}
|
|
||||||
valuePropName="value"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
options={[
|
|
||||||
{ value: "timetickets.labels.shift", label: t("timetickets.labels.shift") },
|
|
||||||
...(bodyshop.cdk_dealerid ||
|
|
||||||
bodyshop.pbs_serialnumber ||
|
|
||||||
bodyshop.rr_dealerid ||
|
|
||||||
Enhanced_Payroll.treatment === "on"
|
|
||||||
? CiecaSelect(false, true)
|
|
||||||
: bodyshop.md_responsibility_centers.costs.map((c) => ({
|
|
||||||
value: c.name,
|
|
||||||
label: c.name
|
|
||||||
})))
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("employees.fields.rate")}
|
|
||||||
key={`${index}`}
|
|
||||||
name={[field.name, "rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} precision={2} />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
|
||||||
</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>
|
</Form.Item>
|
||||||
</div>
|
</Col>
|
||||||
);
|
<Col {...employeeOptionsColProps}>
|
||||||
}}
|
<Form.Item
|
||||||
</Form.List>
|
label={t("employees.fields.last_name")}
|
||||||
|
name="last_name"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item
|
||||||
|
name="employee_number"
|
||||||
|
label={t("employees.fields.employee_number")}
|
||||||
|
validateTrigger="onBlur"
|
||||||
|
hasFeedback
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
() => ({
|
||||||
|
async validator(rule, value) {
|
||||||
|
if (value) {
|
||||||
|
const response = await client.query({
|
||||||
|
query: CHECK_EMPLOYEE_NUMBER,
|
||||||
|
variables: {
|
||||||
|
employeenumber: value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.employees_aggregate.aggregate.count === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
} else if (
|
||||||
|
response.data.employees_aggregate.nodes.length === 1 &&
|
||||||
|
response.data.employees_aggregate.nodes[0].id === form.getFieldValue("id")
|
||||||
|
) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(t("employees.validation.unique_employee_number"));
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employees.fields.pin")}
|
||||||
|
name="pin"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={[16, 16]} wrap>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item label={t("employees.fields.active")} valuePropName="checked" name="active">
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item label={t("employees.fields.flat_rate")} name="flat_rate" valuePropName="checked">
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item
|
||||||
|
name="hire_date"
|
||||||
|
label={t("employees.fields.hire_date")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<DateTimePicker isDateOnly />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item label={t("employees.fields.termination_date")} name="termination_date">
|
||||||
|
<DateTimePicker isDateOnly />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employees.fields.user_email")}
|
||||||
|
name="user_email"
|
||||||
|
validateTrigger="onBlur"
|
||||||
|
rules={[
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
async validator(rule, value) {
|
||||||
|
const user_email = getFieldValue("user_email");
|
||||||
|
|
||||||
|
if (user_email && value) {
|
||||||
|
const response = await client.query({
|
||||||
|
query: QUERY_USERS_BY_EMAIL,
|
||||||
|
variables: {
|
||||||
|
email: user_email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.users.length === 1) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(t("bodyshop.validation.useremailmustexist"));
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<FormItemEmail />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...employeeOptionsColProps}>
|
||||||
|
<Form.Item label={t("employees.fields.external_id")} name="external_id">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</LayoutFormRow>
|
||||||
|
<LayoutFormRow title={t("bodyshop.labels.employee_rates")}>
|
||||||
|
<Form.List name={["rates"]}>
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => {
|
||||||
|
const employeeRate = employeeRates[field.name] || {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
|
||||||
|
<LayoutFormRow
|
||||||
|
grow
|
||||||
|
title={getFormListItemTitle(
|
||||||
|
t("employees.fields.cost_center"),
|
||||||
|
index,
|
||||||
|
employeeRate.cost_center
|
||||||
|
)}
|
||||||
|
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
|
||||||
|
label={t("employees.fields.cost_center")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "cost_center"]}
|
||||||
|
valuePropName="value"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={[
|
||||||
|
{ value: "timetickets.labels.shift", label: t("timetickets.labels.shift") },
|
||||||
|
...(bodyshop.cdk_dealerid ||
|
||||||
|
bodyshop.pbs_serialnumber ||
|
||||||
|
bodyshop.rr_dealerid ||
|
||||||
|
Enhanced_Payroll.treatment === "on"
|
||||||
|
? CiecaSelect(false, true)
|
||||||
|
: bodyshop.md_responsibility_centers.costs.map((c) => ({
|
||||||
|
value: c.name,
|
||||||
|
label: c.name
|
||||||
|
})))
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employees.fields.rate")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "rate"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</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>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</LayoutFormRow>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
<ResponsiveTable
|
<LayoutFormRow title={t("bodyshop.labels.employee_vacation")}>
|
||||||
title={() => <ShopEmployeeAddVacation employee={data && data.employees_by_pk} />}
|
<div>
|
||||||
columns={columns}
|
<ResponsiveTable
|
||||||
mobileColumnKeys={["start", "length", "actions"]}
|
columns={columns}
|
||||||
rowKey={"id"}
|
mobileColumnKeys={["start", "length", "actions"]}
|
||||||
dataSource={data?.employees_by_pk?.employee_vacations ?? []}
|
rowKey={"id"}
|
||||||
/>
|
dataSource={data?.employees_by_pk?.employee_vacations ?? []}
|
||||||
|
pagination={false}
|
||||||
|
/>
|
||||||
|
<div style={{ marginTop: 12 }}>
|
||||||
|
<ShopEmployeeAddVacation
|
||||||
|
employee={data && data.employees_by_pk}
|
||||||
|
buttonProps={{
|
||||||
|
type: "dashed",
|
||||||
|
block: true
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</LayoutFormRow>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
|
|||||||
};
|
};
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("employees.fields.employee_number"),
|
title: t("employees.labels.employee_number_short"),
|
||||||
dataIndex: "employee_number",
|
dataIndex: "employee_number",
|
||||||
key: "employee_number",
|
key: "employee_number",
|
||||||
sorter: (a, b) => alphaSort(a.employee_number, b.employee_number),
|
sorter: (a, b) => alphaSort(a.employee_number, b.employee_number),
|
||||||
|
|||||||
@@ -1,29 +1,47 @@
|
|||||||
import { useQuery } from "@apollo/client/react";
|
import { useQuery } from "@apollo/client/react";
|
||||||
|
import queryString from "query-string";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { QUERY_EMPLOYEES } from "../../graphql/employees.queries";
|
import { QUERY_EMPLOYEES } from "../../graphql/employees.queries";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ShopEmployeesFormComponent from "./shop-employees-form.component";
|
import ShopEmployeesFormComponent from "./shop-employees-form.component";
|
||||||
import ShopEmployeesListComponent from "./shop-employees-list.component";
|
import ShopEmployeesListComponent from "./shop-employees-list.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
|
import "./shop-employees.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({});
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
function ShopEmployeesContainer() {
|
function ShopEmployeesContainer() {
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
const { loading, error, data } = useQuery(QUERY_EMPLOYEES, {
|
const { loading, error, data } = useQuery(QUERY_EMPLOYEES, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only"
|
nextFetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
|
const hasSelectedEmployee = Boolean(search.employeeId);
|
||||||
|
|
||||||
if (error) return <AlertComponent title={error.message} type="error" />;
|
if (error) return <AlertComponent title={error.message} type="error" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<RbacWrapper action="employees:page">
|
||||||
<RbacWrapper action="employees:page">
|
<div
|
||||||
<ShopEmployeesListComponent employees={data ? data.employees : []} loading={loading} />
|
className={[
|
||||||
<ShopEmployeesFormComponent />
|
"shop-employees-layout",
|
||||||
</RbacWrapper>
|
hasSelectedEmployee ? "shop-employees-layout--with-detail" : null
|
||||||
</div>
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ")}
|
||||||
|
>
|
||||||
|
<div className="shop-employees-layout__list">
|
||||||
|
<ShopEmployeesListComponent employees={data ? data.employees : []} loading={loading} />
|
||||||
|
</div>
|
||||||
|
{hasSelectedEmployee ? (
|
||||||
|
<div className="shop-employees-layout__details">
|
||||||
|
<ShopEmployeesFormComponent />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</RbacWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
.shop-employees-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-employees-layout__list,
|
||||||
|
.shop-employees-layout__details {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1700px) {
|
||||||
|
.shop-employees-layout--with-detail {
|
||||||
|
grid-template-columns: minmax(420px, 500px) minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Card, Typography } from "antd";
|
import { Card } from "antd";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -15,9 +15,8 @@ function ShopInfoConsentComponent({ bodyshop }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card title={t("settings.title")}>
|
||||||
<Typography.Title level={4}>{t("settings.title")}</Typography.Title>
|
<PhoneNumberConsentList bodyshop={bodyshop} />
|
||||||
{<PhoneNumberConsentList bodyshop={bodyshop} />}
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,9 +132,22 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
|
|||||||
const teamName = Form.useWatch("name", form);
|
const teamName = Form.useWatch("name", form);
|
||||||
const teamMembers = Form.useWatch(["employee_team_members"], form) || [];
|
const teamMembers = Form.useWatch(["employee_team_members"], form) || [];
|
||||||
const isTeamHydrating = !isNewTeam && Boolean(search.employeeTeamId) && hydratedTeamId !== search.employeeTeamId;
|
const isTeamHydrating = !isNewTeam && Boolean(search.employeeTeamId) && hydratedTeamId !== search.employeeTeamId;
|
||||||
|
const isAllocationTotalExact = hasExactSplitTotal(teamMembers);
|
||||||
|
const allocationTotalValue = formatAllocationPercentage(getSplitTotal(teamMembers))?.replace("%", "") || "0";
|
||||||
|
const teamNameDisplay = teamName?.trim() || t("employee_teams.fields.name");
|
||||||
const teamCardTitle = isTeamHydrating
|
const teamCardTitle = isTeamHydrating
|
||||||
? t("employee_teams.fields.name")
|
? t("employee_teams.fields.name")
|
||||||
: teamName?.trim() || t("employee_teams.fields.name");
|
: (
|
||||||
|
<span>
|
||||||
|
<span>{teamNameDisplay}</span>
|
||||||
|
<span> - </span>
|
||||||
|
<Typography.Text type={isAllocationTotalExact ? undefined : "danger"}>
|
||||||
|
{t("employee_teams.labels.allocation_total", {
|
||||||
|
total: allocationTotalValue
|
||||||
|
})}
|
||||||
|
</Typography.Text>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
const getTeamMemberTitle = (teamMember = {}) => {
|
const getTeamMemberTitle = (teamMember = {}) => {
|
||||||
const employeeName =
|
const employeeName =
|
||||||
@@ -241,7 +254,7 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
|
|||||||
<Skeleton active title={false} paragraph={{ rows: 12 }} />
|
<Skeleton active title={false} paragraph={{ rows: 12 }} />
|
||||||
) : (
|
) : (
|
||||||
<Form onFinish={handleFinish} autoComplete={"off"} layout="vertical" form={form}>
|
<Form onFinish={handleFinish} autoComplete={"off"} layout="vertical" form={form}>
|
||||||
<LayoutFormRow>
|
<LayoutFormRow title={t("employee_teams.labels.team_options")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="name"
|
name="name"
|
||||||
label={t("employee_teams.fields.name")}
|
label={t("employee_teams.fields.name")}
|
||||||
@@ -268,156 +281,144 @@ export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
|
|||||||
<InputNumber min={0} precision={1} />
|
<InputNumber min={0} precision={1} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<Form.List name={["employee_team_members"]}>
|
<LayoutFormRow title={t("employee_teams.labels.members")}>
|
||||||
{(fields, { add, remove, move }) => {
|
<Form.List name={["employee_team_members"]}>
|
||||||
return (
|
{(fields, { add, remove, move }) => {
|
||||||
<div>
|
return (
|
||||||
{fields.map((field, index) => {
|
<div>
|
||||||
const teamMember = normalizeTeamMember(teamMembers[field.name]);
|
{fields.map((field, index) => {
|
||||||
|
const teamMember = normalizeTeamMember(teamMembers[field.name]);
|
||||||
return (
|
|
||||||
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
|
|
||||||
<Form.Item label={t("employees.fields.id")} key={`${index}`} name={[field.name, "id"]} hidden>
|
|
||||||
<Input type="hidden" />
|
|
||||||
</Form.Item>
|
|
||||||
<LayoutFormRow
|
|
||||||
grow
|
|
||||||
title={getTeamMemberTitle(teamMember)}
|
|
||||||
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>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<Row gutter={[16, 0]}>
|
|
||||||
<Col {...TEAM_MEMBER_PRIMARY_FIELD_COLS.employee}>
|
|
||||||
<Form.Item
|
|
||||||
label={t("employee_teams.fields.employeeid")}
|
|
||||||
key={`${index}`}
|
|
||||||
name={[field.name, "employeeid"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<EmployeeSearchSelectComponent options={bodyshop.employees} />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col {...TEAM_MEMBER_PRIMARY_FIELD_COLS.allocation}>
|
|
||||||
<Form.Item
|
|
||||||
label={t("employee_teams.fields.allocation_percentage")}
|
|
||||||
key={`${index}`}
|
|
||||||
name={[field.name, "percentage"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} max={100} precision={2} />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col {...TEAM_MEMBER_PRIMARY_FIELD_COLS.payoutMethod}>
|
|
||||||
<Form.Item
|
|
||||||
label={t("employee_teams.fields.payout_method")}
|
|
||||||
key={`${index}-payout-method`}
|
|
||||||
name={[field.name, "payout_method"]}
|
|
||||||
initialValue="hourly"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select options={payoutMethodOptions} />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Form.Item noStyle dependencies={[["employee_team_members", field.name, "payout_method"]]}>
|
|
||||||
{() => {
|
|
||||||
const payoutMethod =
|
|
||||||
form.getFieldValue(["employee_team_members", field.name, "payout_method"]) ||
|
|
||||||
"hourly";
|
|
||||||
const fieldName = payoutMethod === "commission" ? "commission_rates" : "labor_rates";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row gutter={[16, 0]}>
|
|
||||||
{LABOR_TYPES.map((laborType) => (
|
|
||||||
<Col {...TEAM_MEMBER_RATE_FIELD_COLS} key={`${index}-${fieldName}-${laborType}`}>
|
|
||||||
<Form.Item
|
|
||||||
label={t(`joblines.fields.lbr_types.${laborType}`)}
|
|
||||||
name={[field.name, fieldName, laborType]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{payoutMethod === "commission" ? (
|
|
||||||
<InputNumber min={0} max={100} precision={2} />
|
|
||||||
) : (
|
|
||||||
<CurrencyInput />
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</LayoutFormRow>
|
|
||||||
</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>
|
|
||||||
<Form.Item noStyle shouldUpdate>
|
|
||||||
{() => {
|
|
||||||
const teamMembers = form.getFieldValue(["employee_team_members"]) || [];
|
|
||||||
const splitTotal = getSplitTotal(teamMembers);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Typography.Text type={hasExactSplitTotal(teamMembers) ? undefined : "danger"}>
|
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
|
||||||
{t("employee_teams.labels.allocation_total", {
|
<Form.Item label={t("employees.fields.id")} key={`${index}`} name={[field.name, "id"]} hidden>
|
||||||
total: splitTotal.toFixed(2)
|
<Input type="hidden" />
|
||||||
})}
|
</Form.Item>
|
||||||
</Typography.Text>
|
<LayoutFormRow
|
||||||
|
grow
|
||||||
|
title={getTeamMemberTitle(teamMember)}
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Row gutter={[16, 0]}>
|
||||||
|
<Col {...TEAM_MEMBER_PRIMARY_FIELD_COLS.employee}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employee_teams.fields.employeeid")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "employeeid"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<EmployeeSearchSelectComponent options={bodyshop.employees} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...TEAM_MEMBER_PRIMARY_FIELD_COLS.allocation}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employee_teams.fields.allocation_percentage")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "percentage"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} max={100} precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col {...TEAM_MEMBER_PRIMARY_FIELD_COLS.payoutMethod}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employee_teams.fields.payout_method")}
|
||||||
|
key={`${index}-payout-method`}
|
||||||
|
name={[field.name, "payout_method"]}
|
||||||
|
initialValue="hourly"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select options={payoutMethodOptions} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Form.Item noStyle dependencies={[["employee_team_members", field.name, "payout_method"]]}>
|
||||||
|
{() => {
|
||||||
|
const payoutMethod =
|
||||||
|
form.getFieldValue(["employee_team_members", field.name, "payout_method"]) ||
|
||||||
|
"hourly";
|
||||||
|
const fieldName = payoutMethod === "commission" ? "commission_rates" : "labor_rates";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row gutter={[16, 0]}>
|
||||||
|
{LABOR_TYPES.map((laborType) => (
|
||||||
|
<Col {...TEAM_MEMBER_RATE_FIELD_COLS} key={`${index}-${fieldName}-${laborType}`}>
|
||||||
|
<Form.Item
|
||||||
|
label={t(`joblines.fields.lbr_types.${laborType}`)}
|
||||||
|
name={[field.name, fieldName, laborType]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{payoutMethod === "commission" ? (
|
||||||
|
<InputNumber min={0} max={100} precision={2} />
|
||||||
|
) : (
|
||||||
|
<CurrencyInput />
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}}
|
})}
|
||||||
</Form.Item>
|
<Form.Item>
|
||||||
</div>
|
<Button
|
||||||
);
|
type="dashed"
|
||||||
}}
|
onClick={() => {
|
||||||
</Form.List>
|
add({
|
||||||
|
percentage: 0,
|
||||||
|
payout_method: "hourly",
|
||||||
|
labor_rates: {},
|
||||||
|
commission_rates: {}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("employee_teams.actions.newmember")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</LayoutFormRow>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,36 +1,44 @@
|
|||||||
import { useQuery } from "@apollo/client/react";
|
import { useQuery } from "@apollo/client/react";
|
||||||
|
import queryString from "query-string";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
|
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import ShopEmployeeTeamsListComponent from "./shop-employee-teams.list";
|
import ShopEmployeeTeamsListComponent from "./shop-employee-teams.list";
|
||||||
import ShopEmployeeTeamsFormComponent from "./shop-employee-teams.form.component";
|
import ShopEmployeeTeamsFormComponent from "./shop-employee-teams.form.component";
|
||||||
import { Col, Row } from "antd";
|
import "./shop-teams.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({});
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
function ShopTeamsContainer() {
|
function ShopTeamsContainer() {
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
const { loading, error, data } = useQuery(QUERY_TEAMS, {
|
const { loading, error, data } = useQuery(QUERY_TEAMS, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only"
|
nextFetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
|
const hasSelectedTeam = Boolean(search.employeeTeamId);
|
||||||
|
|
||||||
if (error) return <AlertComponent title={error.message} type="error" />;
|
if (error) return <AlertComponent title={error.message} type="error" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<RbacWrapper action="employee_teams:page">
|
||||||
<RbacWrapper action="employee_teams:page">
|
<div
|
||||||
<Row gutter={[16, 16]}>
|
className={["shop-teams-layout", hasSelectedTeam ? "shop-teams-layout--with-detail" : null]
|
||||||
<Col span={6}>
|
.filter(Boolean)
|
||||||
<ShopEmployeeTeamsListComponent employee_teams={data ? data.employee_teams : []} loading={loading} />
|
.join(" ")}
|
||||||
</Col>
|
>
|
||||||
<Col span={18}>
|
<div className="shop-teams-layout__list">
|
||||||
|
<ShopEmployeeTeamsListComponent employee_teams={data ? data.employee_teams : []} loading={loading} />
|
||||||
|
</div>
|
||||||
|
{hasSelectedTeam ? (
|
||||||
|
<div className="shop-teams-layout__details">
|
||||||
<ShopEmployeeTeamsFormComponent />
|
<ShopEmployeeTeamsFormComponent />
|
||||||
</Col>
|
</div>
|
||||||
</Row>
|
) : null}
|
||||||
</RbacWrapper>
|
</div>
|
||||||
</div>
|
</RbacWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
client/src/components/shop-teams/shop-teams.styles.scss
Normal file
16
client/src/components/shop-teams/shop-teams.styles.scss
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.shop-teams-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-teams-layout__list,
|
||||||
|
.shop-teams-layout__details {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1700px) {
|
||||||
|
.shop-teams-layout--with-detail {
|
||||||
|
grid-template-columns: minmax(420px, 500px) minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -736,6 +736,9 @@
|
|||||||
},
|
},
|
||||||
"emaillater": "Email Later",
|
"emaillater": "Email Later",
|
||||||
"employee_teams": "Employee Teams",
|
"employee_teams": "Employee Teams",
|
||||||
|
"employee_options": "Employee Options",
|
||||||
|
"employee_rates": "Employee Rates",
|
||||||
|
"employee_vacation": "Employee Vacation",
|
||||||
"employees": "Employees",
|
"employees": "Employees",
|
||||||
"estimators": "Estimators",
|
"estimators": "Estimators",
|
||||||
"filehandlers": "Adjusters",
|
"filehandlers": "Adjusters",
|
||||||
@@ -1202,7 +1205,9 @@
|
|||||||
"percentage": "Percent"
|
"percentage": "Percent"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"allocation_total": "Allocation Total: {{total}}%"
|
"allocation_total": "Allocation Total: {{total}}%",
|
||||||
|
"members": "Members",
|
||||||
|
"team_options": "Team Options"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"commission": "Commission",
|
"commission": "Commission",
|
||||||
@@ -1246,6 +1251,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
|
"employee_number_short": "Employee #",
|
||||||
"endmustbeafterstart": "End date must be after start date.",
|
"endmustbeafterstart": "End date must be after start date.",
|
||||||
"flat_rate": "Flat Rate",
|
"flat_rate": "Flat Rate",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
|
|||||||
@@ -736,6 +736,9 @@
|
|||||||
},
|
},
|
||||||
"emaillater": "",
|
"emaillater": "",
|
||||||
"employee_teams": "",
|
"employee_teams": "",
|
||||||
|
"employee_options": "",
|
||||||
|
"employee_rates": "",
|
||||||
|
"employee_vacation": "",
|
||||||
"employees": "",
|
"employees": "",
|
||||||
"estimators": "",
|
"estimators": "",
|
||||||
"filehandlers": "",
|
"filehandlers": "",
|
||||||
@@ -1202,7 +1205,9 @@
|
|||||||
"percentage": ""
|
"percentage": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"allocation_total": ""
|
"allocation_total": "",
|
||||||
|
"members": "",
|
||||||
|
"team_options": ""
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"commission": "",
|
"commission": "",
|
||||||
@@ -1246,6 +1251,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"actions": "",
|
"actions": "",
|
||||||
"active": "",
|
"active": "",
|
||||||
|
"employee_number_short": "",
|
||||||
"endmustbeafterstart": "",
|
"endmustbeafterstart": "",
|
||||||
"flat_rate": "",
|
"flat_rate": "",
|
||||||
"inactive": "",
|
"inactive": "",
|
||||||
|
|||||||
@@ -736,6 +736,9 @@
|
|||||||
},
|
},
|
||||||
"emaillater": "",
|
"emaillater": "",
|
||||||
"employee_teams": "",
|
"employee_teams": "",
|
||||||
|
"employee_options": "",
|
||||||
|
"employee_rates": "",
|
||||||
|
"employee_vacation": "",
|
||||||
"employees": "",
|
"employees": "",
|
||||||
"estimators": "",
|
"estimators": "",
|
||||||
"filehandlers": "",
|
"filehandlers": "",
|
||||||
@@ -1202,7 +1205,9 @@
|
|||||||
"percentage": ""
|
"percentage": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"allocation_total": ""
|
"allocation_total": "",
|
||||||
|
"members": "",
|
||||||
|
"team_options": ""
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"commission": "",
|
"commission": "",
|
||||||
@@ -1246,6 +1251,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"actions": "",
|
"actions": "",
|
||||||
"active": "",
|
"active": "",
|
||||||
|
"employee_number_short": "",
|
||||||
"endmustbeafterstart": "",
|
"endmustbeafterstart": "",
|
||||||
"flat_rate": "",
|
"flat_rate": "",
|
||||||
"inactive": "",
|
"inactive": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user