Merged in feature/IO-3499-React-19 (pull request #2900)

Feature/IO-3499 React 19
This commit is contained in:
Dave Richer
2026-01-28 00:27:51 +00:00
12 changed files with 53 additions and 23 deletions

View File

@@ -108,9 +108,12 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
hasLoadedConversationsOnceRef.current = true; hasLoadedConversationsOnceRef.current = true;
getConversations({ variables: { offset: 0 } }).catch((err) => { getConversations({ variables: { offset: 0 } }).catch((err) => {
console.error(`Error fetching conversations: ${err?.message || ""}`, err); // Ignore abort errors (they're expected when component unmounts)
if (err?.name !== "AbortError") {
console.error(`Error fetching conversations: ${err?.message || ""}`, err);
}
}); });
}, [getConversations]); }, []);
const handleManualRefresh = async () => { const handleManualRefresh = async () => {
try { try {

View File

@@ -143,7 +143,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
label={t("jobs.fields.comment")} label={t("jobs.fields.comment")}
styles={{ value: { overflow: "hidden", textOverflow: "ellipsis" } }} styles={{ value: { overflow: "hidden", textOverflow: "ellipsis" } }}
> >
<ProductionListColumnComment record={job} /> <ProductionListColumnComment record={job} usePortal={true} />
</DataLabel> </DataLabel>
{!isPartsEntry && <DataLabel label={t("jobs.fields.ins_co_nm_short")}>{job.ins_co_nm}</DataLabel>} {!isPartsEntry && <DataLabel label={t("jobs.fields.ins_co_nm_short")}>{job.ins_co_nm}</DataLabel>}
<DataLabel label={t("jobs.fields.clm_no")}>{job.clm_no}</DataLabel> <DataLabel label={t("jobs.fields.clm_no")}>{job.clm_no}</DataLabel>
@@ -176,7 +176,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
</DataLabel> </DataLabel>
)} )}
<DataLabel label={t("jobs.fields.production_vars.note")}> <DataLabel label={t("jobs.fields.production_vars.note")}>
<ProductionListColumnProductionNote record={job} /> <ProductionListColumnProductionNote record={job} usePortal={true} />
</DataLabel> </DataLabel>
<DataLabel label={t("jobs.fields.estimate_sent_approval")}> <DataLabel label={t("jobs.fields.estimate_sent_approval")}>
<Space> <Space>

View File

@@ -63,10 +63,10 @@ const ProductionListColumnAlert = ({ id, productionVars, refetch, insertAuditTra
okText={t("general.labels.yes")} okText={t("general.labels.yes")}
cancelText={t("general.labels.no")} cancelText={t("general.labels.no")}
> >
<Button className="production-alert" icon={<ExclamationCircleFilled />} /> <Button className="production-alert" icon={<ExclamationCircleFilled />} style={{ cursor: "pointer" }} />
</Popconfirm> </Popconfirm>
) : ( ) : (
<Button className="muted-button" icon={<PlusCircleFilled />} onClick={handleAlertToggle} /> <Button className="muted-button" icon={<PlusCircleFilled />} onClick={handleAlertToggle} style={{ cursor: "pointer" }} />
); );
}; };

View File

@@ -48,7 +48,7 @@ export default function ProductionListColumnBodyPriority({ record }) {
return ( return (
<Dropdown menu={menu} trigger={["click"]}> <Dropdown menu={menu} trigger={["click"]}>
<div style={{ width: "100%", height: "19px" }}>{record.production_vars?.bodypriority}</div> <div style={{ width: "100%", height: "19px", cursor: "pointer" }}>{record.production_vars?.bodypriority}</div>
</Dropdown> </Dropdown>
); );
} }

View File

