IO-2327 posting bills test caes
This commit is contained in:
194
client/cypress/e2e/posting-bills/posting-bills.cy.js
Normal file
194
client/cypress/e2e/posting-bills/posting-bills.cy.js
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import uniqueId from "lodash/uniqueId";
|
||||||
|
|
||||||
|
describe(
|
||||||
|
"Billing job parts orders",
|
||||||
|
{
|
||||||
|
defaultCommandTimeout: 5000,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit("/manage");
|
||||||
|
|
||||||
|
cy.get("body").then(($body) => {
|
||||||
|
if ($body.text().includes("Login")) {
|
||||||
|
// Log in
|
||||||
|
cy.get('[data-cy="username"]').type("john@imex.dev");
|
||||||
|
cy.get('[data-cy="password"]').type("john123");
|
||||||
|
cy.get('[data-cy="sign-in-button"]').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.get(".ant-table-tbody")
|
||||||
|
.should("be.visible")
|
||||||
|
.find("tr")
|
||||||
|
.should("not.have.class", "ant-table-placeholder");
|
||||||
|
|
||||||
|
cy.get(".ant-table-row")
|
||||||
|
.not(':contains("Open")')
|
||||||
|
.first()
|
||||||
|
.find("a")
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.url().should("include", "/manage/jobs");
|
||||||
|
|
||||||
|
// Go to repair data tab
|
||||||
|
cy.get('[data-cy="tab-partssublet"]').should("be.visible").click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("receives a part bill", () => {
|
||||||
|
// Find the first row in the parts order
|
||||||
|
cy.get('[data-cy="part-orders-table"]')
|
||||||
|
.find(".ant-table-tbody")
|
||||||
|
.find("> tr:not(.ant-table-measure-row)")
|
||||||
|
.as("orders-table")
|
||||||
|
.should("not.have.class", "ant-table-placeholder");
|
||||||
|
|
||||||
|
cy.get("@orders-table")
|
||||||
|
.first()
|
||||||
|
.should("be.visible")
|
||||||
|
.find('[data-cy="receive-bill-button"]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
// fill out form
|
||||||
|
// data-cy="bill-form-invoice"
|
||||||
|
cy.get('[data-cy="bill-form-invoice"]').type(uniqueId());
|
||||||
|
|
||||||
|
cy.get("#bill-form-date").click();
|
||||||
|
cy.get('[title="2023-06-22"]').should("be.visible").click();
|
||||||
|
|
||||||
|
// get table
|
||||||
|
cy.get('[data-cy="bill-line-table"]').each(($row) => {
|
||||||
|
// get retail amount
|
||||||
|
cy.wrap($row)
|
||||||
|
.find('[data-cy="bill-line-actual-price"]')
|
||||||
|
.invoke("val")
|
||||||
|
.then((p) => {
|
||||||
|
const vendorPercentOff = 0.2;
|
||||||
|
|
||||||
|
// get cost - vendor's percent off
|
||||||
|
const cost = p - p * vendorPercentOff;
|
||||||
|
cy.get('[data-cy="bill-form-bill-total"]').type(p);
|
||||||
|
|
||||||
|
cy.wrap($row).find('[data-cy="bill-line-actual-cost"]').type(cost);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click save
|
||||||
|
cy.get('[data-cy="bill-form-save-button"]')
|
||||||
|
.should("not.be.disabled")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get(".ant-notification-notice-message").contains(
|
||||||
|
"Invoice added successfully."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("posts bill directly", () => {
|
||||||
|
cy.get('[data-cy="bills-post-button"]').should("be.visible").click();
|
||||||
|
|
||||||
|
// Add New Line
|
||||||
|
cy.get('[data-cy="bill-line-add-button"]')
|
||||||
|
.should("not.be.disabled")
|
||||||
|
.click();
|
||||||
|
// Select Vendor
|
||||||
|
cy.antdSelect("bill-vendor");
|
||||||
|
// Select Line
|
||||||
|
cy.antdSelect("bill-line", "-- Not On Estimate --");
|
||||||
|
// Fill the Form
|
||||||
|
cy.get('[data-cy="bill-form-invoice"]').type(uniqueId("DBP"));
|
||||||
|
|
||||||
|
cy.get("#bill-form-date").click();
|
||||||
|
cy.get('[title="2023-06-22"]').should("be.visible").click();
|
||||||
|
|
||||||
|
// get table
|
||||||
|
cy.get('[data-cy="bill-line-table"]').each(($row) => {
|
||||||
|
// get retail amount
|
||||||
|
cy.wrap($row)
|
||||||
|
.find('[data-cy="bill-line-actual-price"]')
|
||||||
|
.invoke("val")
|
||||||
|
.then((p) => {
|
||||||
|
const vendorPercentOff = 0.2;
|
||||||
|
|
||||||
|
// get cost - vendor's percent off
|
||||||
|
const cost = p - p * vendorPercentOff;
|
||||||
|
cy.get('[data-cy="bill-form-bill-total"]').type(p);
|
||||||
|
|
||||||
|
cy.wrap($row).find('[data-cy="bill-line-actual-cost"]').type(cost);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click save
|
||||||
|
cy.get('[data-cy="bill-form-save-button"]')
|
||||||
|
.should("not.be.disabled")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get(".ant-notification-notice-message").contains(
|
||||||
|
"Invoice added successfully."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("posts a bill with save and new", () => {
|
||||||
|
cy.get('[data-cy="bills-post-button"]').should("be.visible").click();
|
||||||
|
|
||||||
|
// Add New Line
|
||||||
|
cy.get('[data-cy="bill-line-add-button"]')
|
||||||
|
.should("not.be.disabled")
|
||||||
|
.click();
|
||||||
|
// Select Vendor
|
||||||
|
cy.antdSelect("bill-vendor");
|
||||||
|
// Select Line
|
||||||
|
cy.antdSelect("bill-line", "-- Not On Estimate --");
|
||||||
|
// Fill the Form
|
||||||
|
cy.get('[data-cy="bill-form-invoice"]').type(uniqueId("DBP"));
|
||||||
|
|
||||||
|
cy.get("#bill-form-date").click();
|
||||||
|
cy.get('[title="2023-06-22"]').should("be.visible").click();
|
||||||
|
|
||||||
|
// get table
|
||||||
|
cy.get('[data-cy="bill-line-table"]').each(($row) => {
|
||||||
|
// get retail amount
|
||||||
|
cy.wrap($row)
|
||||||
|
.find('[data-cy="bill-line-actual-price"]')
|
||||||
|
.invoke("val")
|
||||||
|
.then((p) => {
|
||||||
|
const vendorPercentOff = 0.2;
|
||||||
|
|
||||||
|
// get cost - vendor's percent off
|
||||||
|
const cost = p - p * vendorPercentOff;
|
||||||
|
cy.get('[data-cy="bill-form-bill-total"]').type(p);
|
||||||
|
|
||||||
|
cy.wrap($row).find('[data-cy="bill-line-actual-cost"]').type(cost);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click save
|
||||||
|
cy.get('[data-cy="bill-form-savenew-button"]')
|
||||||
|
.should("not.be.disabled")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get(".ant-notification-notice-message").contains(
|
||||||
|
"Invoice added successfully."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uploads a document to a bill", () => {
|
||||||
|
cy.get('[data-cy="bills-table"]')
|
||||||
|
.find(".ant-table-tbody")
|
||||||
|
.find("> tr:not(.ant-table-measure-row)")
|
||||||
|
.as("bills-table")
|
||||||
|
.should("not.have.class", "ant-table-placeholder");
|
||||||
|
|
||||||
|
cy.get("@bills-table")
|
||||||
|
.first()
|
||||||
|
.should("be.visible")
|
||||||
|
.find('[data-cy="edit-bill-button"]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.location("search").should("include", "billid");
|
||||||
|
cy.get('[data-cy="bill-edit-form"]')
|
||||||
|
.find(".ant-upload #bill-document-upload")
|
||||||
|
.selectFile("job.json", { force: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -50,7 +50,7 @@ Cypress.on("uncaught:exception", (err, runnable) => {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("antdSelect", (selector) => {
|
Cypress.Commands.add("antdSelect", (selector, filter) => {
|
||||||
cy.get(`.ant-select-${selector} > .ant-select-selector`).click();
|
cy.get(`.ant-select-${selector} > .ant-select-selector`).click();
|
||||||
cy.get(`.ant-select-${selector} .ant-select-selection-search input`)
|
cy.get(`.ant-select-${selector} .ant-select-selection-search input`)
|
||||||
.invoke("attr", "id")
|
.invoke("attr", "id")
|
||||||
@@ -59,6 +59,7 @@ Cypress.Commands.add("antdSelect", (selector) => {
|
|||||||
cy.get(dropDownSelector)
|
cy.get(dropDownSelector)
|
||||||
.next()
|
.next()
|
||||||
.find(".ant-select-item-option-content")
|
.find(".ant-select-item-option-content")
|
||||||
|
.not(`:contains("${filter}")`)
|
||||||
.first()
|
.first()
|
||||||
.click();
|
.click();
|
||||||
});
|
});
|
||||||
|
|||||||
5912
client/job.json
Normal file
5912
client/job.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import {
|
import {
|
||||||
DELETE_BILL_LINE,
|
DELETE_BILL_LINE,
|
||||||
INSERT_NEW_BILL_LINES,
|
INSERT_NEW_BILL_LINES,
|
||||||
UPDATE_BILL_LINE
|
UPDATE_BILL_LINE,
|
||||||
} from "../../graphql/bill-lines.queries";
|
} from "../../graphql/bill-lines.queries";
|
||||||
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
@@ -200,6 +200,7 @@ export function BillDetailEditcontainer({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Form
|
<Form
|
||||||
|
data-cy="bill-edit-form"
|
||||||
form={form}
|
form={form}
|
||||||
onFinish={handleFinish}
|
onFinish={handleFinish}
|
||||||
initialValues={transformData(data)}
|
initialValues={transformData(data)}
|
||||||
|
|||||||
@@ -362,11 +362,16 @@ function BillEnterModalContainer({
|
|||||||
{t("bills.labels.generatepartslabel")}
|
{t("bills.labels.generatepartslabel")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
||||||
<Button loading={loading} onClick={() => form.submit()}>
|
<Button
|
||||||
|
data-cy="bill-form-save-button"
|
||||||
|
loading={loading}
|
||||||
|
onClick={() => form.submit()}
|
||||||
|
>
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
{billEnterModal.context && billEnterModal.context.id ? null : (
|
{billEnterModal.context && billEnterModal.context.id ? null : (
|
||||||
<Button
|
<Button
|
||||||
|
data-cy="bill-form-savenew-button"
|
||||||
type="primary"
|
type="primary"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ export function BillFormComponent({
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<VendorSearchSelect
|
<VendorSearchSelect
|
||||||
|
className="ant-select-bill-vendor"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={vendorAutoCompleteOptions}
|
options={vendorAutoCompleteOptions}
|
||||||
onSelect={handleVendorSelect}
|
onSelect={handleVendorSelect}
|
||||||
@@ -249,7 +250,10 @@ export function BillFormComponent({
|
|||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={disabled || disableInvNumber} />
|
<Input
|
||||||
|
data-cy="bill-form-invoice"
|
||||||
|
disabled={disabled || disableInvNumber}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bills.fields.date")}
|
label={t("bills.fields.date")}
|
||||||
@@ -261,7 +265,7 @@ export function BillFormComponent({
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<FormDatePicker disabled={disabled} />
|
<FormDatePicker id="bill-form-date" disabled={disabled} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bills.fields.is_credit_memo")}
|
label={t("bills.fields.is_credit_memo")}
|
||||||
@@ -303,6 +307,7 @@ export function BillFormComponent({
|
|||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
data-cy="bill-form-bill-total"
|
||||||
label={t("bills.fields.total")}
|
label={t("bills.fields.total")}
|
||||||
name="total"
|
name="total"
|
||||||
rules={[
|
rules={[
|
||||||
@@ -456,19 +461,20 @@ export function BillFormComponent({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Upload.Dragger
|
<Upload.Dragger
|
||||||
|
id="bill-image-upload"
|
||||||
multiple={true}
|
multiple={true}
|
||||||
name="logo"
|
name="logo"
|
||||||
beforeUpload={() => false}
|
beforeUpload={() => false}
|
||||||
listType="picture"
|
listType="picture"
|
||||||
>
|
>
|
||||||
<>
|
<div>
|
||||||
<p className="ant-upload-drag-icon">
|
<p className="ant-upload-drag-icon">
|
||||||
<UploadOutlined />
|
<UploadOutlined />
|
||||||
</p>
|
</p>
|
||||||
<p className="ant-upload-text">
|
<p className="ant-upload-text">
|
||||||
Click or drag files to this area to upload.
|
Click or drag files to this area to upload.
|
||||||
</p>
|
</p>
|
||||||
</>
|
</div>
|
||||||
</Upload.Dragger>
|
</Upload.Dragger>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {
|
||||||
Button, Form,
|
Button,
|
||||||
|
Form,
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
Tooltip
|
Tooltip,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -79,6 +80,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
),
|
),
|
||||||
formInput: (record, index) => (
|
formInput: (record, index) => (
|
||||||
<BillLineSearchSelect
|
<BillLineSearchSelect
|
||||||
|
className="ant-select-bill-line"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
options={lineData}
|
options={lineData}
|
||||||
style={{ width: "100%", minWidth: "10rem" }}
|
style={{ width: "100%", minWidth: "10rem" }}
|
||||||
@@ -194,6 +196,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
},
|
},
|
||||||
formInput: (record, index) => (
|
formInput: (record, index) => (
|
||||||
<CurrencyInput
|
<CurrencyInput
|
||||||
|
data-cy="bill-line-actual-price"
|
||||||
min={0}
|
min={0}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
@@ -241,6 +244,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
},
|
},
|
||||||
formInput: (record, index) => (
|
formInput: (record, index) => (
|
||||||
<CurrencyInput
|
<CurrencyInput
|
||||||
|
data-cy="bill-line-actual-cost"
|
||||||
min={0}
|
min={0}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
controls={false}
|
controls={false}
|
||||||
@@ -572,6 +576,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table
|
<Table
|
||||||
|
data-cy="bill-line-table"
|
||||||
components={{
|
components={{
|
||||||
body: {
|
body: {
|
||||||
cell: EditableCell,
|
cell: EditableCell,
|
||||||
@@ -587,6 +592,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
/>
|
/>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button
|
<Button
|
||||||
|
data-cy="bill-line-add-button"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
add();
|
add();
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ export function BillsListTableComponent({
|
|||||||
const recordActions = (record, showView = false) => (
|
const recordActions = (record, showView = false) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{showView && (
|
{showView && (
|
||||||
<Button onClick={() => handleOnRowClick(record)}>
|
<Button
|
||||||
|
onClick={() => handleOnRowClick(record)}
|
||||||
|
data-cy="edit-bill-button"
|
||||||
|
>
|
||||||
<EditFilled />
|
<EditFilled />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -178,6 +181,7 @@ export function BillsListTableComponent({
|
|||||||
{job && job.converted ? (
|
{job && job.converted ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
data-cy="bills-post-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setBillEnterContext({
|
setBillEnterContext({
|
||||||
actions: { refetch: billsQuery.refetch },
|
actions: { refetch: billsQuery.refetch },
|
||||||
@@ -217,6 +221,7 @@ export function BillsListTableComponent({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
|
data-cy="bills-table"
|
||||||
loading={billsQuery.loading}
|
loading={billsQuery.loading}
|
||||||
scroll={{
|
scroll={{
|
||||||
x: true, // y: "50rem"
|
x: true, // y: "50rem"
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export function DocumentsLocalUploadComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Upload.Dragger
|
<Upload.Dragger
|
||||||
|
id="bill-document-upload"
|
||||||
multiple={true}
|
multiple={true}
|
||||||
fileList={fileList}
|
fileList={fileList}
|
||||||
onChange={(f) => {
|
onChange={(f) => {
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ export function PartsOrderListTableComponent({
|
|||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
<Button
|
<Button
|
||||||
|
data-cy="receive-bill-button"
|
||||||
disabled={
|
disabled={
|
||||||
(jobRO ? !record.return : jobRO) ||
|
(jobRO ? !record.return : jobRO) ||
|
||||||
record.vendor.id === bodyshop.inhousevendorid
|
record.vendor.id === bodyshop.inhousevendorid
|
||||||
@@ -478,6 +479,7 @@ export function PartsOrderListTableComponent({
|
|||||||
{selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
|
{selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
<Table
|
<Table
|
||||||
|
data-cy="part-orders-table"
|
||||||
loading={billsQuery.loading}
|
loading={billsQuery.loading}
|
||||||
scroll={{
|
scroll={{
|
||||||
x: true,
|
x: true,
|
||||||
|
|||||||
@@ -7,7 +7,16 @@ const { Option } = Select;
|
|||||||
//To be used as a form element only.
|
//To be used as a form element only.
|
||||||
|
|
||||||
const VendorSearchSelect = (
|
const VendorSearchSelect = (
|
||||||
{ value, onChange, options, onSelect, disabled, preferredMake, showPhone },
|
{
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
options,
|
||||||
|
onSelect,
|
||||||
|
disabled,
|
||||||
|
preferredMake,
|
||||||
|
showPhone,
|
||||||
|
...rest
|
||||||
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const [option, setOption] = useState(value);
|
const [option, setOption] = useState(value);
|
||||||
@@ -43,6 +52,7 @@ const VendorSearchSelect = (
|
|||||||
optionFilterProp="name"
|
optionFilterProp="name"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
disabled={disabled || false}
|
disabled={disabled || false}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
{favorites
|
{favorites
|
||||||
? favorites.map((o) => (
|
? favorites.map((o) => (
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ export function JobsDetailPage({
|
|||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane
|
<Tabs.TabPane
|
||||||
tab={
|
tab={
|
||||||
<span>
|
<span data-cy="tab-partssublet">
|
||||||
<ToolFilled />
|
<ToolFilled />
|
||||||
{t("menus.jobsdetail.partssublet")}
|
{t("menus.jobsdetail.partssublet")}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user