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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import AllocationsAssignmentComponent from "./allocations-assignment.component"; 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 { INSERT_ALLOCATION } from "../../graphql/allocations.queries";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";

View File

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

View File

@@ -1,6 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import AllocationsBulkAssignment from "./allocations-bulk-assignment.component"; 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 { INSERT_ALLOCATION } from "../../graphql/allocations.queries";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; 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 { DELETE_ALLOCATION } from "../../graphql/allocations.queries";
import AllocationsLabelComponent from "./allocations-employee-label.component"; import AllocationsLabelComponent from "./allocations-employee-label.component";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import { PageHeader } from "@ant-design/pro-layout"; 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 { Button, Divider, Form, Popconfirm, Space } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import { useState } from "react"; import { useState } from "react";
@@ -148,7 +148,7 @@ export function BillDetailEditcontainer({ insertAuditTrail, bodyshop }) {
setUpdateLoading(false); 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>; if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
const exported = data?.bills_by_pk && data.bills_by_pk.exported; 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"> <Form form={form} onFinish={handleFinish} initialValues={transformData(data)} layout="vertical">
<BillFormContainer form={form} billEdit disabled={exported} disableInHouse={isinhouse} /> <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 ? ( {bodyshop.uselocalmediaserver ? (
<JobsDocumentsLocalGallery <JobsDocumentsLocalGallery
job={{ id: data ? data.bills_by_pk.jobid : null }} 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 { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
import { Button, Checkbox, Form, Modal, Space } from "antd"; import { Button, Checkbox, Form, Modal, Space } from "antd";
import _ from "lodash"; import _ from "lodash";

View File

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

View File

@@ -10,18 +10,19 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
<Select <Select
disabled={disabled} disabled={disabled}
ref={ref} 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} popupMatchSelectWidth={true}
optionLabelProp={"name"} optionLabelProp={"name"}
// optionFilterProp="line_desc" // 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."} notFoundContent={"Removed."}
options={[ options={[
{ value: "noline", label: t("billlines.labels.other"), name: t("billlines.labels.other") }, { 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 { Button } from "antd";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; 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 { Button } from "antd";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ export function ChatConversationComponent({
if (conversation?.archived) return null; if (conversation?.archived) return null;
if (loading) return <LoadingSkeleton />; if (loading) return <LoadingSkeleton />;
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent title={error.message} type="error" />;
return ( return (
<div <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 axios from "axios";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";

View File

@@ -1,5 +1,5 @@
import { PlusOutlined } from "@ant-design/icons"; 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 { Input, Spin, Tag, Tooltip } from "antd";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

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

View File

@@ -1,5 +1,5 @@
import { InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined } from "@ant-design/icons"; 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 { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; 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. // 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.) // (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", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
skip: chatVisible || socket?.connected, skip: chatVisible || socket?.connected,
pollInterval: socket?.connected ? 0 : 60 * 1000, 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);
}
}); });
// 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 // Socket connection status -> polling strategy for CONVERSATION_LIST_QUERY
useEffect(() => { useEffect(() => {
const handleSocketStatus = () => { const handleSocketStatus = () => {
@@ -97,9 +105,7 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
hasLoadedConversationsOnceRef.current = true; hasLoadedConversationsOnceRef.current = true;
getConversations({ getConversations({ offset: 0 }).catch((err) => {
variables: { offset: 0 }
}).catch((err) => {
console.error(`Error fetching conversations: ${err?.message || ""}`, err); console.error(`Error fetching conversations: ${err?.message || ""}`, err);
}); });
}, [getConversations]); }, [getConversations]);
@@ -107,9 +113,9 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
const handleManualRefresh = async () => { const handleManualRefresh = async () => {
try { try {
if (called && typeof refetch === "function") { if (called && typeof refetch === "function") {
await refetch({ offset: 0 }); await refetch({ variables: { offset: 0 } });
} else { } else {
await getConversations({ variables: { offset: 0 } }); await getConversations({ offset: 0 });
} }
} catch (err) { } catch (err) {
console.error(`Error refreshing conversations: ${err?.message || ""}`, 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 { selectBodyshop } from "../../redux/user/user.selectors";
import ChatMediaSelector from "../chat-media-selector/chat-media-selector.component"; import ChatMediaSelector from "../chat-media-selector/chat-media-selector.component";
import ChatPresetsComponent from "../chat-presets/chat-presets.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 { phone } from "phone";
import { GET_PHONE_NUMBER_OPT_OUT } from "../../graphql/phone-number-opt-out.queries"; import { GET_PHONE_NUMBER_OPT_OUT } from "../../graphql/phone-number-opt-out.queries";
@@ -68,13 +68,13 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
}; };
return ( return (
<Space direction="vertical" style={{ width: "100%" }} size="middle"> <Space orientation="vertical" style={{ width: "100%" }} size="middle">
{isOptedOut && ( {isOptedOut && (
<Tooltip title={t("consent.text_body")}> <Tooltip title={t("consent.text_body")}>
<Alert <Alert
showIcon={true} showIcon={true}
icon={<ExclamationCircleOutlined />} icon={<ExclamationCircleOutlined />}
message={t("messaging.errors.no_consent")} title={t("messaging.errors.no_consent")}
type="error" type="error"
/> />
</Tooltip> </Tooltip>

View File

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

View File

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

View File

@@ -104,7 +104,7 @@ export default function ContractsCarsComponent({ loading, data, selectedCarId, h
> >
<Table <Table
loading={loading} loading={loading}
pagination={{ position: "top" }} pagination={{ placement: "top" }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={filteredData} 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 dayjs from "../../utils/day";
import { QUERY_AVAILABLE_CC } from "../../graphql/courtesy-car.queries"; import { QUERY_AVAILABLE_CC } from "../../graphql/courtesy-car.queries";
import AlertComponent from "../alert/alert.component"; 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 ( return (
<ContractCarsComponent <ContractCarsComponent
handleSelect={handleSelect} 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 { Button, Form, InputNumber, Popover, Radio, Select, Space } from "antd";
import axios from "axios"; import axios from "axios";
import dayjs from "../../utils/day"; 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 { Button } from "antd";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -11,7 +11,7 @@ export default function ContractCreateJobPrefillComponent({ jobId, form }) {
const notification = useNotification(); const notification = useNotification();
const handleClick = () => { const handleClick = () => {
call({ variables: { id: jobId } }); call({ id: jobId });
}; };
useEffect(() => { useEffect(() => {

View File

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

View File

@@ -128,11 +128,7 @@ export default function ContractsJobsComponent({ loading, data, selectedJob, han
> >
<Table <Table
loading={loading} loading={loading}
pagination={{ pagination={{ placement: "top", defaultPageSize: pageLimit, defaultCurrent: defaultCurrent }}
position: "top",
defaultPageSize: pageLimit,
defaultCurrent: defaultCurrent
}}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={filteredData} 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 { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries"; import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
@@ -26,7 +26,7 @@ export function ContractJobsContainer({ selectedJobState, bodyshop }) {
setSelectedJob(record.id); setSelectedJob(record.id);
}; };
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent title={error.message} type="error" />;
return ( return (
<ContractJobsComponent <ContractJobsComponent
handleSelect={handleSelect} 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 { Button, Form, Modal, Table } from "antd";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -35,10 +35,8 @@ export function ContractsFindModalContainer({ contractFinderModal, toggleModalVi
//Execute contract find //Execute contract find
callSearch({ callSearch({
variables: { plate: (values.plate && values.plate !== "" && values.plate) || undefined,
plate: (values.plate && values.plate !== "" && values.plate) || undefined, time: values.time
time: values.time
}
}); });
}; };
@@ -63,7 +61,7 @@ export function ContractsFindModalContainer({ contractFinderModal, toggleModalVi
<Button onClick={() => form.submit()} type="primary" loading={loading}> <Button onClick={() => form.submit()} type="primary" loading={loading}>
{t("general.labels.search")} {t("general.labels.search")}
</Button> </Button>
{error && <AlertComponent type="error" message={JSON.stringify(error)} />} {error && <AlertComponent type="error" title={JSON.stringify(error)} />}
<Table <Table
loading={loading} loading={loading}
columns={[ columns={[

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import { WarningFilled } from "@ant-design/icons"; 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 { Button, Form, Input, InputNumber, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout"; import { PageHeader } from "@ant-design/pro-layout";
import dayjs from "../../utils/day"; import dayjs from "../../utils/day";
@@ -208,7 +208,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
const mileageOver = nextservicekm ? nextservicekm <= form.getFieldValue("mileage") : false; const mileageOver = nextservicekm ? nextservicekm <= form.getFieldValue("mileage") : false;
if (mileageOver) if (mileageOver)
return ( return (
<Space direction="vertical" style={{ color: "tomato" }}> <Space orientation="vertical" style={{ color: "tomato" }}>
<span> <span>
<WarningFilled style={{ marginRight: ".3rem" }} /> <WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")} {t("contracts.labels.cardueforservice")}
@@ -232,7 +232,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
if (dueForService) if (dueForService)
return ( return (
<Space direction="vertical" style={{ color: "tomato" }}> <Space orientation="vertical" style={{ color: "tomato" }}>
<span> <span>
<WarningFilled style={{ marginRight: ".3rem" }} /> <WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")} {t("contracts.labels.cardueforservice")}
@@ -265,7 +265,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
if (dateover) if (dateover)
return ( return (
<Space direction="vertical" style={{ color: "tomato" }}> <Space orientation="vertical" style={{ color: "tomato" }}>
<span> <span>
<WarningFilled style={{ marginRight: ".3rem" }} /> <WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.dateinpast")} {t("contracts.labels.dateinpast")}
@@ -298,7 +298,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
if (dateover) if (dateover)
return ( return (
<Space direction="vertical" style={{ color: "tomato" }}> <Space orientation="vertical" style={{ color: "tomato" }}>
<span> <span>
<WarningFilled style={{ marginRight: ".3rem" }} /> <WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.dateinpast")} {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 CourtesyCarReturnModalComponent from "./courtesy-car-return-modal.component";
import dayjs from "../../utils/day"; import dayjs from "../../utils/day";
import { RETURN_CONTRACT } from "../../graphql/cccontracts.queries"; 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"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({

View File

@@ -278,7 +278,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
> >
<Table <Table
loading={loading} loading={loading}
pagination={{ position: "top" }} pagination={{ placement: "top" }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={tableData} 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 { Card, Form, Result } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import { useEffect } from "react"; import { useEffect } from "react";
@@ -36,7 +36,7 @@ export default function CsiResponseFormContainer() {
); );
if (loading) return <LoadingSpinner />; if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent title={error.message} type="error" />;
return ( return (
<Card> <Card>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,15 +1,15 @@
import Icon, { SyncOutlined } from "@ant-design/icons"; 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 { Button, Dropdown, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout"; import { PageHeader } from "@ant-design/pro-layout";
import { useMemo, useState, useEffect } from "react"; import { useEffect, useMemo, useState } from "react";
import { Responsive, WidthProvider } from "react-grid-layout"; import { Responsive, WidthProvider } from "react-grid-layout/legacy";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { MdClose } from "react-icons/md"; import { MdClose } from "react-icons/md";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; 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 { QUERY_DASHBOARD_BODYSHOP } from "../../graphql/bodyshop.queries";
import { selectCurrentUser } from "../../redux/user/user.selectors"; import { selectCurrentUser } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; 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 (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) => { const handleLayoutChange = async (layout, layouts) => {
logImEXEvent("dashboard_change_layout"); logImEXEvent("dashboard_change_layout");

View File

@@ -5,7 +5,7 @@ export default function DataLabel({
hideIfNull, hideIfNull,
children, children,
open = true, open = true,
valueStyle = {}, styles,
valueClassName, valueClassName,
onValueClick, onValueClick,
...props ...props
@@ -33,7 +33,11 @@ export default function DataLabel({
className={valueClassName} className={valueClassName}
onClick={onValueClick} 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>
</div> </div>
); );

View File

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

View File

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

View File

@@ -331,10 +331,10 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat
} }
> >
{bodyshop.pbs_configuration?.disablebillwip && ( {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} /> <Tabs defaultActiveKey="rogog" items={tabItems} />
</Card> </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 { Button, Input, Modal, Table } from "antd";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -56,11 +56,11 @@ export function DmsCdkVehicles({ form, job }) {
setOpen(false); setOpen(false);
}} }}
> >
{error && <AlertComponent error={error.message} />} {error && <AlertComponent title={error.message} type="error" />}
<Table <Table
title={() => ( title={() => (
<Input.Search <Input.Search
onSearch={(val) => callSearch({ variables: { search: val } })} onSearch={(val) => callSearch({ search: val })}
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
/> />
)} )}
@@ -87,9 +87,7 @@ export function DmsCdkVehicles({ form, job }) {
onClick={() => { onClick={() => {
setOpen(true); setOpen(true);
callSearch({ 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> <Button onClick={onCreateNew}>{t("jobs.actions.dms.createnewcustomer")}</Button>
</div> </div>
)} )}
pagination={{ position: "top" }} pagination={{ placement: "top" }}
columns={columns} columns={columns}
rowKey={rowKey} rowKey={rowKey}
dataSource={customerList} dataSource={customerList}

View File

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

View File

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

View File

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

View File

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

View File

@@ -404,7 +404,7 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode,
<Typography.Title>=</Typography.Title> <Typography.Title>=</Typography.Title>
<Statistic <Statistic
title={t("jobs.labels.dms.notallocated")} title={t("jobs.labels.dms.notallocated")}
valueStyle={{ color: discrep.getAmount() === 0 ? "green" : "red" }} styles={{ value: { color: discrep.getAmount() === 0 ? "green" : "red" } }}
value={discrep.toFormat()} value={discrep.toFormat()}
/> />
<Button disabled={disablePost} htmlType="submit"> <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 { Result } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import { useEffect } from "react"; import { useEffect } from "react";
@@ -51,7 +51,7 @@ export function DocumentEditorContainer({ setBodyshop }) {
}, [dataShop, setBodyshop]); }, [dataShop, setBodyshop]);
if (loadingShop) return <LoadingSpinner />; 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 (isLocalMedia) {
if (imageUrl && filename && jobid) { if (imageUrl && filename && jobid) {
@@ -66,7 +66,7 @@ export function DocumentEditorContainer({ setBodyshop }) {
} }
if (loadingDoc) return <LoadingSpinner />; 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")} />; if (!dataDoc || !dataDoc.documents_by_pk) return <Result status="404" title={t("general.errors.notfound")} />;
return ( return (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import { createStructuredSelector } from "reselect";
import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors"; import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries"; 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 { acceptEula } from "../../redux/user/user.actions";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import dayjs from "../../utils/day"; import dayjs from "../../utils/day";

View File

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

View File

@@ -89,7 +89,7 @@ const DateTimePicker = ({
return ( return (
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}> <div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
{isSeparatedTime && ( {isSeparatedTime && (
<Space direction="vertical" style={{ width: "100%" }}> <Space orientation="vertical" style={{ width: "100%" }}>
<DatePicker <DatePicker
showTime={false} showTime={false}
format="MM/DD/YYYY" format="MM/DD/YYYY"
@@ -131,10 +131,7 @@ const DateTimePicker = ({
if (timeValue) { if (timeValue) {
// When time changes, combine it with the existing date // When time changes, combine it with the existing date
const existingDate = dayjs(value); const existingDate = dayjs(value);
const newDateTime = existingDate const newDateTime = existingDate.hour(timeValue.hour()).minute(timeValue.minute()).second(0);
.hour(timeValue.hour())
.minute(timeValue.minute())
.second(0);
handleChange(newDateTime); handleChange(newDateTime);
} else { } else {
// If time is cleared, just update with null time but keep date // 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); const errors = form.getFieldsError().filter((e) => e.errors.length > 0);
if (form.isFieldsTouched()) if (form.isFieldsTouched())
return ( return (
<Space direction="vertical" style={{ width: "100%" }}> <Space orientation="vertical" style={{ width: "100%" }}>
<Prompt when={!skipPrompt} beforeUnload={true} message={t("general.messages.unsavedchangespopup")} /> <Prompt when={!skipPrompt} beforeUnload={true} message={t("general.messages.unsavedchangespopup")} />
<AlertComponent <AlertComponent
type="warning" type="warning"
message={ title={
<div> <div>
<span>{t("general.messages.unsavedchanges")} </span> <span>{t("general.messages.unsavedchanges")} </span>
<span <span
@@ -39,7 +39,7 @@ export default function FormsFieldChanged({ form, skipPrompt }) {
{errors.length > 0 && ( {errors.length > 0 && (
<AlertComponent <AlertComponent
type="error" type="error"
message={ title={
<div> <div>
<ul>{errors.map((e, idx) => e.errors.map((e2, idx2) => <li key={`${idx}${idx2}`}>{e2}</li>))}</ul> <ul>{errors.map((e, idx) => e.errors.map((e2, idx2) => <li key={`${idx}${idx2}`}>{e2}</li>))}</ul>
</div> </div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -183,12 +183,7 @@ export function JobsList({ refetch, loading, jobs, total, setInventoryUpsertCont
> >
<Table <Table
loading={loading} loading={loading}
pagination={{ pagination={{ placement: "top", pageSize: pageLimit, current: parseInt(page || 1), total: total }}
position: "top",
pageSize: pageLimit,
current: parseInt(page || 1),
total: total
}}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={jobs} 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 queryString from "query-string";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useLocation } from "react-router-dom"; 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 ( return (
<InventoryListPaginated <InventoryListPaginated
refetch={refetch} 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 { Form, Modal } from "antd";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next"; 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 { Button, Form, Input, InputNumber, Modal, Radio, Select } from "antd";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; 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 { UPDATE_JOB } from "../../graphql/jobs.queries";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; 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 { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";

View File

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

View File

@@ -1,5 +1,5 @@
import { EditFilled, SaveFilled } from "@ant-design/icons"; 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 { Button, Input, Space } from "antd";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; 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 { Button, Card, Col, Row, Table, Tag } from "antd";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";

View File

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

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