Merged in feature/IO-3499-React-19 (pull request #2849)
Feature/IO-3499 React 19
This commit is contained in:
@@ -13,31 +13,32 @@ export default function DataLabel({
|
||||
if (!open || (hideIfNull && !children)) return null;
|
||||
|
||||
return (
|
||||
<div {...props} style={{ display: "flex" }}>
|
||||
<div {...props} style={{ display: "flex", alignItems: "flex-start" }}>
|
||||
<div
|
||||
style={{
|
||||
// flex: 2,
|
||||
marginRight: ".2rem"
|
||||
marginRight: ".2rem",
|
||||
flexShrink: 0, // <-- key: don't let the label collapse
|
||||
whiteSpace: "nowrap" // <-- key: keep "Email:" on one line
|
||||
}}
|
||||
>
|
||||
<Typography.Text type="secondary">{`${label}:`}</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
flex: 4,
|
||||
flex: 1, // <-- key: take remaining space
|
||||
minWidth: 0, // <-- key: allow this flex item to shrink
|
||||
marginLeft: ".3rem",
|
||||
fontWeight: "bolder",
|
||||
wordWrap: "break-word",
|
||||
cursor: onValueClick !== undefined ? "pointer" : ""
|
||||
overflowWrap: "anywhere", // <-- key: break long tokens (email/vin)
|
||||
wordBreak: "break-word", // (backup behavior across browsers)
|
||||
cursor: onValueClick !== undefined ? "pointer" : "",
|
||||
...(styles?.value ?? {}) // apply your per-field overrides to ALL children types
|
||||
}}
|
||||
className={valueClassName}
|
||||
onClick={onValueClick}
|
||||
>
|
||||
{typeof children === "string" ? (
|
||||
<Typography.Text style={styles?.value}>{children}</Typography.Text>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
{typeof children === "string" ? <Typography.Text>{children}</Typography.Text> : children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@ export function EmailOverlayContainer({ emailConfig, modalVisible, toggleEmailOv
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [sending, setSending] = useState(false);
|
||||
const [rawHtml, setRawHtml] = useState("");
|
||||
const [htmlSize, setHtmlSize] = useState(0);
|
||||
const [pdfCopytoAttach, setPdfCopytoAttach] = useState({
|
||||
filename: null,
|
||||
pdf: null
|
||||
@@ -151,6 +152,13 @@ export function EmailOverlayContainer({ emailConfig, modalVisible, toggleEmailOv
|
||||
if (modalVisible) render();
|
||||
}, [modalVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
const html = form.getFieldValue("html");
|
||||
if (html) {
|
||||
setHtmlSize(new Blob([html]).size);
|
||||
}
|
||||
}, [form, rawHtml]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnHidden
|
||||
@@ -169,7 +177,7 @@ export function EmailOverlayContainer({ emailConfig, modalVisible, toggleEmailOv
|
||||
disabled:
|
||||
selectedMedia &&
|
||||
(selectedMedia.filter((s) => s.isSelected).reduce((acc, val) => (acc = acc + val.size), 0) >=
|
||||
10485760 - new Blob([form.getFieldValue("html")]).size ||
|
||||
10485760 - htmlSize ||
|
||||
selectedMedia.filter((s) => s.isSelected).length > 10)
|
||||
}}
|
||||
>
|
||||
@@ -195,7 +203,7 @@ export function EmailOverlayContainer({ emailConfig, modalVisible, toggleEmailOv
|
||||
disabled={
|
||||
selectedMedia &&
|
||||
(selectedMedia.filter((s) => s.isSelected).reduce((acc, val) => (acc = acc + val.size), 0) >=
|
||||
10485760 - new Blob([form.getFieldValue("html")]).size ||
|
||||
10485760 - htmlSize ||
|
||||
selectedMedia.filter((s) => s.isSelected).length > 10)
|
||||
}
|
||||
type="primary"
|
||||
|
||||
@@ -120,6 +120,13 @@ export function ScheduleEventComponent({
|
||||
);
|
||||
|
||||
const handleConvert = async (values) => {
|
||||
if (!event.job?.id) {
|
||||
notification.error({
|
||||
title: t("appointments.errors.nojob")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await mutationUpdateJob({
|
||||
variables: {
|
||||
jobId: event.job.id,
|
||||
@@ -397,21 +404,21 @@ export function ScheduleEventComponent({
|
||||
(HasFeatureAccess({ featureName: "checklist", bodyshop }) ? (
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/manage/jobs/${event.job && event.job.id}/intake`,
|
||||
pathname: `/manage/jobs/${event.job.id}/intake`,
|
||||
search: `?appointmentId=${event.id}`
|
||||
}}
|
||||
>
|
||||
<Button disabled={event.arrived}>{t("appointments.actions.intake")}</Button>
|
||||
</Link>
|
||||
) : (
|
||||
<Popover //open={open}
|
||||
<Popover
|
||||
content={popMenu}
|
||||
open={popOverVisible}
|
||||
onOpenChange={setPopOverVisible}
|
||||
onClick={(e) => {
|
||||
if (event.job?.id) {
|
||||
e.stopPropagation();
|
||||
getJobDetails({ id: event.job.id });
|
||||
getJobDetails({ variables: { id: event.job.id } });
|
||||
}
|
||||
}}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
@@ -434,37 +441,36 @@ export function ScheduleEventComponent({
|
||||
return baseColor;
|
||||
};
|
||||
|
||||
const RegularEvent = event.isintake ? (
|
||||
<Space
|
||||
wrap
|
||||
size="small"
|
||||
style={{
|
||||
backgroundColor: getEventBackground()
|
||||
}}
|
||||
>
|
||||
{event.note && <AlertFilled className="production-alert" />}
|
||||
<strong>{`${event.job.ro_number || t("general.labels.na")}`}</strong>
|
||||
<OwnerNameDisplay ownerObject={event.job} />
|
||||
{`${(event.job && event.job.v_model_yr) || ""} ${
|
||||
(event.job && event.job.v_make_desc) || ""
|
||||
} ${(event.job && event.job.v_model_desc) || ""}`}
|
||||
{`(${(event.job && event.job.labhrs.aggregate.sum.mod_lb_hrs) || "0"} / ${
|
||||
(event.job && event.job.larhrs.aggregate.sum.mod_lb_hrs) || "0"
|
||||
})`}
|
||||
{event.job && event.job.alt_transport && <div style={{ margin: ".1rem" }}>{event.job.alt_transport}</div>}
|
||||
{event?.job?.comment && `C: ${event.job.comment}`}
|
||||
</Space>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: getEventBackground()
|
||||
}}
|
||||
>
|
||||
<strong>{`${event.title || ""}`}</strong>
|
||||
</div>
|
||||
);
|
||||
const RegularEvent =
|
||||
event.isintake && event.job ? (
|
||||
<Space
|
||||
wrap
|
||||
size="small"
|
||||
style={{
|
||||
backgroundColor: getEventBackground()
|
||||
}}
|
||||
>
|
||||
{event.note && <AlertFilled className="production-alert" />}
|
||||
<strong>{`${event.job.ro_number || t("general.labels.na")}`}</strong>
|
||||
<OwnerNameDisplay ownerObject={event.job} />
|
||||
{`${event.job.v_model_yr || ""} ${event.job.v_make_desc || ""} ${event.job.v_model_desc || ""}`}
|
||||
{`(${event.job.labhrs?.aggregate?.sum?.mod_lb_hrs || "0"} / ${
|
||||
event.job.larhrs?.aggregate?.sum?.mod_lb_hrs || "0"
|
||||
})`}
|
||||
{event.job.alt_transport && <div style={{ margin: ".1rem" }}>{event.job.alt_transport}</div>}
|
||||
{event.job.comment && `C: ${event.job.comment}`}
|
||||
</Space>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: getEventBackground()
|
||||
}}
|
||||
>
|
||||
<strong>{`${event.title || ""}`}</strong>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
|
||||
@@ -86,10 +86,10 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail, isPar
|
||||
|
||||
const statusMenu = {
|
||||
items: [
|
||||
...availableStatuses.map((item) => ({
|
||||
...(availableStatuses?.map((item) => ({
|
||||
key: item,
|
||||
label: item
|
||||
}))
|
||||
})) ?? [])
|
||||
],
|
||||
onClick: (e) => updateJobStatus(e.key)
|
||||
};
|
||||
|
||||
@@ -65,8 +65,8 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
const colSpan = {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 24 },
|
||||
md: { span: isPartsEntry ? 8 : 12 },
|
||||
lg: { span: isPartsEntry ? 8 : 6 },
|
||||
md: { span: 12 },
|
||||
lg: { span: 12 },
|
||||
xl: { span: isPartsEntry ? 8 : 6 }
|
||||
};
|
||||
|
||||
@@ -260,19 +260,19 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
<ChatOpenButton type={job.ownr_ph1_ty} phone={job.ownr_ph1} jobid={job.id} />
|
||||
)}
|
||||
</DataLabel>
|
||||
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
|
||||
<DataLabel key="3" label={t("jobs.fields.ownr_ph2")}>
|
||||
{disabled || isPartsEntry ? (
|
||||
<PhoneNumberFormatter type={job.ownr_ph2_ty}>{job.ownr_ph2}</PhoneNumberFormatter>
|
||||
) : (
|
||||
<ChatOpenButton type={job.ownr_ph2_ty} phone={job.ownr_ph2} jobid={job.id} />
|
||||
)}
|
||||
</DataLabel>
|
||||
<DataLabel key="3" label={t("owners.fields.address")}>
|
||||
<DataLabel key="4" label={t("owners.fields.address")}>
|
||||
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
|
||||
job.ownr_city || ""
|
||||
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
|
||||
</DataLabel>
|
||||
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
||||
<DataLabel key="5" label={t("owners.fields.ownr_ea")}>
|
||||
{disabled || isPartsEntry ? (
|
||||
<>{job.ownr_ea || ""}</>
|
||||
) : job.ownr_ea ? (
|
||||
@@ -280,13 +280,14 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
) : null}
|
||||
</DataLabel>
|
||||
{job.owner?.tax_number && (
|
||||
<DataLabel key="5" label={t("owners.fields.tax_number")}>
|
||||
<DataLabel key="6" label={t("owners.fields.tax_number")}>
|
||||
{job.owner?.tax_number || ""}
|
||||
</DataLabel>
|
||||
)}
|
||||
<DataLabel
|
||||
label={t("owners.fields.note")}
|
||||
styles={{ value: { overflow: "hidden", textOverflow: "ellipsis" } }}
|
||||
key="7"
|
||||
>
|
||||
{job.owner?.note || ""}
|
||||
</DataLabel>
|
||||
|
||||
@@ -18,6 +18,7 @@ export default function ProductionListColumnComment({ record }) {
|
||||
|
||||
const handleSaveNote = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setOpen(false);
|
||||
updateAlert({
|
||||
variables: {
|
||||
@@ -33,7 +34,6 @@ export default function ProductionListColumnComment({ record }) {
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
e.stopPropagation();
|
||||
setNote(e.target.value);
|
||||
};
|
||||
|
||||
@@ -42,26 +42,38 @@ export default function ProductionListColumnComment({ record }) {
|
||||
if (flag) setNote(record.comment || "");
|
||||
};
|
||||
|
||||
const content = (
|
||||
<div
|
||||
style={{ width: "30em" }}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={5}
|
||||
value={note}
|
||||
onChange={handleChange}
|
||||
autoFocus
|
||||
allowClear
|
||||
style={{ marginBottom: "1em" }}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
<div>
|
||||
<Button onClick={handleSaveNote} type="primary">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
onOpenChange={handleOpenChange}
|
||||
open={open}
|
||||
content={
|
||||
<div style={{ width: "30em" }}>
|
||||
<Input.TextArea
|
||||
rows={5}
|
||||
value={note}
|
||||
onChange={handleChange}
|
||||
// onPressEnter={handleSaveNote}
|
||||
autoFocus
|
||||
allowClear
|
||||
/>
|
||||
<div>
|
||||
<Button onClick={handleSaveNote}>{t("general.actions.save")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
trigger={["click"]}
|
||||
<Popover
|
||||
onOpenChange={handleOpenChange}
|
||||
open={open}
|
||||
content={content}
|
||||
trigger="click"
|
||||
fresh
|
||||
getPopupContainer={(trigger) => trigger.parentElement}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
@@ -69,9 +81,9 @@ export default function ProductionListColumnComment({ record }) {
|
||||
height: "19px",
|
||||
cursor: "pointer",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
display: "inline-block"
|
||||
textOverflow: "ellipsis"
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Icon component={FaRegStickyNote} style={{ marginRight: ".2rem" }} />
|
||||
<Tooltip title={record.comment}>{record.comment || " "}</Tooltip>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Icon from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client/react";
|
||||
import { Button, Input, Popover, Space } from "antd";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaRegStickyNote } from "react-icons/fa";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
@@ -27,6 +27,7 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
(e) => {
|
||||
logImEXEvent("production_add_note");
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setOpen(false);
|
||||
updateAlert({
|
||||
variables: {
|
||||
@@ -46,7 +47,6 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
);
|
||||
|
||||
const handleChange = useCallback((e) => {
|
||||
e.stopPropagation();
|
||||
setNote(e.target.value);
|
||||
}, []);
|
||||
|
||||
@@ -58,42 +58,48 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
[record]
|
||||
);
|
||||
|
||||
const popoverContent = useMemo(
|
||||
() => (
|
||||
<div style={{ width: "30em" }}>
|
||||
<Input.TextArea
|
||||
rows={5}
|
||||
value={note}
|
||||
onChange={handleChange}
|
||||
autoFocus
|
||||
allowClear
|
||||
style={{ marginBottom: "1em" }}
|
||||
/>
|
||||
<Space>
|
||||
<Button onClick={handleSaveNote} type="primary">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setNoteUpsertContext({
|
||||
context: {
|
||||
jobId: record.id,
|
||||
text: note
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("notes.actions.savetojobnotes")}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
[note, handleSaveNote, handleChange, record, setNoteUpsertContext, t]
|
||||
const content = (
|
||||
<div style={{ width: "30em" }} onMouseDown={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}>
|
||||
<Input.TextArea
|
||||
rows={5}
|
||||
value={note}
|
||||
onChange={handleChange}
|
||||
autoFocus
|
||||
allowClear
|
||||
style={{ marginBottom: "1em" }}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
<Space>
|
||||
<Button onClick={handleSaveNote} type="primary">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setNoteUpsertContext({
|
||||
context: {
|
||||
jobId: record.id,
|
||||
text: note
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("notes.actions.savetojobnotes")}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover onOpenChange={handleOpenChange} open={open} content={popoverContent} trigger={["click"]}>
|
||||
<Popover
|
||||
onOpenChange={handleOpenChange}
|
||||
open={open}
|
||||
content={content}
|
||||
trigger="click"
|
||||
fresh
|
||||
getPopupContainer={(trigger) => trigger.parentElement}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
@@ -102,6 +108,7 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis"
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Icon component={FaRegStickyNote} style={{ marginRight: ".2rem" }} />
|
||||
{record.production_vars?.note || " "}
|
||||
|
||||
Reference in New Issue
Block a user