Merged in feature/IO-3497-Ant-Design-v5-to-v6 (pull request #2807)

Feature/IO-3497 Ant Design v5 to v6
This commit is contained in:
Dave Richer
2026-01-13 19:07:35 +00:00
394 changed files with 9262 additions and 8161 deletions

View File

@@ -0,0 +1,251 @@
# React Grid Layout Migration Guide
## Current Status: Legacy API (v2.2.2)
### What Changed
- **Package Version**: 1.3.4 → 2.2.2
- **API Strategy**: Using legacy compatibility layer
### Migration Completed ✅
#### Changes Made:
```javascript
// Before (v1.3.4):
import { Responsive, WidthProvider } from "react-grid-layout";
// After (v2.2.2 with legacy API):
import { Responsive, WidthProvider } from "react-grid-layout/legacy";
```
#### Files Updated:
- `src/components/dashboard-grid/dashboard-grid.component.jsx`
#### Why Legacy API?
The v2.x release introduces a completely new hooks-based API with breaking changes. The legacy API provides 100% backward compatibility, allowing us to:
- ✅ Get bug fixes and security updates
- ✅ Maintain existing functionality without code rewrites
- ✅ Plan migration to new API incrementally
---
## Future: Migration to New v2 API
When ready to fully migrate to the modern v2 API, follow this guide:
### Breaking Changes in v2
1. **Width Provider Removed**
- Old: `WidthProvider(Responsive)`
- New: Use `useContainerWidth` hook
2. **Props Restructured**
- Old: Flat props structure
- New: Grouped configs (`gridConfig`, `dragConfig`, `resizeConfig`)
3. **Layout Prop Required**
- Old: Could use `data-grid` attribute
- New: Must provide `layout` prop explicitly
4. **Compaction Changes**
- Old: `verticalCompact` prop
- New: `compactor` prop with pluggable algorithms
### Migration Steps
#### Step 1: Replace WidthProvider with useContainerWidth hook
**Before:**
```javascript
const ResponsiveReactGridLayout = WidthProvider(Responsive);
return (
<ResponsiveReactGridLayout
className="layout"
breakpoints={GRID_BREAKPOINTS}
cols={GRID_COLS}
layouts={state.layouts}
onLayoutChange={handleLayoutChange}
>
{children}
</ResponsiveReactGridLayout>
);
```
**After:**
```javascript
import ReactGridLayout, { useContainerWidth, verticalCompactor } from 'react-grid-layout';
function DashboardGridComponent({ currentUser }) {
const { width, containerRef, mounted } = useContainerWidth();
return (
<div ref={containerRef}>
{mounted && (
<ReactGridLayout
width={width}
layout={state.layout}
gridConfig={{
cols: 12,
rowHeight: 30,
margin: [10, 10]
}}
dragConfig={{
enabled: true,
handle: '.drag-handle' // optional
}}
resizeConfig={{
enabled: true
}}
compactor={verticalCompactor}
onLayoutChange={handleLayoutChange}
>
{children}
</ReactGridLayout>
)}
</div>
);
}
```
#### Step 2: Update Responsive Layouts
For responsive behavior, manage breakpoints manually:
```javascript
function DashboardGridComponent() {
const { width, containerRef, mounted } = useContainerWidth();
const [currentBreakpoint, setCurrentBreakpoint] = useState('lg');
useEffect(() => {
if (width > 1200) setCurrentBreakpoint('lg');
else if (width > 996) setCurrentBreakpoint('md');
else if (width > 768) setCurrentBreakpoint('sm');
else if (width > 480) setCurrentBreakpoint('xs');
else setCurrentBreakpoint('xxs');
}, [width]);
const currentLayout = state.layouts[currentBreakpoint] || state.layout;
const currentCols = GRID_COLS[currentBreakpoint];
return (
<div ref={containerRef}>
{mounted && (
<ReactGridLayout
width={width}
layout={currentLayout}
gridConfig={{
cols: currentCols,
rowHeight: 30
}}
// ... other props
>
{children}
</ReactGridLayout>
)}
</div>
);
}
```
#### Step 3: Update Child Components
The `data-grid` attribute still works, but explicitly managing layout is preferred:
**Before:**
```javascript
<div
key={item.i}
data-grid={{
...item,
minH,
minW
}}
>
{content}
</div>
```
**After (Preferred):**
```javascript
// Manage layout in parent state
const layout = state.items.map(item => ({
i: item.i,
x: item.x,
y: item.y,
w: item.w,
h: item.h,
minW: componentList[item.i]?.minW || 1,
minH: componentList[item.i]?.minH || 1
}));
// Children just need keys
<div key={item.i}>
{content}
</div>
```
#### Step 4: Update Styles (if needed)
The CSS classes remain mostly the same, but check the new documentation for any changes.
### Benefits of New API
- 🚀 **Better Performance**: Optimized rendering with hooks
- 📦 **TypeScript Support**: Full type definitions included
- 🎯 **Better API**: More intuitive props organization
- 🔧 **Extensibility**: Pluggable compactors and strategies
- 📱 **Modern React**: Uses hooks pattern
### Testing Checklist
When migrating to new API:
- [ ] Grid items render correctly
- [ ] Drag functionality works
- [ ] Resize functionality works
- [ ] Responsive breakpoints work
- [ ] Layout persistence works
- [ ] Add/remove components works
- [ ] Min/max constraints respected
- [ ] Performance is acceptable
- [ ] No console errors or warnings
### Resources
- [React Grid Layout v2 Documentation](https://github.com/react-grid-layout/react-grid-layout)
- [Migration Guide](https://www.npmjs.com/package/react-grid-layout)
- [Examples](https://github.com/react-grid-layout/react-grid-layout/tree/master/examples)
---
## Current Implementation Notes
### Component Structure
- **File**: `src/components/dashboard-grid/dashboard-grid.component.jsx`
- **Styles**: `src/components/dashboard-grid/dashboard-grid.styles.scss`
- **Pattern**: Responsive grid with dynamic component loading
### Key Features Used
- ✅ Responsive layouts with breakpoints
- ✅ Drag and drop
- ✅ Resize handles
- ✅ Layout persistence to database
- ✅ Dynamic component add/remove
- ✅ Min/max size constraints
### Configuration
```javascript
const GRID_BREAKPOINTS = { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 };
const GRID_COLS = { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 };
```
### Performance Considerations
- Layout changes debounced via database updates
- Memoized dashboard queries to prevent re-fetches
- Memoized menu items and layout keys
---
**Last Updated**: 2026-01-13
**Current Version**: react-grid-layout@2.2.2 (legacy API)
**Target Version**: react-grid-layout@2.2.2 (new API) - Future migration

4139
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,9 +10,9 @@
"dependencies": {
"@amplitude/analytics-browser": "^2.33.1",
"@ant-design/pro-layout": "^7.22.6",
"@apollo/client": "^3.13.9",
"@apollo/client": "^4.0.12",
"@emotion/is-prop-valid": "^1.4.0",
"@fingerprintjs/fingerprintjs": "^4.6.1",
"@fingerprintjs/fingerprintjs": "^5.0.1",
"@firebase/analytics": "^0.10.19",
"@firebase/app": "^0.14.6",
"@firebase/auth": "^1.12.0",
@@ -20,14 +20,13 @@
"@firebase/messaging": "^0.12.22",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.11.2",
"@sentry/cli": "^2.58.2",
"@sentry/react": "^9.43.0",
"@sentry/cli": "^3.1.0",
"@sentry/react": "^10.33.0",
"@sentry/vite-plugin": "^4.6.1",
"@splitsoftware/splitio-react": "^2.6.1",
"@tanem/react-nprogress": "^5.0.56",
"antd": "^5.28.1",
"apollo-link-logger": "^2.0.1",
"apollo-link-sentry": "^4.4.0",
"antd": "^6.2.0",
"apollo-link-logger": "^3.0.0",
"autosize": "^6.0.1",
"axios": "^1.13.2",
"classnames": "^2.5.1",
@@ -36,21 +35,22 @@
"dayjs-business-days2": "^1.3.2",
"dinero.js": "^1.9.1",
"dotenv": "^17.2.3",
"env-cmd": "^10.1.0",
"env-cmd": "^11.0.0",
"exifr": "^7.1.3",
"graphql": "^16.12.0",
"graphql-ws": "^6.0.6",
"i18next": "^25.7.4",
"i18next-browser-languagedetector": "^8.2.0",
"immutability-helper": "^3.1.1",
"libphonenumber-js": "^1.12.33",
"libphonenumber-js": "^1.12.34",
"lightningcss": "^1.30.2",
"logrocket": "^9.0.2",
"logrocket": "^11.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.315.1",
"posthog-js": "^1.319.1",
"prop-types": "^15.8.1",
"query-string": "^9.3.1",
"raf-schd": "^4.0.3",
@@ -61,8 +61,8 @@
"react-dom": "^18.3.1",
"react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.1",
"react-grid-layout": "1.3.4",
"react-i18next": "^15.7.3",
"react-grid-layout": "^2.2.2",
"react-i18next": "^16.5.2",
"react-icons": "^5.5.0",
"react-image-lightbox": "^5.1.4",
"react-markdown": "^10.1.0",
@@ -74,20 +74,20 @@
"react-router-dom": "^6.30.0",
"react-sticky": "^6.0.3",
"react-virtuoso": "^4.18.1",
"recharts": "^2.15.2",
"recharts": "^3.6.0",
"redux": "^5.0.1",
"redux-actions": "^3.0.3",
"redux-persist": "^6.0.0",
"redux-saga": "^1.4.2",
"redux-state-sync": "^3.1.4",
"reselect": "^5.1.1",
"rxjs": "^7.8.2",
"sass": "^1.97.2",
"socket.io-client": "^4.8.3",
"styled-components": "^6.2.0",
"subscriptions-transport-ws": "^0.11.0",
"styled-components": "^6.3.6",
"use-memo-one": "^1.1.3",
"vite-plugin-ejs": "^1.7.0",
"web-vitals": "^3.5.2"
"web-vitals": "^5.1.0"
},
"scripts": {
"postinstall": "echo 'when updating react-big-calendar, remember to check to localizer in the calendar wrapper'",
@@ -98,10 +98,10 @@
"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: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",
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular .",
"eulaize": "node src/utils/eulaize.js",
"test:unit": "vitest run",
@@ -141,18 +141,17 @@
"@emotion/react": "^11.14.0",
"@eslint/js": "^9.39.2",
"@playwright/test": "^1.57.0",
"@sentry/webpack-plugin": "^4.6.1",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1",
"@vitejs/plugin-react": "^4.6.0",
"@vitejs/plugin-react": "^5.1.2",
"browserslist": "^4.28.1",
"browserslist-to-esbuild": "^2.1.1",
"chalk": "^5.6.2",
"eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5",
"globals": "^15.15.0",
"jsdom": "^26.0.0",
"globals": "^17.0.0",
"jsdom": "^27.4.0",
"memfs": "^4.51.1",
"os-browserify": "^0.3.0",
"playwright": "^1.57.0",
@@ -160,12 +159,12 @@
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.3",
"vite": "^7.3.1",
"vite-plugin-babel": "^1.3.2",
"vite-plugin-babel": "^1.4.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-node-polyfills": "^0.24.0",
"vite-plugin-pwa": "^1.2.0",
"vite-plugin-style-import": "^2.0.0",
"vitest": "^3.2.4",
"vitest": "^4.0.17",
"workbox-window": "^7.4.0"
}
}

View File

@@ -1,4 +1,4 @@
import { ApolloProvider } from "@apollo/client";
import { ApolloProvider } from "@apollo/client/react";
import * as Sentry from "@sentry/react";
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd";

View File

@@ -176,7 +176,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
<Table
loading={loading}
dataSource={dataSource}
pagination={{ position: "top", pageSize: exportPageLimit }}
pagination={{ placement: "top", pageSize: exportPageLimit }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -189,7 +189,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
<Table
loading={loading}
dataSource={dataSource}
pagination={{ position: "top", pageSize: exportPageLimit }}
pagination={{ placement: "top", pageSize: exportPageLimit }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -211,7 +211,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
<Table
loading={loading}
dataSource={dataSource}
pagination={{ position: "top", pageSize: exportPageLimit }}
pagination={{ placement: "top", pageSize: exportPageLimit }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -4,27 +4,27 @@ import AlertComponent from "./alert.component";
describe("AlertComponent", () => {
it("renders with default props", () => {
render(<AlertComponent message="Default Alert" />);
render(<AlertComponent title="Default Alert" />);
expect(screen.getByText("Default Alert")).toBeInTheDocument();
expect(screen.getByRole("alert")).toHaveClass("ant-alert");
});
it("applies type prop correctly", () => {
render(<AlertComponent message="Success Alert" type="success" />);
render(<AlertComponent title="Success Alert" type="success" />);
const alert = screen.getByRole("alert");
expect(screen.getByText("Success Alert")).toBeInTheDocument();
expect(alert).toHaveClass("ant-alert-success");
});
it("displays description when provided", () => {
render(<AlertComponent message="Error Alert" description="Something went wrong" type="error" />);
render(<AlertComponent title="Error Alert" description="Something went wrong" type="error" />);
expect(screen.getByText("Error Alert")).toBeInTheDocument();
expect(screen.getByText("Something went wrong")).toBeInTheDocument();
expect(screen.getByRole("alert")).toHaveClass("ant-alert-error");
});
it("is closable and shows icon when props are set", () => {
render(<AlertComponent message="Warning Alert" type="warning" showIcon closable />);
render(<AlertComponent title="Warning Alert" type="warning" showIcon closable />);
expect(screen.getByText("Warning Alert")).toBeInTheDocument();
expect(screen.getByRole("button", { name: /close/i })).toBeInTheDocument(); // Close button
});

View File

@@ -28,12 +28,13 @@ export function AllocationsAssignmentComponent({
<div>
<Select
id="employeeSelector"
showSearch
showSearch={{
optionFilterProp: "children",
filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children"
onChange={onChange}
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
>
{bodyshop.employees.map((emp) => (
<Select.Option value={emp.id} key={emp.id}>

View File

@@ -1,6 +1,6 @@
import { useState } from "react";
import AllocationsAssignmentComponent from "./allocations-assignment.component";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { INSERT_ALLOCATION } from "../../graphql/allocations.queries";
import { useTranslation } from "react-i18next";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";

View File

@@ -30,12 +30,13 @@ export default connect(
const popContent = (
<div>
<Select
showSearch
showSearch={{
optionFilterProp: "children",
filterOption: (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}}
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children"
onChange={onChange}
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
>
{bodyshop.employees.map((emp) => (
<Select.Option value={emp.id} key={emp.id}>

View File

@@ -1,6 +1,6 @@
import { useState } from "react";
import AllocationsBulkAssignment from "./allocations-bulk-assignment.component";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { INSERT_ALLOCATION } from "../../graphql/allocations.queries";
import { useTranslation } from "react-i18next";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { DELETE_ALLOCATION } from "../../graphql/allocations.queries";
import AllocationsLabelComponent from "./allocations-employee-label.component";
import { useTranslation } from "react-i18next";

View File

@@ -65,7 +65,7 @@ export default function AuditTrailListComponent({ loading, data }) {
<Table
{...formItemLayout}
loading={loading}
pagination={{ position: "top", defaultPageSize: pageLimit }}
pagination={{ placement: "top", defaultPageSize: pageLimit }}
columns={columns}
rowKey="id"
dataSource={data}

View File

@@ -1,5 +1,5 @@
import AuditTrailListComponent from "./audit-trail-list.component";
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { QUERY_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";
import AlertComponent from "../alert/alert.component";
import { logImEXEvent } from "../../firebase/firebase.utils";
@@ -17,7 +17,7 @@ export default function AuditTrailListContainer({ recordId }) {
return (
<div>
{error ? (
<AlertComponent type="error" message={error.message} />
<AlertComponent type="error" title={error.message} />
) : (
<Row gutter={[16, 16]}>
<Card>

View File

@@ -50,7 +50,7 @@ export default function EmailAuditTrailListComponent({ loading, data }) {
<Table
{...formItemLayout}
loading={loading}
pagination={{ position: "top", defaultPageSize: pageLimit }}
pagination={{ placement: "top", defaultPageSize: pageLimit }}
columns={columns}
rowKey="id"
dataSource={data}

View File

@@ -1,5 +1,5 @@
import { DeleteFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Button, Popconfirm } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,5 +1,5 @@
import { PageHeader } from "@ant-design/pro-layout";
import { useMutation, useQuery } from "@apollo/client";
import { useMutation, useQuery } from "@apollo/client/react";
import { Button, Divider, Form, Popconfirm, Space } from "antd";
import queryString from "query-string";
import { useState } from "react";
@@ -148,7 +148,7 @@ export function BillDetailEditcontainer({ insertAuditTrail, bodyshop }) {
setUpdateLoading(false);
};
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
const exported = data?.bills_by_pk && data.bills_by_pk.exported;
@@ -189,7 +189,7 @@ export function BillDetailEditcontainer({ insertAuditTrail, bodyshop }) {
/>
<Form form={form} onFinish={handleFinish} initialValues={transformData(data)} layout="vertical">
<BillFormContainer form={form} billEdit disabled={exported} disableInHouse={isinhouse} />
<Divider orientation="left">{t("general.labels.media")}</Divider>
<Divider titlePlacement="left">{t("general.labels.media")}</Divider>
{bodyshop.uselocalmediaserver ? (
<JobsDocumentsLocalGallery
job={{ id: data ? data.bills_by_pk.jobid : null }}

View File

@@ -1,4 +1,4 @@
import { useApolloClient, useMutation } from "@apollo/client";
import { useApolloClient, useMutation } from "@apollo/client/react";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { Button, Checkbox, Form, Modal, Space } from "antd";
import _ from "lodash";

View File

@@ -1,5 +1,5 @@
import Icon, { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { useApolloClient } from "@apollo/client/react";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { Alert, Divider, Form, Input, Select, Space, Statistic, Switch, Upload } from "antd";
import { useEffect, useState } from "react";
@@ -98,13 +98,11 @@ export function BillFormComponent({
}
const jobId = form.getFieldValue("jobid");
if (jobId) {
loadLines({ variables: { id: jobId } });
loadLines({ id: jobId });
if (form.getFieldValue("is_credit_memo") && vendorId && !billEdit) {
loadOutstandingReturns({
variables: {
jobId: jobId,
vendorId: vendorId
}
jobId: jobId,
vendorId: vendorId
});
}
}
@@ -146,13 +144,11 @@ export function BillFormComponent({
notExported={false}
onBlur={() => {
if (form.getFieldValue("jobid") !== null && form.getFieldValue("jobid") !== undefined) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
loadLines({ id: form.getFieldValue("jobid") });
if (form.getFieldValue("vendorid") !== null && form.getFieldValue("vendorid") !== undefined) {
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid")
}
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid")
});
}
}
@@ -413,15 +409,17 @@ export function BillFormComponent({
/>
<Statistic
title={t("bills.labels.discrepancy")}
valueStyle={{
color: totals.discrepancy.getAmount() === 0 ? "green" : "red"
styles={{
value: {
color: totals.discrepancy.getAmount() === 0 ? "green" : "red"
}
}}
value={totals.discrepancy.toFormat()}
precision={2}
/>
</Space>
{form.getFieldValue("is_credit_memo") ? (
<AlertComponent type="warning" message={t("bills.labels.enteringcreditmemo")} />
<AlertComponent type="warning" title={t("bills.labels.enteringcreditmemo")} />
) : null}
</div>
);
@@ -429,7 +427,7 @@ export function BillFormComponent({
}}
</Form.Item>
</LayoutFormRow>
<Divider orientation="left">{t("bills.labels.bill_lines")}</Divider>
<Divider titlePlacement="left">{t("bills.labels.bill_lines")}</Divider>
{Extended_Bill_Posting.treatment === "on" ? (
<BillFormLinesExtended
@@ -449,7 +447,7 @@ export function BillFormComponent({
billEdit={billEdit}
/>
)}
<Divider orientation="left" style={{ display: billEdit ? "none" : null }}>
<Divider titlePlacement="left" style={{ display: billEdit ? "none" : null }}>
{t("documents.labels.upload")}
</Divider>
<Form.Item

View File

@@ -1,4 +1,4 @@
import { useLazyQuery, useQuery } from "@apollo/client";
import { useLazyQuery, useQuery } from "@apollo/client/react";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -10,18 +10,19 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
<Select
disabled={disabled}
ref={ref}
showSearch
showSearch={{
filterOption: (inputValue, option) => {
return (
(option.line_desc && option.line_desc.toLowerCase().includes(inputValue.toLowerCase())) ||
(option.oem_partno && option.oem_partno.toLowerCase().includes(inputValue.toLowerCase())) ||
(option.alt_partno && option.alt_partno.toLowerCase().includes(inputValue.toLowerCase())) ||
(option.act_price && option.act_price.toString().startsWith(inputValue.toString()))
);
}
}}
popupMatchSelectWidth={true}
optionLabelProp={"name"}
// optionFilterProp="line_desc"
filterOption={(inputValue, option) => {
return (
(option.line_desc && option.line_desc.toLowerCase().includes(inputValue.toLowerCase())) ||
(option.oem_partno && option.oem_partno.toLowerCase().includes(inputValue.toLowerCase())) ||
(option.alt_partno && option.alt_partno.toLowerCase().includes(inputValue.toLowerCase())) ||
(option.act_price && option.act_price.toString().startsWith(inputValue.toString()))
);
}}
notFoundContent={"Removed."}
options={[
{ value: "noline", label: t("billlines.labels.other"), name: t("billlines.labels.other") },

View File

@@ -1,4 +1,6 @@
import { gql, useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { gql } from "@apollo/client";
import { Button } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,4 +1,5 @@
import { gql, useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { gql } from "@apollo/client";
import { Button } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,5 +1,5 @@
import { FileAddFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Button, Tooltip } from "antd";
import { t } from "i18next";
import dayjs from "./../../utils/day";

View File

@@ -1,6 +1,6 @@
import { useState } from "react";
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import queryString from "query-string";
import { useLocation, useNavigate } from "react-router-dom";
import { Input, Table } from "antd";
@@ -67,7 +67,7 @@ export default function BillsVendorsList() {
setState({ ...state, search: e.target.value });
};
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
const dataSource = state.search
? data.vendors.filter(
@@ -89,7 +89,7 @@ export default function BillsVendorsList() {
);
}}
dataSource={dataSource}
pagination={{ position: "top" }}
pagination={{ placement: "top" }}
columns={columns}
rowKey="id"
onChange={handleTableChange}

View File

@@ -1,5 +1,5 @@
import { CopyFilled, DeleteFilled } from "@ant-design/icons";
import { useLazyQuery, useMutation } from "@apollo/client";
import { useLazyQuery, useMutation } from "@apollo/client/react";
import { Button, Card, Col, Form, Input, message, Row, Space, Spin, Statistic } from "antd";
import axios from "axios";
import { useState } from "react";
@@ -52,7 +52,6 @@ const CardPaymentModalComponent = ({
const notification = useNotification();
const [, { data, refetch, queryLoading }] = useLazyQuery(QUERY_RO_AND_OWNER_BY_JOB_PKS, {
variables: { jobids: [context.jobid] },
skip: !context?.jobid
});
@@ -258,7 +257,7 @@ const CardPaymentModalComponent = ({
//If all of the job ids have been fileld in, then query and update the IP field.
const { payments } = form.getFieldsValue();
if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) {
refetch({ jobids: payments.map((p) => p.jobid) });
refetch({ variables: { jobids: payments.map((p) => p.jobid) } });
}
return (
<>
@@ -316,7 +315,7 @@ const CardPaymentModalComponent = ({
>
{t("job_payments.buttons.proceedtopayment")}
</Button>
<Space direction="vertical" align="center">
<Space orientation="vertical" align="center">
<Button
type="primary"
// data-ipayname="submit"

View File

@@ -1,4 +1,4 @@
import { useApolloClient } from "@apollo/client";
import { useApolloClient } from "@apollo/client/react";
import { getToken } from "@firebase/messaging";
import axios from "axios";
import { useEffect } from "react";

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Button } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -11,7 +11,7 @@ import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-displ
import _ from "lodash";
import { ExclamationCircleOutlined } from "@ant-design/icons";
import "./chat-conversation-list.styles.scss";
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { GET_PHONE_NUMBER_OPT_OUTS_BY_NUMBERS } from "../../graphql/phone-number-opt-out.queries.js";
import { phone } from "phone";
import { useTranslation } from "react-i18next";
@@ -95,7 +95,7 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
<>
{item.label && <Tag color="blue">{item.label}</Tag>}
{item.job_conversations.length > 0 ? (
<Space direction="vertical">{names}</Space>
<Space orientation="vertical">{names}</Space>
) : (
<Space>
<PhoneFormatter>{item.phone_num}</PhoneFormatter>

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Tag } from "antd";
import { Link } from "react-router-dom";
import { logImEXEvent } from "../../firebase/firebase.utils";

View File

@@ -27,7 +27,7 @@ export function ChatConversationComponent({
if (conversation?.archived) return null;
if (loading) return <LoadingSkeleton />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
return (
<div

View File

@@ -1,4 +1,5 @@
import { gql, useApolloClient, useQuery, useSubscription } from "@apollo/client";
import { useApolloClient, useQuery, useSubscription } from "@apollo/client/react";
import { gql } from "@apollo/client";
import axios from "axios";
import { useCallback, useEffect, useMemo, useState } from "react";
import { connect } from "react-redux";

View File

@@ -1,5 +1,5 @@
import { PlusOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Input, Spin, Tag, Tooltip } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,5 +1,5 @@
import { PictureFilled } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { Badge, Popover } from "antd";
import { useEffect, useState } from "react";
@@ -63,7 +63,7 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
const content = (
<div className="media-selector-content">
{loading && <LoadingSpinner />}
{error && <AlertComponent message={error.message} type="error" />}
{error && <AlertComponent title={error.message} type="error" />}
{selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
<div className="error-message">{t("messaging.labels.maxtenimages")}</div>
) : null}

View File

@@ -1,5 +1,5 @@
import { InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined } from "@ant-design/icons";
import { useApolloClient, useLazyQuery, useQuery } from "@apollo/client";
import { useApolloClient, useLazyQuery, useQuery } from "@apollo/client/react";
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -51,21 +51,29 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
// Query for unread count when chat is not visible and socket is not connected.
// (Once socket connects, we stop this query; we keep the last known value in state.)
useQuery(UNREAD_CONVERSATION_COUNT, {
const { data: unreadData, error: unreadError } = useQuery(UNREAD_CONVERSATION_COUNT, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
skip: chatVisible || socket?.connected,
pollInterval: socket?.connected ? 0 : 60 * 1000,
onCompleted: (result) => {
const nextCount = result?.messages_aggregate?.aggregate?.count;
if (typeof nextCount === "number") setUnreadAggregateCount(nextCount);
},
onError: (err) => {
// Keep last known count; do not force badge to zero on transient failures
console.warn("UNREAD_CONVERSATION_COUNT failed:", err?.message || err);
}
pollInterval: socket?.connected ? 0 : 60 * 1000
});
// Handle unread count updates in useEffect
useEffect(() => {
if (unreadData) {
const nextCount = unreadData?.messages_aggregate?.aggregate?.count;
if (typeof nextCount === "number") setUnreadAggregateCount(nextCount);
}
}, [unreadData]);
// Handle unread count errors in useEffect
useEffect(() => {
if (unreadError) {
// Keep last known count; do not force badge to zero on transient failures
console.warn("UNREAD_CONVERSATION_COUNT failed:", unreadError?.message || unreadError);
}
}, [unreadError]);
// Socket connection status -> polling strategy for CONVERSATION_LIST_QUERY
useEffect(() => {
const handleSocketStatus = () => {
@@ -97,9 +105,7 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
hasLoadedConversationsOnceRef.current = true;
getConversations({
variables: { offset: 0 }
}).catch((err) => {
getConversations({ offset: 0 }).catch((err) => {
console.error(`Error fetching conversations: ${err?.message || ""}`, err);
});
}, [getConversations]);
@@ -107,9 +113,9 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
const handleManualRefresh = async () => {
try {
if (called && typeof refetch === "function") {
await refetch({ offset: 0 });
await refetch({ variables: { offset: 0 } });
} else {
await getConversations({ variables: { offset: 0 } });
await getConversations({ offset: 0 });
}
} catch (err) {
console.error(`Error refreshing conversations: ${err?.message || ""}`, err);

View File

@@ -10,7 +10,7 @@ import { selectIsSending, selectMessage } from "../../redux/messaging/messaging.
import { selectBodyshop } from "../../redux/user/user.selectors";
import ChatMediaSelector from "../chat-media-selector/chat-media-selector.component";
import ChatPresetsComponent from "../chat-presets/chat-presets.component";
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { phone } from "phone";
import { GET_PHONE_NUMBER_OPT_OUT } from "../../graphql/phone-number-opt-out.queries";
@@ -68,13 +68,13 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
};
return (
<Space direction="vertical" style={{ width: "100%" }} size="middle">
<Space orientation="vertical" style={{ width: "100%" }} size="middle">
{isOptedOut && (
<Tooltip title={t("consent.text_body")}>
<Alert
showIcon={true}
icon={<ExclamationCircleOutlined />}
message={t("messaging.errors.no_consent")}
title={t("messaging.errors.no_consent")}
type="error"
/>
</Tooltip>

View File

@@ -10,12 +10,13 @@ export default function ChatTagRoComponent({ roOptions, loading, handleSearch, h
<Space>
<div style={{ width: "15rem" }}>
<Select
showSearch
showSearch={{
filterOption: false,
onSearch: handleSearch
}}
autoFocus
popupMatchSelectWidth
placeholder={t("general.labels.search")}
filterOption={false}
onSearch={handleSearch}
onSelect={handleInsertTag}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
>

View File

@@ -1,5 +1,5 @@
import { PlusOutlined } from "@ant-design/icons";
import { useLazyQuery, useMutation } from "@apollo/client";
import { useLazyQuery, useMutation } from "@apollo/client/react";
import { Tag } from "antd";
import _ from "lodash";
import { useState } from "react";
@@ -34,7 +34,7 @@ export function ChatTagRoContainer({ conversation, bodyshop }) {
const debouncedExecuteSearch = _.debounce(executeSearch, 500);
const handleSearch = (value) => {
debouncedExecuteSearch({ variables: { search: value } });
debouncedExecuteSearch({ search: value });
};
const [insertTag] = useMutation(INSERT_CONVERSATION_TAG, {

View File

@@ -104,7 +104,7 @@ export default function ContractsCarsComponent({ loading, data, selectedCarId, h
>
<Table
loading={loading}
pagination={{ position: "top" }}
pagination={{ placement: "top" }}
columns={columns}
rowKey="id"
dataSource={filteredData}

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import dayjs from "../../utils/day";
import { QUERY_AVAILABLE_CC } from "../../graphql/courtesy-car.queries";
import AlertComponent from "../alert/alert.component";
@@ -24,7 +24,7 @@ export default function ContractCarsContainer({ selectedCarState, form }) {
});
};
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
return (
<ContractCarsComponent
handleSelect={handleSelect}

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Button, Form, InputNumber, Popover, Radio, Select, Space } from "antd";
import axios from "axios";
import dayjs from "../../utils/day";

View File

@@ -1,4 +1,4 @@
import { useLazyQuery } from "@apollo/client";
import { useLazyQuery } from "@apollo/client/react";
import { Button } from "antd";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
@@ -11,7 +11,7 @@ export default function ContractCreateJobPrefillComponent({ jobId, form }) {
const notification = useNotification();
const handleClick = () => {
call({ variables: { id: jobId } });
call({ id: jobId });
};
useEffect(() => {

View File

@@ -67,7 +67,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
.isBefore(dayjs(form.getFieldValue("scheduledreturn")));
if (insuranceOver)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<Space orientation="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.insuranceexpired")}
@@ -107,7 +107,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
.isSameOrBefore(dayjs(form.getFieldValue("scheduledreturn")));
if (mileageOver || dueForService)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<Space orientation="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")}

View File

@@ -128,11 +128,7 @@ export default function ContractsJobsComponent({ loading, data, selectedJob, han
>
<Table
loading={loading}
pagination={{
position: "top",
defaultPageSize: pageLimit,
defaultCurrent: defaultCurrent
}}
pagination={{ placement: "top", defaultPageSize: pageLimit, defaultCurrent: defaultCurrent }}
columns={columns}
rowKey="id"
dataSource={filteredData}

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
@@ -26,7 +26,7 @@ export function ContractJobsContainer({ selectedJobState, bodyshop }) {
setSelectedJob(record.id);
};
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
return (
<ContractJobsComponent
handleSelect={handleSelect}

View File

@@ -1,4 +1,4 @@
import { useLazyQuery } from "@apollo/client";
import { useLazyQuery } from "@apollo/client/react";
import { Button, Form, Modal, Table } from "antd";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
@@ -35,10 +35,8 @@ export function ContractsFindModalContainer({ contractFinderModal, toggleModalVi
//Execute contract find
callSearch({
variables: {
plate: (values.plate && values.plate !== "" && values.plate) || undefined,
time: values.time
}
plate: (values.plate && values.plate !== "" && values.plate) || undefined,
time: values.time
});
};
@@ -63,7 +61,7 @@ export function ContractsFindModalContainer({ contractFinderModal, toggleModalVi
<Button onClick={() => form.submit()} type="primary" loading={loading}>
{t("general.labels.search")}
</Button>
{error && <AlertComponent type="error" message={JSON.stringify(error)} />}
{error && <AlertComponent type="error" title={JSON.stringify(error)} />}
<Table
loading={loading}
columns={[

View File

@@ -172,12 +172,7 @@ export function ContractsList({ bodyshop, loading, contracts, refetch, total, se
scroll={{
x: "50%" //y: "40rem"
}}
pagination={{
position: "top",
pageSize: pageLimit,
current: parseInt(page || 1, 10),
total: total
}}
pagination={{ placement: "top", pageSize: pageLimit, current: parseInt(page || 1, 10), total: total }}
columns={columns}
rowKey="id"
dataSource={contracts}

View File

@@ -75,12 +75,7 @@ export default function CourtesyCarContractListComponent({ contracts, totalContr
<Card title={t("menus.header.courtesycars-contracts")}>
<Table
scroll={{ x: true }}
pagination={{
position: "top",
pageSize: pageLimit,
current: parseInt(page || 1),
total: totalContracts
}}
pagination={{ placement: "top", pageSize: pageLimit, current: parseInt(page || 1), total: totalContracts }}
columns={columns}
rowKey="id"
dataSource={contracts}

View File

@@ -1,5 +1,5 @@
import { WarningFilled } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { useApolloClient } from "@apollo/client/react";
import { Button, Form, Input, InputNumber, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import dayjs from "../../utils/day";
@@ -208,7 +208,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
const mileageOver = nextservicekm ? nextservicekm <= form.getFieldValue("mileage") : false;
if (mileageOver)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<Space orientation="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")}
@@ -232,7 +232,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
if (dueForService)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<Space orientation="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")}
@@ -265,7 +265,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
if (dateover)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<Space orientation="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.dateinpast")}
@@ -298,7 +298,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
if (dateover)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<Space orientation="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.dateinpast")}

View File

@@ -8,7 +8,7 @@ import { selectCourtesyCarReturn } from "../../redux/modals/modals.selectors";
import CourtesyCarReturnModalComponent from "./courtesy-car-return-modal.component";
import dayjs from "../../utils/day";
import { RETURN_CONTRACT } from "../../graphql/cccontracts.queries";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
const mapStateToProps = createStructuredSelector({

View File

@@ -278,7 +278,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
>
<Table
loading={loading}
pagination={{ position: "top" }}
pagination={{ placement: "top" }}
columns={columns}
rowKey="id"
dataSource={tableData}

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { Card, Form, Result } from "antd";
import queryString from "query-string";
import { useEffect } from "react";
@@ -36,7 +36,7 @@ export default function CsiResponseFormContainer() {
);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
return (
<Card>

View File

@@ -94,12 +94,7 @@ export default function CsiResponseListPaginated({ refetch, loading, responses,
>
<Table
loading={loading}
pagination={{
position: "top",
pageSize: pageLimit,
current: parseInt(state.page || 1),
total: total
}}
pagination={{ placement: "top", pageSize: pageLimit, current: parseInt(state.page || 1), total: total }}
columns={columns}
rowKey="id"
dataSource={responses}

View File

@@ -60,7 +60,7 @@ export default function DashboardMonthlyEmployeeEfficiency({ data, ...cardProps
return (
<Card title={t("dashboard.titles.monthlyemployeeefficiency")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ResponsiveContainer width="100%" height="100%" minHeight={100}>
<ComposedChart data={chartData} margin={{ top: 20, right: 20, bottom: 20, left: 20 }}>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />

View File

@@ -105,7 +105,7 @@ export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
<div style={{ height: "100%" }}>
<Table
onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: pageLimit }}
pagination={{ placement: "top", defaultPageSize: pageLimit }}
columns={columns}
scroll={{ x: true, y: "calc(100% - 4em)" }}
rowKey="id"

View File

@@ -1,13 +1,11 @@
import { Card } from "antd";
import Dinero from "dinero.js";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyLaborSales({ data, ...cardProps }) {
const { t } = useTranslation();
const [activeIndex, setActiveIndex] = useState(0);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
@@ -36,19 +34,17 @@ export default function DashboardMonthlyLaborSales({ data, ...cardProps }) {
return (
<Card title={t("dashboard.titles.monthlylaborsales")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ResponsiveContainer width="100%" height="100%" minHeight={100}>
<PieChart margin={0} padding={0}>
<Pie
data={chartData}
activeIndex={activeIndex}
activeShape={renderActiveShape}
shape={renderActiveShape}
cx="50%"
cy="50%"
innerRadius="60%"
// outerRadius={80}
fill="#8884d8"
dataKey="value"
onMouseEnter={(throwaway, index) => setActiveIndex(index)}
>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
@@ -95,7 +91,8 @@ const renderActiveShape = (props) => {
fill,
payload,
// percent,
value
value,
isActive
} = props;
// const sin = Math.sin(-RADIAN * midAngle);
// const cos = Math.cos(-RADIAN * midAngle);
@@ -109,12 +106,16 @@ const renderActiveShape = (props) => {
return (
<g>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
{isActive && (
<>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
</>
)}
<Sector
cx={cx}
cy={cy}
@@ -124,15 +125,17 @@ const renderActiveShape = (props) => {
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
{isActive && (
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
)}
</g>
);
};

View File

@@ -1,13 +1,11 @@
import { Card } from "antd";
import Dinero from "dinero.js";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
const { t } = useTranslation();
const [activeIndex, setActiveIndex] = useState(0);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
@@ -34,19 +32,17 @@ export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
return (
<Card title={t("dashboard.titles.monthlypartssales")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ResponsiveContainer width="100%" height="100%" minHeight={100}>
<PieChart margin={0} padding={0}>
<Pie
data={chartData}
activeIndex={activeIndex}
activeShape={renderActiveShape}
shape={renderActiveShape}
cx="50%"
cy="50%"
innerRadius="60%"
// outerRadius={80}
fill="#8884d8"
dataKey="value"
onMouseEnter={(throwaway, index) => setActiveIndex(index)}
>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
@@ -91,7 +87,8 @@ const renderActiveShape = (props) => {
fill,
payload,
// percent,
value
value,
isActive
} = props;
// const sin = Math.sin(-RADIAN * midAngle);
// const cos = Math.cos(-RADIAN * midAngle);
@@ -105,12 +102,16 @@ const renderActiveShape = (props) => {
return (
<g>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
{isActive && (
<>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
</>
)}
<Sector
cx={cx}
cy={cy}
@@ -120,15 +121,17 @@ const renderActiveShape = (props) => {
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
{isActive && (
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
)}
</g>
);
};

View File

@@ -40,7 +40,7 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
return (
<Card title={t("dashboard.titles.monthlyrevenuegraph")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ResponsiveContainer width="100%" height="100%" minHeight={100}>
<ComposedChart data={chartData} margin={{ top: 20, right: 20, bottom: 20, left: 20 }}>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />

View File

@@ -36,7 +36,7 @@ export function DashboardTotalProductionHours({ bodyshop, data, ...cardProps })
<Statistic
title={t("dashboard.labels.prodhrs")}
value={hours.total.toFixed(1)}
valueStyle={{ color: aboveTargetHours ? "green" : "red" }}
styles={{ value: { color: aboveTargetHours ? "green" : "red" } }}
/>
</Space>
</Card>

View File

@@ -1,15 +1,15 @@
import Icon, { SyncOutlined } from "@ant-design/icons";
import { useMutation, useQuery } from "@apollo/client";
import { useMutation, useQuery } from "@apollo/client/react";
import { Button, Dropdown, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import { useMemo, useState, useEffect } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import { useEffect, useMemo, useState } from "react";
import { Responsive, WidthProvider } from "react-grid-layout/legacy";
import { useTranslation } from "react-i18next";
import { MdClose } from "react-icons/md";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_DASHBOARD_LAYOUT, QUERY_USER_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
import { QUERY_USER_DASHBOARD_LAYOUT, UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
import { QUERY_DASHBOARD_BODYSHOP } from "../../graphql/bodyshop.queries";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
@@ -156,7 +156,7 @@ export function DashboardGridComponent({ currentUser }) {
);
if (loading || dashboardLoading) return <LoadingSkeleton message={t("general.labels.loading")} />;
if (error || dashboardError) return <AlertComponent message={(error || dashboardError).message} type="error" />;
if (error || dashboardError) return <AlertComponent title={(error || dashboardError).message} type="error" />;
const handleLayoutChange = async (layout, layouts) => {
logImEXEvent("dashboard_change_layout");

View File

@@ -5,7 +5,7 @@ export default function DataLabel({
hideIfNull,
children,
open = true,
valueStyle = {},
styles,
valueClassName,
onValueClick,
...props
@@ -33,7 +33,11 @@ export default function DataLabel({
className={valueClassName}
onClick={onValueClick}
>
{typeof children === "string" ? <Typography.Text style={valueStyle}>{children}</Typography.Text> : children}
{typeof children === "string" ? (
<Typography.Text style={styles?.value}>{children}</Typography.Text>
) : (
children
)}
</div>
</div>
);

View File

@@ -112,7 +112,7 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
}
>
<Table
pagination={{ position: "top", defaultPageSize: pageLimit }}
pagination={{ placement: "top", defaultPageSize: pageLimit }}
columns={columns}
rowKey={(record) => `${record.InvoiceNumber}${record.Account}`}
dataSource={allocationsSummary}

View File

@@ -112,11 +112,11 @@ export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title, on
}
>
{bodyshop.pbs_configuration?.disablebillwip && (
<Alert type="warning" message={t("jobs.labels.dms.disablebillwip")} />
<Alert type="warning" title={t("jobs.labels.dms.disablebillwip")} />
)}
<Table
pagination={{ position: "top", defaultPageSize: pageLimit }}
pagination={{ placement: "top", defaultPageSize: pageLimit }}
columns={columns}
rowKey="center"
dataSource={allocationsSummary}

View File

@@ -331,10 +331,10 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat
}
>
{bodyshop.pbs_configuration?.disablebillwip && (
<Alert type="warning" message={t("jobs.labels.dms.disablebillwip")} />
<Alert type="warning" title={t("jobs.labels.dms.disablebillwip")} />
)}
{error && <Alert type="error" style={{ marginTop: 8, marginBottom: 8 }} message={error} />}
{error && <Alert type="error" style={{ marginTop: 8, marginBottom: 8 }} title={error} />}
<Tabs defaultActiveKey="rogog" items={tabItems} />
</Card>

View File

@@ -1,4 +1,4 @@
import { useLazyQuery } from "@apollo/client";
import { useLazyQuery } from "@apollo/client/react";
import { Button, Input, Modal, Table } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -56,11 +56,11 @@ export function DmsCdkVehicles({ form, job }) {
setOpen(false);
}}
>
{error && <AlertComponent error={error.message} />}
{error && <AlertComponent title={error.message} type="error" />}
<Table
title={() => (
<Input.Search
onSearch={(val) => callSearch({ variables: { search: val } })}
onSearch={(val) => callSearch({ search: val })}
placeholder={t("general.labels.search")}
/>
)}
@@ -87,9 +87,7 @@ export function DmsCdkVehicles({ form, job }) {
onClick={() => {
setOpen(true);
callSearch({
variables: {
search: job?.v_model_desc && job.v_model_desc.substr(0, 3)
}
search: job?.v_model_desc && job.v_model_desc.substr(0, 3)
});
}}
>

View File

@@ -84,7 +84,7 @@ export default function CDKCustomerSelector({ bodyshop, socket }) {
<Button onClick={onCreateNew}>{t("jobs.actions.dms.createnewcustomer")}</Button>
</div>
)}
pagination={{ position: "top" }}
pagination={{ placement: "top" }}
columns={columns}
rowKey={rowKey}
dataSource={customerList}

View File

@@ -90,7 +90,7 @@ export default function FortellisCustomerSelector({ bodyshop, jobid, socket }) {
<Button onClick={onCreateNew}>{t("jobs.actions.dms.createnewcustomer")}</Button>
</div>
)}
pagination={{ position: "top" }}
pagination={{ placement: "top" }}
columns={columns}
rowKey={(r) => r.customerId}
dataSource={customerList}

View File

@@ -78,7 +78,7 @@ export default function PBSCustomerSelector({ bodyshop, socket }) {
<Button onClick={onCreateNew}>{t("jobs.actions.dms.createnewcustomer")}</Button>
</div>
)}
pagination={{ position: "top" }}
pagination={{ placement: "top" }}
columns={columns}
rowKey={(r) => r.ContactId}
dataSource={customerList}

View File

@@ -179,7 +179,7 @@ export default function RRCustomerSelector({
<Alert
type="error"
showIcon
message="Open RO limit reached in Reynolds"
title="Open RO limit reached in Reynolds"
description={
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<div>
@@ -201,7 +201,7 @@ export default function RRCustomerSelector({
<Alert
type="info"
showIcon
message="Complete Validation in Reynolds"
title="Complete Validation in Reynolds"
description={
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<div>
@@ -234,7 +234,7 @@ export default function RRCustomerSelector({
<Alert
type="warning"
showIcon
message="VIN ownership enforced"
title="VIN ownership enforced"
description={
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12 }}>
<div>
@@ -251,7 +251,7 @@ export default function RRCustomerSelector({
)}
</div>
)}
pagination={{ position: "top" }}
pagination={{ placement: "top" }}
columns={columns}
rowKey={(r) => r.custNo}
dataSource={customerList}

View File

@@ -70,17 +70,17 @@ export function DmsLogEvents({
key: idx,
color: logLevelColor(level),
children: (
<Space direction="vertical" size={4} style={{ display: "flex" }}>
<Space orientation="vertical" size={4} style={{ display: "flex" }}>
{/* Row 1: summary + inline "Details" toggle */}
<Space wrap align="start">
<Tag color={logLevelColor(level)}>{level}</Tag>
<Divider type="vertical" />
<Divider orientation="vertical" />
<span>{dayjs(timestamp).format("MM/DD/YYYY HH:mm:ss")}</span>
<Divider type="vertical" />
<Divider orientation="vertical" />
<span>{message}</span>
{hasMeta && (
<>
<Divider type="vertical" />
<Divider orientation="vertical" />
<a
role="button"
aria-expanded={isOpen}

View File

@@ -404,7 +404,7 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode,
<Typography.Title>=</Typography.Title>
<Statistic
title={t("jobs.labels.dms.notallocated")}
valueStyle={{ color: discrep.getAmount() === 0 ? "green" : "red" }}
styles={{ value: { color: discrep.getAmount() === 0 ? "green" : "red" } }}
value={discrep.toFormat()}
/>
<Button disabled={disablePost} htmlType="submit">

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { Result } from "antd";
import queryString from "query-string";
import { useEffect } from "react";
@@ -51,7 +51,7 @@ export function DocumentEditorContainer({ setBodyshop }) {
}, [dataShop, setBodyshop]);
if (loadingShop) return <LoadingSpinner />;
if (errorShop) return <AlertComponent message={errorShop.message} type="error" />;
if (errorShop) return <AlertComponent title={errorShop.message} type="error" />;
if (isLocalMedia) {
if (imageUrl && filename && jobid) {
@@ -66,7 +66,7 @@ export function DocumentEditorContainer({ setBodyshop }) {
}
if (loadingDoc) return <LoadingSpinner />;
if (errorDoc) return <AlertComponent message={errorDoc.message} type="error" />;
if (errorDoc) return <AlertComponent title={errorDoc.message} type="error" />;
if (!dataDoc || !dataDoc.documents_by_pk) return <Result status="404" title={t("general.errors.notfound")} />;
return (

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -45,7 +45,7 @@ export function EmailDocumentsComponent({ emailConfig, form, selectedMediaState,
return (
<div>
{loading && <LoadingSpinner />}
{error && <AlertComponent message={error.message} type="error" />}
{error && <AlertComponent title={error.message} type="error" />}
{selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
<div style={{ color: "red" }}>{t("messaging.labels.maxtenimages")}</div>
) : null}

View File

@@ -9,12 +9,13 @@ const EmployeeSearchSelectEmail = ({ options, ...props }) => {
return (
<Select
showSearch
showSearch={{
optionFilterProp: "search"
}}
// value={option}
style={{
width: 400
}}
optionFilterProp="search"
{...props}
>
{options

View File

@@ -9,12 +9,13 @@ const EmployeeSearchSelect = ({ options, showEmail, ...props }) => {
return (
<Select
showSearch
showSearch={{
optionFilterProp: "search"
}}
// value={option}
style={{
width: 400
}}
optionFilterProp="search"
{...props}
>
{options

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { Select } from "antd";
import { forwardRef } from "react";
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
@@ -9,7 +9,7 @@ import AlertComponent from "../alert/alert.component";
const EmployeeTeamSearchSelect = ({ ...props }) => {
const { loading, error, data } = useQuery(QUERY_TEAMS);
if (error) return <AlertComponent message={JSON.stringify(error)} />;
if (error) return <AlertComponent title={JSON.stringify(error)} type="error" />;
return (
<Select
showSearch

View File

@@ -122,14 +122,23 @@ class ErrorBoundary extends React.Component {
/>
<Row>
<Col offset={6} span={12}>
<Collapse bordered={false}>
<Collapse.Panel key="errors-panel" header={t("general.labels.errors")}>
<div>
<strong>{this.state.error.message}</strong>
</div>
<div>{this.state.error.stack}</div>
</Collapse.Panel>
</Collapse>
<Collapse
bordered={false}
items={[
{
key: "errors-panel",
label: t("general.labels.errors"),
children: (
<>
<div>
<strong>{this.state.error.message}</strong>
</div>
<div>{this.state.error.stack}</div>
</>
)
}
]}
/>
</Col>
</Row>
</div>

View File

@@ -5,7 +5,7 @@ import { createStructuredSelector } from "reselect";
import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors";
import { connect } from "react-redux";
import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { acceptEula } from "../../redux/user/user.actions";
import { useTranslation } from "react-i18next";
import dayjs from "../../utils/day";

View File

@@ -56,7 +56,7 @@ function FeatureWrapper({
return (
noauth || (
<AlertComponent
message={t("general.messages.nofeatureaccess", {
title={t("general.messages.nofeatureaccess", {
app: InstanceRenderManager({
imex: "$t(titles.imexonline)",
rome: "$t(titles.romeonline)"

View File

@@ -89,7 +89,7 @@ const DateTimePicker = ({
return (
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
{isSeparatedTime && (
<Space direction="vertical" style={{ width: "100%" }}>
<Space orientation="vertical" style={{ width: "100%" }}>
<DatePicker
showTime={false}
format="MM/DD/YYYY"
@@ -131,10 +131,7 @@ const DateTimePicker = ({
if (timeValue) {
// When time changes, combine it with the existing date
const existingDate = dayjs(value);
const newDateTime = existingDate
.hour(timeValue.hour())
.minute(timeValue.minute())
.second(0);
const newDateTime = existingDate.hour(timeValue.hour()).minute(timeValue.minute()).second(0);
handleChange(newDateTime);
} else {
// If time is cleared, just update with null time but keep date

View File

@@ -17,11 +17,11 @@ export default function FormsFieldChanged({ form, skipPrompt }) {
const errors = form.getFieldsError().filter((e) => e.errors.length > 0);
if (form.isFieldsTouched())
return (
<Space direction="vertical" style={{ width: "100%" }}>
<Space orientation="vertical" style={{ width: "100%" }}>
<Prompt when={!skipPrompt} beforeUnload={true} message={t("general.messages.unsavedchangespopup")} />
<AlertComponent
type="warning"
message={
title={
<div>
<span>{t("general.messages.unsavedchanges")} </span>
<span
@@ -39,7 +39,7 @@ export default function FormsFieldChanged({ form, skipPrompt }) {
{errors.length > 0 && (
<AlertComponent
type="error"
message={
title={
<div>
<ul>{errors.map((e, idx) => e.errors.map((e2, idx2) => <li key={`${idx}${idx2}`}>{e2}</li>))}</ul>
</div>

View File

@@ -1,22 +1,20 @@
import { MailFilled } from "@ant-design/icons";
import { Input } from "antd";
import { Button, Input, Space } from "antd";
import { forwardRef } from "react";
function FormItemEmail(props, ref) {
const { defaultValue, value, ...restProps } = props;
const emailValue = defaultValue || value;
return (
<Input
{...props}
ref={ref}
addonAfter={
props.defaultValue || props.value ? (
<a href={`mailto:${props.defaultValue || props.value}`}>
<MailFilled />
</a>
) : (
<MailFilled />
)
}
/>
<Space.Compact style={{ width: "100%" }}>
<Input {...restProps} ref={ref} value={value} defaultValue={defaultValue} />
{emailValue ? (
<Button icon={<MailFilled />} href={`mailto:${emailValue}`} target="_blank" rel="noopener noreferrer" />
) : (
<Button icon={<MailFilled />} disabled />
)}
</Space.Compact>
);
}

View File

@@ -14,7 +14,7 @@ export default function FormListMoveArrows({ move, index, total }) {
};
return (
<Space direction="vertical">
<Space orientation="vertical">
<UpOutlined disabled={upDisabled} onClick={handleUp} />
<DownOutlined disabled={downDisabled} onClick={handleDown} />
</Space>

View File

@@ -46,7 +46,7 @@ export default function GlobalSearchOs() {
value: job.ro_number || "N/A",
label: (
<Link to={`/manage/jobs/${job.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<strong>{job.ro_number || t("general.labels.na")}</strong>
<span>{`${job.status || ""}`}</span>
<span>
@@ -69,7 +69,7 @@ export default function GlobalSearchOs() {
value: OwnerNameDisplayFunction(owner),
label: (
<Link to={`/manage/owners/${owner.id}`}>
<Space size="small" split={<Divider type="vertical" />} wrap>
<Space size="small" separator={<Divider orientation="vertical" />} wrap>
<span>
<OwnerNameDisplay ownerObject={owner} />
</span>
@@ -89,7 +89,7 @@ export default function GlobalSearchOs() {
value: `${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`,
label: (
<Link to={`/manage/vehicles/${vehicle.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<span>
{`${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`}
</span>
@@ -111,7 +111,7 @@ export default function GlobalSearchOs() {
value: `${payment.job?.ro_number} ${payment.amount}`,
label: (
<Link to={`/manage/jobs/${payment.job?.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<span>{payment.paymentnum}</span>
<span>{payment.job?.ro_number}</span>
<span>{payment.memo || ""}</span>
@@ -131,7 +131,7 @@ export default function GlobalSearchOs() {
value: `${bill.invoice_number} - ${bill.vendor.name}`,
label: (
<Link to={`/manage/bills?billid=${bill.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<span>{bill.invoice_number}</span>
<span>{bill.vendor.name}</span>
<span>{bill.date}</span>
@@ -151,7 +151,7 @@ export default function GlobalSearchOs() {
// }`,
// label: (
// <Link to={`/manage/phonebook?phonebookentry=${pb.id}`}>
// <Space size="small" split={<Divider type="vertical" />}>
// <Space size="small" separator={<Divider orientation="vertical" />}>
// <span>{`${pb.firstname || ""} ${pb.lastname || ""} ${
// pb.company || ""
// }`}</span>

View File

@@ -1,4 +1,4 @@
import { useLazyQuery } from "@apollo/client";
import { useLazyQuery } from "@apollo/client/react";
import { AutoComplete, Divider, Input, Space } from "antd";
import _ from "lodash";
import { useTranslation } from "react-i18next";
@@ -20,7 +20,7 @@ export default function GlobalSearch() {
const debouncedExecuteSearch = _.debounce(executeSearch, 750);
const handleSearch = (value) => {
debouncedExecuteSearch({ variables: { search: value } });
debouncedExecuteSearch({ search: value });
};
const renderTitle = (title) => {
@@ -37,7 +37,7 @@ export default function GlobalSearch() {
value: job.ro_number || "N/A",
label: (
<Link to={`/manage/jobs/${job.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<strong>{job.ro_number || t("general.labels.na")}</strong>
<span>{`${job.status || ""}`}</span>
<span>
@@ -59,7 +59,7 @@ export default function GlobalSearch() {
value: OwnerNameDisplayFunction(owner),
label: (
<Link to={`/manage/owners/${owner.id}`}>
<Space size="small" split={<Divider type="vertical" />} wrap>
<Space size="small" separator={<Divider orientation="vertical" />} wrap>
<span>
<OwnerNameDisplay ownerObject={owner} />
</span>
@@ -79,7 +79,7 @@ export default function GlobalSearch() {
value: `${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`,
label: (
<Link to={`/manage/vehicles/${vehicle.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<span>
{`${vehicle.v_model_yr || ""} ${vehicle.v_make_desc || ""} ${vehicle.v_model_desc || ""}`}
</span>
@@ -101,7 +101,7 @@ export default function GlobalSearch() {
value: `${payment.job.ro_number} ${payment.payer} ${payment.amount}`,
label: (
<Link to={`/manage/jobs/${payment.job.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<span>{payment.paymentnum}</span>
<span>{payment.job.ro_number}</span>
<span>{payment.memo || ""}</span>
@@ -121,7 +121,7 @@ export default function GlobalSearch() {
value: `${bill.invoice_number} - ${bill.vendor.name}`,
label: (
<Link to={`/manage/bills?billid=${bill.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<span>{bill.invoice_number}</span>
<span>{bill.vendor.name}</span>
<span>{bill.date}</span>
@@ -139,7 +139,7 @@ export default function GlobalSearch() {
value: `${pb.firstname || ""} ${pb.lastname || ""} ${pb.company || ""}`,
label: (
<Link to={`/manage/phonebook?phonebookentry=${pb.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" separator={<Divider orientation="vertical" />}>
<span>{`${pb.firstname || ""} ${pb.lastname || ""} ${pb.company || ""}`}</span>
<PhoneNumberFormatter>{pb.phone1}</PhoneNumberFormatter>
<span>{pb.email}</span>
@@ -152,7 +152,7 @@ export default function GlobalSearch() {
]
: [];
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
return (
<AutoComplete

View File

@@ -1,7 +1,7 @@
// noinspection RegExpAnonymousGroup
import { BellFilled } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { Badge, Layout, Menu, Spin, Tooltip } from "antd";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";

View File

@@ -19,7 +19,7 @@ export default function HelpRescue() {
return (
<div style={{ display: "flex", justifyContent: "center" }}>
<Space direction="vertical" align="center">
<Space orientation="vertical" align="center">
<div>{t("help.labels.rescuedesc")}</div>
<Input
size="large"

View File

@@ -1,5 +1,5 @@
import { DeleteFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Button, Popconfirm } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -183,12 +183,7 @@ export function JobsList({ refetch, loading, jobs, total, setInventoryUpsertCont
>
<Table
loading={loading}
pagination={{
position: "top",
pageSize: pageLimit,
current: parseInt(page || 1),
total: total
}}
pagination={{ placement: "top", pageSize: pageLimit, current: parseInt(page || 1), total: total }}
columns={columns}
rowKey="id"
dataSource={jobs}

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import queryString from "query-string";
import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
@@ -33,7 +33,7 @@ export function InventoryList() {
}
});
if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent title={error.message} type="error" />;
return (
<InventoryListPaginated
refetch={refetch}

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Form, Modal } from "antd";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { Button, Form, Input, InputNumber, Modal, Radio, Select } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";

View File

@@ -1,5 +1,5 @@
import { AlertFilled } from "@ant-design/icons";
import { useLazyQuery, useMutation } from "@apollo/client";
import { useLazyQuery, useMutation } from "@apollo/client/react";
import { Button, Divider, Dropdown, Form, Input, Popover, Select, Space } from "antd";
import parsePhoneNumber from "libphonenumber-js";
import queryString from "query-string";
@@ -66,7 +66,6 @@ export function ScheduleEventComponent({
const [form] = Form.useForm();
const [popOverVisible, setPopOverVisible] = useState(false);
const [getJobDetails] = useLazyQuery(GET_JOB_BY_PK_QUICK_INTAKE, {
variables: { id: event.job?.id },
onCompleted: (data) => {
if (data?.jobs_by_pk) {
const totalHours =
@@ -87,7 +86,7 @@ export function ScheduleEventComponent({
});
const blockContent = (
<Space direction="vertical" wrap>
<Space orientation="vertical" wrap>
<Input
value={title}
onChange={(e) => setTitle(e.currentTarget.value)}
@@ -232,7 +231,10 @@ export function ScheduleEventComponent({
{(event.job && event.job.alt_transport) || ""}
<ScheduleAtChange job={event && event.job} />
</DataLabel>
<DataLabel label={t("jobs.fields.comment")} valueStyle={{ overflow: "hidden", textOverflow: "ellipsis" }}>
<DataLabel
label={t("jobs.fields.comment")}
styles={{ value: { overflow: "hidden", textOverflow: "ellipsis" } }}
>
<ProductionListColumnComment record={event && event.job} />
</DataLabel>
<ScheduleEventNote event={event} />
@@ -409,7 +411,7 @@ export function ScheduleEventComponent({
onClick={(e) => {
if (event.job?.id) {
e.stopPropagation();
getJobDetails();
getJobDetails({ id: event.job.id });
}
}}
getPopupContainer={(trigger) => trigger.parentNode}

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { logImEXEvent } from "../../firebase/firebase.utils";

View File

@@ -1,5 +1,5 @@
import { EditFilled, SaveFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client/react";
import { Button, Input, Space } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { Button, Card, Col, Row, Table, Tag } from "antd";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";

View File

@@ -33,7 +33,7 @@ export function JobBillsTotalComponent({
if (showWarning && warningCallback && typeof warningCallback === "function") {
warningCallback({ key: "bills", warning: t("jobs.errors.nofinancial") });
}
return <AlertComponent type="error" message={t("jobs.errors.nofinancial")} />;
return <AlertComponent type="error" title={t("jobs.errors.nofinancial")} />;
}
const totals = jobTotals;
@@ -55,8 +55,9 @@ export function JobBillsTotalComponent({
);
if (pol.cm_received === null) {
return; //TODO:AIO This was previously removed. Check if functionality impacted.
//TODO:AIO This was previously removed. Check if functionality impacted.
// Skip this calculation for bills posted prior to the CNR change.
return;
} else {
if (pol.cm_received === false) {
totalReturnsMarkedNotReceived = totalReturnsMarkedNotReceived.add(
@@ -174,8 +175,10 @@ export function JobBillsTotalComponent({
<BlurWrapperComponent featureName="bills" overrideValueFunction="RandomDinero">
<Statistic
title={t("bills.labels.discrepancy")}
valueStyle={{
color: discrepancy.getAmount() === 0 ? "green" : "red"
styles={{
content: {
color: discrepancy.getAmount() === 0 ? "green" : "red"
}
}}
value={discrepancy.toFormat()}
/>
@@ -208,8 +211,10 @@ export function JobBillsTotalComponent({
<BlurWrapperComponent featureName="bills" overrideValueFunction="RandomDinero">
<Statistic
title={t("bills.labels.discrepancy")}
valueStyle={{
color: discrepWithLbrAdj.getAmount() === 0 ? "green" : "red"
styles={{
content: {
color: discrepWithLbrAdj.getAmount() === 0 ? "green" : "red"
}
}}
value={discrepWithLbrAdj.toFormat()}
/>
@@ -242,8 +247,10 @@ export function JobBillsTotalComponent({
<BlurWrapperComponent featureName="bills" overrideValueFunction="RandomDinero">
<Statistic
title={t("bills.labels.discrepancy")}
valueStyle={{
color: discrepWithCms.getAmount() === 0 ? "green" : "red"
styles={{
content: {
color: discrepWithCms.getAmount() === 0 ? "green" : "red"
}
}}
value={discrepWithCms.toFormat()}
/>
@@ -257,7 +264,7 @@ export function JobBillsTotalComponent({
<Alert
style={{ margin: "8px 0px" }}
type="warning"
message={t("jobs.labels.outstanding_reconciliation_discrep")}
title={t("jobs.labels.outstanding_reconciliation_discrep")}
/>
)}
</Card>
@@ -290,8 +297,10 @@ export function JobBillsTotalComponent({
<BlurWrapperComponent featureName="bills" overrideValueFunction="RandomDinero">
<Statistic
title={t("bills.labels.calculatedcreditsnotreceived")}
valueStyle={{
color: calculatedCreditsNotReceived.getAmount() <= 0 ? "green" : "red"
styles={{
content: {
color: calculatedCreditsNotReceived.getAmount() <= 0 ? "green" : "red"
}
}}
value={
calculatedCreditsNotReceived.getAmount() >= 0
@@ -313,8 +322,10 @@ export function JobBillsTotalComponent({
<BlurWrapperComponent featureName="bills" overrideValueFunction="RandomDinero">
<Statistic
title={t("bills.labels.creditsnotreceived")}
valueStyle={{
color: totalReturnsMarkedNotReceived.getAmount() <= 0 ? "green" : "red"
styles={{
content: {
color: totalReturnsMarkedNotReceived.getAmount() <= 0 ? "green" : "red"
}
}}
value={
totalReturnsMarkedNotReceived.getAmount() >= 0
@@ -326,7 +337,7 @@ export function JobBillsTotalComponent({
</Tooltip>
</Space>
{showWarning && calculatedCreditsNotReceived.getAmount() > 0 && (
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstanding_credit_memos")} />
<Alert style={{ margin: "8px 0px" }} type="warning" title={t("jobs.labels.outstanding_credit_memos")} />
)}
</Card>
</Col>

Some files were not shown because too many files have changed in this diff Show More