@@ -7,7 +7,7 @@ import { FaRegStickyNote } from "react-icons/fa";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
export default function ProductionListColumnComment({ record }) { export default function ProductionListColumnComment({ record, usePortal = false }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [note, setNote] = useState(record.comment || ""); const [note, setNote] = useState(record.comment || "");
@@ -43,16 +43,20 @@ export default function ProductionListColumnComment({ record }) {
}; };
const content = ( const content = (
<div style={{ width: "30em" }} onMouseDown={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}> <div
style={{ width: "30em" }}
onClick={(e) => e.stopPropagation()}
onPointerDown={(e) => e.stopPropagation()}
>
<Input.TextArea <Input.TextArea
id={`job-comment-${record.id}`}
name="comment"
rows={5} rows={5}
value={note} value={note}
onChange={handleChange} onChange={handleChange}
autoFocus autoFocus
allowClear allowClear
style={{ marginBottom: "1em" }} style={{ marginBottom: "1em" }}
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
/> />
<div> <div>
<Button onClick={handleSaveNote} type="primary"> <Button onClick={handleSaveNote} type="primary">
@@ -63,7 +67,15 @@ export default function ProductionListColumnComment({ record }) {
); );
return ( return (
<Popover onOpenChange={handleOpenChange} open={open} content={content} trigger="click" fresh> <Popover
onOpenChange={handleOpenChange}
open={open}
content={content}
trigger="click"
destroyOnHidden
styles={{ body: { padding: '12px' } }}
{...(usePortal ? { getPopupContainer: (trigger) => trigger.parentElement || document.body } : {})}
>
<div <div
style={{ style={{
width: "100%", width: "100%",

View File

@@ -64,6 +64,7 @@ const productionListColumnsData = ({ technician, state, activeStatuses, data, bo
ellipsis: true, ellipsis: true,
render: (text, record) => ( render: (text, record) => (
<div <div
style={{ cursor: "pointer" }}
onClick={() => { onClick={() => {
store.dispatch( store.dispatch(
setModalContext({ setModalContext({

View File

@@ -86,7 +86,8 @@ export default function ProductionListDate({ record, field, time, pastIndicator
<div <div
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
style={{ style={{
height: "19px" height: "19px",
cursor: "pointer"
}} }}
className={className} className={className}
> >

View File

@@ -48,7 +48,7 @@ export default function ProductionListColumnDetailPriority({ record }) {
return ( return (
<Dropdown menu={menu} trigger={["click"]}> <Dropdown menu={menu} trigger={["click"]}>
<div style={{ width: "100%", height: "19px" }}>{record.production_vars?.detailpriority}</div> <div style={{ width: "100%", height: "19px", cursor: "pointer" }}>{record.production_vars?.detailpriority}</div>
</Dropdown> </Dropdown>
); );
} }

View File

@@ -144,13 +144,13 @@ export function ProductionListEmpAssignment({ insertAuditTrail, bodyshop, record
<Popover destroyOnHidden content={popContent} open={visibility}> <Popover destroyOnHidden content={popContent} open={visibility}>
<Spin spinning={loading}> <Spin spinning={loading}>
{record[type] ? ( {record[type] ? (
<div> <div style={{ cursor: "pointer" }}>
<span>{`${theEmployee?.first_name || ""} ${theEmployee?.last_name || ""}`}</span> <span>{`${theEmployee?.first_name || ""} ${theEmployee?.last_name || ""}`}</span>
<DeleteFilled style={iconStyle} onClick={() => handleRemove(type)} /> <DeleteFilled style={iconStyle} onClick={() => handleRemove(type)} />
</div> </div>
) : ( ) : (
<PlusCircleFilled <PlusCircleFilled
style={iconStyle} style={{ ...iconStyle, cursor: "pointer" }}
className="muted-button" className="muted-button"
onClick={() => { onClick={() => {
setAssignment({ operation: type }); setAssignment({ operation: type });

View File

@@ -124,7 +124,8 @@ export function ProductionLastContacted({ currentUser, record }) {
<div <div
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
style={{ style={{
height: "19px" height: "19px",
cursor: "pointer"
}} }}
> >
<DateFormatter bordered={false}>{record.date_last_contacted}</DateFormatter> <DateFormatter bordered={false}>{record.date_last_contacted}</DateFormatter>

View File

@@ -48,7 +48,7 @@ export default function ProductionListColumnPaintPriority({ record }) {
return ( return (
<Dropdown menu={menu} trigger={["click"]}> <Dropdown menu={menu} trigger={["click"]}>
<div style={{ width: "100%", height: "19px" }}>{record.production_vars?.paintpriority}</div> <div style={{ width: "100%", height: "19px", cursor: "pointer" }}>{record.production_vars?.paintpriority}</div>
</Dropdown> </Dropdown>
); );
} }

View File

@@ -16,7 +16,7 @@ const mapDispatchToProps = (dispatch) => ({
setNoteUpsertContext: (context) => dispatch(setModalContext({ context: context, modal: "noteUpsert" })) setNoteUpsertContext: (context) => dispatch(setModalContext({ context: context, modal: "noteUpsert" }))
}); });
function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) { function ProductionListColumnProductionNote({ record, setNoteUpsertContext, usePortal = false }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [note, setNote] = useState(record.production_vars?.note || ""); const [note, setNote] = useState(record.production_vars?.note || "");
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -59,16 +59,20 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
); );
const content = ( const content = (
<div style={{ width: "30em" }} onMouseDown={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}> <div
style={{ width: "30em" }}
onClick={(e) => e.stopPropagation()}
onPointerDown={(e) => e.stopPropagation()}
>
<Input.TextArea <Input.TextArea
id={`job-production-note-${record.id}`}
name="production_note"
rows={5} rows={5}
value={note} value={note}
onChange={handleChange} onChange={handleChange}
autoFocus autoFocus
allowClear allowClear
style={{ marginBottom: "1em" }} style={{ marginBottom: "1em" }}
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
/> />
<Space> <Space>
<Button onClick={handleSaveNote} type="primary"> <Button onClick={handleSaveNote} type="primary">
@@ -92,7 +96,15 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
); );
return ( return (
<Popover onOpenChange={handleOpenChange} open={open} content={content} trigger="click" fresh> <Popover
onOpenChange={handleOpenChange}
open={open}
content={content}
trigger="click"
destroyOnHidden
styles={{ body: { padding: '12px' } }}
{...(usePortal ? { getPopupContainer: (trigger) => trigger.parentElement || document.body } : {})}
>
<div <div
style={{ style={{
width: "100%", width: "100%",