Compare commits
21 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52c9b9a290 | ||
|
|
4a1b1fe905 | ||
|
|
5f81ec2099 | ||
|
|
147977be58 | ||
|
|
4dfda4b371 | ||
|
|
02feba2804 | ||
|
|
a9fb77189e | ||
|
|
3bacad69e3 | ||
|
|
6415b302dc | ||
|
|
d40dd649e2 | ||
|
|
35366eda22 | ||
|
|
9a53896aa4 | ||
|
|
278765d019 | ||
|
|
6fd5fc8f66 | ||
|
|
346a6e69c7 | ||
|
|
5d5fa8fead | ||
|
|
30dae4e365 | ||
|
|
f6899f744b | ||
|
|
cca23a5b11 | ||
|
|
2acddcb9ac | ||
|
|
ff57592c12 |
@@ -18,4 +18,3 @@ VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
|
||||
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
||||
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
|
||||
VITE_APP_AMP_KEY=6228a598e57cd66875cfd41604f1f891
|
||||
VITE_ENABLE_COMPILER_IN_DEV=1
|
||||
|
||||
@@ -20,4 +20,3 @@ VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
|
||||
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
||||
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
|
||||
VITE_APP_AMP_KEY=46b1193a867d4e3131ae4c3a64a3fc78
|
||||
VITE_ENABLE_COMPILER_IN_DEV=1
|
||||
|
||||
3
client/.gitignore
vendored
3
client/.gitignore
vendored
@@ -13,3 +13,6 @@ playwright/.cache/
|
||||
# Sentry Config File
|
||||
.sentryclirc
|
||||
/dev-dist
|
||||
|
||||
# Local environment overrides (not version controlled)
|
||||
.env.development.local.overrides
|
||||
|
||||
915
client/package-lock.json
generated
915
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,9 @@
|
||||
"private": true,
|
||||
"proxy": "http://localhost:4000",
|
||||
"dependencies": {
|
||||
"@amplitude/analytics-browser": "^2.33.4",
|
||||
"@amplitude/analytics-browser": "^2.34.0",
|
||||
"@ant-design/pro-layout": "^7.22.6",
|
||||
"@apollo/client": "^4.1.1",
|
||||
"@apollo/client": "^4.1.2",
|
||||
"@emotion/is-prop-valid": "^1.4.0",
|
||||
"@fingerprintjs/fingerprintjs": "^5.0.1",
|
||||
"@firebase/analytics": "^0.10.19",
|
||||
@@ -21,14 +21,14 @@
|
||||
"@jsreport/browser-client": "^3.1.0",
|
||||
"@reduxjs/toolkit": "^2.11.2",
|
||||
"@sentry/cli": "^3.1.0",
|
||||
"@sentry/react": "^10.35.0",
|
||||
"@sentry/vite-plugin": "^4.7.0",
|
||||
"@sentry/react": "^10.37.0",
|
||||
"@sentry/vite-plugin": "^4.8.0",
|
||||
"@splitsoftware/splitio-react": "^2.6.1",
|
||||
"@tanem/react-nprogress": "^5.0.56",
|
||||
"antd": "^6.2.1",
|
||||
"@tanem/react-nprogress": "^5.0.58",
|
||||
"antd": "^6.2.2",
|
||||
"apollo-link-logger": "^3.0.0",
|
||||
"autosize": "^6.0.1",
|
||||
"axios": "^1.13.2",
|
||||
"axios": "^1.13.4",
|
||||
"classnames": "^2.5.1",
|
||||
"css-box-model": "^1.2.1",
|
||||
"dayjs": "^1.11.19",
|
||||
@@ -38,31 +38,31 @@
|
||||
"env-cmd": "^11.0.0",
|
||||
"exifr": "^7.1.3",
|
||||
"graphql": "^16.12.0",
|
||||
"graphql-ws": "^6.0.6",
|
||||
"graphql-ws": "^6.0.7",
|
||||
"i18next": "^25.8.0",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"libphonenumber-js": "^1.12.34",
|
||||
"lightningcss": "^1.31.0",
|
||||
"logrocket": "^11.0.0",
|
||||
"libphonenumber-js": "^1.12.35",
|
||||
"lightningcss": "^1.31.1",
|
||||
"logrocket": "^12.0.0",
|
||||
"markerjs2": "^2.32.7",
|
||||
"memoize-one": "^6.0.0",
|
||||
"normalize-url": "^8.1.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"phone": "^3.1.69",
|
||||
"posthog-js": "^1.335.0",
|
||||
"phone": "^3.1.70",
|
||||
"posthog-js": "^1.335.5",
|
||||
"prop-types": "^15.8.1",
|
||||
"query-string": "^9.3.1",
|
||||
"raf-schd": "^4.0.3",
|
||||
"react": "^19.2.3",
|
||||
"react": "^19.2.4",
|
||||
"react-big-calendar": "^1.19.4",
|
||||
"react-color": "^2.19.3",
|
||||
"react-cookie": "^8.0.1",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-drag-listview": "^2.0.0",
|
||||
"react-grid-gallery": "^1.0.1",
|
||||
"react-grid-layout": "^2.2.2",
|
||||
"react-i18next": "^16.5.3",
|
||||
"react-i18next": "^16.5.4",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-image-lightbox": "^5.1.4",
|
||||
"react-markdown": "^10.1.0",
|
||||
@@ -71,10 +71,10 @@
|
||||
"react-product-fruits": "^2.2.62",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-resizable": "^3.1.3",
|
||||
"react-router-dom": "^7.12.0",
|
||||
"react-router-dom": "^7.13.0",
|
||||
"react-sticky": "^6.0.3",
|
||||
"react-virtuoso": "^4.18.1",
|
||||
"recharts": "^3.6.0",
|
||||
"recharts": "^3.7.0",
|
||||
"redux": "^5.0.1",
|
||||
"redux-actions": "^3.0.3",
|
||||
"redux-persist": "^6.0.0",
|
||||
@@ -82,7 +82,7 @@
|
||||
"redux-state-sync": "^3.1.4",
|
||||
"reselect": "^5.1.1",
|
||||
"rxjs": "^7.8.2",
|
||||
"sass": "^1.97.2",
|
||||
"sass": "^1.97.3",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"styled-components": "^6.3.8",
|
||||
"vite-plugin-ejs": "^1.7.0",
|
||||
@@ -92,15 +92,17 @@
|
||||
"postinstall": "echo 'when updating react-big-calendar, remember to check to localizer in the calendar wrapper'",
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "vite",
|
||||
"build": "dotenvx run --env-file=.env.development.imex -- vite build",
|
||||
"start:imex": "dotenvx run --env-file=.env.development.imex -- vite",
|
||||
"start:rome": "dotenvx run --env-file=.env.development.rome -- vite",
|
||||
"preview:imex": "dotenvx run --env-file=.env.development.imex -- vite preview",
|
||||
"preview:rome": "dotenvx run --env-file=.env.development.rome -- vite preview",
|
||||
"build:test:imex": "env-cmd -f .env.test.imex -- npm run build",
|
||||
"build:test:rome": "env-cmd -f .env.test.rome -- npm run build",
|
||||
"build:production:imex": "env-cmd -f .env.production.imex -- npm run build",
|
||||
"build:production:rome": "env-cmd -f .env.production.rome -- npm run build",
|
||||
"build": "vite build",
|
||||
"build:dev:imex": "dotenvx run --env-file=.env.development.imex --env-file=.env.development.local.overrides -- vite build",
|
||||
"build:dev:rome": "dotenvx run --env-file=.env.development.rome --env-file=.env.development.local.overrides -- vite build",
|
||||
"build:test:imex": "dotenvx run --env-file=.env.test.imex -- vite build",
|
||||
"build:test:rome": "dotenvx run --env-file=.env.test.rome -- vite build",
|
||||
"build:production:imex": "env-cmd -f .env.production.imex vite build",
|
||||
"build:production:rome": "env-cmd -f .env.production.rome vite build",
|
||||
"start:imex": "dotenvx run --env-file=.env.development.imex --env-file=.env.development.local.overrides -- vite",
|
||||
"start:rome": "dotenvx run --env-file=.env.development.rome --env-file=.env.development.local.overrides -- vite",
|
||||
"preview:imex": "dotenvx run --env-file=.env.development.imex --env-file=.env.development.local.overrides -- vite preview",
|
||||
"preview:rome": "dotenvx run --env-file=.env.development.rome --env-file=.env.development.local.overrides -- vite preview",
|
||||
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular .",
|
||||
"eulaize": "node src/utils/eulaize.js",
|
||||
"test:unit": "vitest run",
|
||||
@@ -151,7 +153,7 @@
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
||||
"globals": "^17.1.0",
|
||||
"globals": "^17.2.0",
|
||||
"jsdom": "^27.4.0",
|
||||
"memfs": "^4.56.10",
|
||||
"os-browserify": "^0.3.0",
|
||||
|
||||
@@ -100,14 +100,7 @@ export function App({
|
||||
if (currentUser.authorized && bodyshop) {
|
||||
client.setAttribute("imexshopid", bodyshop.imexshopid);
|
||||
|
||||
if (
|
||||
client.getTreatment("LogRocket_Tracking") === "on" ||
|
||||
window.location.hostname ===
|
||||
InstanceRenderMgr({
|
||||
imex: "beta.imex.online",
|
||||
rome: "beta.romeonline.io"
|
||||
})
|
||||
) {
|
||||
if (client.getTreatment("LogRocket_Tracking") === "on") {
|
||||
console.log("LR Start");
|
||||
LogRocket.init(
|
||||
InstanceRenderMgr({
|
||||
|
||||
@@ -169,7 +169,13 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
|
||||
refetch={refetch}
|
||||
/>
|
||||
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && <QboAuthorizeComponent />}
|
||||
<Input value={state.search} onChange={handleSearch} placeholder={t("general.labels.search")} allowClear />
|
||||
<Input
|
||||
value={state.search}
|
||||
onChange={handleSearch}
|
||||
placeholder={t("general.labels.search")}
|
||||
allowClear
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -182,7 +182,13 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
|
||||
refetch={refetch}
|
||||
/>
|
||||
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && <QboAuthorizeComponent />}
|
||||
<Input value={state.search} onChange={handleSearch} placeholder={t("general.labels.search")} allowClear />
|
||||
<Input
|
||||
value={state.search}
|
||||
onChange={handleSearch}
|
||||
placeholder={t("general.labels.search")}
|
||||
allowClear
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -204,6 +204,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
|
||||
onChange={handleSearch}
|
||||
placeholder={t("general.labels.search")}
|
||||
allowClear
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ export function BillsListTableComponent({
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -108,9 +108,12 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
|
||||
hasLoadedConversationsOnceRef.current = true;
|
||||
|
||||
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 () => {
|
||||
try {
|
||||
|
||||
@@ -99,6 +99,7 @@ export default function ContractsCarsComponent({ loading, data, selectedCarId, h
|
||||
placeholder={t("general.labels.search")}
|
||||
value={state.search}
|
||||
onChange={(e) => setState({ ...state, search: e.target.value })}
|
||||
enterButton
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -123,6 +123,7 @@ export default function ContractsJobsComponent({ loading, data, selectedJob, han
|
||||
placeholder={t("general.labels.search")}
|
||||
value={state.search}
|
||||
onChange={(e) => setState({ ...state, search: e.target.value })}
|
||||
enterButton
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -164,6 +164,7 @@ export function ContractsList({ bodyshop, loading, contracts, refetch, total, se
|
||||
const updatedSearch = { ...search, search: value };
|
||||
history({ search: queryString.stringify(updatedSearch) });
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ export function DmsCdkVehicles({ form, job }) {
|
||||
<Input.Search
|
||||
onSearch={(val) => callSearch({ variables: { search: val } })}
|
||||
placeholder={t("general.labels.search")}
|
||||
enterButton
|
||||
/>
|
||||
)}
|
||||
columns={columns}
|
||||
|
||||
@@ -76,6 +76,7 @@ export default function JobCostingPartsTable({ data, summaryData }) {
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
|
||||
@@ -50,7 +50,8 @@ export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines, tec
|
||||
config: {
|
||||
status: bodyshop.md_ro_statuses.default_open,
|
||||
bodyshopid: bodyshop.id,
|
||||
useremail: currentUser.email
|
||||
useremail: currentUser.email,
|
||||
timezone: bodyshop.timezone
|
||||
},
|
||||
currentUser
|
||||
});
|
||||
|
||||
@@ -682,6 +682,7 @@ export function JobLinesComponent({
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ export function JobsAvailableScan({ partnerVersion, refetch }) {
|
||||
onChange={(e) => {
|
||||
setSearchText(e.currentTarget.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -196,13 +196,16 @@ export function JobsAvailableComponent({ bodyshop, loading, data, refetch, addJo
|
||||
>
|
||||
{t("general.actions.deleteall")}
|
||||
</Button>
|
||||
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.currentTarget.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
<Link to="/manage/jobs/new">
|
||||
<Button>{t("jobs.actions.manualnew")}</Button>
|
||||
</Link>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function JobsCreateVehicleInfoPredefined({ disabled, form }) {
|
||||
<div>
|
||||
<Table
|
||||
size="small"
|
||||
title={() => <Input.Search onSearch={(value) => setSearch(value)} />}
|
||||
title={() => <Input.Search onSearch={(value) => setSearch(value)} enterButton/>}
|
||||
dataSource={filteredPredefinedVehicles}
|
||||
columns={[
|
||||
{
|
||||
|
||||
@@ -264,7 +264,7 @@ export function JobsDetailHeaderActions({
|
||||
DuplicateJob({
|
||||
apolloClient: client,
|
||||
jobId: job.id,
|
||||
config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
|
||||
config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported, timezone: bodyshop.timezone },
|
||||
completionCallback: (newJobId) => {
|
||||
history(`/manage/jobs/${newJobId}`);
|
||||
notification.success({
|
||||
@@ -279,7 +279,7 @@ export function JobsDetailHeaderActions({
|
||||
DuplicateJob({
|
||||
apolloClient: client,
|
||||
jobId: job.id,
|
||||
config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
|
||||
config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported, timezone: bodyshop.timezone },
|
||||
completionCallback: (newJobId) => {
|
||||
history(`/manage/jobs/${newJobId}`);
|
||||
notification.success({
|
||||
|
||||
@@ -15,7 +15,7 @@ export default async function DuplicateJob({
|
||||
}) {
|
||||
logImEXEvent("job_duplicate");
|
||||
|
||||
const { defaultOpenStatus } = config;
|
||||
const { defaultOpenStatus, timezone } = config;
|
||||
//get a list of all fields on the job
|
||||
const res = await apolloClient.query({
|
||||
query: QUERY_JOB_FOR_DUPE,
|
||||
@@ -31,9 +31,12 @@ export default async function DuplicateJob({
|
||||
delete existingJob.updatedat;
|
||||
delete existingJob.cieca_stl;
|
||||
delete existingJob.cieca_ttl;
|
||||
!keepJobLines && delete existingJob.clm_total;
|
||||
|
||||
const newJob = {
|
||||
...existingJob,
|
||||
date_estimated: dayjs().tz(timezone, false).format("YYYY-MM-DD"),
|
||||
date_open: dayjs(),
|
||||
status: defaultOpenStatus
|
||||
};
|
||||
|
||||
@@ -70,7 +73,7 @@ export default async function DuplicateJob({
|
||||
export async function CreateIouForJob({ apolloClient, jobId, config, jobLinesToKeep, currentUser }) {
|
||||
logImEXEvent("job_create_iou");
|
||||
|
||||
const { status } = config;
|
||||
const { status, timezone } = config;
|
||||
//get a list of all fields on the job
|
||||
const res = await apolloClient.query({
|
||||
query: QUERY_JOB_FOR_DUPE,
|
||||
@@ -88,10 +91,10 @@ export async function CreateIouForJob({ apolloClient, jobId, config, jobLinesToK
|
||||
|
||||
const newJob = {
|
||||
...existingJob,
|
||||
|
||||
converted: true,
|
||||
status: status,
|
||||
iouparent: jobId,
|
||||
date_estimated: dayjs().tz(timezone, false).format("YYYY-MM-DD"),
|
||||
date_open: dayjs(),
|
||||
audit_trails: {
|
||||
data: [
|
||||
|
||||
@@ -143,7 +143,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
label={t("jobs.fields.comment")}
|
||||
styles={{ value: { overflow: "hidden", textOverflow: "ellipsis" } }}
|
||||
>
|
||||
<ProductionListColumnComment record={job} />
|
||||
<ProductionListColumnComment record={job} usePortal={true} />
|
||||
</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>
|
||||
@@ -176,7 +176,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
</DataLabel>
|
||||
)}
|
||||
<DataLabel label={t("jobs.fields.production_vars.note")}>
|
||||
<ProductionListColumnProductionNote record={job} />
|
||||
<ProductionListColumnProductionNote record={job} usePortal={true} />
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.estimate_sent_approval")}>
|
||||
<Space>
|
||||
|
||||
@@ -48,6 +48,7 @@ export default function OwnerFindModalContainer({
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onSearch={(val) => callSearchowners({ variables: { search: val.trim() } })}
|
||||
enterButton
|
||||
/>
|
||||
<OwnerFindModalComponent
|
||||
selectedOwner={selectedOwner}
|
||||
|
||||
@@ -101,6 +101,7 @@ export function PartDispatchTableComponent({ bodyshop, job, billsQuery }) {
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@ export function PartsOrderListTableComponent({
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -269,6 +269,7 @@ export function PartsQueueListComponent({ bodyshop }) {
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("titles.bc.parts-queue")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
|
||||
@@ -129,6 +129,7 @@ function PhoneNumberConsentList({ bodyshop }) {
|
||||
placeholder={t("general.labels.search")}
|
||||
onSearch={(value) => setSearch(value)}
|
||||
style={{ marginBottom: 16 }}
|
||||
enterButton
|
||||
/>
|
||||
|
||||
<Table
|
||||
|
||||
@@ -45,6 +45,7 @@ export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading })
|
||||
setFilter({ ...filter, search: e.target.value });
|
||||
logImEXEvent("visual_board_filter_search", { search: e.target.value });
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
<EmployeeSearchSelectComponent
|
||||
style={{ minWidth: "20rem" }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { DragDropContext } from "../dnd/lib";
|
||||
import PropTypes from "prop-types";
|
||||
@@ -7,6 +7,7 @@ import { PopoverWrapper } from "react-popopo";
|
||||
import * as actions from "../../../../redux/trello/trello.actions.js";
|
||||
import { BoardWrapper } from "../styles/Base.js";
|
||||
import ProductionStatistics from "../../production-board-kanban.statistics.jsx";
|
||||
import isEqual from "lodash/isEqual";
|
||||
|
||||
const useDragMap = () => {
|
||||
const dragMapRef = useRef(new Map());
|
||||
@@ -47,8 +48,9 @@ const BoardContainer = ({
|
||||
const dispatch = useDispatch();
|
||||
const currentReducerData = useSelector((state) => (state.trello.lanes ? state.trello : {}));
|
||||
const { setDragTime, getLastDragTime } = useDragMap();
|
||||
const previousDataRef = useRef(null);
|
||||
|
||||
const wireEventBus = () => {
|
||||
const wireEventBus = useCallback(() => {
|
||||
const eventBus = {
|
||||
publish: (event) => {
|
||||
switch (event.type) {
|
||||
@@ -68,14 +70,17 @@ const BoardContainer = ({
|
||||
}
|
||||
};
|
||||
eventBusHandle(eventBus);
|
||||
};
|
||||
}, [dispatch, eventBusHandle]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(actions.loadBoard(data));
|
||||
if (eventBusHandle) {
|
||||
wireEventBus();
|
||||
if (!isEqual(previousDataRef.current, data)) {
|
||||
previousDataRef.current = data;
|
||||
dispatch(actions.loadBoard(data));
|
||||
if (eventBusHandle) {
|
||||
wireEventBus();
|
||||
}
|
||||
}
|
||||
}, [data, eventBusHandle, dispatch]);
|
||||
}, [data, wireEventBus, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
onDataChange(currentReducerData);
|
||||
@@ -153,12 +158,17 @@ const BoardContainer = ({
|
||||
}
|
||||
};
|
||||
|
||||
const boardKey = useMemo(() => {
|
||||
// React Compiler: Generate stable key from lane IDs
|
||||
return currentReducerData.lanes?.map((l) => l.id).join("-") || "empty";
|
||||
}, [currentReducerData.lanes]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ProductionStatistics data={queryData} reducerData={currentReducerData} cardSettings={cardSettings} />
|
||||
<PopoverWrapper>
|
||||
<BoardWrapper orientation={orientation}>
|
||||
<DragDropContext onDragEnd={onLaneDrag} onDragStart={onDragStart} contextId="production-board">
|
||||
<DragDropContext key={boardKey} onDragEnd={onLaneDrag} onDragStart={onDragStart} contextId="production-board">
|
||||
{currentReducerData.lanes.map((lane, index) => (
|
||||
<Lane
|
||||
key={lane.id}
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import createRegistry from "./create-registry";
|
||||
|
||||
export default function useRegistry() {
|
||||
const registry = useMemo(createRegistry, []);
|
||||
const cleanupScheduledRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Cancel any scheduled cleanup when component mounts
|
||||
// This handles React StrictMode double-mounting
|
||||
cleanupScheduledRef.current = false;
|
||||
|
||||
return function unmount() {
|
||||
// Mark cleanup as scheduled
|
||||
cleanupScheduledRef.current = true;
|
||||
|
||||
// clean up the registry to avoid any leaks
|
||||
// doing it after an animation frame so that other things unmounting
|
||||
// can continue to interact with the registry
|
||||
requestAnimationFrame(registry.clean);
|
||||
requestAnimationFrame(() => {
|
||||
// Only clean if still scheduled (not cancelled by remount)
|
||||
if (cleanupScheduledRef.current) {
|
||||
registry.clean();
|
||||
}
|
||||
});
|
||||
};
|
||||
}, [registry]);
|
||||
return registry;
|
||||
|
||||
@@ -171,6 +171,7 @@ export default function useDroppablePublisher(args) {
|
||||
}
|
||||
registry.droppable.unregister(entry);
|
||||
};
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
}, [callbacks, descriptor, dragStopped, entry, marshal, registry.droppable]);
|
||||
|
||||
// update is enabled with the marshal
|
||||
|
||||
@@ -63,10 +63,10 @@ const ProductionListColumnAlert = ({ id, productionVars, refetch, insertAuditTra
|
||||
okText={t("general.labels.yes")}
|
||||
cancelText={t("general.labels.no")}
|
||||
>
|
||||
<Button className="production-alert" icon={<ExclamationCircleFilled />} />
|
||||
<Button className="production-alert" icon={<ExclamationCircleFilled />} style={{ cursor: "pointer" }} />
|
||||
</Popconfirm>
|
||||
) : (
|
||||
<Button className="muted-button" icon={<PlusCircleFilled />} onClick={handleAlertToggle} />
|
||||
<Button className="muted-button" icon={<PlusCircleFilled />} onClick={handleAlertToggle} style={{ cursor: "pointer" }} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function ProductionListColumnBodyPriority({ record }) {
|
||||
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { FaRegStickyNote } from "react-icons/fa";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
export default function ProductionListColumnComment({ record }) {
|
||||
export default function ProductionListColumnComment({ record, usePortal = false }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [note, setNote] = useState(record.comment || "");
|
||||
@@ -43,16 +43,20 @@ export default function ProductionListColumnComment({ record }) {
|
||||
};
|
||||
|
||||
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
|
||||
id={`job-comment-${record.id}`}
|
||||
name="comment"
|
||||
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">
|
||||
@@ -63,7 +67,15 @@ export default function ProductionListColumnComment({ record }) {
|
||||
);
|
||||
|
||||
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
|
||||
style={{
|
||||
width: "100%",
|
||||
|
||||
@@ -64,6 +64,7 @@ const productionListColumnsData = ({ technician, state, activeStatuses, data, bo
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<div
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
store.dispatch(
|
||||
setModalContext({
|
||||
|
||||
@@ -86,7 +86,8 @@ export default function ProductionListDate({ record, field, time, pastIndicator
|
||||
<div
|
||||
onClick={() => setOpen(true)}
|
||||
style={{
|
||||
height: "19px"
|
||||
height: "19px",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function ProductionListColumnDetailPriority({ record }) {
|
||||
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,13 +144,13 @@ export function ProductionListEmpAssignment({ insertAuditTrail, bodyshop, record
|
||||
<Popover destroyOnHidden content={popContent} open={visibility}>
|
||||
<Spin spinning={loading}>
|
||||
{record[type] ? (
|
||||
<div>
|
||||
<div style={{ cursor: "pointer" }}>
|
||||
<span>{`${theEmployee?.first_name || ""} ${theEmployee?.last_name || ""}`}</span>
|
||||
<DeleteFilled style={iconStyle} onClick={() => handleRemove(type)} />
|
||||
</div>
|
||||
) : (
|
||||
<PlusCircleFilled
|
||||
style={iconStyle}
|
||||
style={{ ...iconStyle, cursor: "pointer" }}
|
||||
className="muted-button"
|
||||
onClick={() => {
|
||||
setAssignment({ operation: type });
|
||||
|
||||
@@ -124,7 +124,8 @@ export function ProductionLastContacted({ currentUser, record }) {
|
||||
<div
|
||||
onClick={() => setOpen(true)}
|
||||
style={{
|
||||
height: "19px"
|
||||
height: "19px",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
<DateFormatter bordered={false}>{record.date_last_contacted}</DateFormatter>
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function ProductionListColumnPaintPriority({ record }) {
|
||||
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
setNoteUpsertContext: (context) => dispatch(setModalContext({ context: context, modal: "noteUpsert" }))
|
||||
});
|
||||
|
||||
function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
function ProductionListColumnProductionNote({ record, setNoteUpsertContext, usePortal = false }) {
|
||||
const { t } = useTranslation();
|
||||
const [note, setNote] = useState(record.production_vars?.note || "");
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -59,16 +59,20 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
);
|
||||
|
||||
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
|
||||
id={`job-production-note-${record.id}`}
|
||||
name="production_note"
|
||||
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">
|
||||
@@ -92,7 +96,15 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
);
|
||||
|
||||
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
|
||||
style={{
|
||||
width: "100%",
|
||||
|
||||
@@ -44,6 +44,7 @@ export default function ProfileShopsComponent({ loading, data, updateActiveShop
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
allowClear
|
||||
placeholder={t("general.labels.search")}
|
||||
enterButton
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -145,7 +145,7 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
||||
return (
|
||||
<div className="report-center-modal">
|
||||
<Form onFinish={handleFinish} autoComplete={"off"} layout="vertical" form={form}>
|
||||
<Input.Search onChange={(e) => setSearch(e.target.value)} value={search} />
|
||||
<Input.Search onChange={(e) => setSearch(e.target.value)} value={search} enterButton />
|
||||
<Form.Item name="defaultSorters" hidden>
|
||||
<Input type="hidden" />
|
||||
</Form.Item>
|
||||
|
||||
@@ -3,8 +3,7 @@ import { Button, Form, Input, InputNumber, Select, Space, Switch } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FeatureWrapper, { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
||||
@@ -12,15 +11,13 @@ import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.c
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = () => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
|
||||
|
||||
export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
export function ShopInfoGeneral({ form }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -378,34 +375,6 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
...(HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||
? [
|
||||
<Form.Item
|
||||
key="tt_allow_post_to_invoiced"
|
||||
name={["tt_allow_post_to_invoiced"]}
|
||||
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="tt_enforce_hours_for_tech_console"
|
||||
name={["tt_enforce_hours_for_tech_console"]}
|
||||
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="bill_allow_post_to_closed"
|
||||
name={["bill_allow_post_to_closed"]}
|
||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
]
|
||||
: []),
|
||||
<Form.Item
|
||||
key="md_ded_notes"
|
||||
name={["md_ded_notes"]}
|
||||
|
||||
@@ -313,6 +313,38 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
...(HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||
? [
|
||||
<Form.Item
|
||||
key="tt_allow_post_to_invoiced"
|
||||
name={["tt_allow_post_to_invoiced"]}
|
||||
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="tt_enforce_hours_for_tech_console"
|
||||
name={["tt_enforce_hours_for_tech_console"]}
|
||||
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
]
|
||||
: []),
|
||||
...(HasFeatureAccess({ featureName: "bills", bodyshop })
|
||||
? [
|
||||
<Form.Item
|
||||
key="bill_allow_post_to_closed"
|
||||
name={["bill_allow_post_to_closed"]}
|
||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
]
|
||||
: []),
|
||||
...(HasFeatureAccess({ featureName: "export", bodyshop })
|
||||
? [
|
||||
...(ClosingPeriod.treatment === "on"
|
||||
|
||||
@@ -82,13 +82,14 @@ const rootEl = document.getElementById("root");
|
||||
|
||||
if (!rootEl) throw new Error('Missing root element: <div id="root" />');
|
||||
|
||||
const appTree = import.meta.env.DEV ? (
|
||||
<StrictMode>
|
||||
const appTree =
|
||||
import.meta.env.DEV && import.meta.env?.VITE_DISABLE_STRICT_MODE !== "true" ? (
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
) : (
|
||||
<App />
|
||||
</StrictMode>
|
||||
) : (
|
||||
<App />
|
||||
);
|
||||
);
|
||||
|
||||
ReactDOM.createRoot(rootEl).render(appTree);
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ export function ExportLogsPageComponent() {
|
||||
searchParams.search = value;
|
||||
history({ search: queryString.stringify(searchParams) });
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { PageHeader } from "@ant-design/pro-layout";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobsAvailableTableContainer from "../../components/jobs-available-table/jobs-available-table.container";
|
||||
@@ -25,6 +24,26 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
export function JobsAvailablePageContainer({ partnerVersion, setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getOS = () => {
|
||||
const userAgent = navigator.userAgent;
|
||||
if (userAgent.indexOf("Win") !== -1) return "windows";
|
||||
if (userAgent.indexOf("Mac") !== -1) return "mac";
|
||||
if (userAgent.indexOf("Linux") !== -1) return "linux";
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
const os = getOS();
|
||||
const downloadUrl = InstanceRenderManager({
|
||||
imex:
|
||||
os === "windows"
|
||||
? "https://imex-partner.s3.ca-central-1.amazonaws.com/imex-partner-x64.exe"
|
||||
: "https://imex-partner.s3.ca-central-1.amazonaws.com/imex-partner-arm64.dmg",
|
||||
rome:
|
||||
os === "windows"
|
||||
? "https://rome-partner.s3.us-east-2.amazonaws.com/rome-partner-x64.exe"
|
||||
: "https://rome-partner.s3.us-east-2.amazonaws.com/rome-partner-arm64.dmg"
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobsavailable", {
|
||||
app: InstanceRenderManager({
|
||||
@@ -39,24 +58,12 @@ export function JobsAvailablePageContainer({ partnerVersion, setBreadcrumbs, set
|
||||
return (
|
||||
<RbacWrapper action="jobs:available-list">
|
||||
<div>
|
||||
<PageHeader
|
||||
title={t("titles.bc.availablejobs")}
|
||||
extra={
|
||||
<Link to="/manage/jobs/new">
|
||||
<Button>{t("jobs.actions.manualnew")}</Button>
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
<PageHeader />
|
||||
{!partnerVersion && (
|
||||
<AlertComponent
|
||||
type="warning"
|
||||
action={
|
||||
<a
|
||||
href={InstanceRenderManager({
|
||||
imex: "https://partner.imex.online/Setup.exe",
|
||||
rome: "https://partner.romeonline.io/Setup.exe"
|
||||
})}
|
||||
>
|
||||
<a href={downloadUrl}>
|
||||
<Button size="small">{t("general.actions.download")}</Button>
|
||||
</a>
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FloatButton, Layout, Spin } from "antd";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
|
||||
// import preval from "preval.macro";
|
||||
import { lazy, Suspense, useEffect, useRef, useState } from "react";
|
||||
import { Suspense, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -29,87 +29,88 @@ import {
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||
import useAlertsNotifications from "../../hooks/useAlertsNotifications.jsx";
|
||||
import { selectDarkMode } from "../../redux/application/application.selectors.js";
|
||||
import { lazyDev } from "../../utils/lazyWithPreload.jsx";
|
||||
|
||||
const PrintCenterModalContainer = lazy(
|
||||
const PrintCenterModalContainer = lazyDev(
|
||||
() => import("../../components/print-center-modal/print-center-modal.container")
|
||||
);
|
||||
|
||||
const NoteUpsertModal = lazy(() => import("../../components/note-upsert-modal/note-upsert-modal.container.jsx"));
|
||||
const NoteUpsertModal = lazyDev(() => import("../../components/note-upsert-modal/note-upsert-modal.container.jsx"));
|
||||
|
||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||
const JobsPage = lazyDev(() => import("../jobs/jobs.page"));
|
||||
|
||||
const CardPaymentModalContainer = lazy(
|
||||
const CardPaymentModalContainer = lazyDev(
|
||||
() => import("../../components/card-payment-modal/card-payment-modal.container.jsx")
|
||||
);
|
||||
|
||||
const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.container"));
|
||||
const InventoryListPage = lazy(() => import("../inventory/inventory.page"));
|
||||
const ProfilePage = lazy(() => import("../profile/profile.container.page"));
|
||||
const JobsAvailablePage = lazy(() => import("../jobs-available/jobs-available.page.container"));
|
||||
const ScheduleContainer = lazy(() => import("../schedule/schedule.page.container"));
|
||||
const VehiclesContainer = lazy(() => import("../vehicles/vehicles.page.container"));
|
||||
const VehiclesDetailContainer = lazy(() => import("../vehicles-detail/vehicles-detail.page.container"));
|
||||
const OwnersContainer = lazy(() => import("../owners/owners.page.container"));
|
||||
const OwnersDetailContainer = lazy(() => import("../owners-detail/owners-detail.page.container"));
|
||||
const ShopPage = lazy(() => import("../shop/shop.page.component"));
|
||||
const ShopVendorPageContainer = lazy(() => import("../shop-vendor/shop-vendor.page.container"));
|
||||
const EmailOverlayContainer = lazy(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
||||
const JobsCreateContainerPage = lazy(() => import("../jobs-create/jobs-create.container"));
|
||||
const CourtesyCarCreateContainer = lazy(() => import("../courtesy-car-create/courtesy-car-create.page.container"));
|
||||
const CourtesyCarDetailContainer = lazy(() => import("../courtesy-car-detail/courtesy-car-detail.page.container"));
|
||||
const CourtesyCarsPage = lazy(() => import("../courtesy-cars/courtesy-cars.page.container"));
|
||||
const ContractCreatePage = lazy(() => import("../contract-create/contract-create.page.container"));
|
||||
const ContractDetailPage = lazy(() => import("../contract-detail/contract-detail.page.container"));
|
||||
const ContractsList = lazy(() => import("../contracts/contracts.page.container"));
|
||||
const BillsListPage = lazy(() => import("../bills/bills.page.container"));
|
||||
const FeatureRequestPage = lazy(() => import("../feature-request/feature-request.page.jsx"));
|
||||
const JobsDetailPage = lazyDev(() => import("../jobs-detail/jobs-detail.page.container"));
|
||||
const InventoryListPage = lazyDev(() => import("../inventory/inventory.page"));
|
||||
const ProfilePage = lazyDev(() => import("../profile/profile.container.page"));
|
||||
const JobsAvailablePage = lazyDev(() => import("../jobs-available/jobs-available.page.container"));
|
||||
const ScheduleContainer = lazyDev(() => import("../schedule/schedule.page.container"));
|
||||
const VehiclesContainer = lazyDev(() => import("../vehicles/vehicles.page.container"));
|
||||
const VehiclesDetailContainer = lazyDev(() => import("../vehicles-detail/vehicles-detail.page.container"));
|
||||
const OwnersContainer = lazyDev(() => import("../owners/owners.page.container"));
|
||||
const OwnersDetailContainer = lazyDev(() => import("../owners-detail/owners-detail.page.container"));
|
||||
const ShopPage = lazyDev(() => import("../shop/shop.page.component"));
|
||||
const ShopVendorPageContainer = lazyDev(() => import("../shop-vendor/shop-vendor.page.container"));
|
||||
const EmailOverlayContainer = lazyDev(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
||||
const JobsCreateContainerPage = lazyDev(() => import("../jobs-create/jobs-create.container"));
|
||||
const CourtesyCarCreateContainer = lazyDev(() => import("../courtesy-car-create/courtesy-car-create.page.container"));
|
||||
const CourtesyCarDetailContainer = lazyDev(() => import("../courtesy-car-detail/courtesy-car-detail.page.container"));
|
||||
const CourtesyCarsPage = lazyDev(() => import("../courtesy-cars/courtesy-cars.page.container"));
|
||||
const ContractCreatePage = lazyDev(() => import("../contract-create/contract-create.page.container"));
|
||||
const ContractDetailPage = lazyDev(() => import("../contract-detail/contract-detail.page.container"));
|
||||
const ContractsList = lazyDev(() => import("../contracts/contracts.page.container"));
|
||||
const BillsListPage = lazyDev(() => import("../bills/bills.page.container"));
|
||||
const FeatureRequestPage = lazyDev(() => import("../feature-request/feature-request.page.jsx"));
|
||||
|
||||
const JobCostingModal = lazy(() => import("../../components/job-costing-modal/job-costing-modal.container"));
|
||||
const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container"));
|
||||
const BillEnterModalContainer = lazy(() => import("../../components/bill-enter-modal/bill-enter-modal.container"));
|
||||
const TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container"));
|
||||
const TimeTicketModalTask = lazy(
|
||||
const JobCostingModal = lazyDev(() => import("../../components/job-costing-modal/job-costing-modal.container"));
|
||||
const ReportCenterModal = lazyDev(() => import("../../components/report-center-modal/report-center-modal.container"));
|
||||
const BillEnterModalContainer = lazyDev(() => import("../../components/bill-enter-modal/bill-enter-modal.container"));
|
||||
const TimeTicketModalContainer = lazyDev(() => import("../../components/time-ticket-modal/time-ticket-modal.container"));
|
||||
const TimeTicketModalTask = lazyDev(
|
||||
() => import("../../components/time-ticket-task-modal/time-ticket-task-modal.container")
|
||||
);
|
||||
const PaymentModalContainer = lazy(() => import("../../components/payment-modal/payment-modal.container"));
|
||||
const ProductionListPage = lazy(() => import("../production-list/production-list.container"));
|
||||
const ProductionBoardPage = lazy(() => import("../production-board/production-board.container"));
|
||||
// const ShopTemplates = lazy(() =>
|
||||
const PaymentModalContainer = lazyDev(() => import("../../components/payment-modal/payment-modal.container"));
|
||||
const ProductionListPage = lazyDev(() => import("../production-list/production-list.container"));
|
||||
const ProductionBoardPage = lazyDev(() => import("../production-board/production-board.container"));
|
||||
// const ShopTemplates = lazyDev(() =>
|
||||
// import("../shop-templates/shop-templates.container")
|
||||
// );
|
||||
const JobIntake = lazy(() => import("../jobs-intake/jobs-intake.page.container"));
|
||||
const JobChecklistView = lazy(() => import("../jobs-checklist-view/jobs-checklist-view.page"));
|
||||
const JobDeliver = lazy(() => import("../jobs-deliver/jobs-delivery.page.container"));
|
||||
const AccountingQboCallback = lazy(() => import("../accounting-qbo/accounting-qbo.page"));
|
||||
const AccountingReceivables = lazy(() => import("../accounting-receivables/accounting-receivables.container"));
|
||||
const AccountingPayables = lazy(() => import("../accounting-payables/accounting-payables.container"));
|
||||
const AccountingPayments = lazy(() => import("../accounting-payments/accounting-payments.container"));
|
||||
const AllJobs = lazy(() => import("../jobs-all/jobs-all.container"));
|
||||
const ReadyJobs = lazy(() => import("../jobs-ready/jobs-ready.page"));
|
||||
const JobsClose = lazy(() => import("../jobs-close/jobs-close.container"));
|
||||
const JobsAdmin = lazy(() => import("../jobs-admin/jobs-admin.page"));
|
||||
const TempDocs = lazy(() => import("../temporary-docs/temporary-docs.container"));
|
||||
const JobIntake = lazyDev(() => import("../jobs-intake/jobs-intake.page.container"));
|
||||
const JobChecklistView = lazyDev(() => import("../jobs-checklist-view/jobs-checklist-view.page"));
|
||||
const JobDeliver = lazyDev(() => import("../jobs-deliver/jobs-delivery.page.container"));
|
||||
const AccountingQboCallback = lazyDev(() => import("../accounting-qbo/accounting-qbo.page"));
|
||||
const AccountingReceivables = lazyDev(() => import("../accounting-receivables/accounting-receivables.container"));
|
||||
const AccountingPayables = lazyDev(() => import("../accounting-payables/accounting-payables.container"));
|
||||
const AccountingPayments = lazyDev(() => import("../accounting-payments/accounting-payments.container"));
|
||||
const AllJobs = lazyDev(() => import("../jobs-all/jobs-all.container"));
|
||||
const ReadyJobs = lazyDev(() => import("../jobs-ready/jobs-ready.page"));
|
||||
const JobsClose = lazyDev(() => import("../jobs-close/jobs-close.container"));
|
||||
const JobsAdmin = lazyDev(() => import("../jobs-admin/jobs-admin.page"));
|
||||
const TempDocs = lazyDev(() => import("../temporary-docs/temporary-docs.container"));
|
||||
|
||||
const ShopCsiPageContainer = lazy(() => import("../shop-csi/shop-csi.container.page"));
|
||||
const PaymentsAll = lazy(() => import("../payments-all/payments-all.container.page"));
|
||||
const ShiftClock = lazy(() => import("../shift-clock/shift-clock.page"));
|
||||
const Scoreboard = lazy(() => import("../scoreboard/scoreboard.page.container"));
|
||||
const TimeTicketsAll = lazy(() => import("../time-tickets/time-tickets.container"));
|
||||
const Help = lazy(() => import("../help/help.page"));
|
||||
const PartsQueue = lazy(() => import("../parts-queue/parts-queue.page.container"));
|
||||
const ExportLogs = lazy(() => import("../export-logs/export-logs.page.container"));
|
||||
const Phonebook = lazy(() => import("../phonebook/phonebook.page.container"));
|
||||
const ShopCsiPageContainer = lazyDev(() => import("../shop-csi/shop-csi.container.page"));
|
||||
const PaymentsAll = lazyDev(() => import("../payments-all/payments-all.container.page"));
|
||||
const ShiftClock = lazyDev(() => import("../shift-clock/shift-clock.page"));
|
||||
const Scoreboard = lazyDev(() => import("../scoreboard/scoreboard.page.container"));
|
||||
const TimeTicketsAll = lazyDev(() => import("../time-tickets/time-tickets.container"));
|
||||
const Help = lazyDev(() => import("../help/help.page"));
|
||||
const PartsQueue = lazyDev(() => import("../parts-queue/parts-queue.page.container"));
|
||||
const ExportLogs = lazyDev(() => import("../export-logs/export-logs.page.container"));
|
||||
const Phonebook = lazyDev(() => import("../phonebook/phonebook.page.container"));
|
||||
|
||||
const EmailTest = lazy(() => import("../../components/email-test/email-test-component"));
|
||||
const Dashboard = lazy(() => import("../dashboard/dashboard.container"));
|
||||
const Dms = lazy(() => import("../dms/dms.container"));
|
||||
const DmsPayables = lazy(() => import("../dms-payables/dms-payables.container"));
|
||||
const ManageRootPage = lazy(() => import("../manage-root/manage-root.page.container"));
|
||||
const TtApprovals = lazy(() => import("../tt-approvals/tt-approvals.page.container"));
|
||||
const MyTasksPage = lazy(() => import("../tasks/myTasksPageContainer.jsx"));
|
||||
const AllTasksPage = lazy(() => import("../tasks/allTasksPageContainer.jsx"));
|
||||
const EmailTest = lazyDev(() => import("../../components/email-test/email-test-component"));
|
||||
const Dashboard = lazyDev(() => import("../dashboard/dashboard.container"));
|
||||
const Dms = lazyDev(() => import("../dms/dms.container"));
|
||||
const DmsPayables = lazyDev(() => import("../dms-payables/dms-payables.container"));
|
||||
const ManageRootPage = lazyDev(() => import("../manage-root/manage-root.page.container"));
|
||||
const TtApprovals = lazyDev(() => import("../tt-approvals/tt-approvals.page.container"));
|
||||
const MyTasksPage = lazyDev(() => import("../tasks/myTasksPageContainer.jsx"));
|
||||
const AllTasksPage = lazyDev(() => import("../tasks/allTasksPageContainer.jsx"));
|
||||
|
||||
const TaskUpsertModalContainer = lazy(() => import("../../components/task-upsert-modal/task-upsert-modal.container"));
|
||||
const TaskUpsertModalContainer = lazyDev(() => import("../../components/task-upsert-modal/task-upsert-modal.container"));
|
||||
const { Content } = Layout;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
|
||||
@@ -167,6 +167,7 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
|
||||
searchParams.page = 1;
|
||||
history({ search: queryString.stringify(searchParams) });
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { FloatButton, Layout, Spin } from "antd";
|
||||
import { lazy, Suspense, useEffect } from "react";
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Navigate, Route, Routes, useLocation } from "react-router-dom";
|
||||
@@ -15,20 +15,21 @@ import UpdateAlert from "../../components/update-alert/update-alert.component.js
|
||||
import { selectBodyshop, selectInstanceConflict } from "../../redux/user/user.selectors.js";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||
import useAlertsNotifications from "../../hooks/useAlertsNotifications.jsx";
|
||||
import { lazyDev } from "../../utils/lazyWithPreload.jsx";
|
||||
|
||||
const SimplifiedPartsJobsPage = lazy(() => import("../simplified-parts-jobs/simplified-parts-jobs.page.jsx"));
|
||||
const SimplifiedPartsJobsDetailPage = lazy(
|
||||
const SimplifiedPartsJobsPage = lazyDev(() => import("../simplified-parts-jobs/simplified-parts-jobs.page.jsx"));
|
||||
const SimplifiedPartsJobsDetailPage = lazyDev(
|
||||
() => import("../simplified-parts-jobs-detail/simplified-parts-jobs-detail.container.jsx")
|
||||
);
|
||||
const PartsSettingsPage = lazy(() => import("../parts-settings/parts-settings.page.component.jsx"));
|
||||
const ShopVendorPageContainer = lazy(() => import("../shop-vendor/shop-vendor.page.container.jsx"));
|
||||
const EmailOverlayContainer = lazy(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
||||
const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container.jsx"));
|
||||
const PrintCenterModalContainer = lazy(
|
||||
const PartsSettingsPage = lazyDev(() => import("../parts-settings/parts-settings.page.component.jsx"));
|
||||
const ShopVendorPageContainer = lazyDev(() => import("../shop-vendor/shop-vendor.page.container.jsx"));
|
||||
const EmailOverlayContainer = lazyDev(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
||||
const ReportCenterModal = lazyDev(() => import("../../components/report-center-modal/report-center-modal.container.jsx"));
|
||||
const PrintCenterModalContainer = lazyDev(
|
||||
() => import("../../components/print-center-modal/print-center-modal.container")
|
||||
);
|
||||
const VehiclesContainer = lazy(() => import("../vehicles/vehicles.page.container.jsx"));
|
||||
const VehiclesDetailContainer = lazy(() => import("../vehicles-detail/vehicles-detail.page.container.jsx"));
|
||||
const VehiclesContainer = lazyDev(() => import("../vehicles/vehicles.page.container.jsx"));
|
||||
const VehiclesDetailContainer = lazyDev(() => import("../vehicles-detail/vehicles-detail.page.container.jsx"));
|
||||
const { Content } = Layout;
|
||||
|
||||
// Redirector to strip '/parts/jobs' from path for non-detail routes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Card, FloatButton, Layout } from "antd";
|
||||
import { lazy, Suspense, useEffect } from "react";
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Route, Routes, useNavigate } from "react-router-dom";
|
||||
@@ -15,25 +15,26 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
import "./tech.page.styles.scss";
|
||||
import UpsellComponent, { upsellEnum } from "../../components/upsell/upsell.component.jsx";
|
||||
import { lazyDev } from "../../utils/lazyWithPreload.jsx";
|
||||
|
||||
const TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container"));
|
||||
const EmailOverlayContainer = lazy(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
||||
const PrintCenterModalContainer = lazy(
|
||||
const TimeTicketModalContainer = lazyDev(() => import("../../components/time-ticket-modal/time-ticket-modal.container"));
|
||||
const EmailOverlayContainer = lazyDev(() => import("../../components/email-overlay/email-overlay.container.jsx"));
|
||||
const PrintCenterModalContainer = lazyDev(
|
||||
() => import("../../components/print-center-modal/print-center-modal.container")
|
||||
);
|
||||
const TechLogin = lazy(() => import("../../components/tech-login/tech-login.component"));
|
||||
const TechLookup = lazy(() => import("../tech-lookup/tech-lookup.container"));
|
||||
const ProductionListPage = lazy(() => import("../production-list/production-list.container"));
|
||||
const ProductionBoardPage = lazy(() => import("../production-board/production-board.container"));
|
||||
const TechJobClock = lazy(() => import("../tech-job-clock/tech-job-clock.component"));
|
||||
const TechShiftClock = lazy(() => import("../tech-shift-clock/tech-shift-clock.component"));
|
||||
const TimeTicketModalTask = lazy(
|
||||
const TechLogin = lazyDev(() => import("../../components/tech-login/tech-login.component"));
|
||||
const TechLookup = lazyDev(() => import("../tech-lookup/tech-lookup.container"));
|
||||
const ProductionListPage = lazyDev(() => import("../production-list/production-list.container"));
|
||||
const ProductionBoardPage = lazyDev(() => import("../production-board/production-board.container"));
|
||||
const TechJobClock = lazyDev(() => import("../tech-job-clock/tech-job-clock.component"));
|
||||
const TechShiftClock = lazyDev(() => import("../tech-shift-clock/tech-shift-clock.component"));
|
||||
const TimeTicketModalTask = lazyDev(
|
||||
() => import("../../components/time-ticket-task-modal/time-ticket-task-modal.container")
|
||||
);
|
||||
const TechAssignedProdJobs = lazy(() => import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component"));
|
||||
const TechDispatchedParts = lazy(() => import("../tech-dispatched-parts/tech-dispatched-parts.page"));
|
||||
const TechAssignedProdJobs = lazyDev(() => import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component"));
|
||||
const TechDispatchedParts = lazyDev(() => import("../tech-dispatched-parts/tech-dispatched-parts.page"));
|
||||
|
||||
const TaskUpsertModalContainer = lazy(() => import("../../components/task-upsert-modal/task-upsert-modal.container"));
|
||||
const TaskUpsertModalContainer = lazyDev(() => import("../../components/task-upsert-modal/task-upsert-modal.container"));
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
|
||||
35
client/src/utils/lazyWithPreload.jsx
Normal file
35
client/src/utils/lazyWithPreload.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { lazy } from "react";
|
||||
|
||||
/**
|
||||
* Conditionally uses lazy loading based on environment.
|
||||
* By default, uses React.lazy in all environments.
|
||||
* Set VITE_DISABLE_LAZY_LOADING=true to load modules immediately in development (avoids compilation delays).
|
||||
*
|
||||
* Usage: const MyComponent = lazyDev(() => import('./MyComponent'));
|
||||
*/
|
||||
export const lazyDev = (importFunc) => {
|
||||
// Check if lazy loading should be disabled (dev only, opt-in via env var)
|
||||
const disableLazyLoading = import.meta.env.DEV && import.meta.env?.VITE_DISABLE_LAZY_LOADING === "true";
|
||||
|
||||
if (!disableLazyLoading) {
|
||||
// Default behavior: use React.lazy for code splitting
|
||||
return lazy(importFunc);
|
||||
}
|
||||
|
||||
// Dev mode with lazy loading disabled: load immediately to avoid delays
|
||||
let Component = null;
|
||||
const promise = importFunc().then((module) => {
|
||||
Component = module.default;
|
||||
});
|
||||
|
||||
const LazyDevComponent = (props) => {
|
||||
if (!Component) {
|
||||
throw promise; // Suspense will catch this
|
||||
}
|
||||
return <Component {...props} />;
|
||||
};
|
||||
|
||||
LazyDevComponent.displayName = "LazyDevComponent";
|
||||
|
||||
return LazyDevComponent;
|
||||
};
|
||||
@@ -11,6 +11,8 @@ import { VitePWA } from "vite-plugin-pwa";
|
||||
import InstanceRenderManager from "./src/utils/instanceRenderMgr";
|
||||
import browserslist from "browserslist";
|
||||
import { browserslistToTargets } from "lightningcss";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname, resolve } from "path";
|
||||
|
||||
process.env.VITE_APP_GIT_SHA_DATE = new Date().toLocaleString("en-US", { timeZone: "America/Los_Angeles" });
|
||||
const commitHash = child.execSync("git rev-parse HEAD").toString().trimEnd();
|
||||
@@ -43,13 +45,19 @@ const httpsCerts = {
|
||||
cert: await fsPromises.readFile("../certs/cert.pem")
|
||||
};
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
// Only enable React Compiler on build in production/test (keeps dev as fast as possible)
|
||||
// React Compiler is always enabled for production/test builds
|
||||
// In dev mode, it's enabled by default but can be disabled with VITE_DISABLE_COMPILER_IN_DEV=true
|
||||
const isBuild = command === "build";
|
||||
const isTestBuild =
|
||||
mode === "test" || process.env.VITE_APP_IS_TEST === "true" || process.env.VITE_APP_IS_TEST === "1";
|
||||
|
||||
const enableReactCompiler =
|
||||
process.env.VITE_ENABLE_COMPILER_IN_DEV || (isBuild && (mode === "production" || isTestBuild));
|
||||
(isBuild && (mode === "production" || isTestBuild)) || // Always enable for prod/test builds
|
||||
process.env?.VITE_DISABLE_COMPILER_IN_DEV !== "true"; // In dev, enable unless explicitly disabled
|
||||
|
||||
logger.info(
|
||||
enableReactCompiler ? chalk.green.bold("React Compiler enabled") : chalk.yellow.bold("React Compiler disabled")
|
||||
@@ -57,6 +65,13 @@ export default defineConfig(({ command, mode }) => {
|
||||
|
||||
return {
|
||||
base: "/",
|
||||
resolve: {
|
||||
dedupe: ["styled-components", "react", "react-dom"],
|
||||
alias: {
|
||||
// Force all styled-components imports to resolve to the same location (absolute path)
|
||||
"styled-components": resolve(__dirname, "node_modules/styled-components/dist/styled-components.browser.esm.js")
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
ViteEjsPlugin((viteConfig) => ({ env: viteConfig.env })),
|
||||
|
||||
@@ -243,7 +258,8 @@ export default defineConfig(({ command, mode }) => {
|
||||
|
||||
// Strip console/debugger in prod to shrink bundles
|
||||
esbuild: {
|
||||
//drop: ["console", "debugger"]
|
||||
// drop: mode === "production" ? ["console", "debugger"] : [],
|
||||
legalComments: "none" // Remove license comments in production
|
||||
},
|
||||
|
||||
optimizeDeps: {
|
||||
@@ -265,11 +281,14 @@ export default defineConfig(({ command, mode }) => {
|
||||
"@firebase/firestore",
|
||||
"@firebase/auth",
|
||||
"@firebase/messaging",
|
||||
"@firebase/util"
|
||||
"@firebase/util",
|
||||
"styled-components"
|
||||
],
|
||||
esbuildOptions: {
|
||||
loader: { ".jsx": "jsx", ".tsx": "tsx" }
|
||||
}
|
||||
},
|
||||
// Force styled-components to be pre-bundled and deduplicated
|
||||
force: mode === "development"
|
||||
},
|
||||
|
||||
css: {
|
||||
|
||||
196
package-lock.json
generated
196
package-lock.json
generated
@@ -9,23 +9,23 @@
|
||||
"version": "0.2.0",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-cloudwatch-logs": "^3.974.0",
|
||||
"@aws-sdk/client-elasticache": "^3.974.0",
|
||||
"@aws-sdk/client-s3": "^3.974.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.974.0",
|
||||
"@aws-sdk/client-ses": "^3.974.0",
|
||||
"@aws-sdk/client-cloudwatch-logs": "^3.975.0",
|
||||
"@aws-sdk/client-elasticache": "^3.975.0",
|
||||
"@aws-sdk/client-s3": "^3.975.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.975.0",
|
||||
"@aws-sdk/client-ses": "^3.975.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
||||
"@aws-sdk/lib-storage": "^3.974.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.974.0",
|
||||
"@aws-sdk/lib-storage": "^3.975.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.975.0",
|
||||
"@opensearch-project/opensearch": "^2.13.0",
|
||||
"@socket.io/admin-ui": "^0.5.1",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"archiver": "^7.0.1",
|
||||
"aws4": "^1.13.2",
|
||||
"axios": "^1.13.2",
|
||||
"axios": "^1.13.3",
|
||||
"axios-curlirize": "^2.0.0",
|
||||
"better-queue": "^3.8.12",
|
||||
"bullmq": "^5.66.7",
|
||||
"bullmq": "^5.67.1",
|
||||
"chart.js": "^4.5.1",
|
||||
"cloudinary": "^2.9.0",
|
||||
"compression": "^1.8.1",
|
||||
@@ -288,26 +288,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-cloudwatch-logs": {
|
||||
"version": "3.974.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.974.0.tgz",
|
||||
"integrity": "sha512-bWEBpjk+Shs02pLovWmt/uz4TkQCbmY2LdQ0wBnijkhgknAbMIy9MUdXTDZXauIMzuxAIntAKaJ+3QYp1PT1bQ==",
|
||||
"version": "3.975.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.975.0.tgz",
|
||||
"integrity": "sha512-HB29ZkHZbjLJUSYuCAgnaAEx3tE0s8agMNZ6Jb77qGoR92KH1Beu9+7JWwl2PfWF/p3FaJYMRZIgnH4TPLnaqw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "^3.973.0",
|
||||
"@aws-sdk/core": "^3.973.1",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
||||
"@aws-sdk/middleware-host-header": "^3.972.1",
|
||||
"@aws-sdk/middleware-logger": "^3.972.1",
|
||||
"@aws-sdk/middleware-recursion-detection": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.2",
|
||||
"@aws-sdk/region-config-resolver": "^3.972.1",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/util-endpoints": "3.972.0",
|
||||
"@aws-sdk/util-user-agent-browser": "^3.972.1",
|
||||
"@aws-sdk/util-user-agent-node": "^3.972.1",
|
||||
"@smithy/config-resolver": "^4.4.6",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/eventstream-serde-browser": "^4.2.8",
|
||||
"@smithy/eventstream-serde-config-resolver": "^4.3.8",
|
||||
"@smithy/eventstream-serde-node": "^4.2.8",
|
||||
@@ -315,21 +315,21 @@
|
||||
"@smithy/hash-node": "^4.2.8",
|
||||
"@smithy/invalid-dependency": "^4.2.8",
|
||||
"@smithy/middleware-content-length": "^4.2.8",
|
||||
"@smithy/middleware-endpoint": "^4.4.10",
|
||||
"@smithy/middleware-retry": "^4.4.26",
|
||||
"@smithy/middleware-endpoint": "^4.4.11",
|
||||
"@smithy/middleware-retry": "^4.4.27",
|
||||
"@smithy/middleware-serde": "^4.2.9",
|
||||
"@smithy/middleware-stack": "^4.2.8",
|
||||
"@smithy/node-config-provider": "^4.3.8",
|
||||
"@smithy/node-http-handler": "^4.4.8",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"@smithy/url-parser": "^4.2.8",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/util-body-length-browser": "^4.2.0",
|
||||
"@smithy/util-body-length-node": "^4.2.1",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.25",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.28",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.26",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.29",
|
||||
"@smithy/util-endpoints": "^3.2.8",
|
||||
"@smithy/util-middleware": "^4.2.8",
|
||||
"@smithy/util-retry": "^4.2.8",
|
||||
@@ -341,45 +341,45 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-elasticache": {
|
||||
"version": "3.974.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-elasticache/-/client-elasticache-3.974.0.tgz",
|
||||
"integrity": "sha512-jzTLOmmi/zeg8YVNhDYne72r1o+dH9Q5or6bETeXN3+uw4SWxWxXjslqe+YXCLZ/FdppARY5SeYgazJEVPeCOQ==",
|
||||
"version": "3.975.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-elasticache/-/client-elasticache-3.975.0.tgz",
|
||||
"integrity": "sha512-sLrnoJk/iKTeR1bvoNc7ESc1ijek6GAwXrdWSBccAtWocQPFiHeiWk/7LjUk/TVUp36KYUVNdS8RIqMi6YjNIA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "^3.973.0",
|
||||
"@aws-sdk/core": "^3.973.1",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
||||
"@aws-sdk/middleware-host-header": "^3.972.1",
|
||||
"@aws-sdk/middleware-logger": "^3.972.1",
|
||||
"@aws-sdk/middleware-recursion-detection": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.2",
|
||||
"@aws-sdk/region-config-resolver": "^3.972.1",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/util-endpoints": "3.972.0",
|
||||
"@aws-sdk/util-user-agent-browser": "^3.972.1",
|
||||
"@aws-sdk/util-user-agent-node": "^3.972.1",
|
||||
"@smithy/config-resolver": "^4.4.6",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/fetch-http-handler": "^5.3.9",
|
||||
"@smithy/hash-node": "^4.2.8",
|
||||
"@smithy/invalid-dependency": "^4.2.8",
|
||||
"@smithy/middleware-content-length": "^4.2.8",
|
||||
"@smithy/middleware-endpoint": "^4.4.10",
|
||||
"@smithy/middleware-retry": "^4.4.26",
|
||||
"@smithy/middleware-endpoint": "^4.4.11",
|
||||
"@smithy/middleware-retry": "^4.4.27",
|
||||
"@smithy/middleware-serde": "^4.2.9",
|
||||
"@smithy/middleware-stack": "^4.2.8",
|
||||
"@smithy/node-config-provider": "^4.3.8",
|
||||
"@smithy/node-http-handler": "^4.4.8",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"@smithy/url-parser": "^4.2.8",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/util-body-length-browser": "^4.2.0",
|
||||
"@smithy/util-body-length-node": "^4.2.1",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.25",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.28",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.26",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.29",
|
||||
"@smithy/util-endpoints": "^3.2.8",
|
||||
"@smithy/util-middleware": "^4.2.8",
|
||||
"@smithy/util-retry": "^4.2.8",
|
||||
@@ -392,15 +392,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-s3": {
|
||||
"version": "3.974.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.974.0.tgz",
|
||||
"integrity": "sha512-X+vpXNJ8cU8Iw1FtDgDHxo9z6RxlXfcTtpdGnKws4rk+tCYKSAor/DG6BRMzbh4E5xAA7DiU1Ny3BTrRRSt/Yg==",
|
||||
"version": "3.975.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.975.0.tgz",
|
||||
"integrity": "sha512-aF1M/iMD29BPcpxjqoym0YFa4WR9Xie1/IhVumwOGH6TB45DaqYO7vLwantDBcYNRn/cZH6DFHksO7RmwTFBhw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha1-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "^3.973.0",
|
||||
"@aws-sdk/core": "^3.973.1",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
||||
"@aws-sdk/middleware-bucket-endpoint": "^3.972.1",
|
||||
"@aws-sdk/middleware-expect-continue": "^3.972.1",
|
||||
@@ -409,9 +409,9 @@
|
||||
"@aws-sdk/middleware-location-constraint": "^3.972.1",
|
||||
"@aws-sdk/middleware-logger": "^3.972.1",
|
||||
"@aws-sdk/middleware-recursion-detection": "^3.972.1",
|
||||
"@aws-sdk/middleware-sdk-s3": "^3.972.1",
|
||||
"@aws-sdk/middleware-sdk-s3": "^3.972.2",
|
||||
"@aws-sdk/middleware-ssec": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.2",
|
||||
"@aws-sdk/region-config-resolver": "^3.972.1",
|
||||
"@aws-sdk/signature-v4-multi-region": "3.972.0",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
@@ -419,7 +419,7 @@
|
||||
"@aws-sdk/util-user-agent-browser": "^3.972.1",
|
||||
"@aws-sdk/util-user-agent-node": "^3.972.1",
|
||||
"@smithy/config-resolver": "^4.4.6",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/eventstream-serde-browser": "^4.2.8",
|
||||
"@smithy/eventstream-serde-config-resolver": "^4.3.8",
|
||||
"@smithy/eventstream-serde-node": "^4.2.8",
|
||||
@@ -430,21 +430,21 @@
|
||||
"@smithy/invalid-dependency": "^4.2.8",
|
||||
"@smithy/md5-js": "^4.2.8",
|
||||
"@smithy/middleware-content-length": "^4.2.8",
|
||||
"@smithy/middleware-endpoint": "^4.4.10",
|
||||
"@smithy/middleware-retry": "^4.4.26",
|
||||
"@smithy/middleware-endpoint": "^4.4.11",
|
||||
"@smithy/middleware-retry": "^4.4.27",
|
||||
"@smithy/middleware-serde": "^4.2.9",
|
||||
"@smithy/middleware-stack": "^4.2.8",
|
||||
"@smithy/node-config-provider": "^4.3.8",
|
||||
"@smithy/node-http-handler": "^4.4.8",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"@smithy/url-parser": "^4.2.8",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/util-body-length-browser": "^4.2.0",
|
||||
"@smithy/util-body-length-node": "^4.2.1",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.25",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.28",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.26",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.29",
|
||||
"@smithy/util-endpoints": "^3.2.8",
|
||||
"@smithy/util-middleware": "^4.2.8",
|
||||
"@smithy/util-retry": "^4.2.8",
|
||||
@@ -458,45 +458,45 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-secrets-manager": {
|
||||
"version": "3.974.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.974.0.tgz",
|
||||
"integrity": "sha512-YLCnCZjK6fX8OMbJEVuQePoYLlm/3SloSN1NtysTZ7vo9GIkaFBKEapkud4rUJika9eDAyjVkhgvxRdEPjhKUw==",
|
||||
"version": "3.975.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.975.0.tgz",
|
||||
"integrity": "sha512-KY67ghh2BBBhfaCvOquazOWWTe8CEaEsKOFtNVtECIttRlmm1YAuIDUTk7reaQhTqb+wwuS2xoGsu5z1FZkFyA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "^3.973.0",
|
||||
"@aws-sdk/core": "^3.973.1",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
||||
"@aws-sdk/middleware-host-header": "^3.972.1",
|
||||
"@aws-sdk/middleware-logger": "^3.972.1",
|
||||
"@aws-sdk/middleware-recursion-detection": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.2",
|
||||
"@aws-sdk/region-config-resolver": "^3.972.1",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/util-endpoints": "3.972.0",
|
||||
"@aws-sdk/util-user-agent-browser": "^3.972.1",
|
||||
"@aws-sdk/util-user-agent-node": "^3.972.1",
|
||||
"@smithy/config-resolver": "^4.4.6",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/fetch-http-handler": "^5.3.9",
|
||||
"@smithy/hash-node": "^4.2.8",
|
||||
"@smithy/invalid-dependency": "^4.2.8",
|
||||
"@smithy/middleware-content-length": "^4.2.8",
|
||||
"@smithy/middleware-endpoint": "^4.4.10",
|
||||
"@smithy/middleware-retry": "^4.4.26",
|
||||
"@smithy/middleware-endpoint": "^4.4.11",
|
||||
"@smithy/middleware-retry": "^4.4.27",
|
||||
"@smithy/middleware-serde": "^4.2.9",
|
||||
"@smithy/middleware-stack": "^4.2.8",
|
||||
"@smithy/node-config-provider": "^4.3.8",
|
||||
"@smithy/node-http-handler": "^4.4.8",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"@smithy/url-parser": "^4.2.8",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/util-body-length-browser": "^4.2.0",
|
||||
"@smithy/util-body-length-node": "^4.2.1",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.25",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.28",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.26",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.29",
|
||||
"@smithy/util-endpoints": "^3.2.8",
|
||||
"@smithy/util-middleware": "^4.2.8",
|
||||
"@smithy/util-retry": "^4.2.8",
|
||||
@@ -508,45 +508,45 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-ses": {
|
||||
"version": "3.974.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.974.0.tgz",
|
||||
"integrity": "sha512-rALm5rcyFIHxTCRTUEETslVDaxN82uPY6JW84UNIRZKZIOMx08pEGr0OgJgp1wNQ92bw6ha1CGVk4D28YGLiaw==",
|
||||
"version": "3.975.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.975.0.tgz",
|
||||
"integrity": "sha512-pmt5phZzTwTDPxzBhke96HXuaUbMAWlo1aX+D2E6oyx7SBGKUTFSf2S1ONvHvJpvXW/yEaoZLCXbK6R7XcfTZA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "^3.973.0",
|
||||
"@aws-sdk/core": "^3.973.1",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
||||
"@aws-sdk/middleware-host-header": "^3.972.1",
|
||||
"@aws-sdk/middleware-logger": "^3.972.1",
|
||||
"@aws-sdk/middleware-recursion-detection": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.1",
|
||||
"@aws-sdk/middleware-user-agent": "^3.972.2",
|
||||
"@aws-sdk/region-config-resolver": "^3.972.1",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/util-endpoints": "3.972.0",
|
||||
"@aws-sdk/util-user-agent-browser": "^3.972.1",
|
||||
"@aws-sdk/util-user-agent-node": "^3.972.1",
|
||||
"@smithy/config-resolver": "^4.4.6",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/fetch-http-handler": "^5.3.9",
|
||||
"@smithy/hash-node": "^4.2.8",
|
||||
"@smithy/invalid-dependency": "^4.2.8",
|
||||
"@smithy/middleware-content-length": "^4.2.8",
|
||||
"@smithy/middleware-endpoint": "^4.4.10",
|
||||
"@smithy/middleware-retry": "^4.4.26",
|
||||
"@smithy/middleware-endpoint": "^4.4.11",
|
||||
"@smithy/middleware-retry": "^4.4.27",
|
||||
"@smithy/middleware-serde": "^4.2.9",
|
||||
"@smithy/middleware-stack": "^4.2.8",
|
||||
"@smithy/node-config-provider": "^4.3.8",
|
||||
"@smithy/node-http-handler": "^4.4.8",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"@smithy/url-parser": "^4.2.8",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/util-body-length-browser": "^4.2.0",
|
||||
"@smithy/util-body-length-node": "^4.2.1",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.25",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.28",
|
||||
"@smithy/util-defaults-mode-browser": "^4.3.26",
|
||||
"@smithy/util-defaults-mode-node": "^4.2.29",
|
||||
"@smithy/util-endpoints": "^3.2.8",
|
||||
"@smithy/util-middleware": "^4.2.8",
|
||||
"@smithy/util-retry": "^4.2.8",
|
||||
@@ -608,19 +608,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/core": {
|
||||
"version": "3.973.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.0.tgz",
|
||||
"integrity": "sha512-qy3Fmt8z4PRInM3ZqJmHihQ2tfCdj/MzbGaZpuHjYjgl1/Gcar4Pyp/zzHXh9hGEb61WNbWgsJcDUhnGIiX1TA==",
|
||||
"version": "3.973.1",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.1.tgz",
|
||||
"integrity": "sha512-Ocubx42QsMyVs9ANSmFpRm0S+hubWljpPLjOi9UFrtcnVJjrVJTzQ51sN0e5g4e8i8QZ7uY73zosLmgYL7kZTQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/xml-builder": "^3.972.1",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/node-config-provider": "^4.3.8",
|
||||
"@smithy/property-provider": "^4.2.8",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/signature-v4": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"@smithy/util-base64": "^4.3.0",
|
||||
"@smithy/util-middleware": "^4.2.8",
|
||||
@@ -803,14 +803,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/lib-storage": {
|
||||
"version": "3.974.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.974.0.tgz",
|
||||
"integrity": "sha512-6okXc+jsVUszk04A+Xx01J/ThygiisBK0Y2sE8T+sPLKKSplnr8VAusud3Fn8w9D9RmYBEWFx8E6Mg+vLZ5J0A==",
|
||||
"version": "3.975.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.975.0.tgz",
|
||||
"integrity": "sha512-F6vrnZ3F7oqr3oONCIpx+uZDTwXWfh8sBoNNJollDn5pIn7TI+R+7WxVIXAMq/JWLXE6N8T3M6ogWk4Y4JWPPw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@smithy/abort-controller": "^4.2.8",
|
||||
"@smithy/middleware-endpoint": "^4.4.10",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/middleware-endpoint": "^4.4.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"buffer": "5.6.0",
|
||||
"events": "3.3.0",
|
||||
"stream-browserify": "3.0.0",
|
||||
@@ -820,7 +820,7 @@
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-s3": "3.974.0"
|
||||
"@aws-sdk/client-s3": "3.975.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/middleware-bucket-endpoint": {
|
||||
@@ -941,19 +941,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/middleware-sdk-s3": {
|
||||
"version": "3.972.1",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.1.tgz",
|
||||
"integrity": "sha512-q/hK0ZNf/aafFRv2wIlDM3p+izi5cXwktVNvRvW646A0MvVZmT4/vwadv/jPA9AORFbnpyf/0luxiMz181f9yg==",
|
||||
"version": "3.972.2",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.2.tgz",
|
||||
"integrity": "sha512-5f9x9/G+StE8+7wd9EVDF3d+J74xK+WBA3FhZwLSkf3pHFGLKzlmUfxJJE1kkXkbj/j/H+Dh3zL/hrtQE9hNsg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.973.0",
|
||||
"@aws-sdk/core": "^3.973.1",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/util-arn-parser": "^3.972.1",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/node-config-provider": "^4.3.8",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/signature-v4": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"@smithy/util-config-provider": "^4.2.0",
|
||||
"@smithy/util-middleware": "^4.2.8",
|
||||
@@ -980,15 +980,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/middleware-user-agent": {
|
||||
"version": "3.972.1",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.1.tgz",
|
||||
"integrity": "sha512-6SVg4pY/9Oq9MLzO48xuM3lsOb8Rxg55qprEtFRpkUmuvKij31f5SQHEGxuiZ4RqIKrfjr2WMuIgXvqJ0eJsPA==",
|
||||
"version": "3.972.2",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.2.tgz",
|
||||
"integrity": "sha512-d+Exq074wy0X6wvShg/kmZVtkah+28vMuqCtuY3cydg8LUZOJBtbAolCpEJizSyb8mJJZF9BjWaTANXL4OYnkg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/core": "^3.973.0",
|
||||
"@aws-sdk/core": "^3.973.1",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/util-endpoints": "3.972.0",
|
||||
"@smithy/core": "^3.21.0",
|
||||
"@smithy/core": "^3.21.1",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"tslib": "^2.6.2"
|
||||
@@ -1063,17 +1063,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/s3-request-presigner": {
|
||||
"version": "3.974.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.974.0.tgz",
|
||||
"integrity": "sha512-tApmJb4XXBdNQzxTYIBq9aYj8vjJqiMPyeUF25wzvGjLQfXgvcv5sTR4yyzXBxRc8+O7quWDBgMJGtcNerapRQ==",
|
||||
"version": "3.975.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.975.0.tgz",
|
||||
"integrity": "sha512-rqDktHVSB7Ah7XXqr6BWMqDhGXEccp0h1EivaOGED73JjeG/S7taoywJPqi7YkalRb3Ka+LRc7wVoN9wSa4KQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/signature-v4-multi-region": "3.972.0",
|
||||
"@aws-sdk/types": "^3.973.0",
|
||||
"@aws-sdk/util-format-url": "^3.972.1",
|
||||
"@smithy/middleware-endpoint": "^4.4.10",
|
||||
"@smithy/middleware-endpoint": "^4.4.11",
|
||||
"@smithy/protocol-http": "^5.3.8",
|
||||
"@smithy/smithy-client": "^4.10.11",
|
||||
"@smithy/smithy-client": "^4.10.12",
|
||||
"@smithy/types": "^4.12.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
@@ -4598,9 +4598,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.3.tgz",
|
||||
"integrity": "sha512-ERT8kdX7DZjtUm7IitEyV7InTHAF42iJuMArIiDIV5YtPanJkgw4hw5Dyg9fh0mihdWNn1GKaeIWErfe56UQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
@@ -4792,9 +4792,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bullmq": {
|
||||
"version": "5.66.7",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.66.7.tgz",
|
||||
"integrity": "sha512-X6YIjTXVN9fFjrMCKBppu74XZBnfWf0OgvwSVcpJE99irlszpGMKyyBcAOzd3126Lh9PwKygSfmHL4UfYrrIUQ==",
|
||||
"version": "5.67.1",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.67.1.tgz",
|
||||
"integrity": "sha512-ELJEAzwzesgFxk29emvnAakqrwdBEhEyfZREPQ8pbG4ALVz/mk/AhfuChzxkFpJ7SfL2qclPHbiUGBZzaqcLvg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron-parser": "4.9.0",
|
||||
|
||||
18
package.json
18
package.json
@@ -18,23 +18,23 @@
|
||||
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-cloudwatch-logs": "^3.974.0",
|
||||
"@aws-sdk/client-elasticache": "^3.974.0",
|
||||
"@aws-sdk/client-s3": "^3.974.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.974.0",
|
||||
"@aws-sdk/client-ses": "^3.974.0",
|
||||
"@aws-sdk/client-cloudwatch-logs": "^3.975.0",
|
||||
"@aws-sdk/client-elasticache": "^3.975.0",
|
||||
"@aws-sdk/client-s3": "^3.975.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.975.0",
|
||||
"@aws-sdk/client-ses": "^3.975.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.1",
|
||||
"@aws-sdk/lib-storage": "^3.974.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.974.0",
|
||||
"@aws-sdk/lib-storage": "^3.975.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.975.0",
|
||||
"@opensearch-project/opensearch": "^2.13.0",
|
||||
"@socket.io/admin-ui": "^0.5.1",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"archiver": "^7.0.1",
|
||||
"aws4": "^1.13.2",
|
||||
"axios": "^1.13.2",
|
||||
"axios": "^1.13.3",
|
||||
"axios-curlirize": "^2.0.0",
|
||||
"better-queue": "^3.8.12",
|
||||
"bullmq": "^5.66.7",
|
||||
"bullmq": "^5.67.1",
|
||||
"chart.js": "^4.5.1",
|
||||
"cloudinary": "^2.9.0",
|
||||
"compression": "^1.8.1",
|
||||
|
||||
@@ -221,6 +221,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
|
||||
const repairCosts = CreateCosts(job);
|
||||
|
||||
const LaborDetailLines = generateLaborLines(job.timetickets);
|
||||
|
||||
//Calculate detail only lines.
|
||||
const detailAdjustments = job.joblines
|
||||
.filter((jl) => jl.ah_detail_line && jl.mod_lbr_ty)
|
||||
@@ -606,12 +608,14 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
// CSIID: null,
|
||||
InsGroupCode: null
|
||||
},
|
||||
|
||||
DetailLines: {
|
||||
DetailLine:
|
||||
job.joblines.length > 0
|
||||
? job.joblines.map((jl) => GenerateDetailLines(job, jl, job.bodyshop.md_order_statuses))
|
||||
: [generateNullDetailLine()]
|
||||
},
|
||||
LaborDetailLines: {
|
||||
LaborDetailLine: LaborDetailLines
|
||||
}
|
||||
};
|
||||
return ret;
|
||||
@@ -787,6 +791,76 @@ const CreateCosts = (job) => {
|
||||
};
|
||||
};
|
||||
|
||||
const generateLaborLines = (timetickets) => {
|
||||
if (!timetickets || timetickets.length === 0) return [];
|
||||
|
||||
const codeToProps = {
|
||||
LAB: { actual: "LaborBodyActualHours", flag: "LaborBodyFlagHours", cost: "LaborBodyCost" },
|
||||
LAM: { actual: "LaborMechanicalActualHours", flag: "LaborMechanicalFlagHours", cost: "LaborMechanicalCost" },
|
||||
LAG: { actual: "LaborGlassActualHours", flag: "LaborGlassFlagHours", cost: "LaborGlassCost" },
|
||||
LAS: { actual: "LaborStructuralActualHours", flag: "LaborStructuralFlagHours", cost: "LaborStructuralCost" },
|
||||
LAE: { actual: "LaborElectricalActualHours", flag: "LaborElectricalFlagHours", cost: "LaborElectricalCost" },
|
||||
LAA: { actual: "LaborAluminumActualHours", flag: "LaborAluminumFlagHours", cost: "LaborAluminumCost" },
|
||||
LAR: { actual: "LaborRefinishActualHours", flag: "LaborRefinishFlagHours", cost: "LaborRefinishCost" },
|
||||
LAU: { actual: "LaborDetailActualHours", flag: "LaborDetailFlagHours", cost: "LaborDetailCost" },
|
||||
LA1: { actual: "LaborOtherActualHours", flag: "LaborOtherFlagHours", cost: "LaborOtherCost" },
|
||||
LA2: { actual: "LaborOtherActualHours", flag: "LaborOtherFlagHours", cost: "LaborOtherCost" },
|
||||
LA3: { actual: "LaborOtherActualHours", flag: "LaborOtherFlagHours", cost: "LaborOtherCost" },
|
||||
LA4: { actual: "LaborOtherActualHours", flag: "LaborOtherFlagHours", cost: "LaborOtherCost" }
|
||||
};
|
||||
|
||||
return timetickets.map((ticket, idx) => {
|
||||
const { ciecacode, employee, actualhrs = 0, productivehrs = 0, rate = 0 } = ticket;
|
||||
const isFlatRate = employee?.flat_rate;
|
||||
const hours = isFlatRate ? productivehrs : actualhrs;
|
||||
const cost = rate * hours;
|
||||
|
||||
const laborDetail = {
|
||||
LaborDetailLineNumber: idx + 1,
|
||||
TechnicianNameFirst: employee?.first_name || "",
|
||||
TechnicianNameLast: employee?.last_name || "",
|
||||
LaborBodyActualHours: 0,
|
||||
LaborMechanicalActualHours: 0,
|
||||
LaborGlassActualHours: 0,
|
||||
LaborStructuralActualHours: 0,
|
||||
LaborElectricalActualHours: 0,
|
||||
LaborAluminumActualHours: 0,
|
||||
LaborRefinishActualHours: 0,
|
||||
LaborDetailActualHours: 0,
|
||||
LaborOtherActualHours: 0,
|
||||
LaborBodyFlagHours: 0,
|
||||
LaborMechanicalFlagHours: 0,
|
||||
LaborGlassFlagHours: 0,
|
||||
LaborStructuralFlagHours: 0,
|
||||
LaborElectricalFlagHours: 0,
|
||||
LaborAluminumFlagHours: 0,
|
||||
LaborRefinishFlagHours: 0,
|
||||
LaborDetailFlagHours: 0,
|
||||
LaborOtherFlagHours: 0,
|
||||
LaborBodyCost: 0,
|
||||
LaborMechanicalCost: 0,
|
||||
LaborGlassCost: 0,
|
||||
LaborStructuralCost: 0,
|
||||
LaborElectricalCost: 0,
|
||||
LaborAluminumCost: 0,
|
||||
LaborRefinishCost: 0,
|
||||
LaborDetailCost: 0,
|
||||
LaborOtherCost: 0
|
||||
};
|
||||
|
||||
const effectiveCiecacode = ciecacode || "LA4";
|
||||
|
||||
if (codeToProps[effectiveCiecacode]) {
|
||||
const { actual, flag, cost: costProp } = codeToProps[effectiveCiecacode];
|
||||
laborDetail[actual] = actualhrs;
|
||||
laborDetail[flag] = productivehrs;
|
||||
laborDetail[costProp] = cost;
|
||||
}
|
||||
|
||||
return laborDetail;
|
||||
});
|
||||
};
|
||||
|
||||
const StatusMapping = (status, md_ro_statuses) => {
|
||||
//Possible return statuses EST, SCH, ARR, IPR, RDY, DEL, CLO, CAN, UNDEFINED.
|
||||
const {
|
||||
|
||||
@@ -827,13 +827,21 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
||||
quantity
|
||||
}
|
||||
}
|
||||
timetickets {
|
||||
timetickets(where: {cost_center: {_neq: "timetickets.labels.shift"}}) {
|
||||
id
|
||||
rate
|
||||
ciecacode
|
||||
cost_center
|
||||
actualhrs
|
||||
productivehrs
|
||||
flat_rate
|
||||
employeeid
|
||||
employee {
|
||||
employee_number
|
||||
flat_rate
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
}
|
||||
area_of_damage
|
||||
employee_prep_rel {
|
||||
|
||||
@@ -47,14 +47,14 @@ exports.totalsSsu = async function (req, res) {
|
||||
throw new Error("Failed to update job totals");
|
||||
}
|
||||
|
||||
res.status(200).send();
|
||||
res.status(200).json({ success: true });
|
||||
} catch (error) {
|
||||
logger.log("job-totals-ssu-USA-error", "error", req?.user?.email, id, {
|
||||
jobid: id,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
res.status(503).send();
|
||||
res.status(503).json({ error: "Failed to calculate totals" });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -47,14 +47,14 @@ exports.totalsSsu = async function (req, res) {
|
||||
throw new Error("Failed to update job totals");
|
||||
}
|
||||
|
||||
res.status(200).send();
|
||||
res.status(200).json({ success: true });
|
||||
} catch (error) {
|
||||
logger.log("job-totals-ssu-error", "error", req.user.email, id, {
|
||||
jobid: id,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
res.status(503).send();
|
||||
res.status(503).json({ error: "Failed to calculate totals" });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user