initialisation on the doormile express console with astryx

This commit is contained in:
2026-07-29 18:16:25 +05:30
parent d5bde7d1ac
commit 7d672139b5
108 changed files with 12536 additions and 21351 deletions

View File

@@ -1,6 +1,6 @@
# CLAUDE.md — `src/components/nearle_components/`
Rules for editing the shared NearlExpress UI primitives.
Rules for editing the shared Doormile Express UI primitives.
These components are imported across every page. Changes here have **fan-out impact** — measure twice, cut once.
@@ -26,13 +26,13 @@ The `DT` design tokens (palette, alpha helpers, `pillFieldSx`, `SoftPaper`, `Acc
**Hard rules when editing components here:**
- **Universal brand colour is `#662582`** (NearlExpress purple — from `themes/theme/default.js` `primary.main`). Every brand surface (page header, dialog header, KPI primary tile, search bars, edit-action buttons, scrollbars) uses this. Gradient pair: `#662582 → #9255AB`.
- **Semantic status palette is distinct** from the brand. Use these for lifecycle indicators only: sky `#0ea5e9`, emerald `#10b981`, amber `#f59e0b`, red `#ef4444`, status-purple `#8b5cf6` (lighter than brand), cyan `#06b6d4`, teal `#14b8a6`, orange `#f97316`, muted `#94a3b8`, indigo `#6366f1` (Accepted status). Don't replace these with brand purple — operators colour-code on them.
- **Universal brand colour is `#C01227`** (Doormile Express red — from `themes/theme/default.js` `primary.main`). Every brand surface (page header, dialog header, KPI primary tile, search bars, edit-action buttons, scrollbars) uses this. Gradient pair: `#C01227 → #D25463`.
- **Semantic status palette is distinct** from the brand. Use these for lifecycle indicators only: sky `#0ea5e9`, emerald `#10b981`, amber `#f59e0b`, red `#ef4444`, status-purple `#8b5cf6`, cyan `#06b6d4`, teal `#14b8a6`, orange `#f97316`, muted `#94a3b8`, indigo `#6366f1` (Accepted status). Don't replace these with brand red — operators colour-code on them.
- Don't introduce a colour from `theme.palette` for new surfaces — those are Mantis defaults and don't match the DT system. Use the hex values above directly.
- Border radii: `12` (inner), `16` (card), `999` (pill). No other values.
- Shadows: `DT.shadowSoft` / `DT.shadowMd` / `DT.shadowPop`. No raw `box-shadow` strings.
> Some existing redesigned pages (`deliveries.js`, `Tenants.js`, `clientPricing.js`) still use `#6366f1` as the brand accent — this is a legacy from the first design pass. `customers.js` and the `createorder1` Saved-Address dialog are already on `#662582`. When you next edit one of the legacy pages, migrate it to brand purple in the same PR.
> Some existing redesigned pages (`deliveries.js`) still use `#6366f1` as the brand accent — this is a legacy from the first design pass. `customers.js` and the `createorder1` Saved-Address dialog are already on `#C01227`. When you next edit one of the legacy pages, migrate it to brand red in the same PR.
---

View File

@@ -0,0 +1,90 @@
import { useState } from 'react';
import { Box, Button, Divider, Stack, Typography } from '@mui/material';
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 { DT, soft, edge } from 'themes/dt/tokens';
// Drop-in replacement for the unmaintained `mui-daterange-picker` package
// (pinned to React 17, never updated — see the React 19 upgrade plan).
// Matches its public shape: `onChange({ startDate, endDate, label })` fires
// on every selection (preset or custom), `definedRanges` renders the quick
// picks. `open`/`toggle` are accepted for API compatibility but unused —
// visibility is already gated by the parent Dialog.
export const DateRangePicker = ({ onChange, definedRanges = [] }) => {
const [customStart, setCustomStart] = useState(null);
const [customEnd, setCustomEnd] = useState(null);
const pickPreset = (range) => {
onChange({ startDate: range.startDate, endDate: range.endDate, label: range.label });
};
const pickCustom = (nextStart, nextEnd) => {
if (nextStart && nextEnd) {
onChange({ startDate: nextStart.toDate(), endDate: nextEnd.toDate(), label: undefined });
}
};
return (
<Stack spacing={2.5} sx={{ width: '100%', pb: 1 }}>
<Stack direction="row" flexWrap="wrap" gap={1}>
{definedRanges.map((range) => (
<Button
key={range.label}
onClick={() => pickPreset(range)}
sx={{
borderRadius: DT.radiusPill + 'px',
px: 2,
py: 0.75,
fontWeight: 700,
fontSize: 13,
textTransform: 'none',
color: DT.textPrimary,
bgcolor: soft('#f59e0b'),
border: '1px solid',
borderColor: edge('#f59e0b'),
'&:hover': { bgcolor: soft('#f59e0b'), borderColor: '#f59e0b' }
}}
>
{range.label}
</Button>
))}
</Stack>
<Divider sx={{ borderColor: DT.divider }} />
<Box>
<Typography variant="caption" sx={{ color: DT.textSecondary, fontWeight: 700, textTransform: 'uppercase' }}>
Custom range
</Typography>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} sx={{ mt: 1 }}>
<DatePicker
label="Start date"
value={customStart}
onChange={(v) => {
setCustomStart(v);
pickCustom(v, customEnd);
}}
format="DD-MM-YYYY"
slotProps={{ textField: { size: 'small', fullWidth: true } }}
/>
<DatePicker
label="End date"
value={customEnd}
minDate={customStart || undefined}
onChange={(v) => {
setCustomEnd(v);
pickCustom(customStart, v);
}}
format="DD-MM-YYYY"
slotProps={{ textField: { size: 'small', fullWidth: true } }}
/>
</Stack>
</LocalizationProvider>
</Box>
</Stack>
);
};
export default DateRangePicker;

View File

@@ -1,7 +1,7 @@
// LoaderWithImage.jsx
import React from 'react';
import { Box, CircularProgress } from '@mui/material';
import nelogo from '../../assets/images/logo-sm.png';
import nelogo from '../../assets/images/doormile-mark.png';
export default function LoaderWithImage({ size = 70, imgSize = 40, alt = 'loader' }) {
return (
@@ -25,7 +25,8 @@ export default function LoaderWithImage({ size = 70, imgSize = 40, alt = 'loader
width: '100%',
height: '100%',
borderRadius: '50%',
objectFit: 'contain'
objectFit: 'cover',
transform: 'scale(1.4)'
}}
/>
</Box>

View File

@@ -42,7 +42,7 @@ MobileCardList.propTypes = {
// Card shell — coloured accent rail on the left, a header slot (status badge /
// title / action buttons), then any field grid / collapse content as children.
export const MobileCard = ({ accent = '#662582', header, footer, selected = false, onClick, children, sx }) => (
export const MobileCard = ({ accent = '#C01227', header, footer, selected = false, onClick, children, sx }) => (
<Paper
elevation={0}
onClick={onClick}

View File

@@ -10,7 +10,7 @@ import { Avatar, Box, Card, CardContent, Skeleton, Stack, Typography } from '@mu
// `color` is a hex accent (defaults to brand purple); `caption` is the small
// muted line under the value (e.g. "96% of total").
export default function StatCard({ title, value, icon, color = '#662582', caption, loading = false }) {
export default function StatCard({ title, value, icon, color = '#C01227', caption, loading = false }) {
return (
<Card sx={{ height: '100%' }}>
<CardContent sx={{ p: { xs: 1.75, md: 2 }, '&:last-child': { pb: { xs: 1.75, md: 2 } } }}>