Updated packages. Changed date picker package to Day.JS and applied it across whole app.
This commit is contained in:
@@ -96,7 +96,9 @@
|
||||
"mcp__playwright__browser_close",
|
||||
"Bash(wc:*)",
|
||||
"mcp__brave-search__brave_web_search",
|
||||
"mcp__firecrawl__firecrawl_search"
|
||||
"mcp__firecrawl__firecrawl_search",
|
||||
"Bash(ssh:*)",
|
||||
"Bash(git checkout:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
21
backend/eslint.config.js
Normal file
21
backend/eslint.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import js from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist', 'node_modules'] },
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['warn', {
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
ignoreRestSiblings: true,
|
||||
args: 'after-used'
|
||||
}],
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -13,26 +13,26 @@
|
||||
"migrate:all": "ts-node src/_system/migrations/run-all.ts",
|
||||
"migrate:feature": "ts-node src/_system/migrations/run-feature.ts",
|
||||
"schema:generate": "ts-node src/_system/schema/generate.ts",
|
||||
"lint": "eslint src --ext .ts"
|
||||
"lint": "eslint src"
|
||||
},
|
||||
"dependencies": {
|
||||
"pg": "^8.11.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"pg": "^8.13.1",
|
||||
"ioredis": "^5.4.2",
|
||||
"minio": "^7.1.3",
|
||||
"@fastify/multipart": "^8.1.0",
|
||||
"axios": "^1.6.2",
|
||||
"@fastify/multipart": "^9.0.1",
|
||||
"axios": "^1.7.9",
|
||||
"opossum": "^8.0.0",
|
||||
"winston": "^3.11.0",
|
||||
"zod": "^3.22.4",
|
||||
"winston": "^3.17.0",
|
||||
"zod": "^3.24.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"fastify": "^4.24.3",
|
||||
"@fastify/cors": "^9.0.1",
|
||||
"@fastify/helmet": "^11.1.1",
|
||||
"@fastify/jwt": "^8.0.0",
|
||||
"@fastify/type-provider-typebox": "^4.0.0",
|
||||
"@sinclair/typebox": "^0.31.28",
|
||||
"fastify-plugin": "^4.5.1",
|
||||
"@fastify/autoload": "^5.8.0",
|
||||
"fastify": "^5.2.0",
|
||||
"@fastify/cors": "^10.0.1",
|
||||
"@fastify/helmet": "^12.0.1",
|
||||
"@fastify/jwt": "^9.0.1",
|
||||
"@fastify/type-provider-typebox": "^5.0.0",
|
||||
"@sinclair/typebox": "^0.34.0",
|
||||
"fastify-plugin": "^5.0.1",
|
||||
"@fastify/autoload": "^6.0.1",
|
||||
"get-jwks": "^9.0.0",
|
||||
"file-type": "^16.5.4"
|
||||
},
|
||||
@@ -40,17 +40,17 @@
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript": "^5.7.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"nodemon": "^3.0.1",
|
||||
"nodemon": "^3.1.9",
|
||||
"jest": "^29.7.0",
|
||||
"@types/jest": "^29.5.10",
|
||||
"ts-jest": "^29.1.1",
|
||||
"supertest": "^6.3.3",
|
||||
"@types/supertest": "^2.0.16",
|
||||
"@types/opossum": "^8.0.0",
|
||||
"eslint": "^8.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
"@typescript-eslint/parser": "^6.12.0"
|
||||
"eslint": "^9.17.0",
|
||||
"@eslint/js": "^9.17.0",
|
||||
"typescript-eslint": "^8.18.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
* @ai-summary Fastify global error handling plugin
|
||||
* @ai-context Handles uncaught errors with structured logging
|
||||
*/
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { FastifyPluginAsync, FastifyError } from 'fastify';
|
||||
import fp from 'fastify-plugin';
|
||||
import { logger } from '../logging/logger';
|
||||
|
||||
const errorPlugin: FastifyPluginAsync = async (fastify) => {
|
||||
fastify.setErrorHandler((error, request, reply) => {
|
||||
fastify.setErrorHandler((error: FastifyError, request, reply) => {
|
||||
logger.error('Unhandled error', {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
|
||||
@@ -43,7 +43,10 @@ def load_pairs(snapshot_path: Path) -> List[sqlite3.Row]:
|
||||
if not snapshot_path.exists():
|
||||
raise FileNotFoundError(f"Snapshot not found: {snapshot_path}")
|
||||
|
||||
conn = sqlite3.connect(snapshot_path)
|
||||
# Open in immutable mode to prevent any write attempts on read-only filesystems
|
||||
absolute_path = snapshot_path.resolve()
|
||||
uri = f"file:{absolute_path}?immutable=1"
|
||||
conn = sqlite3.connect(uri, uri=True)
|
||||
conn.row_factory = sqlite3.Row
|
||||
try:
|
||||
cursor = conn.execute(
|
||||
|
||||
@@ -20,14 +20,16 @@ Your task is to create a plan that can be dispatched to a seprate set of AI agen
|
||||
|
||||
|
||||
*** ROLE ***
|
||||
You are a senior devops systems reliablity engineer specializing modern web applications. Expert in linux, docker compose and gitlab.
|
||||
Read README.md CLAUDE.md and AI-INDEX.md to understand this code repository in the context of this change.
|
||||
You are a senior web deveoper specializing in nodejs, typescript along with CSS and HTML.
|
||||
|
||||
|
||||
*** ACTION ***
|
||||
- You need to create a plan for the end user to implement to make this application deployable with GitLab runners.
|
||||
- You need to create a plan to upgrade the node packages to the latest versions that are compatible with each other. Some of the packages are very old and while we might not get to the very latest we need to get as close as possible for security reasons.
|
||||
- Use context7 mcp to find the latest versions and compatibilities.
|
||||
- Read README.md CLAUDE.md and AI-INDEX.md to understand this code repository in the context of this change.
|
||||
|
||||
*** CONTEXT ***
|
||||
- This is a docker compose app that is functioning in the local dev environment. It was developed with the plan to move to Kubernetes eventually but right now it's staying in docker compose. There is a secrets architecture that mirrors k8s that needs to be replicated in gitlab deployment into the docker compose environment. The gitlab version is 18.6.2 and is using the shell runtime on the gitlab runners.
|
||||
- This is a modern web app for managing a vehicle fleet. It has both a desktop and mobile versions of the site that both need to maintain feature parity. The UX will be different between mobile and desktop due to resolution differences but that's it.
|
||||
|
||||
*** EXECUTE ***
|
||||
Create a plan the user can execute to make this app deployable with gitlab. Use brave, context7 and firecrawl if needed. Make no assumptions if your data does not have version 18.6 of gitlab. Save the plan to @docs/CICD-DEPLOY.md. Ultrathink.
|
||||
Create a plan that can be tasked to sub agents to explore and find dependancies between all the packages. Make sure nothing breaks. Use context7, brave search and firecrawl MCP's extensively. Make no assumptions.
|
||||
@@ -2,25 +2,23 @@ import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
import tseslint from '@typescript-eslint/eslint-plugin';
|
||||
import parser from '@typescript-eslint/parser';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default [
|
||||
{ ignores: ['dist'] },
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist', 'node_modules'] },
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parser,
|
||||
},
|
||||
plugins: {
|
||||
'@typescript-eslint': tseslint,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
@@ -36,4 +34,4 @@ export default [
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
);
|
||||
@@ -12,47 +12,48 @@
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^6.28.1",
|
||||
"@auth0/auth0-react": "^2.2.3",
|
||||
"axios": "^1.6.2",
|
||||
"zustand": "^4.4.6",
|
||||
"@tanstack/react-query": "^5.8.4",
|
||||
"react-hook-form": "^7.48.2",
|
||||
"@hookform/resolvers": "^3.3.2",
|
||||
"zod": "^3.22.4",
|
||||
"date-fns": "^2.30.0",
|
||||
"axios": "^1.7.9",
|
||||
"zustand": "^4.5.6",
|
||||
"@tanstack/react-query": "^5.84.1",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"zod": "^3.24.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"clsx": "^2.0.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-slick": "^0.30.2",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"framer-motion": "^11.0.0",
|
||||
"@mui/material": "^5.15.0",
|
||||
"@mui/x-date-pickers": "^6.19.0",
|
||||
"@mui/x-data-grid": "^6.19.1",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@mui/icons-material": "^5.15.0"
|
||||
"framer-motion": "^11.15.0",
|
||||
"@mui/material": "^6.3.0",
|
||||
"@mui/x-date-pickers": "^7.23.0",
|
||||
"@mui/x-data-grid": "^7.23.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@mui/icons-material": "^6.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@types/react": "^19.0.2",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"@types/react-slick": "^0.23.13",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
"@typescript-eslint/parser": "^6.12.0",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"postcss": "^8.4.32",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"typescript-eslint": "^8.18.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.17.0",
|
||||
"@eslint/js": "^9.17.0",
|
||||
"globals": "^15.14.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"terser": "^5.24.0",
|
||||
"@emotion/babel-plugin": "^11.11.0",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^5.0.6",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^5.4.11",
|
||||
"jest": "^29.7.0",
|
||||
"@types/jest": "^29.5.10",
|
||||
"ts-jest": "^29.1.1",
|
||||
|
||||
@@ -15,12 +15,15 @@ import {
|
||||
Divider,
|
||||
} from '@mui/material';
|
||||
import { Close, ChevronLeft, ChevronRight } from '@mui/icons-material';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { useAuditLogStream } from '../hooks/useAuditLogStream';
|
||||
import { AdminSkeleton } from './AdminSkeleton';
|
||||
import { EmptyState } from './EmptyState';
|
||||
import { ErrorState } from './ErrorState';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
/**
|
||||
* Props for AuditLogDrawer component
|
||||
*/
|
||||
@@ -154,9 +157,7 @@ export const AuditLogDrawer: React.FC<AuditLogDrawerProps> = ({
|
||||
component="span"
|
||||
color="text.secondary"
|
||||
>
|
||||
{formatDistanceToNow(new Date(log.createdAt), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
{dayjs(log.createdAt).fromNow()}
|
||||
</Typography>
|
||||
{log.resourceType && (
|
||||
<Typography
|
||||
@@ -193,10 +194,7 @@ export const AuditLogDrawer: React.FC<AuditLogDrawerProps> = ({
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Updated{' '}
|
||||
{formatDistanceToNow(lastUpdated, {
|
||||
addSuffix: true,
|
||||
})}
|
||||
Updated {dayjs(lastUpdated).fromNow()}
|
||||
</Typography>
|
||||
<Box>
|
||||
<IconButton
|
||||
|
||||
@@ -21,12 +21,15 @@ import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
} from '@mui/icons-material';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { useAuditLogStream } from '../hooks/useAuditLogStream';
|
||||
import { AdminSkeleton } from './AdminSkeleton';
|
||||
import { EmptyState } from './EmptyState';
|
||||
import { ErrorState } from './ErrorState';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
/**
|
||||
* Props for AuditLogPanel component
|
||||
*/
|
||||
@@ -148,9 +151,7 @@ export const AuditLogPanel: React.FC<AuditLogPanelProps> = ({
|
||||
component="span"
|
||||
color="text.secondary"
|
||||
>
|
||||
{formatDistanceToNow(new Date(log.createdAt), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
{dayjs(log.createdAt).fromNow()}
|
||||
</Typography>
|
||||
{log.resourceType && (
|
||||
<Typography
|
||||
@@ -186,10 +187,7 @@ export const AuditLogPanel: React.FC<AuditLogPanelProps> = ({
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Updated{' '}
|
||||
{formatDistanceToNow(lastUpdated, {
|
||||
addSuffix: true,
|
||||
})}
|
||||
Updated {dayjs(lastUpdated).fromNow()}
|
||||
</Typography>
|
||||
<Box>
|
||||
<IconButton
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Button } from '../../../shared-minimal/components/Button';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||
import dayjs from 'dayjs';
|
||||
import { useCreateDocument } from '../hooks/useDocuments';
|
||||
import { documentsApi } from '../api/documents.api';
|
||||
import type { DocumentType } from '../types/documents.types';
|
||||
@@ -144,6 +148,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
|
||||
};
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<form onSubmit={handleSubmit} className="w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="flex flex-col">
|
||||
@@ -207,21 +212,39 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<label className="text-sm font-medium text-slate-700 mb-1">Effective Date</label>
|
||||
<input
|
||||
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
type="date"
|
||||
value={effectiveDate}
|
||||
onChange={(e) => setEffectiveDate(e.target.value)}
|
||||
<DatePicker
|
||||
label="Effective Date"
|
||||
value={effectiveDate ? dayjs(effectiveDate) : null}
|
||||
onChange={(newValue) => setEffectiveDate(newValue?.format('YYYY-MM-DD') || '')}
|
||||
format="MM/DD/YYYY"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
sx: {
|
||||
'& .MuiOutlinedInput-root': {
|
||||
minHeight: 44,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<label className="text-sm font-medium text-slate-700 mb-1">Expiration Date</label>
|
||||
<input
|
||||
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
type="date"
|
||||
value={expirationDate}
|
||||
onChange={(e) => setExpirationDate(e.target.value)}
|
||||
<DatePicker
|
||||
label="Expiration Date"
|
||||
value={expirationDate ? dayjs(expirationDate) : null}
|
||||
onChange={(newValue) => setExpirationDate(newValue?.format('YYYY-MM-DD') || '')}
|
||||
format="MM/DD/YYYY"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
sx: {
|
||||
'& .MuiOutlinedInput-root': {
|
||||
minHeight: 44,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -282,12 +305,21 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<label className="text-sm font-medium text-slate-700 mb-1">Expiration Date</label>
|
||||
<input
|
||||
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
type="date"
|
||||
value={registrationExpirationDate}
|
||||
onChange={(e) => setRegistrationExpirationDate(e.target.value)}
|
||||
<DatePicker
|
||||
label="Expiration Date"
|
||||
value={registrationExpirationDate ? dayjs(registrationExpirationDate) : null}
|
||||
onChange={(newValue) => setRegistrationExpirationDate(newValue?.format('YYYY-MM-DD') || '')}
|
||||
format="MM/DD/YYYY"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
sx: {
|
||||
'& .MuiOutlinedInput-root': {
|
||||
minHeight: 44,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col md:col-span-2">
|
||||
@@ -336,6 +368,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
|
||||
<Button type="button" variant="secondary" onClick={onCancel} className="min-h-[44px]">Cancel</Button>
|
||||
</div>
|
||||
</form>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ import {
|
||||
useMediaQuery
|
||||
} from '@mui/material';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
|
||||
import dayjs from 'dayjs';
|
||||
import { FuelLogResponse, UpdateFuelLogRequest, FuelType } from '../types/fuel-logs.types';
|
||||
import { useFuelGrades } from '../hooks/useFuelGrades';
|
||||
|
||||
@@ -135,7 +136,7 @@ export const FuelLogEditDialog: React.FC<FuelLogEditDialogProps> = ({
|
||||
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleCancel}
|
||||
@@ -154,9 +155,9 @@ export const FuelLogEditDialog: React.FC<FuelLogEditDialogProps> = ({
|
||||
<Grid item xs={12}>
|
||||
<DateTimePicker
|
||||
label="Date & Time"
|
||||
value={formData.dateTime ? new Date(formData.dateTime) : null}
|
||||
value={formData.dateTime ? dayjs(formData.dateTime) : null}
|
||||
onChange={(newValue) => handleInputChange('dateTime', newValue?.toISOString() || '')}
|
||||
format="MM/dd/yyyy hh:mm a"
|
||||
format="MM/DD/YYYY hh:mm a"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
|
||||
@@ -4,8 +4,9 @@ import { z } from 'zod';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Grid, Card, CardHeader, CardContent, TextField, Box, Button, CircularProgress, ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
|
||||
import dayjs from 'dayjs';
|
||||
import { VehicleSelector } from './VehicleSelector';
|
||||
import { DistanceInput } from './DistanceInput';
|
||||
import { FuelTypeSelector } from './FuelTypeSelector';
|
||||
@@ -143,7 +144,7 @@ const FuelLogFormComponent: React.FC<{ onSuccess?: () => void; initial?: Partial
|
||||
}, [useOdometer, setValue]);
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Card>
|
||||
<CardHeader title="Add Fuel Log" subheader={<UnitSystemDisplay unitSystem={userSettings?.unitSystem} showLabel="Displaying in" />} />
|
||||
<CardContent>
|
||||
@@ -161,9 +162,9 @@ const FuelLogFormComponent: React.FC<{ onSuccess?: () => void; initial?: Partial
|
||||
<Controller name="dateTime" control={control} render={({ field }) => (
|
||||
<DateTimePicker
|
||||
label="Date & Time"
|
||||
value={field.value ? new Date(field.value) : null}
|
||||
value={field.value ? dayjs(field.value) : null}
|
||||
onChange={(newValue) => field.onChange(newValue?.toISOString() || '')}
|
||||
format="MM/dd/yyyy hh:mm a"
|
||||
format="MM/DD/YYYY hh:mm a"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
|
||||
@@ -21,8 +21,9 @@ import {
|
||||
useMediaQuery,
|
||||
} from '@mui/material';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
MaintenanceRecordResponse,
|
||||
UpdateMaintenanceRecordRequest,
|
||||
@@ -149,7 +150,7 @@ export const MaintenanceRecordEditDialog: React.FC<MaintenanceRecordEditDialogPr
|
||||
}
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleCancel}
|
||||
@@ -221,11 +222,11 @@ export const MaintenanceRecordEditDialog: React.FC<MaintenanceRecordEditDialogPr
|
||||
<Grid item xs={12} sm={6}>
|
||||
<DatePicker
|
||||
label="Service Date *"
|
||||
value={formData.date ? new Date(formData.date) : null}
|
||||
value={formData.date ? dayjs(formData.date) : null}
|
||||
onChange={(newValue) =>
|
||||
handleInputChange('date', newValue?.toISOString().split('T')[0] || '')
|
||||
}
|
||||
format="MM/dd/yyyy"
|
||||
format="MM/DD/YYYY"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
|
||||
@@ -25,8 +25,9 @@ import {
|
||||
InputAdornment,
|
||||
} from '@mui/material';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||
import dayjs from 'dayjs';
|
||||
import { useMaintenanceRecords } from '../hooks/useMaintenanceRecords';
|
||||
import { useVehicles } from '../../vehicles/hooks/useVehicles';
|
||||
import { SubtypeCheckboxGroup } from './SubtypeCheckboxGroup';
|
||||
@@ -135,7 +136,7 @@ export const MaintenanceRecordForm: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Card>
|
||||
<CardHeader title="Add Maintenance Record" />
|
||||
<CardContent>
|
||||
@@ -238,11 +239,11 @@ export const MaintenanceRecordForm: React.FC = () => {
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
label="Date *"
|
||||
value={field.value ? new Date(field.value) : null}
|
||||
value={field.value ? dayjs(field.value) : null}
|
||||
onChange={(newValue) =>
|
||||
field.onChange(newValue?.toISOString().split('T')[0] || '')
|
||||
}
|
||||
format="MM/dd/yyyy"
|
||||
format="MM/DD/YYYY"
|
||||
slotProps={{
|
||||
textField: {
|
||||
fullWidth: true,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Box, Typography, Button, Card, CardContent, Divider, FormControl, InputLabel, Select, MenuItem, List, ListItem } from '@mui/material';
|
||||
import { Box, Typography, Button, Card, CardContent, Divider, FormControl, InputLabel, Select, MenuItem, List, ListItem, ListItemButton } from '@mui/material';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { Vehicle } from '../types/vehicles.types';
|
||||
import { useFuelLogs } from '../../fuel-logs/hooks/useFuelLogs';
|
||||
@@ -250,22 +250,24 @@ export const VehicleDetailMobile: React.FC<VehicleDetailMobileProps> = ({
|
||||
) : (
|
||||
<List disablePadding>
|
||||
{filteredRecords.map(rec => (
|
||||
<ListItem key={rec.id} divider button onClick={() => openEditLog(rec.id, rec.type)}>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
{/* Primary line: MPG/km-L and amount */}
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="body1" color="text.primary">
|
||||
{rec.summary || 'MPG: —'}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.primary">
|
||||
{rec.amount || '—'}
|
||||
<ListItem key={rec.id} divider disablePadding>
|
||||
<ListItemButton onClick={() => openEditLog(rec.id, rec.type)}>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
{/* Primary line: MPG/km-L and amount */}
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="body1" color="text.primary">
|
||||
{rec.summary || 'MPG: —'}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.primary">
|
||||
{rec.amount || '—'}
|
||||
</Typography>
|
||||
</Box>
|
||||
{/* Secondary line: Grade • Date • Type */}
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.25 }}>
|
||||
{rec.secondary || `${new Date(rec.date).toLocaleDateString()} • ${rec.type}`}
|
||||
</Typography>
|
||||
</Box>
|
||||
{/* Secondary line: Grade • Date • Type */}
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.25 }}>
|
||||
{rec.secondary || `${new Date(rec.date).toLocaleDateString()} • ${rec.type}`}
|
||||
</Typography>
|
||||
</Box>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
|
||||
@@ -46,7 +46,7 @@ export default defineConfig({
|
||||
'forms': ['react-hook-form', '@hookform/resolvers', 'zod'],
|
||||
|
||||
// Utilities
|
||||
'utils': ['date-fns', 'clsx'],
|
||||
'utils': ['dayjs', 'clsx'],
|
||||
|
||||
// Animation and UI
|
||||
'animation': ['framer-motion', 'react-hot-toast']
|
||||
|
||||
Reference in New Issue
Block a user