Updated UI and added tech elements.

This commit is contained in:
Patrick Fic
2023-07-21 13:40:39 -07:00
parent 8dfcda6c5e
commit 7b49a94edd
14 changed files with 210 additions and 33 deletions

View File

@@ -33996,6 +33996,27 @@
<folder_node> <folder_node>
<name>tech</name> <name>tech</name>
<children> <children>
<concept_node>
<name>claimtask</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>home</name> <name>home</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -46950,6 +46971,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>unassigned</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>zeroactualnegativeprod</name> <name>zeroactualnegativeprod</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -47149,6 +47191,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>unassignedlines</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children> </children>
</folder_node> </folder_node>
</children> </children>

View File

@@ -30,10 +30,6 @@ export function JoblineBulkAssign({
job, job,
currentUser, currentUser,
}) { }) {
console.log(
"🚀 ~ file: job-line-bulk-assign.component.jsx:36 ~ selectedLines:",
selectedLines
);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [form] = Form.useForm(); const [form] = Form.useForm();

View File

@@ -61,7 +61,11 @@ export function PayrollLaborAllocationsTable({
key: "employeeid", key: "employeeid",
render: (text, record) => { render: (text, record) => {
if (record.employeeid === undefined) { if (record.employeeid === undefined) {
return "Unassigned"; return (
<span style={{ color: "tomato", fontWeight: "bolder" }}>
{t("timetickets.labels.unassigned")}
</span>
);
} }
const emp = bodyshop.employees.find((e) => e.id === record.employeeid); const emp = bodyshop.employees.find((e) => e.id === record.employeeid);
return `${emp?.first_name} ${emp?.last_name}`; return `${emp?.first_name} ${emp?.last_name}`;
@@ -71,12 +75,20 @@ export function PayrollLaborAllocationsTable({
title: t("joblines.fields.mod_lbr_ty"), title: t("joblines.fields.mod_lbr_ty"),
dataIndex: "mod_lbr_ty", dataIndex: "mod_lbr_ty",
key: "mod_lbr_ty", key: "mod_lbr_ty",
render: (text, record) =>
record.employeeid === undefined ? (
<span style={{ color: "tomato", fontWeight: "bolder" }}>
{t("timetickets.labels.unassigned")}
</span>
) : (
t(`joblines.fields.lbr_types.${record.mod_lbr_ty?.toUpperCase()}`)
),
}, },
{ // {
title: t("timetickets.fields.rate"), // title: t("timetickets.fields.rate"),
dataIndex: "rate", // dataIndex: "rate",
key: "rate", // key: "rate",
}, // },
{ {
title: t("jobs.labels.hrs_total"), title: t("jobs.labels.hrs_total"),
dataIndex: "expectedHours", dataIndex: "expectedHours",
@@ -251,7 +263,6 @@ export function PayrollLaborAllocationsTable({
</Typography.Title> </Typography.Title>
</Table.Summary.Cell> </Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell> <Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell> <Table.Summary.Cell>
{summary.hrs_total.toFixed(5)} {summary.hrs_total.toFixed(5)}
</Table.Summary.Cell> </Table.Summary.Cell>

View File

@@ -24,6 +24,8 @@ import ProductionListColumnNote from "./production-list-columns.productionnote.c
import ProductionListColumnCategory from "./production-list-columns.status.category"; import ProductionListColumnCategory from "./production-list-columns.status.category";
import ProductionListColumnStatus from "./production-list-columns.status.component"; import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component"; import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
import { store } from "../../redux/store";
import { setModalContext } from "../../redux/modals/modals.actions";
const r = ({ technician, state, activeStatuses, bodyshop }) => { const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [ return [
@@ -38,6 +40,29 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
</Link> </Link>
), ),
}, },
{
title: i18n.t("timetickets.actions.claimtasks"),
dataIndex: "claimtasks",
key: "claimtasks",
ellipsis: true,
render: (text, record) => (
<div
onClick={() => {
store.dispatch(
setModalContext({
context: {
actions: {},
context: { jobid: record.id },
},
modal: "timeTicketTask",
})
);
}}
>
{i18n.t("timetickets.actions.claimtasks")}
</div>
),
},
{ {
title: i18n.t("jobs.fields.ro_number"), title: i18n.t("jobs.fields.ro_number"),
dataIndex: "ro_number", dataIndex: "ro_number",

View File

@@ -11,22 +11,38 @@ import { createStructuredSelector } from "reselect";
import { techLogout } from "../../redux/tech/tech.actions"; import { techLogout } from "../../redux/tech/tech.actions";
import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectTechnician } from "../../redux/tech/tech.selectors";
import { BsKanban } from "react-icons/bs"; import { BsKanban } from "react-icons/bs";
import { useTreatments } from "@splitsoftware/splitio-react";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
const { Sider } = Layout; const { Sider } = Layout;
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
technician: selectTechnician, technician: selectTechnician,
bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
techLogout: () => dispatch(techLogout()), techLogout: () => dispatch(techLogout()),
setTimeTicketTaskContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
}); });
export function TechSider({ technician, techLogout }) { export function TechSider({
technician,
techLogout,
bodyshop,
setTimeTicketTaskContext,
}) {
const [collapsed, setCollapsed] = useState(true); const [collapsed, setCollapsed] = useState(true);
const { t } = useTranslation(); const { t } = useTranslation();
const onCollapse = (collapsed) => { const onCollapse = (collapsed) => {
setCollapsed(collapsed); setCollapsed(collapsed);
}; };
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
return ( return (
<Sider <Sider
@@ -51,13 +67,29 @@ export function TechSider({ technician, techLogout }) {
<Menu.Item key="2" disabled={!!!technician} icon={<SearchOutlined />}> <Menu.Item key="2" disabled={!!!technician} icon={<SearchOutlined />}>
<Link to={`/tech/joblookup`}>{t("menus.tech.joblookup")}</Link> <Link to={`/tech/joblookup`}>{t("menus.tech.joblookup")}</Link>
</Menu.Item> </Menu.Item>
<Menu.Item {Enhanced_Payroll.treatment === "on" ? (
key="3" <Menu.Item
disabled={!!!technician} key="3"
icon={<Icon component={FaBusinessTime} />} disabled={!!!technician}
> icon={<Icon component={FaBusinessTime} />}
<Link to={`/tech/jobclock`}>{t("menus.tech.jobclockin")}</Link> onClick={() => {
</Menu.Item> setTimeTicketTaskContext({
actions: {},
context: { jobid: null },
});
}}
>
{t("menus.tech.claimtask")}
</Menu.Item>
) : (
<Menu.Item
key="3"
disabled={!!!technician}
icon={<Icon component={FaBusinessTime} />}
>
<Link to={`/tech/jobclock`}>{t("menus.tech.jobclockin")}</Link>
</Menu.Item>
)}
<Menu.Item <Menu.Item
key="4" key="4"
disabled={!!!technician} disabled={!!!technician}

View File

@@ -34,7 +34,12 @@ export function TimeTicketTaskModalComponent({
form, form,
loading, loading,
completedTasks, completedTasks,
unassignedHours,
}) { }) {
console.log(
"🚀 ~ file: time-ticket-task-modal.component.jsx:39 ~ unassignedHours:",
unassignedHours
);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -80,7 +85,7 @@ export function TimeTicketTaskModalComponent({
if (!task) return null; if (!task) return null;
return ( return (
<table className="bill-inventory-table"> <table className="task-tickets-table">
<tbody> <tbody>
<tr> <tr>
<td>{t("bodyshop.fields.md_tasks_presets.percent")}</td> <td>{t("bodyshop.fields.md_tasks_presets.percent")}</td>
@@ -116,7 +121,7 @@ export function TimeTicketTaskModalComponent({
<Typography.Title level={4}> <Typography.Title level={4}>
{t("timetickets.labels.claimtaskpreview")} {t("timetickets.labels.claimtaskpreview")}
</Typography.Title> </Typography.Title>
<table className="bill-cm-returns-table"> <table className="task-tickets-table">
<thead> <thead>
<tr> <tr>
<th>{t("timetickets.fields.employee")}</th> <th>{t("timetickets.fields.employee")}</th>
@@ -173,6 +178,14 @@ export function TimeTicketTaskModalComponent({
}} }}
</Form.List> </Form.List>
)} )}
{unassignedHours > 0 && (
<Alert
type="error"
message={t("timetickets.validation.unassignedlines", {
unassignedHours: unassignedHours,
})}
/>
)}
</Col> </Col>
</Row> </Row>

View File

@@ -11,6 +11,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import TimeTicketTaskModalComponent from "./time-ticket-task-modal.component"; import TimeTicketTaskModalComponent from "./time-ticket-task-modal.component";
import { useApolloClient } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import { QUERY_COMPLETED_TASKS } from "../../graphql/jobs.queries"; import { QUERY_COMPLETED_TASKS } from "../../graphql/jobs.queries";
import "./time-ticket-task-modal.styles.scss";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
timeTicketTasksModal: selectTimeTicketTasks, timeTicketTasksModal: selectTimeTicketTasks,
@@ -32,6 +33,7 @@ export function TimeTickeTaskModalContainer({
const [form] = Form.useForm(); const [form] = Form.useForm();
const { context, visible } = timeTicketTasksModal; const { context, visible } = timeTicketTasksModal;
const [completedTasks, setCompletedTasks] = useState([]); const [completedTasks, setCompletedTasks] = useState([]);
const [unassignedHours, setUnassignedHours] = useState(0);
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const client = useApolloClient(); const client = useApolloClient();
@@ -83,7 +85,8 @@ export function TimeTickeTaskModalContainer({
//Close the modal //Close the modal
toggleModalVisible(); toggleModalVisible();
} else if (handleFinish === false) { } else if (handleFinish === false) {
form.setFieldsValue({ timetickets: data }); form.setFieldsValue({ timetickets: data.ticketsToInsert });
setUnassignedHours(data.unassignedHours);
} else { } else {
notification.open({ notification.open({
type: "error", type: "error",
@@ -125,6 +128,7 @@ export function TimeTickeTaskModalContainer({
form={form} form={form}
loading={loading} loading={loading}
completedTasks={completedTasks} completedTasks={completedTasks}
unassignedHours={unassignedHours}
/> />
</Form> </Form>
</Modal> </Modal>

View File

@@ -0,0 +1,19 @@
.task-tickets-table {
table-layout: fixed;
width: 100%;
th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
.ant-form-item {
margin-bottom: 0px !important;
}
}
tr:hover {
background-color: #f5f5f5;
}
}

View File

@@ -35,7 +35,11 @@ const TechJobClock = lazy(() =>
const TechShiftClock = lazy(() => const TechShiftClock = lazy(() =>
import("../tech-shift-clock/tech-shift-clock.component") import("../tech-shift-clock/tech-shift-clock.component")
); );
const TimeTicketModalTask = lazy(() =>
import(
"../../components/time-ticket-task-modal/time-ticket-task-modal.container"
)
);
const { Content } = Layout; const { Content } = Layout;
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -70,6 +74,7 @@ export function TechPage({ technician, match }) {
<FeatureWrapper featureName="tech-console"> <FeatureWrapper featureName="tech-console">
<TimeTicketModalContainer /> <TimeTicketModalContainer />
<PrintCenterModalContainer /> <PrintCenterModalContainer />
<TimeTicketModalTask />
<Switch> <Switch>
<Route <Route
exact exact

View File

@@ -1995,6 +1995,7 @@
"shops": "My Shops" "shops": "My Shops"
}, },
"tech": { "tech": {
"claimtask": "Claim Task",
"home": "Home", "home": "Home",
"jobclockin": "Job Clock In", "jobclockin": "Job Clock In",
"jobclockout": "Job Clock Out", "jobclockout": "Job Clock Out",
@@ -2781,6 +2782,7 @@
"straight_time": "Straight Time", "straight_time": "Straight Time",
"task": "Task", "task": "Task",
"timetickets": "Time Tickets", "timetickets": "Time Tickets",
"unassigned": "Unassigned",
"zeroactualnegativeprod": "Actual hours must be 0 if entering negative productive hours." "zeroactualnegativeprod": "Actual hours must be 0 if entering negative productive hours."
}, },
"successes": { "successes": {
@@ -2793,7 +2795,8 @@
"validation": { "validation": {
"clockoffmustbeafterclockon": "Clock off time must be the same or after clock in time.", "clockoffmustbeafterclockon": "Clock off time must be the same or after clock in time.",
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time.", "clockoffwithoutclockon": "Clock off time cannot be set without a clock in time.",
"hoursenteredmorethanavailable": "The number of hours entered is more than what is available for this cost center." "hoursenteredmorethanavailable": "The number of hours entered is more than what is available for this cost center.",
"unassignedlines": "There are currently {{unassignedHours}} hours of repair lines that are unassigned. These hours are not including in the above calculations and must be paid manually."
} }
}, },
"titles": { "titles": {

View File

@@ -1995,6 +1995,7 @@
"shops": "Mis tiendas" "shops": "Mis tiendas"
}, },
"tech": { "tech": {
"claimtask": "",
"home": "", "home": "",
"jobclockin": "", "jobclockin": "",
"jobclockout": "", "jobclockout": "",
@@ -2781,6 +2782,7 @@
"straight_time": "", "straight_time": "",
"task": "", "task": "",
"timetickets": "", "timetickets": "",
"unassigned": "",
"zeroactualnegativeprod": "" "zeroactualnegativeprod": ""
}, },
"successes": { "successes": {
@@ -2793,7 +2795,8 @@
"validation": { "validation": {
"clockoffmustbeafterclockon": "", "clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": "", "clockoffwithoutclockon": "",
"hoursenteredmorethanavailable": "" "hoursenteredmorethanavailable": "",
"unassignedlines": ""
} }
}, },
"titles": { "titles": {

View File

@@ -1995,6 +1995,7 @@
"shops": "Mes boutiques" "shops": "Mes boutiques"
}, },
"tech": { "tech": {
"claimtask": "",
"home": "", "home": "",
"jobclockin": "", "jobclockin": "",
"jobclockout": "", "jobclockout": "",
@@ -2781,6 +2782,7 @@
"straight_time": "", "straight_time": "",
"task": "", "task": "",
"timetickets": "", "timetickets": "",
"unassigned": "",
"zeroactualnegativeprod": "" "zeroactualnegativeprod": ""
}, },
"successes": { "successes": {
@@ -2793,7 +2795,8 @@
"validation": { "validation": {
"clockoffmustbeafterclockon": "", "clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": "", "clockoffwithoutclockon": "",
"hoursenteredmorethanavailable": "" "hoursenteredmorethanavailable": "",
"unassignedlines": ""
} }
}, },
"titles": { "titles": {

View File

@@ -33,7 +33,7 @@ exports.claimtask = async function (req, res) {
); );
//Get all of the assignments that are filtered. //Get all of the assignments that are filtered.
const { employeeHash } = CalculateExpectedHoursForJob( const { assignmentHash, employeeHash } = CalculateExpectedHoursForJob(
job, job,
theTaskPreset.hourstype theTaskPreset.hourstype
); );
@@ -83,7 +83,7 @@ exports.claimtask = async function (req, res) {
}, },
}); });
} }
res.json(ticketsToInsert); res.json({ unassignedHours: assignmentHash.unassigned, ticketsToInsert });
} catch (error) { } catch (error) {
logger.log("job-payroll-claim-task-error", "ERROR", req.user.email, jobid, { logger.log("job-payroll-claim-task-error", "ERROR", req.user.email, jobid, {
jobid: jobid, jobid: jobid,

View File

@@ -62,7 +62,7 @@ exports.payall = async function (req, res) {
cost_center: cost_center:
job.bodyshop.md_responsibility_centers.defaults.costs[key], job.bodyshop.md_responsibility_centers.defaults.costs[key],
flat_rate: true, flat_rate: true,
memo: "*System* Hours added during Pay All function. (multi)", memo: `*SYS-PAY* Add unclaimed hours. (${req.user.email})`,
}); });
}); });
} else { } else {
@@ -79,7 +79,7 @@ exports.payall = async function (req, res) {
job.bodyshop.md_responsibility_centers.defaults.costs[ job.bodyshop.md_responsibility_centers.defaults.costs[
path.mod_lbr_ty path.mod_lbr_ty
], ],
memo: "*System* Hours added during Pay All function.", memo: `*SYS-PAY* Add unclaimed hours. (${req.user.email})`,
}); });
} }
} else if (diff.op === "update") { } else if (diff.op === "update") {
@@ -97,7 +97,7 @@ exports.payall = async function (req, res) {
job.bodyshop.md_responsibility_centers.defaults.costs[ job.bodyshop.md_responsibility_centers.defaults.costs[
path.mod_lbr_ty path.mod_lbr_ty
], ],
memo: "*System* Hours adjustment during Pay All function. (Update)", memo: `*SYS-PAY* Adjust claimed hours per assignment. (${req.user.email})`,
}); });
} else { } else {
//Has to be a delete //Has to be a delete
@@ -123,7 +123,7 @@ exports.payall = async function (req, res) {
cost_center: cost_center:
job.bodyshop.md_responsibility_centers.defaults.costs[key], job.bodyshop.md_responsibility_centers.defaults.costs[key],
flat_rate: true, flat_rate: true,
memo: "*System* Hours removed during Pay All function. (Change in rate, unassignment, etc.) *multi*", memo: `*SYS-PAY* Remove claimed hours per assignment. (${req.user.email})`,
}); });
}); });
} else { } else {
@@ -140,7 +140,7 @@ exports.payall = async function (req, res) {
path.mod_lbr_ty path.mod_lbr_ty
], ],
flat_rate: true, flat_rate: true,
memo: "*System* Hours removed during Pay All function. (Change in rate, unassignment, etc.)", memo: `*SYS-PAY* Remove claimed hours per assignment. (${req.user.email})`,
}); });
} }
} }