feature/IO-3499-React-19 -Checkpoint
This commit is contained in:
32
client/package-lock.json
generated
32
client/package-lock.json
generated
@@ -11,7 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@amplitude/analytics-browser": "^2.34.0",
|
"@amplitude/analytics-browser": "^2.34.0",
|
||||||
"@ant-design/pro-layout": "^7.22.6",
|
"@ant-design/pro-layout": "^7.22.6",
|
||||||
"@apollo/client": "^4.1.2",
|
"@apollo/client": "^4.1.3",
|
||||||
"@emotion/is-prop-valid": "^1.4.0",
|
"@emotion/is-prop-valid": "^1.4.0",
|
||||||
"@fingerprintjs/fingerprintjs": "^5.0.1",
|
"@fingerprintjs/fingerprintjs": "^5.0.1",
|
||||||
"@firebase/analytics": "^0.10.19",
|
"@firebase/analytics": "^0.10.19",
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"normalize-url": "^8.1.1",
|
"normalize-url": "^8.1.1",
|
||||||
"object-hash": "^3.0.0",
|
"object-hash": "^3.0.0",
|
||||||
"phone": "^3.1.70",
|
"phone": "^3.1.70",
|
||||||
"posthog-js": "^1.335.5",
|
"posthog-js": "^1.336.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"query-string": "^9.3.1",
|
"query-string": "^9.3.1",
|
||||||
"raf-schd": "^4.0.3",
|
"raf-schd": "^4.0.3",
|
||||||
@@ -540,9 +540,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@apollo/client": {
|
"node_modules/@apollo/client": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.1.3.tgz",
|
||||||
"integrity": "sha512-MxlWuO94Y6TRf6+d4KfG5bCUXg5NP4s7zPKRA0PDNNa18K86zcbpHUgWKdx6wMT/5KVMeC5rsZkDqZLr/R0mFw==",
|
"integrity": "sha512-2D0eN9R0IHj9qp1RwjM1/brKqcBGldlDfY0YiP5ecCj9FtVrhOtXqMj98SZ1CA0YGDY5X+dxx32Ljh7J0VHTfA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"dist",
|
"dist",
|
||||||
@@ -4771,18 +4771,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@posthog/core": {
|
"node_modules/@posthog/core": {
|
||||||
"version": "1.14.1",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.15.0.tgz",
|
||||||
"integrity": "sha512-DtmJ1y1IDauX8yAZtIotRAYDRkgCCMLk5S9vFFRX7vufhWblQuRUOgn9WYSJrocJlZKm1aEjDzGQ0uyL7HcdLw==",
|
"integrity": "sha512-n2/Yy0+qc8xhmlcOFiYqTcGHBZuuaQjVolfFXk7yTCynzdMe8Fx1zYvPPUrbdQK5tWwXyilkzybpqhK6I7aV4Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.6"
|
"cross-spawn": "^7.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@posthog/types": {
|
"node_modules/@posthog/types": {
|
||||||
"version": "1.335.5",
|
"version": "1.336.1",
|
||||||
"resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.335.5.tgz",
|
"resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.336.1.tgz",
|
||||||
"integrity": "sha512-QYj5c8wSaXGvV4ugEN65GHD0sIXRveGiZxV4tqpyoP7YIAvAwwA0do0yNfTrEjDXucCQn25pMbCqO25hJrMi5w==",
|
"integrity": "sha512-KSGst/a/HK7GhfLSbwAy35HtU3KjDqjLtq3+PoDlGfbz9SbO0owjc6jo6hAHnMz67QTSvrn/r0xgimDO4NQ+rA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@protobufjs/aspromise": {
|
"node_modules/@protobufjs/aspromise": {
|
||||||
@@ -14974,9 +14974,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/posthog-js": {
|
"node_modules/posthog-js": {
|
||||||
"version": "1.335.5",
|
"version": "1.336.1",
|
||||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.335.5.tgz",
|
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.336.1.tgz",
|
||||||
"integrity": "sha512-1zCEdn7bc1mQ/jpd62YY8U1CyNiftIBE6uKqE2L+mjZ5aJyB2rtUAXefaTbaR/3A98tItjSej4aIa8FBN+O1fw==",
|
"integrity": "sha512-YphbVhXnImmZoALvf2oh129Cxu6IRQ9P9sWhuyY+dGe7jqt1jBp6Dg7QEK39stB4rzxmT/N3OLFcWZM7ZYQzCg==",
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
@@ -14984,8 +14984,8 @@
|
|||||||
"@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
|
"@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
|
||||||
"@opentelemetry/resources": "^2.2.0",
|
"@opentelemetry/resources": "^2.2.0",
|
||||||
"@opentelemetry/sdk-logs": "^0.208.0",
|
"@opentelemetry/sdk-logs": "^0.208.0",
|
||||||
"@posthog/core": "1.14.1",
|
"@posthog/core": "1.15.0",
|
||||||
"@posthog/types": "1.335.5",
|
"@posthog/types": "1.336.1",
|
||||||
"core-js": "^3.38.1",
|
"core-js": "^3.38.1",
|
||||||
"dompurify": "^3.3.1",
|
"dompurify": "^3.3.1",
|
||||||
"fflate": "^0.4.8",
|
"fflate": "^0.4.8",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@amplitude/analytics-browser": "^2.34.0",
|
"@amplitude/analytics-browser": "^2.34.0",
|
||||||
"@ant-design/pro-layout": "^7.22.6",
|
"@ant-design/pro-layout": "^7.22.6",
|
||||||
"@apollo/client": "^4.1.2",
|
"@apollo/client": "^4.1.3",
|
||||||
"@emotion/is-prop-valid": "^1.4.0",
|
"@emotion/is-prop-valid": "^1.4.0",
|
||||||
"@fingerprintjs/fingerprintjs": "^5.0.1",
|
"@fingerprintjs/fingerprintjs": "^5.0.1",
|
||||||
"@firebase/analytics": "^0.10.19",
|
"@firebase/analytics": "^0.10.19",
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
"normalize-url": "^8.1.1",
|
"normalize-url": "^8.1.1",
|
||||||
"object-hash": "^3.0.0",
|
"object-hash": "^3.0.0",
|
||||||
"phone": "^3.1.70",
|
"phone": "^3.1.70",
|
||||||
"posthog-js": "^1.335.5",
|
"posthog-js": "^1.336.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"query-string": "^9.3.1",
|
"query-string": "^9.3.1",
|
||||||
"raf-schd": "^4.0.3",
|
"raf-schd": "^4.0.3",
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { DownCircleFilled } from "@ant-design/icons";
|
import { DownCircleFilled } 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, Dropdown, Form, Input, Modal, Popconfirm, Popover, Select, Space } from "antd";
|
import { Button, Card, Dropdown, Form, Input, Modal, Popover, Select, Space } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import parsePhoneNumber from "libphonenumber-js";
|
import parsePhoneNumber from "libphonenumber-js";
|
||||||
import { useCallback, useMemo, useRef, useState } from "react";
|
import { useCallback, useMemo, useRef, useState } from "react";
|
||||||
|
import { HasRbacAccess } from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
@@ -20,7 +21,7 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
|
|||||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||||
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectAuthLevel, selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
@@ -28,7 +29,6 @@ import dayjs from "../../utils/day";
|
|||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import LockerWrapperComponent from "../lock-wrapper/lock-wrapper.component";
|
import LockerWrapperComponent from "../lock-wrapper/lock-wrapper.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
|
||||||
import ShareToTeamsButton from "../share-to-teams/share-to-teams.component.jsx";
|
import ShareToTeamsButton from "../share-to-teams/share-to-teams.component.jsx";
|
||||||
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||||
@@ -39,7 +39,8 @@ const EMPTY_ARRAY = Object.freeze([]);
|
|||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
currentUser: selectCurrentUser
|
currentUser: selectCurrentUser,
|
||||||
|
authLevel: selectAuthLevel
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -117,7 +118,8 @@ export function JobsDetailHeaderActions({
|
|||||||
openChatByPhone,
|
openChatByPhone,
|
||||||
setMessage,
|
setMessage,
|
||||||
setTimeTicketTaskContext,
|
setTimeTicketTaskContext,
|
||||||
setTaskUpsertContext
|
setTaskUpsertContext,
|
||||||
|
authLevel
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -129,10 +131,6 @@ export function JobsDetailHeaderActions({
|
|||||||
const jobId = job?.id;
|
const jobId = job?.id;
|
||||||
const watcherVars = useMemo(() => ({ jobid: jobId }), [jobId]);
|
const watcherVars = useMemo(() => ({ jobid: jobId }), [jobId]);
|
||||||
|
|
||||||
// Option A: coordinated Dropdown + Popconfirm open state so the menu doesn't unmount before Popconfirm renders.
|
|
||||||
const [confirmKey, setConfirmKey] = useState(null);
|
|
||||||
const confirmKeyRef = useRef(null);
|
|
||||||
|
|
||||||
const [isCancelScheduleModalVisible, setIsCancelScheduleModalVisible] = useState(false);
|
const [isCancelScheduleModalVisible, setIsCancelScheduleModalVisible] = useState(false);
|
||||||
const [insertAppointment] = useMutation(INSERT_MANUAL_APPT);
|
const [insertAppointment] = useMutation(INSERT_MANUAL_APPT);
|
||||||
const [deleteJob] = useMutation(DELETE_JOB);
|
const [deleteJob] = useMutation(DELETE_JOB);
|
||||||
@@ -150,6 +148,8 @@ export function JobsDetailHeaderActions({
|
|||||||
const devEmails = ["imex.dev", "rome.dev"];
|
const devEmails = ["imex.dev", "rome.dev"];
|
||||||
const prodEmails = ["imex.prod", "rome.prod", "imex.test", "rome.test"];
|
const prodEmails = ["imex.prod", "rome.prod", "imex.test", "rome.test"];
|
||||||
|
|
||||||
|
const canVoidJob = useMemo(() => HasRbacAccess({ authLevel, bodyshop, action: "jobs:void" }), [authLevel, bodyshop]);
|
||||||
|
|
||||||
const hasValidEmail = (emails) => emails.some((email) => userEmail.endsWith(email));
|
const hasValidEmail = (emails) => emails.some((email) => userEmail.endsWith(email));
|
||||||
const canSubmitForTesting = (isDevEnv && hasValidEmail(devEmails)) || (isProdEnv && hasValidEmail(prodEmails));
|
const canSubmitForTesting = (isDevEnv && hasValidEmail(devEmails)) || (isProdEnv && hasValidEmail(prodEmails));
|
||||||
|
|
||||||
@@ -179,83 +179,69 @@ export function JobsDetailHeaderActions({
|
|||||||
const jobInPreProduction = preProductionStatuses.includes(jobStatus);
|
const jobInPreProduction = preProductionStatuses.includes(jobStatus);
|
||||||
const jobInPostProduction = postProductionStatuses.includes(jobStatus);
|
const jobInPostProduction = postProductionStatuses.includes(jobStatus);
|
||||||
|
|
||||||
const openConfirm = useCallback((key) => {
|
const makeConfirmId = () =>
|
||||||
confirmKeyRef.current = key;
|
globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||||
setConfirmKey(key);
|
|
||||||
setDropdownOpen(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const closeConfirm = useCallback(() => {
|
const [modal, modalContextHolder] = Modal.useModal();
|
||||||
confirmKeyRef.current = null;
|
|
||||||
setConfirmKey(null);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleDropdownOpenChange = useCallback(
|
const confirmInstancesRef = useRef(new Map());
|
||||||
(nextOpen, info) => {
|
|
||||||
if (!nextOpen && info?.source === "menu" && confirmKeyRef.current) return;
|
|
||||||
setDropdownOpen(nextOpen);
|
|
||||||
if (!nextOpen) closeConfirm();
|
|
||||||
},
|
|
||||||
[closeConfirm]
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderPopconfirmMenuLabel = ({
|
const closeConfirmById = (id) => {
|
||||||
key,
|
const inst = confirmInstancesRef.current.get(id);
|
||||||
text,
|
if (inst) inst.destroy(); // hard close
|
||||||
|
confirmInstancesRef.current.delete(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openConfirmFromMenu = ({
|
||||||
|
variant = "confirm", // "confirm" | "info" | "warning"
|
||||||
title,
|
title,
|
||||||
|
content,
|
||||||
okText,
|
okText,
|
||||||
cancelText,
|
cancelText,
|
||||||
showCancel = true,
|
showCancel = true,
|
||||||
closeDropdownOnConfirm = true,
|
onOk,
|
||||||
onConfirm
|
onCancel
|
||||||
}) => (
|
}) => {
|
||||||
<Popconfirm
|
// close the dropdown immediately; confirm dialog is separate
|
||||||
title={title}
|
setDropdownOpen(false);
|
||||||
okText={okText}
|
|
||||||
cancelText={cancelText}
|
|
||||||
showCancel={showCancel}
|
|
||||||
open={confirmKey === key}
|
|
||||||
onOpenChange={(nextOpen) => {
|
|
||||||
if (nextOpen) openConfirm(key);
|
|
||||||
else closeConfirm();
|
|
||||||
}}
|
|
||||||
onConfirm={(e) => {
|
|
||||||
e?.stopPropagation?.();
|
|
||||||
closeConfirm();
|
|
||||||
|
|
||||||
// Critical: for informational popconfirms, keep the dropdown open so the Popconfirm can cleanly close.
|
const id = makeConfirmId();
|
||||||
if (closeDropdownOnConfirm) {
|
|
||||||
setDropdownOpen(false);
|
const openFn = variant === "info" ? modal.info : variant === "warning" ? modal.warning : modal.confirm;
|
||||||
|
|
||||||
|
const inst = openFn({
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
okText,
|
||||||
|
cancelText,
|
||||||
|
centered: true,
|
||||||
|
maskClosable: false,
|
||||||
|
onCancel: () => {
|
||||||
|
closeConfirmById(id);
|
||||||
|
onCancel?.();
|
||||||
|
},
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await onOk?.();
|
||||||
|
} finally {
|
||||||
|
closeConfirmById(id);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
...(showCancel ? {} : { okCancel: false })
|
||||||
|
});
|
||||||
|
|
||||||
onConfirm?.(e);
|
confirmInstancesRef.current.set(id, inst);
|
||||||
}}
|
return id;
|
||||||
onCancel={(e) => {
|
};
|
||||||
e?.stopPropagation?.();
|
|
||||||
closeConfirm();
|
const handleDropdownOpenChange = useCallback((nextOpen) => {
|
||||||
// Keep dropdown open on cancel so the user can continue using the menu.
|
setDropdownOpen(nextOpen);
|
||||||
}}
|
}, []);
|
||||||
getPopupContainer={() => document.body}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
openConfirm(key);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{text}
|
|
||||||
</div>
|
|
||||||
</Popconfirm>
|
|
||||||
);
|
|
||||||
|
|
||||||
// Function to show modal
|
|
||||||
const showCancelScheduleModal = () => {
|
const showCancelScheduleModal = () => {
|
||||||
setIsCancelScheduleModalVisible(true);
|
setIsCancelScheduleModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to handle Cancel
|
|
||||||
const handleCancelScheduleModalCancel = () => {
|
const handleCancelScheduleModalCancel = () => {
|
||||||
setIsCancelScheduleModalVisible(false);
|
setIsCancelScheduleModalVisible(false);
|
||||||
};
|
};
|
||||||
@@ -476,6 +462,11 @@ export function JobsDetailHeaderActions({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleVoidJob = async () => {
|
const handleVoidJob = async () => {
|
||||||
|
if (!canVoidJob) {
|
||||||
|
notification.error({ title: t("general.messages.rbacunauth") });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//delete the job.
|
//delete the job.
|
||||||
const result = await voidJob({
|
const result = await voidJob({
|
||||||
variables: {
|
variables: {
|
||||||
@@ -964,26 +955,26 @@ export function JobsDetailHeaderActions({
|
|||||||
{
|
{
|
||||||
key: "duplicate",
|
key: "duplicate",
|
||||||
id: "job-actions-duplicate",
|
id: "job-actions-duplicate",
|
||||||
label: renderPopconfirmMenuLabel({
|
label: t("menus.jobsactions.duplicate"),
|
||||||
key: "confirm-duplicate",
|
onClick: () =>
|
||||||
text: t("menus.jobsactions.duplicate"),
|
openConfirmFromMenu({
|
||||||
title: t("jobs.labels.duplicateconfirm"),
|
title: t("jobs.labels.duplicateconfirm"),
|
||||||
okText: t("general.labels.yes"),
|
okText: t("general.labels.yes"),
|
||||||
cancelText: t("general.labels.no"),
|
cancelText: t("general.labels.no"),
|
||||||
onConfirm: handleDuplicate
|
onOk: handleDuplicate
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "duplicatenolines",
|
key: "duplicatenolines",
|
||||||
id: "job-actions-duplicatenolines",
|
id: "job-actions-duplicatenolines",
|
||||||
label: renderPopconfirmMenuLabel({
|
label: t("menus.jobsactions.duplicatenolines"),
|
||||||
key: "confirm-duplicate-nolines",
|
onClick: () =>
|
||||||
text: t("menus.jobsactions.duplicatenolines"),
|
openConfirmFromMenu({
|
||||||
title: t("jobs.labels.duplicateconfirm"),
|
title: t("jobs.labels.duplicateconfirm"),
|
||||||
okText: t("general.labels.yes"),
|
okText: t("general.labels.yes"),
|
||||||
cancelText: t("general.labels.no"),
|
cancelText: t("general.labels.no"),
|
||||||
onConfirm: handleDuplicateConfirm
|
onOk: handleDuplicateConfirm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -1156,26 +1147,25 @@ export function JobsDetailHeaderActions({
|
|||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "deletejob",
|
key: "deletejob",
|
||||||
id: "job-actions-deletejob",
|
id: "job-actions-deletejob",
|
||||||
label:
|
label: t("menus.jobsactions.deletejob"),
|
||||||
jobWatchersCount === 0
|
onClick: () => {
|
||||||
? renderPopconfirmMenuLabel({
|
if (jobWatchersCount === 0) {
|
||||||
key: "confirm-deletejob",
|
openConfirmFromMenu({
|
||||||
text: t("menus.jobsactions.deletejob"),
|
title: t("jobs.labels.deleteconfirm"),
|
||||||
title: t("jobs.labels.deleteconfirm"),
|
okText: t("general.labels.yes"),
|
||||||
okText: t("general.labels.yes"),
|
cancelText: t("general.labels.no"),
|
||||||
cancelText: t("general.labels.no"),
|
onOk: handleDeleteJob
|
||||||
onConfirm: handleDeleteJob
|
});
|
||||||
})
|
} else {
|
||||||
: renderPopconfirmMenuLabel({
|
// informational "OK only"
|
||||||
key: "confirm-deletejob-watchers",
|
openConfirmFromMenu({
|
||||||
text: t("menus.jobsactions.deletejob"),
|
variant: "info",
|
||||||
title: t("jobs.labels.deletewatchers"),
|
title: t("jobs.labels.deletewatchers"),
|
||||||
showCancel: false,
|
okText: t("general.actions.ok") ?? "OK",
|
||||||
closeDropdownOnConfirm: false, // <-- FIX: keep dropdown mounted so Popconfirm can close cleanly
|
showCancel: false
|
||||||
onConfirm: () => {
|
});
|
||||||
// informational confirm only
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,22 +1178,18 @@ export function JobsDetailHeaderActions({
|
|||||||
label: t("appointments.labels.manualevent")
|
label: t("appointments.labels.manualevent")
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!jobRO && job.converted) {
|
if (!jobRO && job.converted && canVoidJob) {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "voidjob",
|
key: "voidjob",
|
||||||
id: "job-actions-voidjob",
|
id: "job-actions-voidjob",
|
||||||
label: (
|
label: t("menus.jobsactions.void"),
|
||||||
<RbacWrapper action="jobs:void" noauth>
|
onClick: () =>
|
||||||
{renderPopconfirmMenuLabel({
|
openConfirmFromMenu({
|
||||||
key: "confirm-voidjob",
|
title: t("jobs.labels.voidjob"),
|
||||||
text: t("menus.jobsactions.void"),
|
okText: t("general.labels.yes"),
|
||||||
title: t("jobs.labels.voidjob"),
|
cancelText: t("general.labels.no"),
|
||||||
okText: t("general.labels.yes"),
|
onOk: handleVoidJob
|
||||||
cancelText: t("general.labels.no"),
|
})
|
||||||
onConfirm: handleVoidJob
|
|
||||||
})}
|
|
||||||
</RbacWrapper>
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1235,6 +1221,7 @@ export function JobsDetailHeaderActions({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{modalContextHolder}
|
||||||
<Modal
|
<Modal
|
||||||
title={t("menus.jobsactions.cancelallappointments")}
|
title={t("menus.jobsactions.cancelallappointments")}
|
||||||
open={isCancelScheduleModalVisible}
|
open={isCancelScheduleModalVisible}
|
||||||
|
|||||||
654
package-lock.json
generated
654
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -18,23 +18,23 @@
|
|||||||
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
|
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-cloudwatch-logs": "^3.975.0",
|
"@aws-sdk/client-cloudwatch-logs": "^3.978.0",
|
||||||
"@aws-sdk/client-elasticache": "^3.975.0",
|
"@aws-sdk/client-elasticache": "^3.978.0",
|
||||||
"@aws-sdk/client-s3": "^3.975.0",
|
"@aws-sdk/client-s3": "^3.978.0",
|
||||||
"@aws-sdk/client-secrets-manager": "^3.975.0",
|
"@aws-sdk/client-secrets-manager": "^3.978.0",
|
||||||
"@aws-sdk/client-ses": "^3.975.0",
|
"@aws-sdk/client-ses": "^3.978.0",
|
||||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
"@aws-sdk/credential-provider-node": "^3.972.2",
|
||||||
"@aws-sdk/lib-storage": "^3.975.0",
|
"@aws-sdk/lib-storage": "^3.978.0",
|
||||||
"@aws-sdk/s3-request-presigner": "^3.975.0",
|
"@aws-sdk/s3-request-presigner": "^3.978.0",
|
||||||
"@opensearch-project/opensearch": "^2.13.0",
|
"@opensearch-project/opensearch": "^2.13.0",
|
||||||
"@socket.io/admin-ui": "^0.5.1",
|
"@socket.io/admin-ui": "^0.5.1",
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"aws4": "^1.13.2",
|
"aws4": "^1.13.2",
|
||||||
"axios": "^1.13.3",
|
"axios": "^1.13.4",
|
||||||
"axios-curlirize": "^2.0.0",
|
"axios-curlirize": "^2.0.0",
|
||||||
"better-queue": "^3.8.12",
|
"better-queue": "^3.8.12",
|
||||||
"bullmq": "^5.67.1",
|
"bullmq": "^5.67.2",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"cloudinary": "^2.9.0",
|
"cloudinary": "^2.9.0",
|
||||||
"compression": "^1.8.1",
|
"compression": "^1.8.1",
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
"recursive-diff": "^1.0.9",
|
"recursive-diff": "^1.0.9",
|
||||||
"rimraf": "^6.1.2",
|
"rimraf": "^6.1.2",
|
||||||
"skia-canvas": "^3.0.8",
|
"skia-canvas": "^3.0.8",
|
||||||
"soap": "^1.6.3",
|
"soap": "^1.6.4",
|
||||||
"socket.io": "^4.8.3",
|
"socket.io": "^4.8.3",
|
||||||
"socket.io-adapter": "^2.5.6",
|
"socket.io-adapter": "^2.5.6",
|
||||||
"ssh2-sftp-client": "^11.0.0",
|
"ssh2-sftp-client": "^11.0.0",
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"globals": "^17.1.0",
|
"globals": "^17.2.0",
|
||||||
"mock-require": "^3.0.3",
|
"mock-require": "^3.0.3",
|
||||||
"p-limit": "^3.1.0",
|
"p-limit": "^3.1.0",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user