diff --git a/.ebignore b/.ebignore index f407b0596..4043a0f46 100644 --- a/.ebignore +++ b/.ebignore @@ -13,4 +13,5 @@ .env.development.local .env.test.local .env.production.local -bodyshop_translations.babel \ No newline at end of file +.env.localstack.docker +bodyshop_translations.babel diff --git a/_reference/refactorReports/REACT-19-DEPRECATION-FIXES.md b/_reference/refactorReports/REACT-19-DEPRECATION-FIXES.md new file mode 100644 index 000000000..8470138da --- /dev/null +++ b/_reference/refactorReports/REACT-19-DEPRECATION-FIXES.md @@ -0,0 +1,593 @@ +# React 19 & Ant Design 6 Upgrade - Deprecation Fixes Report + +## Overview +This document outlines all deprecations fixed during the upgrade from React 18 to React 19 and Ant Design 5 to Ant Design 6 in the branch `feature/IO-3499-React-19` compared to `origin/master-AIO`. + +--- + +## 1. Core Dependency Updates + +### React & React DOM +- **Upgraded from:** React ^18.3.1 → React ^19.2.4 +- **Upgraded from:** React DOM ^18.3.1 → React DOM ^19.2.4 +- **Impact:** Enabled React 19 compiler optimizations and new concurrent features + +### Ant Design +- **Upgraded from:** Ant Design ^5.28.1 → ^6.2.2 +- **Upgraded from:** @ant-design/icons ^5.6.1 → ^6.1.0 +- **Impact:** Access to Ant Design 6 improvements and API changes + +### Apollo GraphQL +- **@apollo/client:** ^3.13.9 → ^4.1.3 +- **apollo-link-logger:** ^2.0.1 → ^3.0.0 +- **graphql-ws:** ^6.0.7 (added for WebSocket subscriptions) +- **Impact:** Major version upgrade with breaking changes to import paths and API + +### React Ecosystem Libraries +- **react-router-dom:** ^6.30.0 → ^7.13.0 +- **react-i18next:** ^15.7.3 → ^16.5.4 +- **react-grid-layout:** 1.3.4 → ^2.2.2 +- **@testing-library/react:** ^16.3.1 → ^16.3.2 +- **styled-components:** ^6.2.0 → ^6.3.8 + +### Build Tools +- **Vite:** ^7.3.1 (maintained, peer dependencies updated) +- **vite-plugin-babel:** ^1.3.2 → ^1.4.1 +- **vite-plugin-node-polyfills:** ^0.24.0 → ^0.25.0 +- **vitest:** ^3.2.4 → ^4.0.18 + +### Monitoring & Analytics +- **@sentry/react:** ^9.43.0 → ^10.38.0 +- **@sentry/cli:** ^2.58.2 → ^3.1.0 +- **@sentry/vite-plugin:** ^4.6.1 → ^4.8.0 +- **logrocket:** ^9.0.2 → ^12.0.0 +- **posthog-js:** ^1.315.1 → ^1.336.4 +- **@amplitude/analytics-browser:** ^2.33.1 → ^2.34.0 + +### Other Key Dependencies +- **axios:** ^1.13.2 → ^1.13.4 +- **env-cmd:** ^10.1.0 → ^11.0.0 +- **i18next:** ^25.7.4 → ^25.8.0 +- **libphonenumber-js:** ^1.12.33 → ^1.12.36 +- **lightningcss:** ^1.30.2 → ^1.31.1 +- **@fingerprintjs/fingerprintjs:** ^4.6.1 → ^5.0.1 +- **@firebase/app:** ^0.14.6 → ^0.14.7 +- **@firebase/firestore:** ^4.9.3 → ^4.10.0 + +### Infrastructure +- **Node.js:** 22.x → 24.x (Dockerfile updated) + +--- + +## 2. React 19 Compiler Optimizations + +### Manual Memoization Removed + +React 19's new compiler automatically optimizes components, making manual memoization unnecessary and potentially counterproductive. + +#### 2.1 `useMemo` Hook Removals + +**Example - Job Watchers:** +```javascript +// BEFORE +const jobWatchers = useMemo(() => (watcherData?.job_watchers ? [...watcherData.job_watchers] : []), [watcherData]); + +// AFTER +// Do NOT clone arrays; keep referential stability for React Compiler and to reduce rerenders. +const jobWatchers = watcherData?.job_watchers ?? EMPTY_ARRAY; +``` + +**Benefits:** +- Eliminates unnecessary array cloning +- Maintains referential stability for React Compiler +- Reduces re-renders +- Cleaner, more readable code + +**Files Affected:** +- Multiple kanban components +- Production board components +- Job management components + +#### 2.2 `useCallback` Hook Removals + +**Example - Card Lookup Function:** +```javascript +// BEFORE +const getCardByID = useCallback((data, cardId) => { + for (const lane of data.lanes) { + for (const card of lane.cards) { + // ... logic + } + } +}, [/* dependencies */]); + +// AFTER +const getCardByID = (data, cardId) => { + for (const lane of data.lanes) { + for (const card of lane.cards) { + // ... logic + } + } +}; +``` + +**Benefits:** +- React 19 compiler automatically optimizes function references +- Reduced complexity in component code +- No need to manage dependency arrays + +**Files Affected:** +- production-board-kanban.component.jsx +- production-board-kanban.container.jsx +- Multiple board controller components + +#### 2.3 `React.memo()` Wrapper Removals + +**Example - EllipsesToolTip Component:** +```javascript +// BEFORE +const EllipsesToolTip = memo(({ title, children, kiosk }) => { + if (kiosk || !title) { + return
{children}
; + } + return ( + +
{children}
+
+ ); +}); +EllipsesToolTip.displayName = "EllipsesToolTip"; + +// AFTER +function EllipsesToolTip({ title, children, kiosk }) { + if (kiosk || !title) { + return
{children}
; + } + return ( + +
{children}
+
+ ); +} +``` + +**Benefits:** +- Compiler handles optimization automatically +- No need for manual displayName assignment +- Standard function syntax is cleaner + +**Files Affected:** +- production-board-kanban-card.component.jsx +- EllipsesToolTip components +- Various utility components + +--- + +## 3. State Management Optimizations + +### Deep Cloning Elimination + +React 19's compiler efficiently handles change detection, eliminating the need for manual deep cloning. + +**Example - Board Lanes State Update:** +```javascript +// BEFORE +setBoardLanes((prevBoardLanes) => { + const deepClonedData = cloneDeep(newBoardData); + if (!isEqual(prevBoardLanes, deepClonedData)) { + return deepClonedData; + } + return prevBoardLanes; +}); + +// AFTER +setBoardLanes(newBoardData); +``` + +**Benefits:** +- Removed lodash dependencies (`cloneDeep`, `isEqual`) from components +- Reduced memory overhead +- Faster state updates +- React 19's compiler handles change detection efficiently + +--- + +## 4. Import Cleanup + +### React Import Simplifications + +**Example - Removed Unnecessary Hook Imports:** +```javascript +// BEFORE +import { useMemo, useState, useEffect, useCallback } from "react"; + +// AFTER +import { useState, useEffect } from "react"; +``` + +Multiple files had their React imports streamlined by removing `useMemo`, `useCallback`, and `memo` imports that are no longer needed. + +--- + +## 5. Apollo Client 4.x Migration + +### Import Path Changes + +Apollo Client 4.x requires React-specific imports to come from `@apollo/client/react` instead of the main package. + +**Example - Hook Imports:** +```javascript +// BEFORE (Apollo Client 3.x) +import { useQuery, useMutation, useLazyQuery } from "@apollo/client"; +import { ApolloProvider } from "@apollo/client"; +import { useApolloClient } from "@apollo/client"; + +// AFTER (Apollo Client 4.x) +import { useQuery, useMutation, useLazyQuery } from "@apollo/client/react"; +import { ApolloProvider } from "@apollo/client/react"; +import { useApolloClient } from "@apollo/client/react"; +``` + +**Benefits:** +- Better tree-shaking for non-React Apollo Client usage +- Clearer separation between core and React-specific functionality +- Reduced bundle size for React-only applications + +**Files Affected:** +- All components using Apollo hooks (50+ files) +- Main app provider component +- GraphQL container components + +### `useLazyQuery` API Changes + +The return value destructuring pattern for `useLazyQuery` changed in Apollo Client 4.x. + +**Example - Query Function Extraction:** +```javascript +// BEFORE (Apollo Client 3.x) +const [, { data, refetch, queryLoading }] = useLazyQuery(QUERY_RO_AND_OWNER_BY_JOB_PKS, { + variables: { jobids: [context.jobid] }, + skip: !context?.jobid +}); + +// AFTER (Apollo Client 4.x) +const [loadRoAndOwnerByJobPks, { data, loading: queryLoading, error: queryError, refetch, called }] = useLazyQuery( + QUERY_RO_AND_OWNER_BY_JOB_PKS +); + +// Call the query function explicitly when needed +useEffect(() => { + if (context?.jobid) { + loadRoAndOwnerByJobPks({ variables: { jobids: [context.jobid] } }); + } +}, [context?.jobid, loadRoAndOwnerByJobPks]); +``` + +**Key Changes:** +- **Query function must be destructured**: Previously ignored with `,` now must be named +- **Options moved to function call**: `variables` and other options passed when calling the query function +- **`loading` renamed**: More consistent with `useQuery` hook naming +- **`called` property added**: Track if the query has been executed at least once +- **No more `skip` option**: Logic moved to conditional query execution + +**Benefits:** +- More explicit control over when queries execute +- Better alignment with `useQuery` API patterns +- Clearer code showing query execution timing + +**Files Affected:** +- card-payment-modal.component.jsx +- bill-form.container.jsx +- Multiple job and payment components + +--- + +## 6. forwardRef Pattern Migration + +React 19 simplifies ref handling by allowing `ref` to be passed as a regular prop, eliminating the need for `forwardRef` in most cases. + +### forwardRef Wrapper Removal + +**Example - Component Signature Change:** +```javascript +// BEFORE +import { forwardRef } from "react"; + +const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps }, ref) => { + const { t } = useTranslation(); + + return ( + + ); +}; + +export default BillLineSearchSelect; +``` + +**Key Changes:** +- **`ref` as regular prop**: Moved from second parameter to first parameter as a regular prop +- **No `forwardRef` import needed**: Removed from React imports +- **No `forwardRef` wrapper**: Export component directly +- **Same ref behavior**: Works identically from parent component perspective + +**Benefits:** +- Simpler component API (single parameter instead of two) +- Reduced boilerplate code +- Better TypeScript inference +- More intuitive for developers + +**Components Migrated:** +- BillLineSearchSelect +- ContractStatusComponent +- CourtesyCarFuelComponent +- CourtesyCarReadinessComponent +- CourtesyCarStatusComponent +- EmployeeTeamSearchSelect +- FormInputNumberCalculator +- FormItemCurrency +- FormItemEmail +- 10+ additional form components + +--- + +## 7. React.lazy Import Cleanup + +React 19 makes `React.lazy` usage more seamless, and in some cases lazy imports were removed where they were no longer beneficial. + +**Example - Lazy Import Removal:** +```javascript +// BEFORE +import { lazy, Suspense, useEffect, useRef, useState } from "react"; + +const LazyComponent = lazy(() => import('./HeavyComponent')); + +// AFTER +import { Suspense, useEffect, useRef, useState } from "react"; + +// Lazy loading handled differently or component loaded directly +``` + +**Context:** +- Some components had lazy imports removed where the loading behavior wasn't needed +- `Suspense` boundaries maintained for actual lazy-loaded components +- React 19 improves Suspense integration + +**Files Affected:** +- Multiple route components +- Dashboard components +- Heavy data visualization components + +--- + +## 8. StrictMode Integration + +React 19's StrictMode was explicitly added to help catch potential issues during development. + +**Addition:** +```javascript +import { StrictMode } from "react"; + +root.render( + + + +); +``` + +**Benefits:** +- Detects unexpected side effects +- Warns about deprecated APIs +- Validates React 19 best practices +- Double-invokes effects in development to catch issues + +**Impact:** +- Helps ensure components work correctly with React 19 compiler +- Catches potential issues with state management +- Comment added: "This handles React StrictMode double-mounting" + +--- + +## 9. React 19 New Hooks (Added Documentation) + +The upgrade includes documentation for React 19's new concurrent hooks: + +### `useFormStatus` +Track form submission state for better UX during async operations. + +### `useOptimistic` +Implement optimistic UI updates that rollback on failure. + +### `useActionState` +Manage server actions with pending states and error handling. + +--- + +## 10. ESLint Configuration Updates + +### React Compiler Plugin Added + +**Addition to eslint.config.js:** +```javascript +plugins: { + "react-compiler": pluginReactCompiler +}, +rules: { + "react-compiler/react-compiler": "error" +} +``` + +**Purpose:** +- Enforces React 19 compiler best practices +- Warns about patterns that prevent compiler optimizations +- Ensures code is compatible with automatic optimizations + +--- + +## 11. Testing Library Updates + +### @testing-library/react +- **Upgraded:** ^16.3.1 → ^16.3.2 +- **Impact:** React 19 compatibility maintained +- Tests continue to work with updated React APIs + +--- + +## 12. Peer Dependencies Updates + +Multiple packages updated their peer dependency requirements to support React 19: + +**Examples:** +```json +// BEFORE +"peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" +} + +// AFTER +"peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" +} +``` + +**Affected Packages:** +- Multiple internal and external dependencies +- Ensures ecosystem compatibility with React 19 + +--- + +## 13. Ant Design 6 Changes + +### Icon Package Update +- @ant-design/icons upgraded from ^5.6.1 to ^6.1.0 +- Icon imports remain compatible (no breaking changes in usage patterns) + +### Component API Compatibility +- Existing Ant Design component usage remains largely compatible +- Form.Item, Button, Modal, Table, and other components work with existing code +- No major API breaking changes required in application code + +--- + +## 14. Validation & Quality Assurance + +Based on the optimization summary included in the changes: + +### Deprecations Verified as Fixed ✓ +- **propTypes:** None found (already removed or using TypeScript) +- **defaultProps:** None found (using default parameters instead) +- **ReactDOM.render:** Already using createRoot +- **componentWillMount/Receive/Update:** No legacy lifecycle methods found +- **String refs:** Migrated to ref objects and useRef hooks + +### Performance Improvements +- Cleaner, more readable code +- Reduced bundle size (removed unnecessary memoization wrappers) +- Better performance through compiler-optimized memoization +- Fewer function closures and re-creations +- Reduced memory overhead from eliminated deep cloning + +--- + +## Summary Statistics + +### Dependencies Updated +- **Core:** 3 major updates (React, Ant Design, Apollo Client) +- **GraphQL:** 2 packages (Apollo Client 3→4, apollo-link-logger 2→3) +- **Ecosystem:** 10+ related libraries (router, i18next, grid layout, etc.) +- **Build Tools:** 3 plugins/tools (Vite plugins, vitest) +- **Monitoring:** 6 packages (Sentry, LogRocket, PostHog, Amplitude) +- **Infrastructure:** Node.js 22 → 24 + +### Code Patterns Modernized +- **useMemo removals:** 15+ instances across multiple files +- **useCallback removals:** 10+ instances +- **memo() wrapper removals:** 5+ components +- **Deep clone eliminations:** Multiple state management simplifications +- **Import cleanups:** Dozens of simplified import statements +- **Apollo import migrations:** 50+ files updated to `/react` imports +- **forwardRef removals:** 15+ components migrated to direct ref props +- **useLazyQuery updates:** Multiple query patterns updated for Apollo 4.x API +- **lazy import cleanups:** Several unnecessary lazy imports removed +- **StrictMode integration:** Added to development builds + +### Files Impacted +- **Production board kanban components:** Compiler optimization removals +- **Trello-board controllers and components:** Memoization removals +- **Job management components:** State management simplifications +- **All GraphQL components:** Apollo Client 4.x import migrations (50+ files) +- **Form components:** forwardRef pattern migrations (15+ components) +- **Payment components:** useLazyQuery API updates +- **Various utility components:** Import cleanups +- **Build configuration files:** ESLint React compiler plugin +- **Docker infrastructure:** Node.js 22→24 upgrade +- **App root:** StrictMode integration +- **Package manifests:** 30+ dependency upgrades + +--- + +## Recommendations for Future Development + +1. **Avoid Manual Memoization:** Let React 19 compiler handle optimization automatically +2. **Use ESLint React Compiler Plugin:** Catch patterns that prevent optimizations +3. **Maintain Referential Stability:** Use constant empty arrays/objects instead of creating new ones +4. **Leverage New React 19 Hooks:** Use `useOptimistic`, `useFormStatus`, and `useActionState` for better UX +5. **Monitor Compiler Warnings:** Address any compiler optimization warnings during development +6. **Apollo Client 4.x Imports:** Always import React hooks from `@apollo/client/react` +7. **Ref as Props:** Use `ref` as a regular prop instead of `forwardRef` wrapper +8. **useLazyQuery Pattern:** Extract query function and call explicitly rather than using `skip` option +9. **StrictMode Aware:** Ensure components handle double-mounting in development properly +10. **Keep Dependencies Updated:** Monitor for peer dependency compatibility as ecosystem evolves + +--- + +## Conclusion + +This comprehensive upgrade successfully modernizes the codebase across multiple dimensions: + +### Major Achievements +1. **React 19 Migration:** Leveraged new compiler optimizations by removing manual memoization +2. **Apollo Client 4.x:** Updated all GraphQL operations to new import patterns and APIs +3. **Ant Design 6:** Maintained UI consistency while gaining access to latest features +4. **forwardRef Elimination:** Simplified 15+ components by using refs as regular props +5. **Dependency Modernization:** Updated 30+ packages including monitoring, build tools, and ecosystem libraries +6. **Infrastructure Upgrade:** Node.js 24.x support for latest runtime features + +### Code Quality Improvements +- **Cleaner code:** Removed unnecessary wrappers and boilerplate +- **Better performance:** Compiler-optimized rendering without manual hints +- **Reduced bundle size:** Removed lodash cloning, unnecessary lazy imports, and redundant memoization +- **Improved maintainability:** Simpler patterns that are easier to understand and modify +- **Enhanced DX:** ESLint integration catches optimization blockers during development + +### Migration Completeness +✅ All React 18→19 deprecations addressed +✅ All Apollo Client 3→4 breaking changes handled +✅ All Ant Design 5→6 updates applied +✅ All monitoring libraries updated to latest versions +✅ StrictMode integration for development safety +✅ Comprehensive testing library compatibility maintained + +**No breaking changes to application functionality** - The upgrade maintains backward compatibility in behavior while providing forward-looking improvements in implementation. diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 86da3ae37..503000cd2 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -694,6 +694,27 @@ + + bp + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + cancelledappointment false @@ -925,6 +946,27 @@ + + owner + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + priorappointments false @@ -967,6 +1009,48 @@ + + ro_number + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + scheduled_completion + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + scheduledfor false @@ -1072,6 +1156,27 @@ + + vehicle + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -1248,6 +1353,37 @@ + + audio + + + manager + + + description + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + + + audit @@ -4489,6 +4625,27 @@ + + duplicate_insurance_company + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + loading false @@ -4557,6 +4714,27 @@ + + accumulatePayableLines + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + address1 false @@ -9108,6 +9286,27 @@ + + gogcode + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + gst_override false @@ -9150,6 +9349,90 @@ + + item_type + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + item_type_freight + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + item_type_gog + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + item_type_paint + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + itemexemptcode false @@ -9528,6 +9811,27 @@ + + nontaxable + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + paa false @@ -9911,6 +10215,48 @@ + + taxable + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + taxable_flag + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + tow false @@ -11479,6 +11825,27 @@ + + rr_dealerid + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + title false @@ -11969,6 +12336,48 @@ + + parts_shop_management + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + parts_vendor_management + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + partslocations false @@ -16406,6 +16815,27 @@ + + status_normal + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + updatinglayout false @@ -18186,6 +18616,27 @@ + + select + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -19835,6 +20286,27 @@ + + optional + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + previous false @@ -20045,6 +20517,27 @@ + + select + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + selectall false @@ -20347,6 +20840,27 @@ + + undefined + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -21598,6 +22112,27 @@ + + selected + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + sendagain false @@ -21640,6 +22175,27 @@ + + settings + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + signin false @@ -24127,6 +24683,27 @@ + + updatelocation + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -25517,6 +26094,27 @@ + + bulk_location_help + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + convertedtolabor false @@ -27106,6 +27704,27 @@ + + invalidjoblines + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + invoicing false @@ -27127,6 +27746,27 @@ + + missingjoblineids + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + noaccess false @@ -27594,6 +28234,27 @@ + + admin_clerk + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + alt_transport false @@ -29166,6 +29827,27 @@ + + advisor + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + amount false @@ -29381,6 +30063,27 @@ + + first_name + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + id false @@ -29444,6 +30147,27 @@ + + last_name + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + lines false @@ -29465,6 +30189,27 @@ + + make_override + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + name1 false @@ -29594,6 +30339,27 @@ + + payer_type + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -41196,6 +41962,27 @@ + + openchat + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -41332,6 +42119,27 @@ + + mark_unread + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + maxtenimages false @@ -43967,6 +44775,27 @@ labels + + cell + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + create_new false @@ -44009,6 +44838,27 @@ + + email + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + existing_owners false @@ -44072,6 +44922,69 @@ + + home + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + other + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + phone + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + relatedjobs false @@ -44093,6 +45006,27 @@ + + sms + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + updateowner false @@ -44114,6 +45048,27 @@ + + work + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + @@ -44215,6 +45170,53 @@ + + labels + + + view_counts_only + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + view_timestamps + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + @@ -50181,6 +51183,27 @@ + + click_for_statuses + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + clm_no false @@ -50601,6 +51624,27 @@ + + partsreceived + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + partsstatus false @@ -54650,6 +55694,27 @@ + + payments_by_date_excel + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + payments_by_date_payment false @@ -60825,6 +61890,27 @@ + + parts + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + parts-queue false @@ -60846,6 +61932,27 @@ + + parts_settings + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + payments-all false @@ -61077,6 +62184,27 @@ + + simplified-parts-jobs + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + tasks false @@ -61793,6 +62921,27 @@ + + parts + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + parts-queue false @@ -61814,6 +62963,27 @@ + + parts_settings + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + payments-all false @@ -62129,6 +63299,27 @@ + + simplified-parts-jobs + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + tasks false @@ -63769,6 +64960,48 @@ + + dark_theme + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + light_theme + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + signout false @@ -63973,6 +65206,132 @@ + + notification_sound_disabled + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + notification_sound_enabled + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + notification_sound_help + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + notification_sound_off + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + notification_sound_on + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + play_sound_for_new_messages + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + profileinfo false @@ -63994,6 +65353,27 @@ + + user_settings + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + diff --git a/client/.env.development.imex b/client/.env.development.imex index 32dac9e44..1a43b6c7f 100644 --- a/client/.env.development.imex +++ b/client/.env.development.imex @@ -18,4 +18,3 @@ VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com VITE_APP_AMP_KEY=6228a598e57cd66875cfd41604f1f891 -VITE_ENABLE_COMPILER_IN_DEV=1 diff --git a/client/.env.development.rome b/client/.env.development.rome index 8a2ad85b7..ac5c8e6fe 100644 --- a/client/.env.development.rome +++ b/client/.env.development.rome @@ -20,4 +20,3 @@ VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com VITE_APP_AMP_KEY=46b1193a867d4e3131ae4c3a64a3fc78 -VITE_ENABLE_COMPILER_IN_DEV=1 diff --git a/client/.gitignore b/client/.gitignore index 0d780b332..7a858909a 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -13,3 +13,6 @@ playwright/.cache/ # Sentry Config File .sentryclirc /dev-dist + +# Local environment overrides (not version controlled) +.env.development.local.overrides diff --git a/client/package-lock.json b/client/package-lock.json index 9efcaaace..225d2ba06 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,9 +9,13 @@ "version": "0.2.1", "hasInstallScript": true, "dependencies": { - "@amplitude/analytics-browser": "^2.33.4", + "@amplitude/analytics-browser": "^2.34.0", "@ant-design/pro-layout": "^7.22.6", - "@apollo/client": "^4.1.1", + "@apollo/client": "^4.1.3", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@emotion/is-prop-valid": "^1.4.0", "@fingerprintjs/fingerprintjs": "^5.0.1", "@firebase/analytics": "^0.10.19", @@ -22,14 +26,14 @@ "@jsreport/browser-client": "^3.1.0", "@reduxjs/toolkit": "^2.11.2", "@sentry/cli": "^3.1.0", - "@sentry/react": "^10.35.0", - "@sentry/vite-plugin": "^4.7.0", + "@sentry/react": "^10.38.0", + "@sentry/vite-plugin": "^4.8.0", "@splitsoftware/splitio-react": "^2.6.1", - "@tanem/react-nprogress": "^5.0.56", - "antd": "^6.2.1", + "@tanem/react-nprogress": "^5.0.58", + "antd": "^6.2.2", "apollo-link-logger": "^3.0.0", "autosize": "^6.0.1", - "axios": "^1.13.2", + "axios": "^1.13.4", "classnames": "^2.5.1", "css-box-model": "^1.2.1", "dayjs": "^1.11.19", @@ -39,31 +43,30 @@ "env-cmd": "^11.0.0", "exifr": "^7.1.3", "graphql": "^16.12.0", - "graphql-ws": "^6.0.6", + "graphql-ws": "^6.0.7", "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "immutability-helper": "^3.1.1", - "libphonenumber-js": "^1.12.34", - "lightningcss": "^1.31.0", - "logrocket": "^11.0.0", + "libphonenumber-js": "^1.12.36", + "lightningcss": "^1.31.1", + "logrocket": "^12.0.0", "markerjs2": "^2.32.7", "memoize-one": "^6.0.0", "normalize-url": "^8.1.1", "object-hash": "^3.0.0", - "phone": "^3.1.69", - "posthog-js": "^1.335.0", + "phone": "^3.1.70", + "posthog-js": "^1.336.4", "prop-types": "^15.8.1", "query-string": "^9.3.1", "raf-schd": "^4.0.3", - "react": "^19.2.3", + "react": "^19.2.4", "react-big-calendar": "^1.19.4", "react-color": "^2.19.3", "react-cookie": "^8.0.1", - "react-dom": "^19.2.3", - "react-drag-listview": "^2.0.0", + "react-dom": "^19.2.4", "react-grid-gallery": "^1.0.1", "react-grid-layout": "^2.2.2", - "react-i18next": "^16.5.3", + "react-i18next": "^16.5.4", "react-icons": "^5.5.0", "react-image-lightbox": "^5.1.4", "react-markdown": "^10.1.0", @@ -72,10 +75,10 @@ "react-product-fruits": "^2.2.62", "react-redux": "^9.2.0", "react-resizable": "^3.1.3", - "react-router-dom": "^7.12.0", + "react-router-dom": "^7.13.0", "react-sticky": "^6.0.3", "react-virtuoso": "^4.18.1", - "recharts": "^3.6.0", + "recharts": "^3.7.0", "redux": "^5.0.1", "redux-actions": "^3.0.3", "redux-persist": "^6.0.0", @@ -83,7 +86,7 @@ "redux-state-sync": "^3.1.4", "reselect": "^5.1.1", "rxjs": "^7.8.2", - "sass": "^1.97.2", + "sass": "^1.97.3", "socket.io-client": "^4.8.3", "styled-components": "^6.3.8", "vite-plugin-ejs": "^1.7.0", @@ -109,7 +112,7 @@ "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-compiler": "^19.1.0-rc.2", - "globals": "^17.1.0", + "globals": "^17.2.0", "jsdom": "^27.4.0", "memfs": "^4.56.10", "os-browserify": "^0.3.0", @@ -148,17 +151,17 @@ "license": "MIT" }, "node_modules/@amplitude/analytics-browser": { - "version": "2.33.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.33.4.tgz", - "integrity": "sha512-5oeZ3fsxbXiE6S7Jq/bsYn10DJ+IPSY1dC08PO2kD9cfaviWtXVrehSwThitEZGKHGs4NeJXCGS1xAhOLR2g0g==", + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.34.0.tgz", + "integrity": "sha512-a5AeUBs6AbgfEPBNVP1/FM8+0ZBjIzbfYcVJq2Lkvb0EaeEI09vrl+zFeJSDcjg5nsDBmz7oKV8j2kbGJHbZ9w==", "license": "MIT", "dependencies": { - "@amplitude/analytics-core": "2.36.0", - "@amplitude/plugin-autocapture-browser": "1.18.6", - "@amplitude/plugin-network-capture-browser": "1.7.6", - "@amplitude/plugin-page-url-enrichment-browser": "0.5.12", - "@amplitude/plugin-page-view-tracking-browser": "2.6.9", - "@amplitude/plugin-web-vitals-browser": "1.1.7", + "@amplitude/analytics-core": "2.37.0", + "@amplitude/plugin-autocapture-browser": "1.19.0", + "@amplitude/plugin-network-capture-browser": "1.7.8", + "@amplitude/plugin-page-url-enrichment-browser": "0.5.14", + "@amplitude/plugin-page-view-tracking-browser": "2.6.11", + "@amplitude/plugin-web-vitals-browser": "1.1.9", "tslib": "^2.4.1" } }, @@ -169,9 +172,9 @@ "license": "MIT" }, "node_modules/@amplitude/analytics-core": { - "version": "2.36.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.36.0.tgz", - "integrity": "sha512-VPkqVK7PZBwkatD22Xu0kshtLeM8bd6KjCsFcnje0FA/LHgixYw1O4ihWpPlUzDNMIXSb2+opN3SkINImmBOnQ==", + "version": "2.37.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.37.0.tgz", + "integrity": "sha512-/2vIyquLMSA29MMM901d5DOhBZ5bc6Qf4s0KVfRk8Avn90mC7KlE5SQiHOAW8+63o5aT1NtB0djrY4cq8Y8Opw==", "license": "MIT", "dependencies": { "@amplitude/analytics-connector": "^1.6.4", @@ -180,52 +183,52 @@ } }, "node_modules/@amplitude/plugin-autocapture-browser": { - "version": "1.18.6", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-autocapture-browser/-/plugin-autocapture-browser-1.18.6.tgz", - "integrity": "sha512-8oZ0jGHjs9Sm98L7l2X5nVjT/qAv+ezk/eibYdHiwA10haHRjXc+v4cFuGeboQkSd87gWvD4LyA7iamVUUak/Q==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-autocapture-browser/-/plugin-autocapture-browser-1.19.0.tgz", + "integrity": "sha512-ga0TXxE87wNMgFvVUPE7a+HGMqMz9/Gy6C7ux3cgcuy8D7Z/Te5iMZmMadK/ztmQ3/pSZjYv9iDvOFsT6ywMZw==", "license": "MIT", "dependencies": { - "@amplitude/analytics-core": "2.36.0", + "@amplitude/analytics-core": "2.37.0", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-network-capture-browser": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-network-capture-browser/-/plugin-network-capture-browser-1.7.6.tgz", - "integrity": "sha512-FJMdpeOV9e4+TYUfUTSIuBuBU4dLRwB7Qq/tFbFHEogAH8NFcsYKxe0rAWmTqMTmKxb2SHTIEC35D+aWVJzWCQ==", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-network-capture-browser/-/plugin-network-capture-browser-1.7.8.tgz", + "integrity": "sha512-SZj3m3O8yI040+Nto9uLi5eM0AclEi8hkZttLSKKVCJVIdi5xE1z9oC7lTir1h2b198unTqP2o7cmzEqI1wBoA==", "license": "MIT", "dependencies": { - "@amplitude/analytics-core": "2.36.0", + "@amplitude/analytics-core": "2.37.0", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-page-url-enrichment-browser": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-url-enrichment-browser/-/plugin-page-url-enrichment-browser-0.5.12.tgz", - "integrity": "sha512-FMPaY+apoyULJSzTMdz2UQ0c8Ry3J/m1yD9sjsRy2VGhbXyLFV5zrfcHkiIZAtDHy2sVpsv130j1eGZIK2aqZw==", + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-url-enrichment-browser/-/plugin-page-url-enrichment-browser-0.5.14.tgz", + "integrity": "sha512-vaAGxMgxQbsRKWtIKiMp3kVdg4RSsAzwlffDw7ifTw1yObVV2vVeBPb/O24cLf49NrhZ3ZuJlaxGmRxoY4FWow==", "license": "MIT", "dependencies": { - "@amplitude/analytics-core": "2.36.0", + "@amplitude/analytics-core": "2.37.0", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.6.9.tgz", - "integrity": "sha512-LfV+4t8V7Kq6TKecaggC2rOszE9sVTs73xPok1UXGvlvVkY+KaEc9ngkansBOKCfCU7inNaIMlGRj1YZDrEjjA==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.6.11.tgz", + "integrity": "sha512-/UqXipdOWOsmn8Uw1BibOfgLMvjE8YYYI8bXL2vZ6D2mk6c0FdGe17BmccsLr1LVGx3HObZoIGnxje+0X1j07w==", "license": "MIT", "dependencies": { - "@amplitude/analytics-core": "2.36.0", + "@amplitude/analytics-core": "2.37.0", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-web-vitals-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-vitals-browser/-/plugin-web-vitals-browser-1.1.7.tgz", - "integrity": "sha512-n1zOsE1RFE3y2IN1OUKTZYQnR7NZMATarHjBsf/tJ+6fQ2g5QDwyTRLzBHmdUcsLe559+ek9QTtIhXmbBOXR3Q==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-vitals-browser/-/plugin-web-vitals-browser-1.1.9.tgz", + "integrity": "sha512-hVGsJWtJQpHlpfC+IubCixe7KNmqaDkWO0XkwkLkn4T/FjFgK+rp9edBJr5KSi0enfcN+49DxJ4qQKu7NncIMA==", "license": "MIT", "dependencies": { - "@amplitude/analytics-core": "2.36.0", + "@amplitude/analytics-core": "2.37.0", "tslib": "^2.4.1", "web-vitals": "5.1.0" } @@ -274,9 +277,9 @@ } }, "node_modules/@ant-design/cssinjs-utils/node_modules/@ant-design/cssinjs": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.0.2.tgz", - "integrity": "sha512-7KDVIigtqlamOLtJ0hbjECX/sDGDaJXsM/KHala8I/1E4lpl9RAO585kbVvh/k1rIrFAV6JeGkXmdWyYj9XvuA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.0.3.tgz", + "integrity": "sha512-HAo8SZ3a6G8v6jT0suCz1270na6EA3obeJWM4uzRijBhdwdoMAXWK2f4WWkwB28yUufsfk3CAhN1coGPQq4kNQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.11.1", @@ -302,9 +305,9 @@ } }, "node_modules/@ant-design/fast-color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.0.tgz", - "integrity": "sha512-eqvpP7xEDm2S7dUzl5srEQCBTXZMmY3ekf97zI+M2DHOYyKdJGH0qua0JACHTqbkRnD/KHFQP9J1uMJ/XWVzzA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz", + "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==", "license": "MIT", "engines": { "node": ">=8.x" @@ -540,9 +543,9 @@ } }, "node_modules/@apollo/client": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.1.1.tgz", - "integrity": "sha512-EizMzR+qfn3kRZ7dy9LxEI2omkyaylWNbBy3Sce8QBmeQP+sOlmYqx2uu5aDFk+uGdrf/QtzHLOI6hUPGfm34A==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.1.3.tgz", + "integrity": "sha512-2D0eN9R0IHj9qp1RwjM1/brKqcBGldlDfY0YiP5ecCj9FtVrhOtXqMj98SZ1CA0YGDY5X+dxx32Ljh7J0VHTfA==", "license": "MIT", "workspaces": [ "dist", @@ -596,9 +599,9 @@ } }, "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -620,9 +623,9 @@ } }, "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -2288,9 +2291,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2494,6 +2497,73 @@ "node": ">=10" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/modifiers": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz", + "integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@dotenvx/dotenvx": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.52.0.tgz", @@ -3815,9 +3885,9 @@ } }, "node_modules/@jsonjoy.com/buffers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.0.0.tgz", - "integrity": "sha512-NDigYR3PHqCnQLXYyoLbnEdzMMvzeiCWo1KOut7Q0CoIqg9tUAPKJ1iq/2nFhc5kZtexzutNY0LFjdwWL3Dw3Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4142,19 +4212,20 @@ } }, "node_modules/@jsonjoy.com/json-pack": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.11.0.tgz", - "integrity": "sha512-nLqSTAYwpk+5ZQIoVp7pfd/oSKNWlEdvTq2LzVA4r2wtWZg6v+5u0VgBOaDJuUfNOuw/4Ysq6glN5QKSrOCgrA==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/base64": "^1.1.2", - "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/buffers": "^1.2.0", "@jsonjoy.com/codegen": "^1.0.0", - "@jsonjoy.com/json-pointer": "^1.0.1", + "@jsonjoy.com/json-pointer": "^1.0.2", "@jsonjoy.com/util": "^1.9.0", "hyperdyperid": "^1.2.0", - "thingies": "^2.5.0" + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" }, "engines": { "node": ">=10.0" @@ -4168,13 +4239,14 @@ } }, "node_modules/@jsonjoy.com/json-pointer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.1.tgz", - "integrity": "sha512-tJpwQfuBuxqZlyoJOSZcqf7OUmiYQ6MiPNmOv4KbZdXE/DdvBSSAwhos0zIlJU/AXxC8XpuO8p08bh2fIl+RKA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/util": "^1.3.0" + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" }, "engines": { "node": ">=10.0" @@ -4348,7 +4420,7 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "node_modules/@opentelemetry/resources": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", @@ -4364,37 +4436,6 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/resources": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.3.0.tgz", - "integrity": "sha512-shlr2l5g+87J8wqYlsLyaUsgKVRO7RtX70Ckd5CtDOWtImZgaUDmf4Z2ozuSKQLM2wPDR0TE/3bPVBNJtRm/cQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.3.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.3.0.tgz", - "integrity": "sha512-PcmxJQzs31cfD0R2dE91YGFcLxOSN4Bxz7gez5UwSUjCai8BwH/GI5HchfVshHkWdTkUs0qcaPJgVHKXUp7I3A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, "node_modules/@opentelemetry/sdk-logs": { "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz", @@ -4412,22 +4453,6 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", - "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, "node_modules/@opentelemetry/sdk-metrics": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz", @@ -4444,22 +4469,6 @@ "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", - "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, "node_modules/@opentelemetry/sdk-trace-base": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", @@ -4477,22 +4486,6 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", - "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, "node_modules/@opentelemetry/semantic-conventions": { "version": "1.38.0", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", @@ -4848,18 +4841,18 @@ } }, "node_modules/@posthog/core": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.13.0.tgz", - "integrity": "sha512-knjncrk7qRmssFRbGzBl1Tunt21GRpe0Wv+uVelyL0Rh7PdQUsgguulzXFTps8hA6wPwTU4kq85qnbAJ3eH6Wg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.17.0.tgz", + "integrity": "sha512-8pDNL+/u9ojzXloA5wILVDXBCV5daJ7w2ipCALQlEEZmL752cCKhRpbyiHn3tjKXh3Hy6aOboJneYa1JdlVHrQ==", "license": "MIT", "dependencies": { "cross-spawn": "^7.0.6" } }, "node_modules/@posthog/types": { - "version": "1.335.0", - "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.335.0.tgz", - "integrity": "sha512-KvxF9Dd9bM/LJyFTm7j8NM8EV6Mect4N8A0Q/gSQknu5pAgOfplToN9hLg+v8aWvtIEDlPHV7mBMKLUE19kVBA==", + "version": "1.336.4", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.336.4.tgz", + "integrity": "sha512-BY3cq/8segbXEvHbEXx9SWmaKJEM0AGgsOgMFH2yy13AV+rUHsGcp4Z5LDI5pU25DURN9EAZvzcoVyYy/Iokmw==", "license": "MIT" }, "node_modules/@protobufjs/aspromise": { @@ -5577,9 +5570,9 @@ } }, "node_modules/@rc-component/select": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.5.0.tgz", - "integrity": "sha512-Zz0hpToAfOdWo/1jj3dW5iooBNU8F6fVgVaYN4Jy1SL3Xcx2OO+IqiQnxqk/PjY6hg1HVt7LjkkrYvpJQyZxoQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.5.1.tgz", + "integrity": "sha512-ARXtwfCVnpDJj1bQjh1cimUlNQkZiN72hvtL2G4mKXIYfkokYdA2Vyu2deAfY7kuHSWpmZygVuohQt6TxOYjnA==", "license": "MIT", "dependencies": { "@rc-component/overflow": "^1.0.0", @@ -6157,9 +6150,9 @@ } }, "node_modules/@rollup/plugin-inject/node_modules/@rollup/pluginutils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", - "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6347,9 +6340,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.47.1.tgz", - "integrity": "sha512-lTahKRJip0knffA/GTNFJMrToD+CM+JJ+Qt5kjzBK/sFQ0EWqfKW3AYQSlZXN98tX0lx66083U9JYIMioMMK7g==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", + "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", "cpu": [ "arm" ], @@ -6361,9 +6354,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.47.1.tgz", - "integrity": "sha512-uqxkb3RJLzlBbh/bbNQ4r7YpSZnjgMgyoEOY7Fy6GCbelkDSAzeiogxMG9TfLsBbqmGsdDObo3mzGqa8hps4MA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", + "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", "cpu": [ "arm64" ], @@ -6375,9 +6368,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.47.1.tgz", - "integrity": "sha512-tV6reObmxBDS4DDyLzTDIpymthNlxrLBGAoQx6m2a7eifSNEZdkXQl1PE4ZjCkEDPVgNXSzND/k9AQ3mC4IOEQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", + "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", "cpu": [ "arm64" ], @@ -6389,9 +6382,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.47.1.tgz", - "integrity": "sha512-XuJRPTnMk1lwsSnS3vYyVMu4x/+WIw1MMSiqj5C4j3QOWsMzbJEK90zG+SWV1h0B1ABGCQ0UZUjti+TQK35uHQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", + "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", "cpu": [ "x64" ], @@ -6403,9 +6396,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.47.1.tgz", - "integrity": "sha512-79BAm8Ag/tmJ5asCqgOXsb3WY28Rdd5Lxj8ONiQzWzy9LvWORd5qVuOnjlqiWWZJw+dWewEktZb5yiM1DLLaHw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", + "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", "cpu": [ "arm64" ], @@ -6417,9 +6410,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.47.1.tgz", - "integrity": "sha512-OQ2/ZDGzdOOlyfqBiip0ZX/jVFekzYrGtUsqAfLDbWy0jh1PUU18+jYp8UMpqhly5ltEqotc2miLngf9FPSWIA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", + "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", "cpu": [ "x64" ], @@ -6431,9 +6424,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.47.1.tgz", - "integrity": "sha512-HZZBXJL1udxlCVvoVadstgiU26seKkHbbAMLg7680gAcMnRNP9SAwTMVet02ANA94kXEI2VhBnXs4e5nf7KG2A==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", + "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", "cpu": [ "arm" ], @@ -6445,9 +6438,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.47.1.tgz", - "integrity": "sha512-sZ5p2I9UA7T950JmuZ3pgdKA6+RTBr+0FpK427ExW0t7n+QwYOcmDTK/aRlzoBrWyTpJNlS3kacgSlSTUg6P/Q==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", + "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", "cpu": [ "arm" ], @@ -6459,9 +6452,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.47.1.tgz", - "integrity": "sha512-3hBFoqPyU89Dyf1mQRXCdpc6qC6At3LV6jbbIOZd72jcx7xNk3aAp+EjzAtN6sDlmHFzsDJN5yeUySvorWeRXA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", + "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", "cpu": [ "arm64" ], @@ -6473,9 +6466,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.47.1.tgz", - "integrity": "sha512-49J4FnMHfGodJWPw73Ve+/hsPjZgcXQGkmqBGZFvltzBKRS+cvMiWNLadOMXKGnYRhs1ToTGM0sItKISoSGUNA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", + "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", "cpu": [ "arm64" ], @@ -6486,10 +6479,24 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.47.1.tgz", - "integrity": "sha512-4yYU8p7AneEpQkRX03pbpLmE21z5JNys16F1BZBZg5fP9rIlb0TkeQjn5du5w4agConCCEoYIG57sNxjryHEGg==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", + "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", + "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", "cpu": [ "loong64" ], @@ -6501,9 +6508,23 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.47.1.tgz", - "integrity": "sha512-fAiq+J28l2YMWgC39jz/zPi2jqc0y3GSRo1yyxlBHt6UN0yYgnegHSRPa3pnHS5amT/efXQrm0ug5+aNEu9UuQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", + "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", + "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", "cpu": [ "ppc64" ], @@ -6515,9 +6536,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.47.1.tgz", - "integrity": "sha512-daoT0PMENNdjVYYU9xec30Y2prb1AbEIbb64sqkcQcSaR0zYuKkoPuhIztfxuqN82KYCKKrj+tQe4Gi7OSm1ow==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", + "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", "cpu": [ "riscv64" ], @@ -6529,9 +6550,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.47.1.tgz", - "integrity": "sha512-JNyXaAhWtdzfXu5pUcHAuNwGQKevR+6z/poYQKVW+pLaYOj9G1meYc57/1Xv2u4uTxfu9qEWmNTjv/H/EpAisw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", + "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", "cpu": [ "riscv64" ], @@ -6543,9 +6564,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.47.1.tgz", - "integrity": "sha512-U/CHbqKSwEQyZXjCpY43/GLYcTVKEXeRHw0rMBJP7fP3x6WpYG4LTJWR3ic6TeYKX6ZK7mrhltP4ppolyVhLVQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", + "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", "cpu": [ "s390x" ], @@ -6570,9 +6591,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.47.1.tgz", - "integrity": "sha512-Ft+d/9DXs30BK7CHCTX11FtQGHUdpNDLJW0HHLign4lgMgBcPFN3NkdIXhC5r9iwsMwYreBBc4Rho5ieOmKNVQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", + "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", "cpu": [ "x64" ], @@ -6583,10 +6604,38 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", + "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", + "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.47.1.tgz", - "integrity": "sha512-N9X5WqGYzZnjGAFsKSfYFtAShYjwOmFJoWbLg3dYixZOZqU7hdMq+/xyS14zKLhFhZDhP9VfkzQnsdk0ZDS9IA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", + "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", "cpu": [ "arm64" ], @@ -6598,9 +6647,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.47.1.tgz", - "integrity": "sha512-O+KcfeCORZADEY8oQJk4HK8wtEOCRE4MdOkb8qGZQNun3jzmj2nmhV/B/ZaaZOkPmJyvm/gW9n0gsB4eRa1eiQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", + "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", "cpu": [ "ia32" ], @@ -6611,10 +6660,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", + "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.47.1.tgz", - "integrity": "sha512-CpKnYa8eHthJa3c+C38v/E+/KZyF1Jdh2Cz3DyKZqEWYgrM1IHFArXNWvBLPQCKUEsAqqKX27tTqVEFbDNUcOA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", + "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", "cpu": [ "x64" ], @@ -6626,88 +6689,88 @@ ] }, "node_modules/@sentry-internal/browser-utils": { - "version": "10.35.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.35.0.tgz", - "integrity": "sha512-YjVbyqpJu6E6U/BCdOgIUuUQPUDZ7XdFiBYXtGy59xqQB1qSqNfei163hkfnXxIN90csDubxWNrnit+W5Wo/uQ==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.38.0.tgz", + "integrity": "sha512-UOJtYmdcxHCcV0NPfXFff/a95iXl/E0EhuQ1y0uE0BuZDMupWSF5t2BgC4HaE5Aw3RTjDF3XkSHWoIF6ohy7eA==", "license": "MIT", "dependencies": { - "@sentry/core": "10.35.0" + "@sentry/core": "10.38.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/feedback": { - "version": "10.35.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.35.0.tgz", - "integrity": "sha512-h/rtGcgvGtZIY9njxnzHHMzMwFYAYG/UwDaNtpf8jN63JD6cTQDQ8wNWp0arD9gmUr96YjER55BNRRF8oSg6Fw==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.38.0.tgz", + "integrity": "sha512-JXneg9zRftyfy1Fyfc39bBlF/Qd8g4UDublFFkVvdc1S6JQPlK+P6q22DKz3Pc8w3ySby+xlIq/eTu9Pzqi4KA==", "license": "MIT", "dependencies": { - "@sentry/core": "10.35.0" + "@sentry/core": "10.38.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/replay": { - "version": "10.35.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.35.0.tgz", - "integrity": "sha512-9hGP3lD+7o/4ovGTdwv3T9K2t9LxSlR/CAcRQeFApW2c0AGsjTdcglOxsgxYei4YmaISx0CBJ/YqJfQVYxaxWw==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.38.0.tgz", + "integrity": "sha512-YWIkL6/dnaiQyFiZXJ/nN+NXGv/15z45ia86bE/TMq01CubX/DUOilgsFz0pk2v/pg3tp/U2MskLO9Hz0cnqeg==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "10.35.0", - "@sentry/core": "10.35.0" + "@sentry-internal/browser-utils": "10.38.0", + "@sentry/core": "10.38.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "10.35.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.35.0.tgz", - "integrity": "sha512-efaz8ETDLd0rSpoqX4m8fMnq7abzUJAdqeChz9Jdq6OgvHeBgM6tTfqWSes6sFnSCvFUVkdFngZQfgmBxWGuEA==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.38.0.tgz", + "integrity": "sha512-OXWM9jEqNYh4VTvrMu7v+z1anz+QKQ/fZXIZdsO7JTT2lGNZe58UUMeoq386M+Saxen8F9SUH7yTORy/8KI5qw==", "license": "MIT", "dependencies": { - "@sentry-internal/replay": "10.35.0", - "@sentry/core": "10.35.0" + "@sentry-internal/replay": "10.38.0", + "@sentry/core": "10.38.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry/babel-plugin-component-annotate": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.7.0.tgz", - "integrity": "sha512-MkyajDiO17/GaHHFgOmh05ZtOwF5hmm9KRjVgn9PXHIdpz+TFM5mkp1dABmR6Y75TyNU98Z1aOwPOgyaR5etJw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.8.0.tgz", + "integrity": "sha512-cy/9Eipkv23MsEJ4IuB4dNlVwS9UqOzI3Eu+QPake5BVFgPYCX0uP0Tr3Z43Ime6Rb+BiDnWC51AJK9i9afHYw==", "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/@sentry/browser": { - "version": "10.35.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.35.0.tgz", - "integrity": "sha512-3wCdmKOTqg6Fvmb9HLHzCVIpSSYCPhXFQ95VaYsb1rESIgL7BMS9nyqhecPcPR3oJppU2a/TqZk4YH3nFrPXmA==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.38.0.tgz", + "integrity": "sha512-3phzp1YX4wcQr9mocGWKbjv0jwtuoDBv7+Y6Yfrys/kwyaL84mDLjjQhRf4gL5SX7JdYkhBp4WaiNlR0UC4kTA==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "10.35.0", - "@sentry-internal/feedback": "10.35.0", - "@sentry-internal/replay": "10.35.0", - "@sentry-internal/replay-canvas": "10.35.0", - "@sentry/core": "10.35.0" + "@sentry-internal/browser-utils": "10.38.0", + "@sentry-internal/feedback": "10.38.0", + "@sentry-internal/replay": "10.38.0", + "@sentry-internal/replay-canvas": "10.38.0", + "@sentry/core": "10.38.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry/bundler-plugin-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.7.0.tgz", - "integrity": "sha512-gFdEtiup/7qYhN3vp1v2f0WL9AG9OorWLtIpfSBYbWjtzklVNg1sizvNyZ8nEiwtnb25LzvvCUbOP1SyP6IodQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.8.0.tgz", + "integrity": "sha512-QaXd/NzaZ2vmiA2FNu2nBkgQU+17N3fE+zVOTzG0YK54QDSJMd4n3AeJIEyPhSzkOob+GqtO22nbYf6AATFMAw==", "license": "MIT", "dependencies": { "@babel/core": "^7.18.5", - "@sentry/babel-plugin-component-annotate": "4.7.0", + "@sentry/babel-plugin-component-annotate": "4.8.0", "@sentry/cli": "^2.57.0", "dotenv": "^16.3.1", "find-up": "^5.0.0", @@ -7103,22 +7166,22 @@ } }, "node_modules/@sentry/core": { - "version": "10.35.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.35.0.tgz", - "integrity": "sha512-lEK1WFqt6oHtMq5dDLVE/FDzHDGs1PlYT5cZH4aBirYtJVyUiTf0NknKFob4a2zTywczlq7SbLv6Ba8UMU9dYg==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.38.0.tgz", + "integrity": "sha512-1pubWDZE5y5HZEPMAZERP4fVl2NH3Ihp1A+vMoVkb3Qc66Diqj1WierAnStlZP7tCx0TBa0dK85GTW/ZFYyB9g==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@sentry/react": { - "version": "10.35.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.35.0.tgz", - "integrity": "sha512-RJsJVZRVe646euf1HLlhbjeAHn2ABd54Y7Zpy4XUJaL4FdKqaaFmqeHKi6IxXFf6IE35onk/kn8CfR7xWBhe2g==", + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.38.0.tgz", + "integrity": "sha512-3UiKo6QsqTyPGUt0XWRY9KLaxc/cs6Kz4vlldBSOXEL6qPDL/EfpwNJT61osRo81VFWu8pKu7ZY2bvLPryrnBQ==", "license": "MIT", "dependencies": { - "@sentry/browser": "10.35.0", - "@sentry/core": "10.35.0" + "@sentry/browser": "10.38.0", + "@sentry/core": "10.38.0" }, "engines": { "node": ">=18" @@ -7128,12 +7191,12 @@ } }, "node_modules/@sentry/vite-plugin": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-4.7.0.tgz", - "integrity": "sha512-eQXDghOQLsYwnHutJo8TCzhG4gp0KLNq3h96iqFMhsbjnNnfYeCX1lIw1pJEh/az3cDwSyPI/KGkvf8hr0dZmQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-4.8.0.tgz", + "integrity": "sha512-/YZJitGsx/o72FFQYy3tucUfs4w3COvSI1d2NYyAhIzay4tjLLRjpM5PdwFnoBT7Uj/7jSbuHkg87PAliLiu2g==", "license": "MIT", "dependencies": { - "@sentry/bundler-plugin-core": "4.7.0", + "@sentry/bundler-plugin-core": "4.8.0", "unplugin": "1.0.1" }, "engines": { @@ -7239,12 +7302,12 @@ } }, "node_modules/@tanem/react-nprogress": { - "version": "5.0.56", - "resolved": "https://registry.npmjs.org/@tanem/react-nprogress/-/react-nprogress-5.0.56.tgz", - "integrity": "sha512-OI5rXB6jxC/RLJqnIuaKNXu3qQ6Lf3+g1HqkqGz01E25iV3pZFYBihMdGsEeg/4pAm0O7xo/umcAA3jsdeibHA==", + "version": "5.0.58", + "resolved": "https://registry.npmjs.org/@tanem/react-nprogress/-/react-nprogress-5.0.58.tgz", + "integrity": "sha512-VbO2PF/lYwyV5axQTLQWrC2l7amVadCikg/WnpncdgkGHzcWgkdV/h+SoYgkB2cfc8yQm0Qpv9DCGK8q4wWvsw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", + "@babel/runtime": "^7.28.6", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { @@ -7941,15 +8004,15 @@ } }, "node_modules/antd": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/antd/-/antd-6.2.1.tgz", - "integrity": "sha512-ycw/XX7So4MdrwYKGfvZJdkGiCYUOSTebAIi+ejE95WJ138b11oy/iJg7iH0qydaD/B5sFd7Tz8XfPBuW7CRmw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/antd/-/antd-6.2.2.tgz", + "integrity": "sha512-f5RvWnhjt2gZTpBMW3msHwA3IeaCJBHDwVyEsskYGp0EXcRhhklWrltkybDki0ysBNywkjLPp3wuuWhIKfplcQ==", "license": "MIT", "dependencies": { "@ant-design/colors": "^8.0.1", "@ant-design/cssinjs": "^2.0.3", "@ant-design/cssinjs-utils": "^2.0.2", - "@ant-design/fast-color": "^3.0.0", + "@ant-design/fast-color": "^3.0.1", "@ant-design/icons": "^6.1.0", "@ant-design/react-slick": "~2.0.0", "@babel/runtime": "^7.28.4", @@ -7976,7 +8039,7 @@ "@rc-component/rate": "~1.0.1", "@rc-component/resize-observer": "^1.1.1", "@rc-component/segmented": "~1.3.0", - "@rc-component/select": "~1.5.0", + "@rc-component/select": "~1.5.1", "@rc-component/slider": "~1.0.1", "@rc-component/steps": "~1.2.2", "@rc-component/switch": "~1.0.3", @@ -8325,9 +8388,9 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -8403,16 +8466,6 @@ "@babel/types": "^7.26.0" } }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", - "license": "MIT", - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -8686,23 +8739,6 @@ "dev": true, "license": "MIT" }, - "node_modules/browserify-sign/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", @@ -9216,14 +9252,6 @@ "node": ">=18" } }, - "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true, - "license": "MIT" - }, "node_modules/core-js-compat": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", @@ -9455,9 +9483,9 @@ } }, "node_modules/cssstyle/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -9687,9 +9715,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -9837,9 +9865,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -11301,9 +11329,9 @@ "integrity": "sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==" }, "node_modules/globals": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.1.0.tgz", - "integrity": "sha512-8HoIcWI5fCvG5NADj4bDav+er9B9JMj2vyL2pI8D0eismKyUvPLTSs+Ln3wqhwcp306i73iyVnEKx3F6T47TGw==", + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.2.0.tgz", + "integrity": "sha512-tovnCz/fEq+Ripoq+p/gN1u7l6A7wwkoBT9pRCzTHzsD/LvADIzXZdjmRymh5Ztf0DYC3Rwg5cZRYjxzBmzbWg==", "dev": true, "license": "MIT", "engines": { @@ -11374,9 +11402,9 @@ } }, "node_modules/graphql-ws": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.6.tgz", - "integrity": "sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.7.tgz", + "integrity": "sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg==", "license": "MIT", "engines": { "node": ">=20" @@ -11385,7 +11413,6 @@ "@fastify/websocket": "^10 || ^11", "crossws": "~0.3", "graphql": "^15.10.1 || ^16", - "uWebSockets.js": "^20", "ws": "^8" }, "peerDependenciesMeta": { @@ -11395,9 +11422,6 @@ "crossws": { "optional": true }, - "uWebSockets.js": { - "optional": true - }, "ws": { "optional": true } @@ -11804,9 +11828,9 @@ } }, "node_modules/immer": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-11.0.1.tgz", - "integrity": "sha512-naDCyggtcBWANtIrjQEajhhBEuL9b0Zg4zmlWK2CzS6xCWSE39/vvf4LqnMjUAWHBhot4m9MHCM/Z+mfWhUkiA==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz", + "integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==", "license": "MIT", "funding": { "type": "opencollective", @@ -12859,15 +12883,15 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.12.34", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.34.tgz", - "integrity": "sha512-v/Ip8k8eYdp7bINpzqDh46V/PaQ8sK+qi97nMQgjZzFlb166YFqlR/HVI+MzsI9JqcyyVWCOipmmretiaSyQyw==", + "version": "1.12.36", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.36.tgz", + "integrity": "sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==", "license": "MIT" }, "node_modules/lightningcss": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.0.tgz", - "integrity": "sha512-mKXR8TIPqVNcs0qQplcnLgDSmyMW5q9Bt5GmcvABpeexaGGPILxDmMNoabSsS9pAPgICYmgzL2wYFPf84/fQ2A==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", + "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -12880,23 +12904,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.31.0", - "lightningcss-darwin-arm64": "1.31.0", - "lightningcss-darwin-x64": "1.31.0", - "lightningcss-freebsd-x64": "1.31.0", - "lightningcss-linux-arm-gnueabihf": "1.31.0", - "lightningcss-linux-arm64-gnu": "1.31.0", - "lightningcss-linux-arm64-musl": "1.31.0", - "lightningcss-linux-x64-gnu": "1.31.0", - "lightningcss-linux-x64-musl": "1.31.0", - "lightningcss-win32-arm64-msvc": "1.31.0", - "lightningcss-win32-x64-msvc": "1.31.0" + "lightningcss-android-arm64": "1.31.1", + "lightningcss-darwin-arm64": "1.31.1", + "lightningcss-darwin-x64": "1.31.1", + "lightningcss-freebsd-x64": "1.31.1", + "lightningcss-linux-arm-gnueabihf": "1.31.1", + "lightningcss-linux-arm64-gnu": "1.31.1", + "lightningcss-linux-arm64-musl": "1.31.1", + "lightningcss-linux-x64-gnu": "1.31.1", + "lightningcss-linux-x64-musl": "1.31.1", + "lightningcss-win32-arm64-msvc": "1.31.1", + "lightningcss-win32-x64-msvc": "1.31.1" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.0.tgz", - "integrity": "sha512-qRdhuBXBgGfO3NZ37l/lA1qqjqptBQoa37YiMDeMRpJpv/+0CGKtL4o5+VUFaHzZb9+hS/DOg3XNff3YmwY2ug==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", + "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", "cpu": [ "arm64" ], @@ -12914,9 +12938,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.0.tgz", - "integrity": "sha512-ctMERKCPJHgEhkCUvcCT5Z1wB+pCyFO+XskTNFB3uTrE9i3LzQXvrlm2PSuYhOYSExrzfmfD/HVyfqZYnfpjvQ==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", + "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", "cpu": [ "arm64" ], @@ -12934,9 +12958,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.0.tgz", - "integrity": "sha512-7+8dwpz4qj/IiKxSs210WKWoJg59npBxvEXrpuxAkfZDPSgXiPcNZfaL9HtcaRntd7DbzVSI5SHMsmlsl+NZgA==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", + "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", "cpu": [ "x64" ], @@ -12954,9 +12978,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.0.tgz", - "integrity": "sha512-HZdu0reyMXO0TzJnK3D37dxgijjJsZt9muQRi+df/sr6WnkSZJKHfOufm0amWz+LiWg9X3H+XIBW24s/y3itmQ==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", + "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", "cpu": [ "x64" ], @@ -12974,9 +12998,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.0.tgz", - "integrity": "sha512-pqp0rGHc9rebDT7vVtu92JqU6gP5zm19m+zCqvHHMI+cEQrCjbNlMbPqn9UEfPYfRltL4pti9MJQ62558nVHnw==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", + "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", "cpu": [ "arm" ], @@ -12994,9 +13018,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.0.tgz", - "integrity": "sha512-EpAQTq6TXL+200bDNMzhbFpqAJsto01R//xuE8yAWN0l4wmJhmS1r/FxoudIUM9PxHMPEiWeLw+1thdF5ZPg7Q==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", + "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", "cpu": [ "arm64" ], @@ -13014,9 +13038,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.0.tgz", - "integrity": "sha512-6tuU37nXStA3kxNnjC49z1tPFEoviC9ZLyB34O3X1/VTLXdZX2vmPZ+45XesagvlgoeJQ9r9XVSovUZny41AQA==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", + "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", "cpu": [ "arm64" ], @@ -13034,9 +13058,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.0.tgz", - "integrity": "sha512-enNePbgDKmJybVz90/8dAGTOulvpn0IwxamHHnIj32gmdbuSPJ9mk+Nob4UmiqLMAdHlH+0c+lpsZkv4TSxi3w==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", + "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", "cpu": [ "x64" ], @@ -13054,9 +13078,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.0.tgz", - "integrity": "sha512-EM4jGT+V+PdFkcrIB5m5yiSzfV7z43k0pOtUmODhFSbuay5JvbVChK1uoaMmwPTKGWatwSRbiu90BUzU262B9g==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", + "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", "cpu": [ "x64" ], @@ -13074,9 +13098,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.0.tgz", - "integrity": "sha512-IGvE0eCsWrYWerlkXFitANJ2vdkzs4EVCm1sEttanqVc4lqdRKyZ7ZIapBfo5OckE+zuq/JNaIkbWHdYDpOblQ==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", + "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", "cpu": [ "arm64" ], @@ -13094,9 +13118,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.0.tgz", - "integrity": "sha512-7V6CPCLNO1Pv5gPPvXWst7V8cvZjbRKgwht1qd4/OH7yacV/kMV5VDq/RDnmdQpXUTnn4ye+vZkU8REXU46iZA==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", + "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", "cpu": [ "x64" ], @@ -13193,9 +13217,9 @@ "license": "MIT" }, "node_modules/logrocket": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/logrocket/-/logrocket-11.0.0.tgz", - "integrity": "sha512-/WBl2VJ/WVRsveU0PhWcO++XZpKUifRQZwAFqjUyPSwxdy+OFEgKk+W69WSQFM9pejizarXRwmb3Wo7msD75+g==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/logrocket/-/logrocket-12.0.0.tgz", + "integrity": "sha512-7VUI2gi3XxACLqJGZJ/8hRx3KT7z2joNXcBqbefkfWI6rLqMJOq0LUmfLbw3qRPPZTjOBiwpKKVecusL3V5K7Q==", "license": "MIT" }, "node_modules/long": { @@ -14870,9 +14894,9 @@ "license": "MIT" }, "node_modules/phone": { - "version": "3.1.69", - "resolved": "https://registry.npmjs.org/phone/-/phone-3.1.69.tgz", - "integrity": "sha512-Nc6lY9p4bbdYdmHewuIC5lZdLOnu5r0Vm9imL4leZCz0tUD5RFPqRtdGmYR6VMpFT5oP+8ty2nu6lxVV3Jzprw==", + "version": "3.1.70", + "resolved": "https://registry.npmjs.org/phone/-/phone-3.1.70.tgz", + "integrity": "sha512-MFRQaIe8UJmkosaU/OlLRDhSoLw9wJRwqtacNHy8cZjDomTQ9ic8+8nKdGFLpRsaqnLHAXSuiIYTzWggMkSG5Q==", "license": "MIT", "engines": { "node": ">=12" @@ -15002,9 +15026,9 @@ "license": "MIT" }, "node_modules/posthog-js": { - "version": "1.335.0", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.335.0.tgz", - "integrity": "sha512-gWNcSb3RZIpzKu8zDWIPzEaMmGxsRKtCCbW0iTCI153PtBVOmiEsYdmMfg7weWUcf8QYc7yWNPl2AhhydnBDMA==", + "version": "1.336.4", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.336.4.tgz", + "integrity": "sha512-NX81XaqOjS/gue3UsbAAuJxi6vD0AGy1HUvywBIhAArCwbTXKS04NhEFwUcYJdrmwXUf94MntEIWGoc1pTFDtg==", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@opentelemetry/api": "^1.9.0", @@ -15012,20 +15036,20 @@ "@opentelemetry/exporter-logs-otlp-http": "^0.208.0", "@opentelemetry/resources": "^2.2.0", "@opentelemetry/sdk-logs": "^0.208.0", - "@posthog/core": "1.13.0", - "@posthog/types": "1.335.0", + "@posthog/core": "1.17.0", + "@posthog/types": "1.336.4", "core-js": "^3.38.1", "dompurify": "^3.3.1", "fflate": "^0.4.8", - "preact": "^10.28.0", + "preact": "^10.28.2", "query-selector-shadow-dom": "^1.0.1", "web-vitals": "^5.1.0" } }, "node_modules/posthog-js/node_modules/core-js": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", - "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -15324,9 +15348,9 @@ } }, "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -15393,25 +15417,15 @@ } }, "node_modules/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.3" - } - }, - "node_modules/react-drag-listview": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/react-drag-listview/-/react-drag-listview-2.0.0.tgz", - "integrity": "sha512-7Apx/1Xt4qu+JHHP0rH6aLgZgS7c2MX8ocHVGCi03KfeIWEu0t14MhT3boQKM33l5eJrE/IWfExFTvoYq22fsg==", - "license": "MIT", - "dependencies": { - "babel-runtime": "^6.26.0", - "prop-types": "^15.5.8" + "react": "^19.2.4" } }, "node_modules/react-draggable": { @@ -15487,9 +15501,9 @@ "license": "MIT" }, "node_modules/react-i18next": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.3.tgz", - "integrity": "sha512-fo+/NNch37zqxOzlBYrWMx0uy/yInPkRfjSuy4lqKdaecR17nvCHnEUt3QyzA8XjQ2B/0iW/5BhaHR3ZmukpGw==", + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.4.tgz", + "integrity": "sha512-6yj+dcfMncEC21QPhOTsW8mOSO+pzFmT6uvU7XXdvM/Cp38zJkmTeMeKmTrmCMD5ToT79FmiE/mRWiYWcJYW4g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", @@ -15701,9 +15715,9 @@ } }, "node_modules/react-router": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", - "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.0.tgz", + "integrity": "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -15723,12 +15737,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.12.0.tgz", - "integrity": "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.0.tgz", + "integrity": "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==", "license": "MIT", "dependencies": { - "react-router": "7.12.0" + "react-router": "7.13.0" }, "engines": { "node": ">=20.0.0" @@ -15800,9 +15814,9 @@ } }, "node_modules/recharts": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", - "integrity": "sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.7.0.tgz", + "integrity": "sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew==", "license": "MIT", "workspaces": [ "www" @@ -16000,12 +16014,6 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -16130,13 +16138,13 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -16257,27 +16265,10 @@ "dev": true, "license": "MIT" }, - "node_modules/ripemd160/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/ripemd160/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/rollup": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.47.1.tgz", - "integrity": "sha512-iasGAQoZ5dWDzULEUX3jiW0oB1qyFOepSyDyoU6S/OhVlDIwj5knI5QBa5RRQ0sK7OE0v+8VIi2JuV+G+3tfNg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", "dev": true, "license": "MIT", "dependencies": { @@ -16291,33 +16282,38 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.47.1", - "@rollup/rollup-android-arm64": "4.47.1", - "@rollup/rollup-darwin-arm64": "4.47.1", - "@rollup/rollup-darwin-x64": "4.47.1", - "@rollup/rollup-freebsd-arm64": "4.47.1", - "@rollup/rollup-freebsd-x64": "4.47.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.47.1", - "@rollup/rollup-linux-arm-musleabihf": "4.47.1", - "@rollup/rollup-linux-arm64-gnu": "4.47.1", - "@rollup/rollup-linux-arm64-musl": "4.47.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.47.1", - "@rollup/rollup-linux-ppc64-gnu": "4.47.1", - "@rollup/rollup-linux-riscv64-gnu": "4.47.1", - "@rollup/rollup-linux-riscv64-musl": "4.47.1", - "@rollup/rollup-linux-s390x-gnu": "4.47.1", - "@rollup/rollup-linux-x64-gnu": "4.47.1", - "@rollup/rollup-linux-x64-musl": "4.47.1", - "@rollup/rollup-win32-arm64-msvc": "4.47.1", - "@rollup/rollup-win32-ia32-msvc": "4.47.1", - "@rollup/rollup-win32-x64-msvc": "4.47.1", + "@rollup/rollup-android-arm-eabi": "4.57.0", + "@rollup/rollup-android-arm64": "4.57.0", + "@rollup/rollup-darwin-arm64": "4.57.0", + "@rollup/rollup-darwin-x64": "4.57.0", + "@rollup/rollup-freebsd-arm64": "4.57.0", + "@rollup/rollup-freebsd-x64": "4.57.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", + "@rollup/rollup-linux-arm-musleabihf": "4.57.0", + "@rollup/rollup-linux-arm64-gnu": "4.57.0", + "@rollup/rollup-linux-arm64-musl": "4.57.0", + "@rollup/rollup-linux-loong64-gnu": "4.57.0", + "@rollup/rollup-linux-loong64-musl": "4.57.0", + "@rollup/rollup-linux-ppc64-gnu": "4.57.0", + "@rollup/rollup-linux-ppc64-musl": "4.57.0", + "@rollup/rollup-linux-riscv64-gnu": "4.57.0", + "@rollup/rollup-linux-riscv64-musl": "4.57.0", + "@rollup/rollup-linux-s390x-gnu": "4.57.0", + "@rollup/rollup-linux-x64-gnu": "4.57.0", + "@rollup/rollup-linux-x64-musl": "4.57.0", + "@rollup/rollup-openbsd-x64": "4.57.0", + "@rollup/rollup-openharmony-arm64": "4.57.0", + "@rollup/rollup-win32-arm64-msvc": "4.57.0", + "@rollup/rollup-win32-ia32-msvc": "4.57.0", + "@rollup/rollup-win32-x64-gnu": "4.57.0", + "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" } }, "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.47.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.47.1.tgz", - "integrity": "sha512-uTLEakjxOTElfeZIGWkC34u2auLHB1AYS6wBjPGI00bWdxdLcCzK5awjs25YXpqB9lS8S0vbO0t9ZcBeNibA7g==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", + "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", "cpu": [ "x64" ], @@ -16423,9 +16419,9 @@ } }, "node_modules/sass": { - "version": "1.97.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.2.tgz", - "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==", + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -17039,15 +17035,22 @@ } }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/string-convert": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", @@ -19037,9 +19040,9 @@ "license": "MIT" }, "node_modules/workbox-build/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -19360,9 +19363,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "dev": true, "license": "MIT", "engines": { diff --git a/client/package.json b/client/package.json index 49f96c9bc..b0cbdf6b8 100644 --- a/client/package.json +++ b/client/package.json @@ -8,9 +8,13 @@ "private": true, "proxy": "http://localhost:4000", "dependencies": { - "@amplitude/analytics-browser": "^2.33.4", + "@amplitude/analytics-browser": "^2.34.0", "@ant-design/pro-layout": "^7.22.6", - "@apollo/client": "^4.1.1", + "@apollo/client": "^4.1.3", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@emotion/is-prop-valid": "^1.4.0", "@fingerprintjs/fingerprintjs": "^5.0.1", "@firebase/analytics": "^0.10.19", @@ -21,14 +25,14 @@ "@jsreport/browser-client": "^3.1.0", "@reduxjs/toolkit": "^2.11.2", "@sentry/cli": "^3.1.0", - "@sentry/react": "^10.35.0", - "@sentry/vite-plugin": "^4.7.0", + "@sentry/react": "^10.38.0", + "@sentry/vite-plugin": "^4.8.0", "@splitsoftware/splitio-react": "^2.6.1", - "@tanem/react-nprogress": "^5.0.56", - "antd": "^6.2.1", + "@tanem/react-nprogress": "^5.0.58", + "antd": "^6.2.2", "apollo-link-logger": "^3.0.0", "autosize": "^6.0.1", - "axios": "^1.13.2", + "axios": "^1.13.4", "classnames": "^2.5.1", "css-box-model": "^1.2.1", "dayjs": "^1.11.19", @@ -38,31 +42,30 @@ "env-cmd": "^11.0.0", "exifr": "^7.1.3", "graphql": "^16.12.0", - "graphql-ws": "^6.0.6", + "graphql-ws": "^6.0.7", "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "immutability-helper": "^3.1.1", - "libphonenumber-js": "^1.12.34", - "lightningcss": "^1.31.0", - "logrocket": "^11.0.0", + "libphonenumber-js": "^1.12.36", + "lightningcss": "^1.31.1", + "logrocket": "^12.0.0", "markerjs2": "^2.32.7", "memoize-one": "^6.0.0", "normalize-url": "^8.1.1", "object-hash": "^3.0.0", - "phone": "^3.1.69", - "posthog-js": "^1.335.0", + "phone": "^3.1.70", + "posthog-js": "^1.336.4", "prop-types": "^15.8.1", "query-string": "^9.3.1", "raf-schd": "^4.0.3", - "react": "^19.2.3", + "react": "^19.2.4", "react-big-calendar": "^1.19.4", "react-color": "^2.19.3", "react-cookie": "^8.0.1", - "react-dom": "^19.2.3", - "react-drag-listview": "^2.0.0", + "react-dom": "^19.2.4", "react-grid-gallery": "^1.0.1", "react-grid-layout": "^2.2.2", - "react-i18next": "^16.5.3", + "react-i18next": "^16.5.4", "react-icons": "^5.5.0", "react-image-lightbox": "^5.1.4", "react-markdown": "^10.1.0", @@ -71,10 +74,10 @@ "react-product-fruits": "^2.2.62", "react-redux": "^9.2.0", "react-resizable": "^3.1.3", - "react-router-dom": "^7.12.0", + "react-router-dom": "^7.13.0", "react-sticky": "^6.0.3", "react-virtuoso": "^4.18.1", - "recharts": "^3.6.0", + "recharts": "^3.7.0", "redux": "^5.0.1", "redux-actions": "^3.0.3", "redux-persist": "^6.0.0", @@ -82,7 +85,7 @@ "redux-state-sync": "^3.1.4", "reselect": "^5.1.1", "rxjs": "^7.8.2", - "sass": "^1.97.2", + "sass": "^1.97.3", "socket.io-client": "^4.8.3", "styled-components": "^6.3.8", "vite-plugin-ejs": "^1.7.0", @@ -92,15 +95,17 @@ "postinstall": "echo 'when updating react-big-calendar, remember to check to localizer in the calendar wrapper'", "analyze": "source-map-explorer 'build/static/js/*.js'", "start": "vite", - "build": "dotenvx run --env-file=.env.development.imex -- vite build", - "start:imex": "dotenvx run --env-file=.env.development.imex -- vite", - "start:rome": "dotenvx run --env-file=.env.development.rome -- vite", - "preview:imex": "dotenvx run --env-file=.env.development.imex -- vite preview", - "preview:rome": "dotenvx run --env-file=.env.development.rome -- vite preview", - "build:test:imex": "env-cmd -f .env.test.imex -- npm run build", - "build:test:rome": "env-cmd -f .env.test.rome -- npm run build", - "build:production:imex": "env-cmd -f .env.production.imex -- npm run build", - "build:production:rome": "env-cmd -f .env.production.rome -- npm run build", + "build": "vite build", + "build:dev:imex": "dotenvx run --env-file=.env.development.imex --env-file=.env.development.local.overrides -- vite build", + "build:dev:rome": "dotenvx run --env-file=.env.development.rome --env-file=.env.development.local.overrides -- vite build", + "build:test:imex": "dotenvx run --env-file=.env.test.imex -- vite build", + "build:test:rome": "dotenvx run --env-file=.env.test.rome -- vite build", + "build:production:imex": "env-cmd -f .env.production.imex vite build", + "build:production:rome": "env-cmd -f .env.production.rome vite build", + "start:imex": "dotenvx run --env-file=.env.development.imex --env-file=.env.development.local.overrides -- vite", + "start:rome": "dotenvx run --env-file=.env.development.rome --env-file=.env.development.local.overrides -- vite", + "preview:imex": "dotenvx run --env-file=.env.development.imex --env-file=.env.development.local.overrides -- vite preview", + "preview:rome": "dotenvx run --env-file=.env.development.rome --env-file=.env.development.local.overrides -- vite preview", "madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular .", "eulaize": "node src/utils/eulaize.js", "test:unit": "vitest run", @@ -151,7 +156,7 @@ "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-compiler": "^19.1.0-rc.2", - "globals": "^17.1.0", + "globals": "^17.2.0", "jsdom": "^27.4.0", "memfs": "^4.56.10", "os-browserify": "^0.3.0", diff --git a/client/src/App/App.container.jsx b/client/src/App/App.container.jsx index 2437979b3..8abf59242 100644 --- a/client/src/App/App.container.jsx +++ b/client/src/App/App.container.jsx @@ -6,8 +6,7 @@ import enLocale from "antd/es/locale/en_US"; import { useEffect, useMemo } from "react"; import { CookiesProvider } from "react-cookie"; import { useTranslation } from "react-i18next"; -import { connect, useSelector } from "react-redux"; -import { createStructuredSelector } from "reselect"; +import { useDispatch, useSelector } from "react-redux"; import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component"; import { setDarkMode } from "../redux/application/application.actions"; import { selectDarkMode } from "../redux/application/application.selectors"; @@ -28,93 +27,102 @@ const config = { function SplitClientProvider({ children }) { const imexshopid = useSelector((state) => state.user.imexshopid); const splitClient = useSplitClient({ key: imexshopid || "anon" }); + useEffect(() => { - if (splitClient && imexshopid) { + if (import.meta.env.DEV && splitClient && imexshopid) { console.log(`Split client initialized with key: ${imexshopid}, isReady: ${splitClient.isReady}`); } }, [splitClient, imexshopid]); + return children; } -const mapStateToProps = createStructuredSelector({ - currentUser: selectCurrentUser -}); - -const mapDispatchToProps = (dispatch) => ({ - setDarkMode: (isDarkMode) => dispatch(setDarkMode(isDarkMode)), - signOutStart: () => dispatch(signOutStart()) -}); - -function AppContainer({ currentUser, setDarkMode, signOutStart }) { +function AppContainer() { const { t } = useTranslation(); + const dispatch = useDispatch(); + + const currentUser = useSelector(selectCurrentUser); const isDarkMode = useSelector(selectDarkMode); + const theme = useMemo(() => getTheme(isDarkMode), [isDarkMode]); + const antdInput = useMemo(() => ({ autoComplete: "new-password" }), []); + + const antdForm = useMemo( + () => ({ + validateMessages: { + required: t("general.validation.required", { label: "${label}" }) + } + }), + [t] + ); + // Global seamless logout listener with redirect to /signin useEffect(() => { const handleSeamlessLogout = (event) => { if (event.data?.type !== "seamlessLogoutRequest") return; - const requestOrigin = event.origin; + // Only accept messages from the parent window + if (event.source !== window.parent) return; + + const targetOrigin = event.origin || "*"; if (currentUser?.authorized !== true) { - window.parent.postMessage( - { type: "seamlessLogoutResponse", status: "already_logged_out" }, - requestOrigin || "*" - ); + window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "already_logged_out" }, targetOrigin); return; } - signOutStart(); - window.parent.postMessage({ type: "seamlessLogoutResponse", status: "logged_out" }, requestOrigin || "*"); + dispatch(signOutStart()); + window.parent?.postMessage({ type: "seamlessLogoutResponse", status: "logged_out" }, targetOrigin); }; window.addEventListener("message", handleSeamlessLogout); return () => { window.removeEventListener("message", handleSeamlessLogout); }; - }, [signOutStart, currentUser]); + }, [dispatch, currentUser?.authorized]); - // Update data-theme attribute + // Update data-theme attribute (no cleanup to avoid transient style churn) useEffect(() => { - document.documentElement.setAttribute("data-theme", isDarkMode ? "dark" : "light"); - return () => document.documentElement.removeAttribute("data-theme"); + document.documentElement.dataset.theme = isDarkMode ? "dark" : "light"; }, [isDarkMode]); // Sync darkMode with localStorage useEffect(() => { - if (currentUser?.uid) { - const savedMode = localStorage.getItem(`dark-mode-${currentUser.uid}`); - if (savedMode !== null) { - setDarkMode(JSON.parse(savedMode)); - } else { - setDarkMode(false); - } - } else { - setDarkMode(false); + const uid = currentUser?.uid; + + if (!uid) { + dispatch(setDarkMode(false)); + return; } - }, [currentUser?.uid, setDarkMode]); + + const key = `dark-mode-${uid}`; + const raw = localStorage.getItem(key); + + if (raw == null) { + dispatch(setDarkMode(false)); + return; + } + + try { + dispatch(setDarkMode(Boolean(JSON.parse(raw)))); + } catch { + dispatch(setDarkMode(false)); + } + }, [currentUser?.uid, dispatch]); // Persist darkMode useEffect(() => { - if (currentUser?.uid) { - localStorage.setItem(`dark-mode-${currentUser.uid}`, JSON.stringify(isDarkMode)); - } + const uid = currentUser?.uid; + if (!uid) return; + + localStorage.setItem(`dark-mode-${uid}`, JSON.stringify(isDarkMode)); }, [isDarkMode, currentUser?.uid]); return ( - + @@ -127,4 +135,4 @@ function AppContainer({ currentUser, setDarkMode, signOutStart }) { ); } -export default Sentry.withProfiler(connect(mapStateToProps, mapDispatchToProps)(AppContainer)); +export default Sentry.withProfiler(AppContainer); diff --git a/client/src/App/App.jsx b/client/src/App/App.jsx index e14162274..2bbda1a43 100644 --- a/client/src/App/App.jsx +++ b/client/src/App/App.jsx @@ -100,14 +100,7 @@ export function App({ if (currentUser.authorized && bodyshop) { client.setAttribute("imexshopid", bodyshop.imexshopid); - if ( - client.getTreatment("LogRocket_Tracking") === "on" || - window.location.hostname === - InstanceRenderMgr({ - imex: "beta.imex.online", - rome: "beta.romeonline.io" - }) - ) { + if (client.getTreatment("LogRocket_Tracking") === "on") { console.log("LR Start"); LogRocket.init( InstanceRenderMgr({ diff --git a/client/src/App/App.styles.scss b/client/src/App/App.styles.scss index 09745eac9..657520e49 100644 --- a/client/src/App/App.styles.scss +++ b/client/src/App/App.styles.scss @@ -446,3 +446,32 @@ //.rbc-time-header-gutter { // padding: 0; //} + +/* globally allow shrink inside table cells */ +.prod-list-table .ant-table-cell, +.prod-list-table .ant-table-cell > * { + min-width: 0; +} + +/* common AntD offenders */ +.prod-list-table > .ant-table-cell .ant-space, +.ant-table-cell .ant-space-item { + min-width: 0; +} + +/* Keep your custom header content on the left, push AntD sorter to the far right */ +.prod-list-table .ant-table-column-sorters { + display: flex !important; + align-items: center; + width: 100%; +} + +.prod-list-table .ant-table-column-title { + flex: 1 1 auto; + min-width: 0; /* allows ellipsis to work */ +} + +.prod-list-table .ant-table-column-sorter { + margin-left: auto; + flex: 0 0 auto; +} diff --git a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx index a2ab52d30..e6d7d9b1e 100644 --- a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx +++ b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx @@ -169,14 +169,20 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref refetch={refetch} /> {bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && } - + } > {bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && } - + } >
} @@ -211,7 +212,7 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
( ); diff --git a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx index 1b8d56156..cf7de778f 100644 --- a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx +++ b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx @@ -17,119 +17,137 @@ const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, currentUser: selectCurrentUser }); -const mapDispatchToProps = () => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); +const mapDispatchToProps = () => ({}); export default connect(mapStateToProps, mapDispatchToProps)(BilllineAddInventory); export function BilllineAddInventory({ currentUser, bodyshop, billline, disabled, jobid }) { const [loading, setLoading] = useState(false); - const { billid } = queryString.parse(useLocation().search); + const qs = queryString.parse(useLocation().search); + const billid = qs?.billid != null ? String(qs.billid) : null; + const [insertInventoryLine] = useMutation(INSERT_INVENTORY_AND_CREDIT); const notification = useNotification(); + const inventoryCount = billline?.inventories?.length ?? 0; + const quantity = billline?.quantity ?? 0; + const addToInventory = async () => { - setLoading(true); + if (loading) return; - //Check to make sure there are no existing items already in the inventory. - - const cm = { - vendorid: bodyshop.inhousevendorid, - invoice_number: "ih", - jobid: jobid, - isinhouse: true, - is_credit_memo: true, - date: dayjs().format("YYYY-MM-DD"), - federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate, - state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate, - local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate, - total: 0, - billlines: [ - { - actual_price: billline.actual_price, - actual_cost: billline.actual_cost, - quantity: billline.quantity, - line_desc: billline.line_desc, - cost_center: billline.cost_center, - deductedfromlbr: billline.deductedfromlbr, - applicable_taxes: { - local: billline.applicable_taxes.local, - state: billline.applicable_taxes.state, - federal: billline.applicable_taxes.federal - } - } - ] - }; - - cm.total = CalculateBillTotal(cm).enteredTotal.getAmount() / 100; - - const insertResult = await insertInventoryLine({ - variables: { - joblineId: billline.joblineid === "noline" ? billline.id : billline.joblineid, //This will return null as there will be no jobline that has the id of the bill line. - //Unfortunately, we can't send null as the GQL syntax validation fails. - joblineStatus: bodyshop.md_order_statuses.default_returned, - inv: { - shopid: bodyshop.id, - billlineid: billline.id, - actual_price: billline.actual_price, - actual_cost: billline.actual_cost, - quantity: billline.quantity, - line_desc: billline.line_desc - }, - cm: { ...cm, billlines: { data: cm.billlines } }, //Fix structure for apollo insert. - pol: { - returnfrombill: billid, - vendorid: bodyshop.inhousevendorid, - deliver_by: dayjs().format("YYYY-MM-DD"), - parts_order_lines: { - data: [ - { - line_desc: billline.line_desc, - - act_price: billline.actual_price, - cost: billline.actual_cost, - quantity: billline.quantity, - job_line_id: billline.joblineid === "noline" ? null : billline.joblineid, - part_type: billline.jobline && billline.jobline.part_type, - cm_received: true - } - ] - }, - order_date: "2022-06-01", - orderedby: currentUser.email, - jobid: jobid, - user_email: currentUser.email, - return: true, - status: "Ordered" - } - }, - refetchQueries: ["QUERY_BILL_BY_PK"] - }); - - if (!insertResult.errors) { - notification.success({ - title: t("inventory.successes.inserted") - }); - } else { + // Defensive: row identity can transiently desync during remove/add reindexing. + if (!billline) { notification.error({ - title: t("inventory.errors.inserting", { - error: JSON.stringify(insertResult.errors) - }) + title: t("inventory.errors.inserting", { error: "Bill line is missing (please try again)." }) }); + return; } - setLoading(false); + setLoading(true); + + try { + const taxes = billline?.applicable_taxes ?? {}; + const cm = { + vendorid: bodyshop.inhousevendorid, + invoice_number: "ih", + jobid: jobid, + isinhouse: true, + is_credit_memo: true, + date: dayjs().format("YYYY-MM-DD"), + federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate, + state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate, + local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate, + total: 0, + billlines: [ + { + actual_price: billline.actual_price, + actual_cost: billline.actual_cost, + quantity: billline.quantity, + line_desc: billline.line_desc, + cost_center: billline.cost_center, + deductedfromlbr: billline.deductedfromlbr, + applicable_taxes: { + local: taxes.local, + state: taxes.state, + federal: taxes.federal + } + } + ] + }; + + cm.total = CalculateBillTotal(cm).enteredTotal.getAmount() / 100; + + const insertResult = await insertInventoryLine({ + variables: { + joblineId: billline.joblineid === "noline" ? billline.id : billline.joblineid, + joblineStatus: bodyshop.md_order_statuses.default_returned, + inv: { + shopid: bodyshop.id, + billlineid: billline.id, + actual_price: billline.actual_price, + actual_cost: billline.actual_cost, + quantity: billline.quantity, + line_desc: billline.line_desc + }, + cm: { ...cm, billlines: { data: cm.billlines } }, + pol: { + returnfrombill: billid, + vendorid: bodyshop.inhousevendorid, + deliver_by: dayjs().format("YYYY-MM-DD"), + parts_order_lines: { + data: [ + { + line_desc: billline.line_desc, + act_price: billline.actual_price, + cost: billline.actual_cost, + quantity: billline.quantity, + job_line_id: billline.joblineid === "noline" ? null : billline.joblineid, + part_type: billline.jobline && billline.jobline.part_type, + cm_received: true + } + ] + }, + order_date: "2022-06-01", + orderedby: currentUser.email, + jobid: jobid, + user_email: currentUser.email, + return: true, + status: "Ordered" + } + }, + refetchQueries: ["QUERY_BILL_BY_PK"] + }); + + if (!insertResult?.errors?.length) { + notification.success({ + title: t("inventory.successes.inserted") + }); + } else { + notification.error({ + title: t("inventory.errors.inserting", { + error: JSON.stringify(insertResult.errors) + }) + }); + } + } catch (err) { + notification.error({ + title: t("inventory.errors.inserting", { + error: err?.message || String(err) + }) + }); + } finally { + setLoading(false); + } }; return ( ); diff --git a/client/src/components/bills-list-table/bills-list-table.component.jsx b/client/src/components/bills-list-table/bills-list-table.component.jsx index 166cd0b5b..283949d91 100644 --- a/client/src/components/bills-list-table/bills-list-table.component.jsx +++ b/client/src/components/bills-list-table/bills-list-table.component.jsx @@ -84,15 +84,14 @@ export function BillsListTableComponent({ } }); }} - > - - + icon={} + /> + - {record.isinhouse && ( - + + ); } diff --git a/client/src/components/chat-popup/chat-popup.component.jsx b/client/src/components/chat-popup/chat-popup.component.jsx index a6392608a..1a3c03f2d 100644 --- a/client/src/components/chat-popup/chat-popup.component.jsx +++ b/client/src/components/chat-popup/chat-popup.component.jsx @@ -47,7 +47,6 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh const [getConversations, { loading, data, refetch, called }] = useLazyQuery(CONVERSATION_LIST_QUERY, { fetchPolicy: "network-only", nextFetchPolicy: "network-only", - notifyOnNetworkStatusChange: true, ...(pollInterval > 0 ? { pollInterval } : {}) }); @@ -108,9 +107,12 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh hasLoadedConversationsOnceRef.current = true; getConversations({ variables: { offset: 0 } }).catch((err) => { - console.error(`Error fetching conversations: ${err?.message || ""}`, err); + // Ignore abort errors (they're expected when component unmounts) + if (err?.name !== "AbortError") { + console.error(`Error fetching conversations: ${err?.message || ""}`, err); + } }); - }, [getConversations]); + }, []); const handleManualRefresh = async () => { try { diff --git a/client/src/components/contract-cars/contract-cars.component.jsx b/client/src/components/contract-cars/contract-cars.component.jsx index 12ed17e89..f43ac2945 100644 --- a/client/src/components/contract-cars/contract-cars.component.jsx +++ b/client/src/components/contract-cars/contract-cars.component.jsx @@ -99,6 +99,7 @@ export default function ContractsCarsComponent({ loading, data, selectedCarId, h placeholder={t("general.labels.search")} value={state.search} onChange={(e) => setState({ ...state, search: e.target.value })} + enterButton /> } > diff --git a/client/src/components/contract-form/contract-form.component.jsx b/client/src/components/contract-form/contract-form.component.jsx index 5245348e6..6326d424d 100644 --- a/client/src/components/contract-form/contract-form.component.jsx +++ b/client/src/components/contract-form/contract-form.component.jsx @@ -1,5 +1,5 @@ import { WarningFilled } from "@ant-design/icons"; -import { Form, Input, InputNumber, Space } from "antd"; +import { Card, Form, Input, InputNumber, Space } from "antd"; import { useTranslation } from "react-i18next"; import { DateFormatter } from "../../utils/DateFormatter"; import dayjs from "../../utils/day"; @@ -19,9 +19,9 @@ import ContractFormJobPrefill from "./contract-form-job-prefill.component"; export default function ContractFormComponent({ form, create = false, selectedJobState, selectedCar }) { const { t } = useTranslation(); return ( - <> + {!create && } - + {!create && ( - + ); } diff --git a/client/src/components/contract-jobs/contract-jobs.component.jsx b/client/src/components/contract-jobs/contract-jobs.component.jsx index 8ed060873..398fcac82 100644 --- a/client/src/components/contract-jobs/contract-jobs.component.jsx +++ b/client/src/components/contract-jobs/contract-jobs.component.jsx @@ -123,6 +123,7 @@ export default function ContractsJobsComponent({ loading, data, selectedJob, han placeholder={t("general.labels.search")} value={state.search} onChange={(e) => setState({ ...state, search: e.target.value })} + enterButton /> } > diff --git a/client/src/components/contracts-list/contracts-list.component.jsx b/client/src/components/contracts-list/contracts-list.component.jsx index 2a76d8630..313393245 100644 --- a/client/src/components/contracts-list/contracts-list.component.jsx +++ b/client/src/components/contracts-list/contracts-list.component.jsx @@ -156,15 +156,15 @@ export function ContractsList({ bodyshop, loading, contracts, refetch, total, se )} - + + diff --git a/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx b/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx index fc47e3df6..5638b7a84 100644 --- a/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx +++ b/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx @@ -85,13 +85,7 @@ export default function CsiResponseListPaginated({ refetch, loading, responses, }; return ( - refetch()}> - - - } - > + refetch()} icon={} />}>
+ }> - +
+ {/* Hidden field to preserve the id */} + } onClick={() => { const values = form.getFieldsValue("billlineskeys"); @@ -53,9 +54,7 @@ export function BillFormItemsExtendedFormItem({ } }); }} - > - - + /> ); return ( @@ -196,6 +195,7 @@ export function BillFormItemsExtendedFormItem({ + /> ); } diff --git a/client/src/components/bill-form/bill-form.component.jsx b/client/src/components/bill-form/bill-form.component.jsx index d46035805..51ce7bee1 100644 --- a/client/src/components/bill-form/bill-form.component.jsx +++ b/client/src/components/bill-form/bill-form.component.jsx @@ -373,9 +373,11 @@ export function BillFormComponent({ "local_tax_rate" ]); let totals; - if (!!values.total && !!values.billlines && values.billlines.length > 0) + if (!!values.total && !!values.billlines && values.billlines.length > 0) { totals = CalculateBillTotal(values); - if (totals) + } + + if (totals) { return ( // TODO: Align is not correct // eslint-disable-next-line react/no-unknown-property @@ -414,7 +416,7 @@ export function BillFormComponent({ ); + } return null; }} diff --git a/client/src/components/bill-form/bill-form.lines.component.jsx b/client/src/components/bill-form/bill-form.lines.component.jsx index c6b2c8d0e..bc36b172c 100644 --- a/client/src/components/bill-form/bill-form.lines.component.jsx +++ b/client/src/components/bill-form/bill-form.lines.component.jsx @@ -1,6 +1,7 @@ import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons"; import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react"; import { Button, Checkbox, Form, Input, InputNumber, Select, Space, Switch, Table, Tooltip } from "antd"; +import { useRef } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -32,14 +33,14 @@ export function BillEnterModalLinesComponent({ }) { const { t } = useTranslation(); const { setFieldsValue, getFieldsValue, getFieldValue } = form; + const firstFieldRefs = useRef({}); - // Keep input row heights consistent with the rest of the table controls. const CONTROL_HEIGHT = 32; const normalizeDiscount = (d) => { const n = Number(d); if (!Number.isFinite(n) || n <= 0) return 0; - return n > 1 ? n / 100 : n; // supports 15 or 0.15 + return n > 1 ? n / 100 : n; }; const round2 = (v) => Math.round((v + Number.EPSILON) * 100) / 100; @@ -79,7 +80,6 @@ export function BillEnterModalLinesComponent({ return NaN; }; - // safe per-field setter (supports AntD 6+ setFieldValue, falls back to setFieldsValue) const setLineField = (index, field, value) => { if (typeof form.setFieldValue === "function") { form.setFieldValue(["billlines", index, field], value); @@ -92,6 +92,7 @@ export function BillEnterModalLinesComponent({ }); }; + // Only fill actual_cost when the user forward-tabs out of Retail (actual_price) const autofillActualCost = (index) => { Promise.resolve().then(() => { const retailRaw = form.getFieldValue(["billlines", index, "actual_price"]); @@ -115,7 +116,6 @@ export function BillEnterModalLinesComponent({ }; const getIndicatorShellStyles = (statusColor) => { - // bring back the “colored shell” feel around the $ indicator while keeping row height stable if (isDarkMode) { if (statusColor === "green") return { borderColor: "rgba(82, 196, 26, 0.75)", background: "rgba(82, 196, 26, 0.10)" }; @@ -145,7 +145,7 @@ export function BillEnterModalLinesComponent({ editable: true, minWidth: "10rem", formItemProps: (field) => ({ - key: `${field.index}joblinename`, + key: `${field.name}joblinename`, name: [field.name, "joblineid"], label: t("billlines.fields.jobline"), rules: [{ required: true }] @@ -157,6 +157,9 @@ export function BillEnterModalLinesComponent({ ), formInput: (record, index) => ( { + firstFieldRefs.current[index] = el; + }} disabled={disabled} options={lineData} style={{ @@ -167,10 +170,9 @@ export function BillEnterModalLinesComponent({ }} allowRemoved={form.getFieldValue("is_credit_memo") || false} onSelect={(value, opt) => { - const d = normalizeDiscount(discount); - const retail = Number(opt.cost); - const computedActual = Number.isFinite(retail) ? round2(retail * (1 - d)) : null; - + // IMPORTANT: + // Do NOT autofill actual_cost here. It should only fill when the user forward-tabs + // from Retail (actual_price) -> Actual Cost (actual_cost). setFieldsValue({ billlines: (getFieldValue("billlines") || []).map((item, idx) => { if (idx !== index) return item; @@ -181,7 +183,7 @@ export function BillEnterModalLinesComponent({ quantity: opt.part_qty || 1, actual_price: opt.cost, original_actual_price: opt.cost, - actual_cost: isBlank(item.actual_cost) ? computedActual : item.actual_cost, + // actual_cost intentionally untouched here cost_center: opt.part_type ? bodyshopHasDmsKey(bodyshop) ? opt.part_type !== "PAE" @@ -203,12 +205,12 @@ export function BillEnterModalLinesComponent({ editable: true, minWidth: "10rem", formItemProps: (field) => ({ - key: `${field.index}line_desc`, + key: `${field.name}line_desc`, name: [field.name, "line_desc"], label: t("billlines.fields.line_desc"), rules: [{ required: true }] }), - formInput: () => + formInput: () => }, { title: t("billlines.fields.confidence"), @@ -228,17 +230,19 @@ export function BillEnterModalLinesComponent({ editable: true, width: "4rem", formItemProps: (field) => ({ - key: `${field.index}quantity`, + key: `${field.name}quantity`, name: [field.name, "quantity"], label: t("billlines.fields.quantity"), rules: [ { required: true }, ({ getFieldValue: gf }) => ({ - validator(rule, value) { - if (value && gf("billlines")[field.fieldKey]?.inventories?.length > value) { + validator(_, value) { + const invLen = gf(["billlines", field.name, "inventories"])?.length ?? 0; + + if (value && invLen > value) { return Promise.reject( t("bills.validation.inventoryquantity", { - number: gf("billlines")[field.fieldKey]?.inventories?.length + number: invLen }) ); } @@ -247,7 +251,7 @@ export function BillEnterModalLinesComponent({ }) ] }), - formInput: () => + formInput: () => }, { title: t("billlines.fields.actual_price"), @@ -255,7 +259,7 @@ export function BillEnterModalLinesComponent({ width: "8rem", editable: true, formItemProps: (field) => ({ - key: `${field.index}actual_price`, + key: `${field.name}actual_price`, name: [field.name, "actual_price"], label: t("billlines.fields.actual_price"), rules: [{ required: true }] @@ -264,9 +268,10 @@ export function BillEnterModalLinesComponent({ autofillActualCost(index)} + tabIndex={0} + // NOTE: Autofill should only happen on forward Tab out of Retail onKeyDown={(e) => { - if (e.key === "Tab") autofillActualCost(index); + if (e.key === "Tab" && !e.shiftKey) autofillActualCost(index); }} /> ), @@ -307,7 +312,7 @@ export function BillEnterModalLinesComponent({ width: "10rem", skipFormItem: true, formItemProps: (field) => ({ - key: `${field.index}actual_cost`, + key: `${field.name}actual_cost`, name: [field.name, "actual_cost"], label: t("billlines.fields.actual_cost"), rules: [{ required: true }] @@ -341,6 +346,7 @@ export function BillEnterModalLinesComponent({ min={0} disabled={disabled} controls={false} + tabIndex={0} style={{ width: "100%", height: CONTROL_HEIGHT }} onFocus={() => autofillActualCost(index)} /> @@ -398,14 +404,14 @@ export function BillEnterModalLinesComponent({ dataIndex: "cost_center", editable: true, formItemProps: (field) => ({ - key: `${field.index}cost_center`, + key: `${field.name}cost_center`, name: [field.name, "cost_center"], label: t("billlines.fields.cost_center"), valuePropName: "value", rules: [{ required: true }] }), formInput: () => ( - {bodyshopHasDmsKey(bodyshop) ? CiecaSelect(true, false) : responsibilityCenters.costs.map((item) => {item.name})} @@ -421,11 +427,11 @@ export function BillEnterModalLinesComponent({ editable: true, label: t("billlines.fields.location"), formItemProps: (field) => ({ - key: `${field.index}location`, + key: `${field.name}location`, name: [field.name, "location"] }), formInput: () => ( - {bodyshop.md_parts_locations.map((loc, idx) => ( {loc} @@ -442,10 +448,10 @@ export function BillEnterModalLinesComponent({ width: "40px", formItemProps: (field) => ({ valuePropName: "checked", - key: `${field.index}deductedfromlbr`, + key: `${field.name}deductedfromlbr`, name: [field.name, "deductedfromlbr"] }), - formInput: () => , + formInput: () => , additional: (record, index) => ( {() => { @@ -528,11 +534,15 @@ export function BillEnterModalLinesComponent({ editable: true, width: "40px", formItemProps: (field) => ({ - key: `${field.index}fedtax`, + key: `${field.name}fedtax`, valuePropName: "checked", - name: [field.name, "applicable_taxes", "federal"] + name: [field.name, "applicable_taxes", "federal"], + initialValue: InstanceRenderManager({ + imex: true, + rome: false + }) }), - formInput: () => + formInput: () => } ] }), @@ -543,11 +553,11 @@ export function BillEnterModalLinesComponent({ editable: true, width: "40px", formItemProps: (field) => ({ - key: `${field.index}statetax`, + key: `${field.name}statetax`, valuePropName: "checked", name: [field.name, "applicable_taxes", "state"] }), - formInput: () => + formInput: () => }, ...InstanceRenderManager({ @@ -559,11 +569,11 @@ export function BillEnterModalLinesComponent({ editable: true, width: "40px", formItemProps: (field) => ({ - key: `${field.index}localtax`, + key: `${field.name}localtax`, valuePropName: "checked", name: [field.name, "applicable_taxes", "local"] }), - formInput: () => + formInput: () => } ] }), @@ -573,24 +583,29 @@ export function BillEnterModalLinesComponent({ dataIndex: "actions", render: (text, record) => ( - {() => ( - - + {() => { + const currentLine = getFieldValue(["billlines", record.name]); + const invLen = currentLine?.inventories?.length ?? 0; - {Simple_Inventory.treatment === "on" && ( - + + {cellInner}
} diff --git a/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx b/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx index 5c29a1962..b9716e84b 100644 --- a/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx +++ b/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx @@ -36,7 +36,7 @@ export function DashboardTotalProductionHours({ bodyshop, data, ...cardProps }) diff --git a/client/src/components/dashboard-grid/dashboard-grid.component.jsx b/client/src/components/dashboard-grid/dashboard-grid.component.jsx index fac7ebb00..0befe7c16 100644 --- a/client/src/components/dashboard-grid/dashboard-grid.component.jsx +++ b/client/src/components/dashboard-grid/dashboard-grid.component.jsx @@ -196,9 +196,7 @@ export function DashboardGridComponent({ currentUser }) { - + diff --git a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx index 2ca64d997..77e887d07 100644 --- a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx +++ b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx @@ -1,6 +1,6 @@ import { SyncOutlined } from "@ant-design/icons"; import { Button, Card, Form, Input, Table } from "antd"; -import { useEffect, useState, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -111,9 +111,8 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) { onClick={() => { socket.emit("pbs-calculate-allocations-ap", billids, (ack) => setAllocationsSummary(ack)); }} - > - - + icon={} + /> } >
- - - } + extra={ - } + extra={
callSearch({ variables: { search: val } })} placeholder={t("general.labels.search")} + enterButton /> )} columns={columns} diff --git a/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx b/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx index 205ceb414..86e08e803 100644 --- a/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx +++ b/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx @@ -23,13 +23,13 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsCustomerSelector) * @constructor */ export function DmsCustomerSelector(props) { - const { bodyshop, jobid, socket, rrOptions = {} } = props; + const { bodyshop, jobid, job, socket, rrOptions = {} } = props; // Centralized "mode" (provider + transport) const mode = props.mode; // Stable base props for children - const base = useMemo(() => ({ bodyshop, jobid, socket }), [bodyshop, jobid, socket]); + const base = useMemo(() => ({ bodyshop, jobid, job, socket }), [bodyshop, jobid, job, socket]); switch (mode) { case DMS_MAP.reynolds: { diff --git a/client/src/components/dms-customer-selector/fortellis-customer-selector.jsx b/client/src/components/dms-customer-selector/fortellis-customer-selector.jsx index 2cb396711..4c2994e64 100644 --- a/client/src/components/dms-customer-selector/fortellis-customer-selector.jsx +++ b/client/src/components/dms-customer-selector/fortellis-customer-selector.jsx @@ -53,13 +53,13 @@ export default function FortellisCustomerSelector({ bodyshop, jobid, socket }) { render: (_t, r) => }, { - title: t("jobs.fields.dms.name1"), + title: t("jobs.fields.dms.first_name"), dataIndex: ["customerName", "firstName"], key: "firstName", sorter: (a, b) => alphaSort(a.customerName?.firstName, b.customerName?.firstName) }, { - title: t("jobs.fields.dms.name1"), + title: t("jobs.fields.dms.last_name"), dataIndex: ["customerName", "lastName"], key: "lastName", sorter: (a, b) => alphaSort(a.customerName?.lastName, b.customerName?.lastName) diff --git a/client/src/components/dms-customer-selector/rr-customer-selector.jsx b/client/src/components/dms-customer-selector/rr-customer-selector.jsx index 3622f61f1..ad5fbd4bf 100644 --- a/client/src/components/dms-customer-selector/rr-customer-selector.jsx +++ b/client/src/components/dms-customer-selector/rr-customer-selector.jsx @@ -1,4 +1,4 @@ -import { Alert, Button, Checkbox, Col, message, Space, Table } from "antd"; +import { Alert, Button, Checkbox, message, Modal, Space, Table } from "antd"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { alphaSort } from "../../utils/sorters"; @@ -47,6 +47,7 @@ const rrAddressToString = (addr) => { export default function RRCustomerSelector({ jobid, socket, + job, rrOpenRoLimit = false, onRrOpenRoFinished, rrValidationPending = false, @@ -59,15 +60,26 @@ export default function RRCustomerSelector({ const [refreshing, setRefreshing] = useState(false); // Show dialog automatically when validation is pending + // BUT: skip this for early RO flow (job already has dms_id) useEffect(() => { - if (rrValidationPending) setOpen(true); - }, [rrValidationPending]); + if (rrValidationPending && !job?.dms_id) { + setOpen(true); + } + }, [rrValidationPending, job?.dms_id]); // Listen for RR customer selection list useEffect(() => { if (!socket) return; const handleRrSelectCustomer = (list) => { const normalized = normalizeRrList(list); + + // If list is empty, it means early RO exists and customer selection should be skipped + // Don't open the modal in this case + if (normalized.length === 0) { + setRefreshing(false); + return; + } + setOpen(true); setCustomerList(normalized); const firstOwner = normalized.find((r) => r.vinOwner)?.custNo; @@ -127,6 +139,10 @@ export default function RRCustomerSelector({ }); }; + const handleClose = () => { + setOpen(false); + }; + const refreshRrSearch = () => { setRefreshing(true); const to = setTimeout(() => setRefreshing(false), 12000); @@ -141,8 +157,6 @@ export default function RRCustomerSelector({ socket.emit("rr-export-job", { jobId: jobid }); }; - if (!open) return null; - const columns = [ { title: t("jobs.fields.dms.id"), dataIndex: "custNo", key: "custNo" }, { @@ -169,8 +183,45 @@ export default function RRCustomerSelector({ return !rrOwnerSet.has(String(record.custNo)); }; + // For early RO flow: show validation banner even when modal is closed + if (!open) { + if (rrValidationPending && job?.dms_id) { + return ( +
+ +
+ We created the Repair Order. Please validate the totals and taxes in the DMS system. When done, + click Finished to finalize and mark this export as complete. +
+
+ + + +
+
+ } + /> + + ); + } + return null; + } + return ( -
+
(
@@ -196,8 +247,8 @@ export default function RRCustomerSelector({ /> )} - {/* Validation step banner */} - {rrValidationPending && ( + {/* Validation step banner - only show for NON-early RO flow (legacy) */} + {rrValidationPending && !job?.dms_id && ( ({ disabled: rrDisableRow(record) }) }} /> - + ); } diff --git a/client/src/components/dms-log-events/dms-log-events.component.jsx b/client/src/components/dms-log-events/dms-log-events.component.jsx index 25f8c27ff..d90623df6 100644 --- a/client/src/components/dms-log-events/dms-log-events.component.jsx +++ b/client/src/components/dms-log-events/dms-log-events.component.jsx @@ -69,7 +69,7 @@ export function DmsLogEvents({ return { key: idx, color: logLevelColor(level), - children: ( + content: ( {/* Row 1: summary + inline "Details" toggle */} @@ -113,7 +113,7 @@ export function DmsLogEvents({ [logs, openSet, colorizeJson, isDarkMode, showDetails] ); - return ; + return ; } /** diff --git a/client/src/components/dms-post-form/cdklike-dms-post-form.jsx b/client/src/components/dms-post-form/cdklike-dms-post-form.jsx index d2d130892..9e006534b 100644 --- a/client/src/components/dms-post-form/cdklike-dms-post-form.jsx +++ b/client/src/components/dms-post-form/cdklike-dms-post-form.jsx @@ -272,7 +272,7 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode, name={[field.name, "name"]} rules={[{ required: true }]} > - handlePayerSelect(value, index)}> {bodyshop.cdk_configuration?.payers?.map((payer) => ( {payer.name} ))} @@ -404,7 +404,7 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode, =
- - - - { + const value = getAdvisorNumber(a); + if (value == null) return null; + return { value: String(value), label: getAdvisorLabel(a) || String(value) }; + }) + .filter(Boolean)} + notFoundContent={advLoading ? t("general.labels.loading") : t("general.labels.none")} + /> + + - )} - - } - > - - - - - - - - - - - - - + aria-label={t("general.actions.refresh")} + icon={} + onClick={() => fetchRrAdvisors(true)} + loading={advLoading} + /> + + + + + )} + + {/* RR OpCode (prefix / base / suffix) - Only show if no early RO */} + {!hasEarlyRO && ( + + + {t("jobs.fields.dms.rr_opcode", "RR OpCode")} + {isCustomOpCode && ( + + )} + + } + > + + + + + + + + + + + + + + )} @@ -355,13 +365,14 @@ export default function RRPostForm({ {/* Validation */} {() => { - const advisorOk = !!form.getFieldValue("advisorNo"); + // When early RO exists, advisor is already set, so we don't need to validate it + const advisorOk = hasEarlyRO ? true : !!form.getFieldValue("advisorNo"); return ( = - ); diff --git a/client/src/components/dms-post-form/rr-early-ro-form.jsx b/client/src/components/dms-post-form/rr-early-ro-form.jsx new file mode 100644 index 000000000..cda20e543 --- /dev/null +++ b/client/src/components/dms-post-form/rr-early-ro-form.jsx @@ -0,0 +1,367 @@ +import { ReloadOutlined } from "@ant-design/icons"; +import { Alert, Button, Form, Input, InputNumber, Modal, Radio, Select, Space, Table, Typography } from "antd"; +import { useEffect, useMemo, useState } from "react"; + +// Simple customer selector table +function CustomerSelectorTable({ customers, onSelect, isSubmitting }) { + const [selectedCustNo, setSelectedCustNo] = useState(null); + + const columns = [ + { + title: "Select", + key: "select", + width: 80, + render: (_, record) => ( + setSelectedCustNo(record.custNo)} /> + ) + }, + { title: "Customer ID", dataIndex: "custNo", key: "custNo" }, + { title: "Name", dataIndex: "name", key: "name" }, + { + title: "VIN Owner", + key: "vinOwner", + render: (_, record) => (record.vinOwner || record.isVehicleOwner ? "Yes" : "No") + } + ]; + + return ( +
+
+
+ + +
+ + ); +} + +/** + * RR Early RO Creation Form + * Used from convert button or admin page to create minimal RO before full export + * @param bodyshop + * @param socket + * @param job + * @param onSuccess - callback when RO is created successfully + * @param onCancel - callback to close modal + * @param showCancelButton - whether to show cancel button + * @returns {JSX.Element} + * @constructor + */ +export default function RREarlyROForm({ bodyshop, socket, job, onSuccess, onCancel, showCancelButton = true }) { + const [form] = Form.useForm(); + + // Advisors + const [advisors, setAdvisors] = useState([]); + const [advLoading, setAdvLoading] = useState(false); + + // Customer selection + const [customerCandidates, setCustomerCandidates] = useState([]); + const [showCustomerSelector, setShowCustomerSelector] = useState(false); + + // Loading and success states + const [isSubmitting, setIsSubmitting] = useState(false); + const [earlyRoCreated, setEarlyRoCreated] = useState(!!job?.dms_id); + const [createdRoNumber, setCreatedRoNumber] = useState(job?.dms_id || null); + + // Derive default OpCode parts from bodyshop config (matching dms.container.jsx logic) + const initialValues = useMemo(() => { + const cfg = bodyshop?.rr_configuration || {}; + const defaults = + cfg.opCodeDefault || + cfg.op_code_default || + cfg.op_codes?.default || + cfg.defaults?.opCode || + cfg.defaults || + cfg.default || + {}; + + const prefix = defaults.prefix ?? defaults.opCodePrefix ?? ""; + const base = defaults.base ?? defaults.opCodeBase ?? ""; + const suffix = defaults.suffix ?? defaults.opCodeSuffix ?? ""; + + return { + kmin: job?.kmin || 0, + opPrefix: prefix, + opBase: base, + opSuffix: suffix + }; + }, [bodyshop, job]); + + const getAdvisorNumber = (a) => a?.advisorId; + const getAdvisorLabel = (a) => `${a?.firstName || ""} ${a?.lastName || ""}`.trim(); + + const fetchRrAdvisors = (refresh = false) => { + if (!socket) return; + setAdvLoading(true); + + const onResult = (payload) => { + try { + const list = payload?.result ?? payload ?? []; + setAdvisors(Array.isArray(list) ? list : []); + } finally { + setAdvLoading(false); + socket.off("rr-get-advisors:result", onResult); + } + }; + + socket.once("rr-get-advisors:result", onResult); + socket.emit("rr-get-advisors", { departmentType: "B", refresh }, (ack) => { + if (ack?.ok) { + const list = ack.result ?? []; + setAdvisors(Array.isArray(list) ? list : []); + } else if (ack) { + console.error("Error fetching RR Advisors:", ack.error); + } + setAdvLoading(false); + socket.off("rr-get-advisors:result", onResult); + }); + }; + + useEffect(() => { + fetchRrAdvisors(false); + }, [bodyshop?.id, socket]); + + const handleStartEarlyRO = async (values) => { + if (!socket) { + console.error("Socket not available"); + return; + } + + setIsSubmitting(true); + + const txEnvelope = { + advisorNo: values.advisorNo, + story: values.story || "", + kmin: values.kmin || job?.kmin || 0, + opPrefix: values.opPrefix || "", + opBase: values.opBase || "", + opSuffix: values.opSuffix || "" + }; + + // Emit the early RO creation request + socket.emit("rr-create-early-ro", { + jobId: job.id, + txEnvelope + }); + + // Wait for customer selection + const customerListener = (candidates) => { + console.log("Received rr-select-customer event with candidates:", candidates); + setCustomerCandidates(candidates || []); + setShowCustomerSelector(true); + setIsSubmitting(false); + socket.off("rr-select-customer", customerListener); + }; + + socket.once("rr-select-customer", customerListener); + + // Handle failures + const failureListener = (payload) => { + if (payload?.jobId === job.id) { + console.error("Early RO creation failed:", payload.error); + alert(`Failed to create early RO: ${payload.error}`); + setIsSubmitting(false); + setShowCustomerSelector(false); + socket.off("export-failed", failureListener); + socket.off("rr-select-customer", customerListener); + } + }; + + socket.once("export-failed", failureListener); + }; + + const handleCustomerSelected = (custNo, createNew = false) => { + if (!socket) return; + + console.log("handleCustomerSelected called:", { custNo, createNew, custNoType: typeof custNo }); + + setIsSubmitting(true); + setShowCustomerSelector(false); + + const payload = { + jobId: job.id, + custNo: createNew ? null : custNo, + create: createNew + }; + + console.log("Emitting rr-early-customer-selected:", payload); + + // Emit customer selection + socket.emit("rr-early-customer-selected", payload, (ack) => { + console.log("Received ack from rr-early-customer-selected:", ack); + setIsSubmitting(false); + + if (ack?.ok) { + const roNumber = ack.dmsRoNo || ack.outsdRoNo; + setEarlyRoCreated(true); + setCreatedRoNumber(roNumber); + onSuccess?.({ roNumber, ...ack }); + } else { + alert(`Failed to create early RO: ${ack?.error || "Unknown error"}`); + } + }); + + // Also listen for socket events + const successListener = (payload) => { + if (payload?.jobId === job.id) { + const roNumber = payload.dmsRoNo || payload.outsdRoNo; + console.log("Early RO created:", roNumber); + socket.off("rr-early-ro-created", successListener); + socket.off("export-failed", failureListener); + } + }; + + const failureListener = (payload) => { + if (payload?.jobId === job.id) { + console.error("Early RO creation failed:", payload.error); + setIsSubmitting(false); + setEarlyRoCreated(false); + socket.off("rr-early-ro-created", successListener); + socket.off("export-failed", failureListener); + } + }; + + socket.once("rr-early-ro-created", successListener); + socket.once("export-failed", failureListener); + }; + + // If early RO already created, show success message + if (earlyRoCreated) { + return ( + + ); + } + + // If showing customer selector, render modal + if (showCustomerSelector) { + return ( + <> + Create Early Reynolds RO + Waiting for customer selection... + + { + setShowCustomerSelector(false); + setIsSubmitting(false); + }} + > + + + + ); + } + + // Handle manual submit (since we can't nest forms) + const handleManualSubmit = async () => { + try { + const values = await form.validateFields(); + handleStartEarlyRO(values); + } catch (error) { + console.error("Validation failed:", error); + } + }; + + // Show the form + return ( +
+ Create Early Reynolds RO + + Complete this section to create a minimal RO in Reynolds before converting the job. + + + + + + + + + + + + {/* RR OpCode (prefix / base / suffix) */} + + + + + + + + + + + + + + + + + + +
+ + + {showCancelButton && } + +
+ +
+ ); +} diff --git a/client/src/components/dms-post-form/rr-early-ro-modal.jsx b/client/src/components/dms-post-form/rr-early-ro-modal.jsx new file mode 100644 index 000000000..ffb1b7f00 --- /dev/null +++ b/client/src/components/dms-post-form/rr-early-ro-modal.jsx @@ -0,0 +1,33 @@ +import { Modal } from "antd"; +import RREarlyROForm from "./rr-early-ro-form"; + +/** + * Modal wrapper for RR Early RO Creation Form + * @param open - boolean to control modal visibility + * @param onClose - callback when modal is closed + * @param onSuccess - callback when RO is created successfully + * @param bodyshop - bodyshop object + * @param socket - socket.io connection + * @param job - job object + * @returns {JSX.Element} + * @constructor + */ +export default function RREarlyROModal({ open, onClose, onSuccess, bodyshop, socket, job }) { + const handleSuccess = (result) => { + onSuccess?.(result); + onClose?.(); + }; + + return ( + + + + ); +} diff --git a/client/src/components/global-search/global-search.component.jsx b/client/src/components/global-search/global-search.component.jsx index 9cdafaf04..665245070 100644 --- a/client/src/components/global-search/global-search.component.jsx +++ b/client/src/components/global-search/global-search.component.jsx @@ -14,8 +14,11 @@ export default function GlobalSearch() { const [callSearch, { loading, error, data }] = useLazyQuery(GLOBAL_SEARCH_QUERY); const navigate = useNavigate(); - const executeSearch = (v) => { - if (v && v.variables.search && v.variables.search !== "" && v.variables.search.length >= 3) callSearch(v); + const executeSearch = (variables) => { + if (variables?.search !== "" && variables?.search?.length >= 3) + callSearch({ + variables + }); }; const debouncedExecuteSearch = _.debounce(executeSearch, 750); @@ -157,7 +160,9 @@ export default function GlobalSearch() { return ( { if (e.key !== "Enter") return; diff --git a/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx b/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx index 1a1439f1b..2afe57f83 100644 --- a/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx +++ b/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx @@ -53,9 +53,7 @@ export default function InventoryLineDelete({ inventoryline, disabled, refetch } onConfirm={handleDelete} title={t("inventory.labels.deleteconfirm")} > - + + icon={} + /> + ) @@ -155,9 +155,9 @@ export function JobsList({ refetch, loading, jobs, total, setInventoryUpsertCont context: {} }); }} - > - - + icon={} + /> + - + - + document.body}>
diff --git a/client/src/components/job-at-change/schedule-event.note.component.jsx b/client/src/components/job-at-change/schedule-event.note.component.jsx index 1cc52a6a9..de73f127a 100644 --- a/client/src/components/job-at-change/schedule-event.note.component.jsx +++ b/client/src/components/job-at-change/schedule-event.note.component.jsx @@ -61,9 +61,7 @@ export function ScheduleEventNote({ event }) { ) : ( setNote(e.target.value)} style={{ maxWidth: "8vw" }} /> )} - + + icon={} + /> } >
diff --git a/client/src/components/job-checklist/components/job-checklist-template-list/job-checklist-template-list.component.jsx b/client/src/components/job-checklist/components/job-checklist-template-list/job-checklist-template-list.component.jsx index d84b8f526..907fe17d2 100644 --- a/client/src/components/job-checklist/components/job-checklist-template-list/job-checklist-template-list.component.jsx +++ b/client/src/components/job-checklist/components/job-checklist-template-list/job-checklist-template-list.component.jsx @@ -61,9 +61,12 @@ export default function JobIntakeTemplateList({ templates }) { renderItem={(template) => ( renderTemplate(template)}> - - + + icon={} + /> )} + icon={} + /> + {(record.manual_line || jobIsPrivate) && !technician && ( + icon={} + /> )} ) @@ -542,9 +540,7 @@ export function JobLinesComponent({ title={t("jobs.labels.estimatelines")} extra={ - + @@ -641,6 +637,7 @@ export function JobLinesComponent({ {!isPartsEntry && ( )} @@ -685,6 +682,7 @@ export function JobLinesComponent({ e.preventDefault(); setSearchText(e.target.value); }} + enterButton /> } diff --git a/client/src/components/job-line-convert-to-labor/job-line-convert-to-labor.component.jsx b/client/src/components/job-line-convert-to-labor/job-line-convert-to-labor.component.jsx index d3807b615..4f088697d 100644 --- a/client/src/components/job-line-convert-to-labor/job-line-convert-to-labor.component.jsx +++ b/client/src/components/job-line-convert-to-labor/job-line-convert-to-labor.component.jsx @@ -187,9 +187,8 @@ export function JobLineConvertToLabor({ loading={loading} onClick={handleClick} {...otherBtnProps} - > - - + icon={} + /> )} diff --git a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx index c7cc3a115..771d36f36 100644 --- a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx +++ b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx @@ -1,29 +1,65 @@ import { useMemo } from "react"; -import { Tag, Tooltip } from "antd"; +import { Tooltip } from "antd"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import { useTranslation } from "react-i18next"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop }); -const mapDispatchToProps = () => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); +const mapDispatchToProps = () => ({}); + +const colorMap = { + gray: { bg: "#fafafa", border: "#d9d9d9", text: "#000000" }, + gold: { bg: "#fffbe6", border: "#ffe58f", text: "#d48806" }, + red: { bg: "#fff1f0", border: "#ffccc7", text: "#cf1322" }, + blue: { bg: "#e6f7ff", border: "#91d5ff", text: "#0958d9" }, + green: { bg: "#f6ffed", border: "#b7eb8f", text: "#389e0d" }, + orange: { bg: "#fff7e6", border: "#ffd591", text: "#d46b08" } +}; + +function CompactTag({ color = "gray", children, tooltip = "" }) { + const colors = colorMap[color] || colorMap.gray; + + return ( + + {children} + + ); +} export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount); export function JobPartsQueueCount({ bodyshop, parts }) { - const { t } = useTranslation(); const partsStatus = useMemo(() => { if (!parts) return null; + const statusKeys = ["default_bo", "default_ordered", "default_received", "default_returned"]; + return parts.reduce( (acc, val) => { if (val.part_type === "PAS" || val.part_type === "PASL") return acc; - acc.total = acc.total + val.count; - acc[val.status] = acc[val.status] + val.count; + + acc.total += val.count; + + // NOTE: if val.status is null, object key becomes "null" + acc[val.status] = (acc[val.status] ?? 0) + val.count; + return acc; }, { @@ -34,45 +70,38 @@ export function JobPartsQueueCount({ bodyshop, parts }) { ); }, [bodyshop, parts]); - if (!parts) return null; + if (!parts || !partsStatus) return null; + return (
- - {partsStatus.total} - - - - {partsStatus["null"]} - - - - - {partsStatus[bodyshop.md_order_statuses.default_bo]} - - - - - {partsStatus[bodyshop.md_order_statuses.default_ordered]} - - - - - {partsStatus[bodyshop.md_order_statuses.default_received]} - - - - - {partsStatus[bodyshop.md_order_statuses.default_returned]} - - + + {partsStatus.total} + + + + {partsStatus["null"]} + + + + {partsStatus[bodyshop.md_order_statuses.default_bo]} + + + + {partsStatus[bodyshop.md_order_statuses.default_ordered]} + + + {partsStatus[bodyshop.md_order_statuses.default_received]} + + + {partsStatus[bodyshop.md_order_statuses.default_returned]} +
); } diff --git a/client/src/components/job-parts-received/job-parts-received.component.jsx b/client/src/components/job-parts-received/job-parts-received.component.jsx index 64521fb5f..dc6c42cbc 100644 --- a/client/src/components/job-parts-received/job-parts-received.component.jsx +++ b/client/src/components/job-parts-received/job-parts-received.component.jsx @@ -18,10 +18,17 @@ const mapStateToProps = createStructuredSelector({ * @param parts * @param displayMode * @param popoverPlacement + * @param countsOnly * @returns {JSX.Element} * @constructor */ -export function JobPartsReceived({ bodyshop, parts, displayMode = "full", popoverPlacement = "top" }) { +export function JobPartsReceived({ + bodyshop, + parts, + displayMode = "full", + popoverPlacement = "top", + countsOnly = false +}) { const [open, setOpen] = useState(false); const { t } = useTranslation(); @@ -61,6 +68,8 @@ export function JobPartsReceived({ bodyshop, parts, displayMode = "full", popove [canOpen] ); + if (countsOnly) return ; + const displayText = displayMode === "compact" ? summary.percentLabel : `${summary.percentLabel} (${summary.received}/${summary.total})`; @@ -74,7 +83,7 @@ export function JobPartsReceived({ bodyshop, parts, displayMode = "full", popove trigger={["click"]} placement={popoverPlacement} content={ -
+
} @@ -99,7 +108,8 @@ JobPartsReceived.propTypes = { bodyshop: PropTypes.object, parts: PropTypes.array, displayMode: PropTypes.oneOf(["full", "compact"]), - popoverPlacement: PropTypes.string + popoverPlacement: PropTypes.string, + countsOnly: PropTypes.bool }; export default connect(mapStateToProps)(JobPartsReceived); diff --git a/client/src/components/job-payments/job-payments.component.jsx b/client/src/components/job-payments/job-payments.component.jsx index 6afcb9ea7..37342aa83 100644 --- a/client/src/components/job-payments/job-payments.component.jsx +++ b/client/src/components/job-payments/job-payments.component.jsx @@ -107,9 +107,8 @@ export function JobPayments({ job, bodyshop, setPaymentContext, setCardPaymentCo context: record }); }} - > - - + icon={} + /> 0) return ( - ); diff --git a/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx b/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx index ce5c45ece..aa4c74d79 100644 --- a/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx +++ b/client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx @@ -53,10 +53,8 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) { return ( - ); diff --git a/client/src/components/jobs-available-scan/jobs-available-scan.component.jsx b/client/src/components/jobs-available-scan/jobs-available-scan.component.jsx index c8dbd1065..763a45d10 100644 --- a/client/src/components/jobs-available-scan/jobs-available-scan.component.jsx +++ b/client/src/components/jobs-available-scan/jobs-available-scan.component.jsx @@ -94,11 +94,7 @@ export function JobsAvailableScan({ partnerVersion, refetch }) { { title: t("general.labels.actions"), key: "actions", - render: (text, record) => ( - - ) + render: (text, record) => + /> { setSearchText(e.currentTarget.value); }} + enterButton /> } diff --git a/client/src/components/jobs-available-table/jobs-available-table.component.jsx b/client/src/components/jobs-available-table/jobs-available-table.component.jsx index 2d48f7062..918f21ec7 100644 --- a/client/src/components/jobs-available-table/jobs-available-table.component.jsx +++ b/client/src/components/jobs-available-table/jobs-available-table.component.jsx @@ -135,17 +135,16 @@ export function JobsAvailableComponent({ bodyshop, loading, data, refetch, addJo refetch(); }); }} - > - - + icon={} + /> {!isClosed && ( <> - - + + icon={} + /> - { setSearchText(e.currentTarget.value); }} + enterButton /> + + + } > diff --git a/client/src/components/jobs-change-status/jobs-change-status.component.jsx b/client/src/components/jobs-change-status/jobs-change-status.component.jsx index 22c65e459..8d4ce36c7 100644 --- a/client/src/components/jobs-change-status/jobs-change-status.component.jsx +++ b/client/src/components/jobs-change-status/jobs-change-status.component.jsx @@ -96,10 +96,8 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail, isPar return ( - ); diff --git a/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx b/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx index f1a1bf3f2..a6ad151fb 100644 --- a/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx +++ b/client/src/components/jobs-close-lines/jobs-close-lines.component.jsx @@ -43,6 +43,10 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) { {fields.map((field, index) => (
*/} + + + ({ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTrail, parentFormIsFieldsTouched }) { const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); + const [earlyRoCreated, setEarlyRoCreated] = useState(!!job?.dms_id); // Track early RO creation state + const [earlyRoCreatedThisSession, setEarlyRoCreatedThisSession] = useState(false); // Track if created in THIS modal session const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO); const { t } = useTranslation(); const [form] = Form.useForm(); const notification = useNotification(); const allFormValues = Form.useWatch([], form); + const { socket } = useSocket(); // Extract socket from context + + // Get Fortellis treatment for proper DMS mode detection + const { + treatments: { Fortellis } + } = useTreatmentsWithConfig({ + attributes: {}, + names: ["Fortellis"], + splitKey: bodyshop?.imexshopid + }); + + // Check if bodyshop has Reynolds integration using the proper getDmsMode function + const dmsMode = getDmsMode(bodyshop, Fortellis.treatment); + const isReynoldsMode = dmsMode === DMS_MAP.reynolds; const handleConvert = async ({ employee_csr, category, ...values }) => { if (parentFormIsFieldsTouched()) { @@ -82,177 +102,227 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr const submitDisabled = useCallback(() => some(allFormValues, (v) => v === undefined), [allFormValues]); - const popMenu = ( -
- { + setEarlyRoCreated(true); // Mark early RO as created + setEarlyRoCreatedThisSession(true); // Mark as created in this session + notification.success({ + title: t("jobs.successes.early_ro_created"), + description: `RO Number: ${result.roNumber || "N/A"}` + }); + // Delay refetch to keep success message visible for 2 seconds + setTimeout(() => { + refetch?.(); + }, 2000); + }; + + const handleModalClose = () => { + setOpen(false); + }; + + if (job.converted) return <>; + + return ( + <> + - - - -
- ); - if (job.converted) return <>; - - return ( - - - + + + + + + + ); } diff --git a/client/src/components/jobs-create-vehicle-info/jobs-create-vehicle-info.predefined.component.jsx b/client/src/components/jobs-create-vehicle-info/jobs-create-vehicle-info.predefined.component.jsx index 2cdd24a06..b6a93e1e7 100644 --- a/client/src/components/jobs-create-vehicle-info/jobs-create-vehicle-info.predefined.component.jsx +++ b/client/src/components/jobs-create-vehicle-info/jobs-create-vehicle-info.predefined.component.jsx @@ -24,7 +24,7 @@ export default function JobsCreateVehicleInfoPredefined({ disabled, form }) {
+ {/* Hidden field to preserve jobline ID without injecting a div under
setSearch(value)} />} + title={() => setSearch(value)} enterButton/>} dataSource={filteredPredefinedVehicles} columns={[ { @@ -56,9 +56,8 @@ export default function JobsCreateVehicleInfoPredefined({ disabled, form }) { setOpen(false); setSearch(""); }} - > - - + icon={} + /> ) } ]} diff --git a/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx b/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx index 172400edd..5eae62abf 100644 --- a/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx +++ b/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx @@ -251,7 +251,6 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) { ))} - @@ -267,6 +266,21 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) { + {bodyshop.rr_dealerid && ( + + + + )} + {bodyshop.rr_dealerid && ( + + + + )} + {bodyshop.rr_dealerid && ( + + + + )} ); diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx index 8e62c5988..bdb08ef01 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx @@ -1,10 +1,11 @@ import { DownCircleFilled } from "@ant-design/icons"; import { useApolloClient, useMutation, useQuery } from "@apollo/client/react"; import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react"; -import { Button, Card, Dropdown, Form, Input, Modal, Popconfirm, Popover, Select, Space } from "antd"; +import { Button, Card, Dropdown, Form, Input, Modal, Popover, Select, Space } from "antd"; import axios from "axios"; import parsePhoneNumber from "libphonenumber-js"; import { useCallback, useMemo, useRef, useState } from "react"; +import { HasRbacAccess } from "../../components/rbac-wrapper/rbac-wrapper.component"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { Link, useNavigate } from "react-router-dom"; @@ -20,7 +21,7 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors import { setEmailOptions } from "../../redux/email/email.actions"; import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions"; import { setModalContext } from "../../redux/modals/modals.actions"; -import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; +import { selectAuthLevel, selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import { DateTimeFormatter } from "../../utils/DateFormatter"; import { TemplateList } from "../../utils/TemplateConstants"; @@ -28,7 +29,6 @@ import dayjs from "../../utils/day"; import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component"; import LockerWrapperComponent from "../lock-wrapper/lock-wrapper.component"; -import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; import ShareToTeamsButton from "../share-to-teams/share-to-teams.component.jsx"; import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util"; import DuplicateJob from "./jobs-detail-header-actions.duplicate.util"; @@ -39,7 +39,8 @@ const EMPTY_ARRAY = Object.freeze([]); const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, jobRO: selectJobReadOnly, - currentUser: selectCurrentUser + currentUser: selectCurrentUser, + authLevel: selectAuthLevel }); const mapDispatchToProps = (dispatch) => ({ @@ -117,7 +118,8 @@ export function JobsDetailHeaderActions({ openChatByPhone, setMessage, setTimeTicketTaskContext, - setTaskUpsertContext + setTaskUpsertContext, + authLevel }) { const { t } = useTranslation(); const client = useApolloClient(); @@ -129,10 +131,6 @@ export function JobsDetailHeaderActions({ const jobId = job?.id; const watcherVars = useMemo(() => ({ jobid: jobId }), [jobId]); - // Option A: coordinated Dropdown + Popconfirm open state so the menu doesn't unmount before Popconfirm renders. - const [confirmKey, setConfirmKey] = useState(null); - const confirmKeyRef = useRef(null); - const [isCancelScheduleModalVisible, setIsCancelScheduleModalVisible] = useState(false); const [insertAppointment] = useMutation(INSERT_MANUAL_APPT); const [deleteJob] = useMutation(DELETE_JOB); @@ -150,6 +148,8 @@ export function JobsDetailHeaderActions({ const devEmails = ["imex.dev", "rome.dev"]; const prodEmails = ["imex.prod", "rome.prod", "imex.test", "rome.test"]; + const canVoidJob = useMemo(() => HasRbacAccess({ authLevel, bodyshop, action: "jobs:void" }), [authLevel, bodyshop]); + const hasValidEmail = (emails) => emails.some((email) => userEmail.endsWith(email)); const canSubmitForTesting = (isDevEnv && hasValidEmail(devEmails)) || (isProdEnv && hasValidEmail(prodEmails)); @@ -157,7 +157,6 @@ export function JobsDetailHeaderActions({ variables: watcherVars, skip: !jobId, fetchPolicy: "cache-first", - notifyOnNetworkStatusChange: true }); const jobWatchersCount = jobWatchersData?.job_watchers?.length ?? job?.job_watchers?.length ?? 0; @@ -179,83 +178,69 @@ export function JobsDetailHeaderActions({ const jobInPreProduction = preProductionStatuses.includes(jobStatus); const jobInPostProduction = postProductionStatuses.includes(jobStatus); - const openConfirm = useCallback((key) => { - confirmKeyRef.current = key; - setConfirmKey(key); - setDropdownOpen(true); - }, []); + const makeConfirmId = () => + globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`; - const closeConfirm = useCallback(() => { - confirmKeyRef.current = null; - setConfirmKey(null); - }, []); + const [modal, modalContextHolder] = Modal.useModal(); - const handleDropdownOpenChange = useCallback( - (nextOpen, info) => { - if (!nextOpen && info?.source === "menu" && confirmKeyRef.current) return; - setDropdownOpen(nextOpen); - if (!nextOpen) closeConfirm(); - }, - [closeConfirm] - ); + const confirmInstancesRef = useRef(new Map()); - const renderPopconfirmMenuLabel = ({ - key, - text, + const closeConfirmById = (id) => { + const inst = confirmInstancesRef.current.get(id); + if (inst) inst.destroy(); // hard close + confirmInstancesRef.current.delete(id); + }; + + const openConfirmFromMenu = ({ + variant = "confirm", // "confirm" | "info" | "warning" title, + content, okText, cancelText, showCancel = true, - closeDropdownOnConfirm = true, - onConfirm - }) => ( - { - if (nextOpen) openConfirm(key); - else closeConfirm(); - }} - onConfirm={(e) => { - e?.stopPropagation?.(); - closeConfirm(); + onOk, + onCancel + }) => { + // close the dropdown immediately; confirm dialog is separate + setDropdownOpen(false); - // Critical: for informational popconfirms, keep the dropdown open so the Popconfirm can cleanly close. - if (closeDropdownOnConfirm) { - setDropdownOpen(false); + const id = makeConfirmId(); + + const openFn = variant === "info" ? modal.info : variant === "warning" ? modal.warning : modal.confirm; + + const inst = openFn({ + title, + content, + okText, + cancelText, + centered: true, + maskClosable: false, + onCancel: () => { + closeConfirmById(id); + onCancel?.(); + }, + onOk: async () => { + try { + await onOk?.(); + } finally { + closeConfirmById(id); } + }, + ...(showCancel ? {} : { okCancel: false }) + }); - onConfirm?.(e); - }} - onCancel={(e) => { - e?.stopPropagation?.(); - closeConfirm(); - // Keep dropdown open on cancel so the user can continue using the menu. - }} - getPopupContainer={() => document.body} - > -
{ - e.preventDefault(); - e.stopPropagation(); - openConfirm(key); - }} - > - {text} -
-
- ); + confirmInstancesRef.current.set(id, inst); + return id; + }; + + const handleDropdownOpenChange = useCallback((nextOpen) => { + setDropdownOpen(nextOpen); + }, []); - // Function to show modal const showCancelScheduleModal = () => { setIsCancelScheduleModalVisible(true); }; - // Function to handle Cancel const handleCancelScheduleModalCancel = () => { setIsCancelScheduleModalVisible(false); }; @@ -264,7 +249,7 @@ export function JobsDetailHeaderActions({ DuplicateJob({ apolloClient: client, jobId: job.id, - config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported }, + config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported, timezone: bodyshop.timezone }, completionCallback: (newJobId) => { history(`/manage/jobs/${newJobId}`); notification.success({ @@ -279,7 +264,7 @@ export function JobsDetailHeaderActions({ DuplicateJob({ apolloClient: client, jobId: job.id, - config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported }, + config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported, timezone: bodyshop.timezone }, completionCallback: (newJobId) => { history(`/manage/jobs/${newJobId}`); notification.success({ @@ -476,6 +461,11 @@ export function JobsDetailHeaderActions({ }; const handleVoidJob = async () => { + if (!canVoidJob) { + notification.error({ title: t("general.messages.rbacunauth") }); + return; + } + //delete the job. const result = await voidJob({ variables: { @@ -964,26 +954,26 @@ export function JobsDetailHeaderActions({ { key: "duplicate", id: "job-actions-duplicate", - label: renderPopconfirmMenuLabel({ - key: "confirm-duplicate", - text: t("menus.jobsactions.duplicate"), - title: t("jobs.labels.duplicateconfirm"), - okText: t("general.labels.yes"), - cancelText: t("general.labels.no"), - onConfirm: handleDuplicate - }) + label: t("menus.jobsactions.duplicate"), + onClick: () => + openConfirmFromMenu({ + title: t("jobs.labels.duplicateconfirm"), + okText: t("general.labels.yes"), + cancelText: t("general.labels.no"), + onOk: handleDuplicate + }) }, { key: "duplicatenolines", id: "job-actions-duplicatenolines", - label: renderPopconfirmMenuLabel({ - key: "confirm-duplicate-nolines", - text: t("menus.jobsactions.duplicatenolines"), - title: t("jobs.labels.duplicateconfirm"), - okText: t("general.labels.yes"), - cancelText: t("general.labels.no"), - onConfirm: handleDuplicateConfirm - }) + label: t("menus.jobsactions.duplicatenolines"), + onClick: () => + openConfirmFromMenu({ + title: t("jobs.labels.duplicateconfirm"), + okText: t("general.labels.yes"), + cancelText: t("general.labels.no"), + onOk: handleDuplicateConfirm + }) } ] }, @@ -1156,26 +1146,25 @@ export function JobsDetailHeaderActions({ menuItems.push({ key: "deletejob", id: "job-actions-deletejob", - label: - jobWatchersCount === 0 - ? renderPopconfirmMenuLabel({ - key: "confirm-deletejob", - text: t("menus.jobsactions.deletejob"), - title: t("jobs.labels.deleteconfirm"), - okText: t("general.labels.yes"), - cancelText: t("general.labels.no"), - onConfirm: handleDeleteJob - }) - : renderPopconfirmMenuLabel({ - key: "confirm-deletejob-watchers", - text: t("menus.jobsactions.deletejob"), - title: t("jobs.labels.deletewatchers"), - showCancel: false, - closeDropdownOnConfirm: false, // <-- FIX: keep dropdown mounted so Popconfirm can close cleanly - onConfirm: () => { - // informational confirm only - } - }) + label: t("menus.jobsactions.deletejob"), + onClick: () => { + if (jobWatchersCount === 0) { + openConfirmFromMenu({ + title: t("jobs.labels.deleteconfirm"), + okText: t("general.labels.yes"), + cancelText: t("general.labels.no"), + onOk: handleDeleteJob + }); + } else { + // informational "OK only" + openConfirmFromMenu({ + variant: "info", + title: t("jobs.labels.deletewatchers"), + okText: t("general.actions.ok"), + showCancel: false + }); + } + } }); } @@ -1188,22 +1177,18 @@ export function JobsDetailHeaderActions({ label: t("appointments.labels.manualevent") }); - if (!jobRO && job.converted) { + if (!jobRO && job.converted && canVoidJob) { menuItems.push({ key: "voidjob", id: "job-actions-voidjob", - label: ( - - {renderPopconfirmMenuLabel({ - key: "confirm-voidjob", - text: t("menus.jobsactions.void"), - title: t("jobs.labels.voidjob"), - okText: t("general.labels.yes"), - cancelText: t("general.labels.no"), - onConfirm: handleVoidJob - })} - - ) + label: t("menus.jobsactions.void"), + onClick: () => + openConfirmFromMenu({ + title: t("jobs.labels.voidjob"), + okText: t("general.labels.yes"), + cancelText: t("general.labels.no"), + onOk: handleVoidJob + }) }); } @@ -1235,6 +1220,7 @@ export function JobsDetailHeaderActions({ return ( <> + {modalContextHolder} - diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util.js b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util.js index 8e0705e79..cbc2ed23c 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util.js +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util.js @@ -15,7 +15,7 @@ export default async function DuplicateJob({ }) { logImEXEvent("job_duplicate"); - const { defaultOpenStatus } = config; + const { defaultOpenStatus, timezone } = config; //get a list of all fields on the job const res = await apolloClient.query({ query: QUERY_JOB_FOR_DUPE, @@ -31,9 +31,12 @@ export default async function DuplicateJob({ delete existingJob.updatedat; delete existingJob.cieca_stl; delete existingJob.cieca_ttl; + !keepJobLines && delete existingJob.clm_total; const newJob = { ...existingJob, + date_estimated: dayjs().tz(timezone, false).format("YYYY-MM-DD"), + date_open: dayjs(), status: defaultOpenStatus }; @@ -70,7 +73,7 @@ export default async function DuplicateJob({ export async function CreateIouForJob({ apolloClient, jobId, config, jobLinesToKeep, currentUser }) { logImEXEvent("job_create_iou"); - const { status } = config; + const { status, timezone } = config; //get a list of all fields on the job const res = await apolloClient.query({ query: QUERY_JOB_FOR_DUPE, @@ -88,10 +91,10 @@ export async function CreateIouForJob({ apolloClient, jobId, config, jobLinesToK const newJob = { ...existingJob, - converted: true, status: status, iouparent: jobId, + date_estimated: dayjs().tz(timezone, false).format("YYYY-MM-DD"), date_open: dayjs(), audit_trails: { data: [ diff --git a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx index 0b05d66be..a3a0bd89f 100644 --- a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx +++ b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx @@ -143,7 +143,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is label={t("jobs.fields.comment")} styles={{ value: { overflow: "hidden", textOverflow: "ellipsis" } }} > - + {!isPartsEntry && {job.ins_co_nm}} {job.clm_no} @@ -176,7 +176,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is )} - + diff --git a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx index a6cd07770..969b49ea1 100644 --- a/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx +++ b/client/src/components/jobs-documents-gallery/jobs-documents-gallery.component.jsx @@ -128,9 +128,7 @@ function JobsDocumentsComponent({
- + + icon={} + /> {!billId && ( diff --git a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx index f4c40fee8..ff4c8dda8 100644 --- a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx +++ b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx @@ -102,9 +102,8 @@ export function JobsDocumentsLocalGallery({ } } }} - > - - + icon={} + /> diff --git a/client/src/components/jobs-find-modal/jobs-find-modal.component.jsx b/client/src/components/jobs-find-modal/jobs-find-modal.component.jsx index 0048ecdb2..47c8ffd5f 100644 --- a/client/src/components/jobs-find-modal/jobs-find-modal.component.jsx +++ b/client/src/components/jobs-find-modal/jobs-find-modal.component.jsx @@ -179,9 +179,8 @@ export default function JobsFindModalComponent({ onClick={() => { jobsListRefetch(); }} - > - - + icon={} + /> { diff --git a/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx b/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx index 70a2f27ef..ec9b77b59 100644 --- a/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx +++ b/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx @@ -224,9 +224,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) { )} - + + + + icon={} + /> ({readyStatuses && readyStatuses.join(", ")}) - + + icon={} + /> } > diff --git a/client/src/components/notification-center/notification-center.component.jsx b/client/src/components/notification-center/notification-center.component.jsx index 0702447c7..439335c40 100644 --- a/client/src/components/notification-center/notification-center.component.jsx +++ b/client/src/components/notification-center/notification-center.component.jsx @@ -24,6 +24,7 @@ const NotificationCenterComponent = ({ onNotificationClick, unreadCount, isEmployee, + isDarkMode, ref }) => { const { t } = useTranslation(); @@ -112,14 +113,16 @@ const NotificationCenterComponent = ({ ) : ( - +
+ +
)} ); diff --git a/client/src/components/notification-center/notification-center.container.jsx b/client/src/components/notification-center/notification-center.container.jsx index 617bf1569..109591228 100644 --- a/client/src/components/notification-center/notification-center.container.jsx +++ b/client/src/components/notification-center/notification-center.container.jsx @@ -5,6 +5,7 @@ import NotificationCenterComponent from "./notification-center.component"; import { GET_NOTIFICATIONS } from "../../graphql/notifications.queries"; import { createStructuredSelector } from "reselect"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js"; +import { selectDarkMode } from "../../redux/application/application.selectors.js"; import day from "../../utils/day.js"; import { INITIAL_NOTIFICATIONS, useSocket } from "../../contexts/SocketIO/useSocket.js"; import { useIsEmployee } from "../../utils/useIsEmployee.js"; @@ -22,7 +23,7 @@ const NOTIFICATION_POLL_INTERVAL_SECONDS = 60; * @returns {JSX.Element} * @constructor */ -const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount, currentUser }) => { +const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount, currentUser, isDarkMode }) => { const [showUnreadOnly, setShowUnreadOnly] = useState(false); const [notifications, setNotifications] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -55,7 +56,6 @@ const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount, where: whereClause }, fetchPolicy: "cache-and-network", - notifyOnNetworkStatusChange: true, errorPolicy: "all", pollInterval: isConnected ? 0 : day.duration(NOTIFICATION_POLL_INTERVAL_SECONDS, "seconds").asMilliseconds(), skip: skipQuery @@ -213,13 +213,15 @@ const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount, loadMore={loadMore} onNotificationClick={handleNotificationClick} unreadCount={unreadCount} + isDarkMode={isDarkMode} /> ); }; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, - currentUser: selectCurrentUser + currentUser: selectCurrentUser, + isDarkMode: selectDarkMode }); export default connect(mapStateToProps, null)(NotificationCenterContainer); diff --git a/client/src/components/notification-center/notification-center.styles.scss b/client/src/components/notification-center/notification-center.styles.scss index 98e2c6890..f897df3d6 100644 --- a/client/src/components/notification-center/notification-center.styles.scss +++ b/client/src/components/notification-center/notification-center.styles.scss @@ -173,3 +173,11 @@ } } } + +.notification-center--dark { + color-scheme: dark; +} + +.notification-center--light { + color-scheme: light; +} diff --git a/client/src/components/owner-find-modal/owner-find-modal.container.jsx b/client/src/components/owner-find-modal/owner-find-modal.container.jsx index 24fea4287..a8dd3927c 100644 --- a/client/src/components/owner-find-modal/owner-find-modal.container.jsx +++ b/client/src/components/owner-find-modal/owner-find-modal.container.jsx @@ -48,6 +48,7 @@ export default function OwnerFindModalContainer({ value={searchText} onChange={(e) => setSearchText(e.target.value)} onSearch={(val) => callSearchowners({ variables: { search: val.trim() } })} + enterButton /> ({ }); export default connect(mapStateToProps, mapDispatchToProps)(OwnerNameDisplay); -export function OwnerNameDisplay({ bodyshop, ownerObject }) { +export function OwnerNameDisplay({ bodyshop, ownerObject, withToolTip = false }) { const emptyTest = ownerObject?.ownr_fn + ownerObject?.ownr_ln + ownerObject?.ownr_co_nm; if (!emptyTest || emptyTest === "null" || emptyTest.trim() === "") return "N/A"; - if (bodyshop.last_name_first) - return `${ownerObject?.ownr_ln || ""}, ${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_co_nm || ""}`.trim(); - - return `${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_ln || ""} ${ownerObject.ownr_co_nm || ""}`.trim(); + let returnString; + if (bodyshop.last_name_first) { + returnString = + `${ownerObject?.ownr_ln || ""}, ${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_co_nm || ""}`.trim(); + } else { + returnString = `${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_ln || ""} ${ownerObject.ownr_co_nm || ""}`.trim(); + } + if (withToolTip) { + return ( + + {returnString} + + ); + } else { + return returnString; + } } export function OwnerNameDisplayFunction(ownerObject, forceFirstLast = false) { diff --git a/client/src/components/owner-search-select/owner-search-select.component.jsx b/client/src/components/owner-search-select/owner-search-select.component.jsx index 9f6f5030a..bc5d7b105 100644 --- a/client/src/components/owner-search-select/owner-search-select.component.jsx +++ b/client/src/components/owner-search-select/owner-search-select.component.jsx @@ -16,9 +16,10 @@ const OwnerSearchSelect = ({ value, onChange, onBlur, disabled, ref }) => { SEARCH_OWNERS_BY_ID_FOR_AUTOCOMPLETE ); - const executeSearch = (v) => { - if (v && v.variables?.search !== "" && v.variables.search.length >= 2) callSearch({ variables: v.variables }); + const executeSearch = (variables) => { + if (variables?.search !== "" && variables?.search?.length >= 2) callSearch({ variables }); }; + const debouncedExecuteSearch = _.debounce(executeSearch, 500); const handleSearch = (value) => { diff --git a/client/src/components/owners-list/owners-list.component.jsx b/client/src/components/owners-list/owners-list.component.jsx index 9959af213..f495a04a4 100644 --- a/client/src/components/owners-list/owners-list.component.jsx +++ b/client/src/components/owners-list/owners-list.component.jsx @@ -99,9 +99,7 @@ export default function OwnersListComponent({ loading, owners, total, refetch }) )} - + - + + + icon={} + /> )} - + + + + + )} + > - % - + % ); diff --git a/client/src/components/parts-order-modal/parts-order-modal.component.jsx b/client/src/components/parts-order-modal/parts-order-modal.component.jsx index edcbd82f4..c59e2cbd3 100644 --- a/client/src/components/parts-order-modal/parts-order-modal.component.jsx +++ b/client/src/components/parts-order-modal/parts-order-modal.component.jsx @@ -13,6 +13,13 @@ import PartsOrderModalPriceChange from "./parts-order-modal-price-change.compone import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx"; import { selectIsPartsEntry } from "../../redux/application/application.selectors.js"; +const PriceInputWrapper = ({ value, onChange, form, field }) => ( + + + + +); + const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, isPartsEntry: selectIsPartsEntry @@ -59,7 +66,7 @@ export function PartsOrderModalComponent({ return (
- - - - + {isReturn && ( !l?.job_line_id); - if (missingIdx !== -1) { - notification.error({ - title: t("parts_orders.errors.creating"), - description: `Missing job_line_id for parts line #${missingIdx + 1}` - }); - setSaving(false); - return; - } - let insertResult; try { insertResult = await insertPartOrder({ @@ -371,6 +361,7 @@ export function PartsOrderModalContainer({ } }, [open, linesToOrder, form]); + //This used to have a loading component spinner for the vendor data. With Apollo 4, the NetworkState isn't emitting correctly, so loading just gets set to true the second time, and no longer works as expected. return ( {error ? : null}
- {loading ? ( - - ) : ( - - )} +
); diff --git a/client/src/components/parts-queue-list/parts-queue.list.component.jsx b/client/src/components/parts-queue-list/parts-queue.list.component.jsx index 1889131fb..766ff9564 100644 --- a/client/src/components/parts-queue-list/parts-queue.list.component.jsx +++ b/client/src/components/parts-queue-list/parts-queue.list.component.jsx @@ -1,6 +1,6 @@ import { SyncOutlined } from "@ant-design/icons"; import { useQuery } from "@apollo/client/react"; -import { Button, Card, Input, Space, Table } from "antd"; +import { Button, Card, Checkbox, Input, Space, Table } from "antd"; import _ from "lodash"; import queryString from "query-string"; import { useState } from "react"; @@ -31,6 +31,8 @@ export function PartsQueueListComponent({ bodyshop }) { const { selected, sortcolumn, sortorder, statusFilters } = searchParams; const history = useNavigate(); const [filter, setFilter] = useLocalStorage("filter_parts_queue", null); + const [viewTimeStamp, setViewTimeStamp] = useLocalStorage("parts_queue_timestamps", false); + const [countsOnly, setCountsOnly] = useLocalStorage("parts_queue_counts_only", false); const { loading, error, data, refetch } = useQuery(QUERY_PARTS_QUEUE, { fetchPolicy: "network-only", @@ -92,6 +94,7 @@ export function PartsQueueListComponent({ bodyshop }) { title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", + width: "110px", sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), sortOrder: sortcolumn === "ro_number" && sortorder, @@ -103,16 +106,20 @@ export function PartsQueueListComponent({ bodyshop }) { title: t("jobs.fields.owner"), dataIndex: "ownr_ln", key: "ownr_ln", + width: "8%", + ellipsis: { + showTitle: true + }, sorter: (a, b) => alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)), sortOrder: sortcolumn === "ownr_ln" && sortorder, render: (text, record) => { return record.ownerid ? ( - + ) : ( - + ); } @@ -187,7 +194,7 @@ export function PartsQueueListComponent({ bodyshop }) { ellipsis: true, sorter: (a, b) => dateSort(a.scheduled_in, b.scheduled_in), sortOrder: sortcolumn === "scheduled_in" && sortorder, - render: (text, record) => {record.scheduled_in} + render: (text, record) => {record.scheduled_in} }, { title: t("jobs.fields.scheduled_completion"), @@ -196,7 +203,9 @@ export function PartsQueueListComponent({ bodyshop }) { ellipsis: true, sorter: (a, b) => dateSort(a.scheduled_completion, b.scheduled_completion), sortOrder: sortcolumn === "scheduled_completion" && sortorder, - render: (text, record) => {record.scheduled_completion} + render: (text, record) => ( + {record.scheduled_completion} + ) }, // { // title: t("vehicles.fields.plate_no"), @@ -227,16 +236,23 @@ export function PartsQueueListComponent({ bodyshop }) { title: t("jobs.fields.updated_at"), dataIndex: "updated_at", key: "updated_at", + width: "110px", sorter: (a, b) => dateSort(a.updated_at, b.updated_at), sortOrder: sortcolumn === "updated_at" && sortorder, - render: (text, record) => {record.updated_at} + render: (text, record) => {record.updated_at} }, { title: t("jobs.fields.partsstatus"), dataIndex: "partsstatus", key: "partsstatus", + width: countsOnly ? "180px" : "110px", render: (text, record) => ( - + ) }, { @@ -249,6 +265,7 @@ export function PartsQueueListComponent({ bodyshop }) { title: t("jobs.fields.queued_for_parts"), dataIndex: "queued_for_parts", key: "queued_for_parts", + width: "120px", sorter: (a, b) => a.queued_for_parts - b.queued_for_parts, sortOrder: sortcolumn === "queued_for_parts" && sortorder, filteredValue: filter?.queued_for_parts || null, @@ -269,11 +286,18 @@ export function PartsQueueListComponent({ bodyshop }) { return ( + setCountsOnly(e.target.checked)}> + {t("parts.labels.view_counts_only")} + + setViewTimeStamp(e.target.checked)}> + {t("parts.labels.view_timestamps")} + { diff --git a/client/src/components/payment-form/payment-form.totalpayments.component.jsx b/client/src/components/payment-form/payment-form.totalpayments.component.jsx index b413158fe..27717919c 100644 --- a/client/src/components/payment-form/payment-form.totalpayments.component.jsx +++ b/client/src/components/payment-form/payment-form.totalpayments.component.jsx @@ -33,7 +33,7 @@ export default function PaymentFormTotalPayments({ jobid }) { {balance && ( )} diff --git a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx index f5f1642f0..58259dcfb 100644 --- a/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx +++ b/client/src/components/payments-list-paginated/payment-list-paginated.component.jsx @@ -144,6 +144,7 @@ export function PaymentsListPaginated({ render: (text, record) => ( + /> setCaBcEtfTableContext()}>{t("payments.labels.ca_bc_etf_table")} )} - +
); return ( - + trigger.parentElement}> ); diff --git a/client/src/components/print-center-jobs/print-center-jobs-parts.component.jsx b/client/src/components/print-center-jobs/print-center-jobs-parts.component.jsx index e86db4255..54d9134c0 100644 --- a/client/src/components/print-center-jobs/print-center-jobs-parts.component.jsx +++ b/client/src/components/print-center-jobs/print-center-jobs-parts.component.jsx @@ -9,6 +9,7 @@ import { TemplateList } from "../../utils/TemplateConstants"; import { GenerateDocument } from "../../utils/RenderTemplate"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; import { HistoryOutlined, MailOutlined, PrinterOutlined, UnorderedListOutlined } from "@ant-design/icons"; +import { bodyshopHasDmsKey } from "../../utils/dmsUtils.js"; const mapStateToProps = createStructuredSelector({ printCenterModal: selectPrintCenter, @@ -29,27 +30,29 @@ export function PrintCenterJobsPartsComponent({ printCenterModal, bodyshop, tech names: ["Enhanced_Payroll"], splitKey: bodyshop.imexshopid }); - - const Templates = - bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null - ? Object.keys(tempList) - .map((key) => tempList[key]) - .filter( - (temp) => - (!temp.regions || - temp.regions?.[bodyshop.region_config] || - (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true)) && - (!temp.dms || temp.dms === false) - ) - : Object.keys(tempList) - .map((key) => tempList[key]) - .filter( - (temp) => - !temp.regions || + const hasDMSKey = bodyshopHasDmsKey(bodyshop); + + const Templates = !hasDMSKey + ? Object.keys(tempList) + .map((key) => tempList[key]) + .filter( + (temp) => + (!temp.regions || temp.regions?.[bodyshop.region_config] || - (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true) - ); - + (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true)) && + (!temp.dms || temp.dms === false) + ) + .filter((temp) => !technician || temp.group !== "financial") + : Object.keys(tempList) + .map((key) => tempList[key]) + .filter( + (temp) => + !temp.regions || + temp.regions?.[bodyshop.region_config] || + (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true) + ) + .filter((temp) => !technician || temp.group !== "financial"); + const JobsReportsList = Enhanced_Payroll.treatment === "on" ? Object.keys(Templates) diff --git a/client/src/components/print-center-jobs/print-center-jobs.component.jsx b/client/src/components/print-center-jobs/print-center-jobs.component.jsx index 2df39c0b5..a33a387e1 100644 --- a/client/src/components/print-center-jobs/print-center-jobs.component.jsx +++ b/client/src/components/print-center-jobs/print-center-jobs.component.jsx @@ -12,15 +12,18 @@ import Jobd3RdPartyModal from "../job-3rd-party-modal/job-3rd-party-modal.compon import PrintCenterItem from "../print-center-item/print-center-item.component"; import PrintCenterJobsLabels from "../print-center-jobs-labels/print-center-jobs-labels.component"; import PrintCenterSpeedPrint from "../print-center-speed-print/print-center-speed-print.component"; +import { bodyshopHasDmsKey } from "../../utils/dmsUtils"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; const mapStateToProps = createStructuredSelector({ printCenterModal: selectPrintCenter, - bodyshop: selectBodyshop + bodyshop: selectBodyshop, + technician: selectTechnician }); const mapDispatchToProps = () => ({}); -export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) { +export function PrintCenterJobsComponent({ printCenterModal, bodyshop, technician }) { const [search, setSearch] = useState(""); const { id: jobId, job } = printCenterModal.context; const tempList = TemplateList("job", {}); @@ -32,30 +35,33 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) { names: ["Enhanced_Payroll"], splitKey: bodyshop.imexshopid }); + const hasDMSKey = bodyshopHasDmsKey(bodyshop); - const Templates = - bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null - ? Object.keys(tempList) - .map((key) => { - return tempList[key]; - }) - .filter( - (temp) => - (!temp.regions || - (temp.regions && temp.regions[bodyshop.region_config]) || - (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true)) && - (!temp.dms || temp.dms === false) - ) - : Object.keys(tempList) - .map((key) => { - return tempList[key]; - }) - .filter( - (temp) => - !temp.regions || + const Templates = !hasDMSKey + ? Object.keys(tempList) + .map((key) => { + return tempList[key]; + }) + .filter( + (temp) => + (!temp.regions || (temp.regions && temp.regions[bodyshop.region_config]) || - (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true) - ); + (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true)) && + (!temp.dms || temp.dms === false) + ) + .filter((temp) => !technician || temp.group !== "financial") + : Object.keys(tempList) + .map((key) => { + return tempList[key]; + }) + .filter( + (temp) => + !temp.regions || + (temp.regions && temp.regions[bodyshop.region_config]) || + (temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true) + ) + .filter((temp) => !technician || temp.group !== "financial"); + const JobsReportsList = Enhanced_Payroll.treatment === "on" ? Object.keys(Templates) @@ -89,7 +95,7 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) { - setSearch(e.target.value)} value={search} /> + setSearch(e.target.value)} value={search} enterButton /> } > diff --git a/client/src/components/print-wrapper/print-wrapper.component.jsx b/client/src/components/print-wrapper/print-wrapper.component.jsx index e5cde9b10..5f20c61a2 100644 --- a/client/src/components/print-wrapper/print-wrapper.component.jsx +++ b/client/src/components/print-wrapper/print-wrapper.component.jsx @@ -1,5 +1,5 @@ import { MailFilled, PrinterFilled } from "@ant-design/icons"; -import { Space, Spin } from "antd"; +import { Button, Space, Spin } from "antd"; import { useState } from "react"; import { GenerateDocument } from "../../utils/RenderTemplate"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; @@ -26,16 +26,18 @@ export default function PrintWrapperComponent({ {children || null} {!emailOnly && ( - } disabled={disabled} onClick={() => handlePrint("p")} - style={{ cursor: disabled ? "not-allowed" : null }} /> )} - } disabled={disabled} onClick={() => handlePrint("e")} - style={{ cursor: disabled ? "not-allowed" : null }} /> {loading && } diff --git a/client/src/components/production-board-filters/production-board-filters.component.jsx b/client/src/components/production-board-filters/production-board-filters.component.jsx index ef8121c84..c053e4fd7 100644 --- a/client/src/components/production-board-filters/production-board-filters.component.jsx +++ b/client/src/components/production-board-filters/production-board-filters.component.jsx @@ -45,6 +45,7 @@ export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading }) setFilter({ ...filter, search: e.target.value }); logImEXEvent("visual_board_filter_search", { search: e.target.value }); }} + enterButton /> - + + options={employeeOptions} + /> @@ -141,25 +143,25 @@ export function ProductionListEmpAssignment({ insertAuditTrail, bodyshop, record if (record[type]) theEmployee = bodyshop.employees.find((e) => e.id === record[type]); return ( - - - {record[type] ? ( -
- {`${theEmployee?.first_name || ""} ${theEmployee?.last_name || ""}`} - handleRemove(type)} /> -
- ) : ( + + {record[type] ? ( +
+ {`${theEmployee?.first_name || ""} ${theEmployee?.last_name || ""}`} + handleRemove(type)} /> +
+ ) : ( + { - setAssignment({ operation: type }); + setAssignment({ operation: type, employeeid: null }); setVisibility(true); }} /> - )} -
-
+ + )} + ); } diff --git a/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx b/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx index 6c6a02492..732189a17 100644 --- a/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx @@ -124,7 +124,8 @@ export function ProductionLastContacted({ currentUser, record }) {
setOpen(true)} style={{ - height: "19px" + height: "19px", + cursor: "pointer" }} > {record.date_last_contacted} diff --git a/client/src/components/production-list-columns/production-list-columns.paintpriority.component.jsx b/client/src/components/production-list-columns/production-list-columns.paintpriority.component.jsx index 8381b08b5..76e1827fe 100644 --- a/client/src/components/production-list-columns/production-list-columns.paintpriority.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.paintpriority.component.jsx @@ -48,7 +48,7 @@ export default function ProductionListColumnPaintPriority({ record }) { return ( -
{record.production_vars?.paintpriority}
+
{record.production_vars?.paintpriority}
); } diff --git a/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx b/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx index 7590947c3..c1e92dfd8 100644 --- a/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx @@ -16,7 +16,7 @@ const mapDispatchToProps = (dispatch) => ({ setNoteUpsertContext: (context) => dispatch(setModalContext({ context: context, modal: "noteUpsert" })) }); -function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) { +function ProductionListColumnProductionNote({ record, setNoteUpsertContext, usePortal = false }) { const { t } = useTranslation(); const [note, setNote] = useState(record.production_vars?.note || ""); const [open, setOpen] = useState(false); @@ -59,16 +59,20 @@ function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) { ); const content = ( -
e.stopPropagation()} onClick={(e) => e.stopPropagation()}> +
e.stopPropagation()} + onPointerDown={(e) => e.stopPropagation()} + > e.stopPropagation()} - onClick={(e) => e.stopPropagation()} /> + icon={} + /> { setHasUnsavedChanges(false); initialStateRef.current = state; + + // NEW: after saving, treat current columns as the baseline + initialColumnsRef.current = columns; }} /> - -
{ - if (!bodyshop.md_ro_statuses.production_colors) return null; - const color = bodyshop.md_ro_statuses.production_colors.find((x) => x.status === record.status); - if (!color) { - if (index % 2 === 0) + + col.key)} strategy={horizontalListSortingStrategy}> +
{ + if (!bodyshop.md_ro_statuses.production_colors) return null; + const color = bodyshop.md_ro_statuses.production_colors.find((x) => x.status === record.status); + if (!color) { + if (index % 2 === 0) + return { + style: { + backgroundColor: "var(--table-row-even-bg)" + } + }; + return null; + } return { + className: "rowWithColor", style: { - backgroundColor: "var(--table-row-even-bg)" + "--bgColor": color.color + ? `rgba(${color.color.r},${color.color.g},${color.color.b},${color.color.a || 1})` + : "var(--status-row-bg-fallback)" } }; - return null; - } - return { - className: "rowWithColor", - style: { - "--bgColor": color.color - ? `rgba(${color.color.r},${color.color.g},${color.color.b},${color.color.a || 1})` - : "var(--status-row-bg-fallback)" } + })} + components={{ + header: { + cell: DraggableHeaderCell + } + }} + columns={columns.map((c) => { + return { + ...c, + filteredValue: state.filteredInfo[c.key] || null, + sortOrder: state.sortedInfo.columnKey === c.key && state.sortedInfo.order, + title: headerItem(c), + ellipsis: true, + width: c.width ?? 100, + onHeaderCell: (column) => ({ + columnKey: column.key, + width: column.width, + onResize: handleResize(column.key), + onResizeStart: handleResizeStart, + onResizeStop: handleResizeStop(column.key) + }) }; - } - })} - components={{ - header: { - cell: ResizeableTitle - } - }} - columns={columns.map((c, index) => { - return { - ...c, - filteredValue: state.filteredInfo[c.key] || null, - sortOrder: state.sortedInfo.columnKey === c.key && state.sortedInfo.order, - title: headerItem(c), - ellipsis: true, - width: c.width ?? 100, - onHeaderCell: (column) => ({ - width: column.width, - onResize: handleResize(index) - }) - }; - })} - rowKey="id" - loading={loading} - dataSource={dataSource} - scroll={{ x: 1000 }} - onChange={handleTableChange} - /> - + })} + rowKey="id" + loading={loading} + dataSource={dataSource} + scroll={{ x: scrollX }} + onChange={handleTableChange} + /> + + + + {activeId ? ( +
+ + + {(() => { + const col = columns.find((c) => c.key === activeId); + const title = typeof col?.title === "string" ? col.title : col?.dataIndex || col?.key || "Column"; + return title; + })()} + +
+ ) : null} +
+ ); } diff --git a/client/src/components/production-list-table/production-list-table.resizeable.component.jsx b/client/src/components/production-list-table/production-list-table.resizeable.component.jsx index bfb634228..15d11906c 100644 --- a/client/src/components/production-list-table/production-list-table.resizeable.component.jsx +++ b/client/src/components/production-list-table/production-list-table.resizeable.component.jsx @@ -1,28 +1,37 @@ +import { forwardRef } from "react"; import { Resizable } from "react-resizable"; +import "react-resizable/css/styles.css"; -export default function ResizableComponent(props) { - const { onResize, width, ...restProps } = props; +const ResizableComponent = forwardRef((props, ref) => { + const { onResize, onResizeStart, onResizeStop, width, dragAttributes, dragListeners, ...restProps } = props; if (!width) { - return
; + return ; } return ( ( { - e.stopPropagation(); - }} + ref={handleRef} + className={`react-resizable-handle react-resizable-handle-${axis}`} + onClick={(e) => e.stopPropagation()} /> - } + )} > - + ); -} +}); + +ResizableComponent.displayName = "ResizableComponent"; + +export default ResizableComponent; diff --git a/client/src/components/production-sublets-manage/production-sublets-manage.component.jsx b/client/src/components/production-sublets-manage/production-sublets-manage.component.jsx index 6de389017..ea35321e1 100644 --- a/client/src/components/production-sublets-manage/production-sublets-manage.component.jsx +++ b/client/src/components/production-sublets-manage/production-sublets-manage.component.jsx @@ -69,9 +69,8 @@ export default function ProductionSubletsManageComponent({ subletJobLines }) { handleSubletMark(s, "complete"); }} type={s.sublet_completed ? "primary" : "ghost"} - > - - , + icon={} + />, + icon={} + /> ]} > diff --git a/client/src/components/profile-shops/profile-shops.component.jsx b/client/src/components/profile-shops/profile-shops.component.jsx index d10f24e62..3bcb7dd29 100644 --- a/client/src/components/profile-shops/profile-shops.component.jsx +++ b/client/src/components/profile-shops/profile-shops.component.jsx @@ -44,6 +44,7 @@ export default function ProfileShopsComponent({ loading, data, updateActiveShop onChange={(e) => setSearch(e.target.value)} allowClear placeholder={t("general.labels.search")} + enterButton /> } > diff --git a/client/src/components/report-center-modal/report-center-modal.component.jsx b/client/src/components/report-center-modal/report-center-modal.component.jsx index b0789e732..7205939f2 100644 --- a/client/src/components/report-center-modal/report-center-modal.component.jsx +++ b/client/src/components/report-center-modal/report-center-modal.component.jsx @@ -145,8 +145,10 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) { return (
- setSearch(e.target.value)} value={search} /> -
); diff --git a/client/src/components/task-center/task-center.container.jsx b/client/src/components/task-center/task-center.container.jsx index 5dd04b340..2aa2b403a 100644 --- a/client/src/components/task-center/task-center.container.jsx +++ b/client/src/components/task-center/task-center.container.jsx @@ -3,6 +3,7 @@ import { useQuery } from "@apollo/client/react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; +import { selectDarkMode } from "../../redux/application/application.selectors.js"; import { INITIAL_TASKS, TASKS_CENTER_POLL_INTERVAL, useSocket } from "../../contexts/SocketIO/useSocket"; import { useIsEmployee } from "../../utils/useIsEmployee"; import TaskCenterComponent from "./task-center.component"; @@ -11,7 +12,8 @@ import { QUERY_TASKS_NO_DUE_DATE_PAGINATED, QUERY_TASKS_WITH_DUE_DATES } from ". const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, - currentUser: selectCurrentUser + currentUser: selectCurrentUser, + isDarkMode: selectDarkMode }); const mapDispatchToProps = (dispatch) => ({ @@ -24,7 +26,8 @@ const TaskCenterContainer = ({ bodyshop, currentUser, setTaskUpsertContext, - incompleteTaskCount + incompleteTaskCount, + isDarkMode }) => { const [tasks, setTasks] = useState([]); const { isConnected } = useSocket(); @@ -128,6 +131,7 @@ const TaskCenterContainer = ({ hasMore={hasMore} createNewTask={createNewTask} incompleteTaskCount={incompleteTaskCount} + isDarkMode={isDarkMode} /> ); }; diff --git a/client/src/components/task-center/task-center.styles.scss b/client/src/components/task-center/task-center.styles.scss index 411158e54..aaa622d74 100644 --- a/client/src/components/task-center/task-center.styles.scss +++ b/client/src/components/task-center/task-center.styles.scss @@ -141,3 +141,11 @@ text-align: center; } } + +.task-center--dark { + color-scheme: dark; +} + +.task-center--light { + color-scheme: light; +} diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx index 1bb350ca4..7aa0447e7 100644 --- a/client/src/components/task-list/task-list.component.jsx +++ b/client/src/components/task-list/task-list.component.jsx @@ -334,11 +334,9 @@ function TaskListComponent({ checked={deleted === "true"} onChange={(value) => handleSwitchChange("deleted", value)} /> - - diff --git a/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx b/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx index 529a5ffcb..9610728a3 100644 --- a/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx +++ b/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx @@ -59,7 +59,8 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) { const handleDrawerClose = () => { // Immutable omit (no delete/mutation) - const { ...rest } = searchParams || {}; + const { selected, ...rest } = searchParams || {}; + void selected; history({ search: queryString.stringify(rest) }); @@ -72,10 +73,10 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) { {data ? ( window.history.back()} title={data.jobs_by_pk.ro_number || t("general.labels.na")} extra={ } diff --git a/client/src/components/tech-lookup-jobs-list/tech-lookup-jobs-list.component.jsx b/client/src/components/tech-lookup-jobs-list/tech-lookup-jobs-list.component.jsx index b4c4785b5..ab5f73bed 100644 --- a/client/src/components/tech-lookup-jobs-list/tech-lookup-jobs-list.component.jsx +++ b/client/src/components/tech-lookup-jobs-list/tech-lookup-jobs-list.component.jsx @@ -158,9 +158,7 @@ export function TechLookupJobsList({ bodyshop }) { - + @@ -112,11 +123,8 @@ export default function TimeTicketCalculatorComponent({ placement="right" destroyOnHidden > - ); diff --git a/client/src/components/time-ticket-list/time-ticket-list.component.jsx b/client/src/components/time-ticket-list/time-ticket-list.component.jsx index d166eeb68..43c222eee 100644 --- a/client/src/components/time-ticket-list/time-ticket-list.component.jsx +++ b/client/src/components/time-ticket-list/time-ticket-list.component.jsx @@ -322,9 +322,8 @@ export function TimeTicketList({ onClick={async () => { refetch(); }} - > - - + icon={} + /> } > diff --git a/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx b/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx index a8d5a3384..35926e16c 100644 --- a/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx +++ b/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx @@ -184,8 +184,8 @@ export function TimeTicketModalComponent({ }} -