mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 17:45:42 +00:00
style: use the proper primary color
This commit is contained in:
@@ -68,7 +68,7 @@ export function ApprovalStepsCard({ booking, mutations }: ApprovalStepsCardProps
|
|||||||
icon={ShieldCheck}
|
icon={ShieldCheck}
|
||||||
title="Approval chain"
|
title="Approval chain"
|
||||||
extra={
|
extra={
|
||||||
<Badge color="green" variant="light" radius="sm">
|
<Badge color="edr-green" variant="light" radius="sm">
|
||||||
{steps.filter((s) => s.status === "APPROVED").length}/{steps.length}
|
{steps.filter((s) => s.status === "APPROVED").length}/{steps.length}
|
||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
@@ -144,11 +144,11 @@ function StepRow({
|
|||||||
const canApprove = canActOnApprovalStep(user, step, steps);
|
const canApprove = canActOnApprovalStep(user, step, steps);
|
||||||
const statusColor =
|
const statusColor =
|
||||||
step.status === "APPROVED"
|
step.status === "APPROVED"
|
||||||
? "green"
|
? "edr-green"
|
||||||
: step.status === "REJECTED"
|
: step.status === "REJECTED"
|
||||||
? "red"
|
? "red"
|
||||||
: isNext
|
: isNext
|
||||||
? "green"
|
? "edr-green"
|
||||||
: "gray";
|
: "gray";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -200,7 +200,7 @@ function StepRow({
|
|||||||
{canApprove && (
|
{canApprove && (
|
||||||
<Button
|
<Button
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
color="green"
|
color="edr-green"
|
||||||
leftSection={<Check size={14} />}
|
leftSection={<Check size={14} />}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
onClick={() => onApprove(step)}
|
onClick={() => onApprove(step)}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export function BookingActionsMenu({
|
|||||||
key={action.id}
|
key={action.id}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant={action.primary && !destructive ? "filled" : "default"}
|
variant={action.primary && !destructive ? "filled" : "default"}
|
||||||
color={destructive ? "red" : action.primary ? "green" : "gray"}
|
color={destructive ? "red" : action.primary ? "edr-green" : "gray"}
|
||||||
leftSection={<Icon size={16} />}
|
leftSection={<Icon size={16} />}
|
||||||
disabled={mutations.isPending}
|
disabled={mutations.isPending}
|
||||||
onClick={() => handleAction(action)}
|
onClick={() => handleAction(action)}
|
||||||
@@ -113,7 +113,7 @@ export function BookingActionsMenu({
|
|||||||
{variant === "table" && primary && (
|
{variant === "table" && primary && (
|
||||||
<Button
|
<Button
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
color="green"
|
color="edr-green"
|
||||||
visibleFrom="lg"
|
visibleFrom="lg"
|
||||||
leftSection={<primary.icon size={14} />}
|
leftSection={<primary.icon size={14} />}
|
||||||
disabled={mutations.isPending}
|
disabled={mutations.isPending}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function BookingConfirmDialog({
|
|||||||
const inputMissing =
|
const inputMissing =
|
||||||
(needsTextInput && !inputValue.trim()) || (needsFileInput && !selectedFile);
|
(needsTextInput && !inputValue.trim()) || (needsFileInput && !selectedFile);
|
||||||
const isDestructive = action.variant === "destructive";
|
const isDestructive = action.variant === "destructive";
|
||||||
const accent = isDestructive ? "red" : "green";
|
const accent = isDestructive ? "red" : "edr-green";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function BookingPricingSummary({ booking }: { booking: BookingDetail }) {
|
|||||||
<Text
|
<Text
|
||||||
size="xl"
|
size="xl"
|
||||||
fw={700}
|
fw={700}
|
||||||
c="green.9"
|
c="edr-green.9"
|
||||||
mt={4}
|
mt={4}
|
||||||
style={{ fontVariantNumeric: "tabular-nums", letterSpacing: "-0.5px" }}
|
style={{ fontVariantNumeric: "tabular-nums", letterSpacing: "-0.5px" }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function BookingRequestsHeader({
|
|||||||
return (
|
return (
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<Group justify="flex-end" gap="sm">
|
<Group justify="flex-end" gap="sm">
|
||||||
<Button color="green" radius="lg" leftSection={<Plus size={18} />} onClick={onCreate}>
|
<Button color="edr-green" radius="lg" leftSection={<Plus size={18} />} onClick={onCreate}>
|
||||||
Create booking
|
Create booking
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const statusColorMap: Record<string, string> = {
|
|||||||
CHANGES_REQUESTED: "orange",
|
CHANGES_REQUESTED: "orange",
|
||||||
PENDING_APPROVAL: "yellow",
|
PENDING_APPROVAL: "yellow",
|
||||||
APPROVED_PENDING_SIGNATURE: "cyan",
|
APPROVED_PENDING_SIGNATURE: "cyan",
|
||||||
APPROVED: "green",
|
APPROVED: "edr-green",
|
||||||
CONTRACT_READY: "indigo",
|
CONTRACT_READY: "indigo",
|
||||||
SIGNED_CUSTOMER: "cyan",
|
SIGNED_CUSTOMER: "cyan",
|
||||||
FULLY_EXECUTED: "indigo",
|
FULLY_EXECUTED: "indigo",
|
||||||
@@ -16,7 +16,7 @@ const statusColorMap: Record<string, string> = {
|
|||||||
PAYMENT_VERIFICATION_IN_PROGRESS: "yellow",
|
PAYMENT_VERIFICATION_IN_PROGRESS: "yellow",
|
||||||
SELECTED_FOR_BATCH: "orange",
|
SELECTED_FOR_BATCH: "orange",
|
||||||
EXPIRED: "red",
|
EXPIRED: "red",
|
||||||
PAID: "green",
|
PAID: "edr-green",
|
||||||
IN_TRANSIT: "cyan",
|
IN_TRANSIT: "cyan",
|
||||||
COMPLETED: "indigo",
|
COMPLETED: "indigo",
|
||||||
REJECTED: "red",
|
REJECTED: "red",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function BookingStatusTabs({
|
|||||||
value={active}
|
value={active}
|
||||||
onChange={(value) => onChange((value as BookingStatusTabKey) ?? "all")}
|
onChange={(value) => onChange((value as BookingStatusTabKey) ?? "all")}
|
||||||
variant="pills"
|
variant="pills"
|
||||||
color="green"
|
color="edr-green"
|
||||||
keepMounted={false}
|
keepMounted={false}
|
||||||
classNames={{ list: "ov-tablist", tab: "ov-tab" }}
|
classNames={{ list: "ov-tablist", tab: "ov-tab" }}
|
||||||
>
|
>
|
||||||
@@ -62,7 +62,7 @@ export function BookingStatusTabs({
|
|||||||
size="sm"
|
size="sm"
|
||||||
radius="sm"
|
radius="sm"
|
||||||
variant={isActive ? "white" : "light"}
|
variant={isActive ? "white" : "light"}
|
||||||
color={isActive ? "green" : "gray"}
|
color={isActive ? "edr-green" : "gray"}
|
||||||
styles={
|
styles={
|
||||||
isActive
|
isActive
|
||||||
? { root: { background: "rgba(255,255,255,0.9)", color: "#15805f" } }
|
? { root: { background: "rgba(255,255,255,0.9)", color: "#15805f" } }
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export function BookingWorkflowStepper({
|
|||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
fw={isActive ? 600 : 500}
|
fw={isActive ? 600 : 500}
|
||||||
c={isActive ? "green.7" : isComplete ? "dark" : "dimmed"}
|
c={isActive ? "edr-green.7" : isComplete ? "dark" : "dimmed"}
|
||||||
ta="center"
|
ta="center"
|
||||||
style={{ whiteSpace: "nowrap" }}
|
style={{ whiteSpace: "nowrap" }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ export function OperationsBookingQueue({
|
|||||||
<Badge variant="light">{selected.length} selected</Badge>
|
<Badge variant="light">{selected.length} selected</Badge>
|
||||||
<Button
|
<Button
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
color="green"
|
color="edr-green"
|
||||||
disabled={!selected.length}
|
disabled={!selected.length}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ export function BookingApprovalCard({ steps, approvedCount }: BookingApprovalCar
|
|||||||
<SectionCard
|
<SectionCard
|
||||||
icon={CheckCircle}
|
icon={CheckCircle}
|
||||||
title="Approval Workflow"
|
title="Approval Workflow"
|
||||||
accent="green"
|
accent="edr-green"
|
||||||
extra={
|
extra={
|
||||||
<Badge color="green" variant="light" radius="sm">
|
<Badge color="edr-green" variant="light" radius="sm">
|
||||||
{approvedCount} / {steps.length} approved
|
{approvedCount} / {steps.length} approved
|
||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Timeline active={approvedCount - 1} bulletSize={26} lineWidth={2} color="green">
|
<Timeline active={approvedCount - 1} bulletSize={26} lineWidth={2} color="edr-green">
|
||||||
{steps.map((step) => (
|
{steps.map((step) => (
|
||||||
<Timeline.Item
|
<Timeline.Item
|
||||||
key={step.id}
|
key={step.id}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function BookingDetailToolbar({
|
|||||||
<Button variant="default" leftSection={<Download size={16} />} onClick={onExport}>
|
<Button variant="default" leftSection={<Download size={16} />} onClick={onExport}>
|
||||||
Export
|
Export
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="green" leftSection={<CheckCircle size={16} />} onClick={onAction}>
|
<Button color="edr-green" leftSection={<CheckCircle size={16} />} onClick={onAction}>
|
||||||
Take Action
|
Take Action
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export function BookingLifecycleStepper({ status }: BookingLifecycleStepperProps
|
|||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
fw={isActive ? 600 : 500}
|
fw={isActive ? 600 : 500}
|
||||||
c={isActive ? "green.7" : isComplete ? "dark" : "dimmed"}
|
c={isActive ? "edr-green.7" : isComplete ? "dark" : "dimmed"}
|
||||||
ta="center"
|
ta="center"
|
||||||
style={{ whiteSpace: "nowrap" }}
|
style={{ whiteSpace: "nowrap" }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function BookingPaymentCard({
|
|||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Badge
|
<Badge
|
||||||
color={paymentStatus === "PAID" ? "green" : "yellow"}
|
color={paymentStatus === "PAID" ? "edr-green" : "yellow"}
|
||||||
variant="light"
|
variant="light"
|
||||||
radius="sm"
|
radius="sm"
|
||||||
mt="xs"
|
mt="xs"
|
||||||
|
|||||||
@@ -49,13 +49,7 @@ export function BookingRequestHero({
|
|||||||
<Paper
|
<Paper
|
||||||
radius="xl"
|
radius="xl"
|
||||||
p="xl"
|
p="xl"
|
||||||
style={{
|
style={{ position: "relative", overflow: "hidden" }}
|
||||||
position: "relative",
|
|
||||||
overflow: "hidden",
|
|
||||||
background: "#ffffff",
|
|
||||||
border: "1px solid var(--mantine-color-gray-2)",
|
|
||||||
boxShadow: "0 1px 3px rgba(15,23,42,0.04)",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Stack gap="lg" style={{ position: "relative" }}>
|
<Stack gap="lg" style={{ position: "relative" }}>
|
||||||
<Group justify="space-between" align="flex-start" wrap="wrap">
|
<Group justify="space-between" align="flex-start" wrap="wrap">
|
||||||
@@ -70,7 +64,7 @@ export function BookingRequestHero({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
leftSection={<RefreshCw size={15} />}
|
leftSection={<RefreshCw size={15} />}
|
||||||
@@ -87,7 +81,7 @@ export function BookingRequestHero({
|
|||||||
Booking reference
|
Booking reference
|
||||||
</Text>
|
</Text>
|
||||||
<Group gap="sm" align="center" wrap="wrap">
|
<Group gap="sm" align="center" wrap="wrap">
|
||||||
<Title order={2} fw={700} style={{ letterSpacing: "-0.4px", color: "#0f172a" }}>
|
<Title order={2} fw={700} style={{ letterSpacing: "-0.4px" }}>
|
||||||
{booking.reference}
|
{booking.reference}
|
||||||
</Title>
|
</Title>
|
||||||
<BookingStatusBadge status={booking.status} />
|
<BookingStatusBadge status={booking.status} />
|
||||||
@@ -130,7 +124,7 @@ export function BookingRequestHero({
|
|||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
})}`}
|
})}`}
|
||||||
hint={booking.paymentStatus}
|
hint={booking.paymentStatus}
|
||||||
accent="green"
|
accent="edr-green"
|
||||||
/>
|
/>
|
||||||
<HeroTile icon={Weight} label="Cargo weight" value={`${weight} T`} hint="VGM total" accent="blue" />
|
<HeroTile icon={Weight} label="Cargo weight" value={`${weight} T`} hint="VGM total" accent="blue" />
|
||||||
<HeroTile
|
<HeroTile
|
||||||
@@ -177,7 +171,7 @@ function HeroTile({
|
|||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
hint,
|
hint,
|
||||||
accent = "green",
|
accent = "edr-green",
|
||||||
}: {
|
}: {
|
||||||
icon: LucideIcon;
|
icon: LucideIcon;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -204,7 +198,7 @@ function HeroTile({
|
|||||||
<Text size="xs" fw={600} tt="uppercase" c="dimmed" style={{ letterSpacing: 0.4 }}>
|
<Text size="xs" fw={600} tt="uppercase" c="dimmed" style={{ letterSpacing: 0.4 }}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fw={700} size="lg" lh={1.1} style={{ whiteSpace: "nowrap", color: "#0f172a" }}>
|
<Text fw={700} size="lg" lh={1.1} style={{ whiteSpace: "nowrap" }}>
|
||||||
{value}
|
{value}
|
||||||
</Text>
|
</Text>
|
||||||
{hint ? (
|
{hint ? (
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function BookingReviewNotesCard({ notes }: BookingReviewNotesCardProps) {
|
|||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Stack gap={2} style={{ flex: 1 }}>
|
<Stack gap={2} style={{ flex: 1 }}>
|
||||||
<Group justify="space-between">
|
<Group justify="space-between">
|
||||||
<Badge color="green" variant="light" size="sm" radius="sm">
|
<Badge color="edr-green" variant="light" size="sm" radius="sm">
|
||||||
{note.type}
|
{note.type}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
|
|||||||
@@ -9,38 +9,29 @@ export interface SectionCardProps {
|
|||||||
title: string;
|
title: string;
|
||||||
/** Optional one-line context shown under the title. */
|
/** Optional one-line context shown under the title. */
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
/** Mantine palette key used to tint the icon chip + top accent (default green). */
|
/** Mantine palette key used to tint the icon chip (default brand green). */
|
||||||
accent?: string;
|
accent?: string;
|
||||||
extra?: ReactNode;
|
extra?: ReactNode;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Consistent card with a colored icon chip + accent stripe header used by every detail section. */
|
/** Consistent card with a colored icon chip header used by every detail section. */
|
||||||
export function SectionCard({
|
export function SectionCard({
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
accent = "green",
|
accent = "edr-green",
|
||||||
extra,
|
extra,
|
||||||
children,
|
children,
|
||||||
}: SectionCardProps) {
|
}: SectionCardProps) {
|
||||||
return (
|
return (
|
||||||
<Paper radius="md" withBorder style={{ ...detailStyles.card, overflow: "hidden" }}>
|
<Paper radius="md" withBorder style={{ ...detailStyles.card, overflow: "hidden" }}>
|
||||||
<Box
|
|
||||||
style={{
|
|
||||||
height: 3,
|
|
||||||
background: `linear-gradient(90deg, var(--mantine-color-${accent}-5) 0%, var(--mantine-color-${accent}-7) 100%)`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Group
|
<Group
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
px="xl"
|
px="xl"
|
||||||
py="md"
|
py="md"
|
||||||
wrap="nowrap"
|
wrap="nowrap"
|
||||||
style={{
|
style={detailStyles.cardHeader}
|
||||||
...detailStyles.cardHeader,
|
|
||||||
background: `linear-gradient(180deg, var(--mantine-color-${accent}-0) 0%, white 100%)`,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Group gap="sm" wrap="nowrap" style={{ minWidth: 0 }}>
|
<Group gap="sm" wrap="nowrap" style={{ minWidth: 0 }}>
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ const FleetCardGrid = ({
|
|||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
background: "var(--mantine-color-green-0)",
|
background: "var(--mantine-color-edr-green-0)",
|
||||||
color: "var(--mantine-color-green-7)",
|
color: "var(--mantine-color-edr-green-7)",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ const FleetFormDialog = ({
|
|||||||
<Button variant="default" onClick={() => onOpenChange(false)}>
|
<Button variant="default" onClick={() => onOpenChange(false)}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="green" loading={isSubmitting} onClick={handleSubmit}>
|
<Button color="edr-green" loading={isSubmitting} onClick={handleSubmit}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const FleetRecordActions = ({
|
|||||||
{showDetail ? (
|
{showDetail ? (
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
radius="md"
|
radius="md"
|
||||||
onClick={handleDetail}
|
onClick={handleDetail}
|
||||||
@@ -72,7 +72,7 @@ const FleetRecordActions = ({
|
|||||||
<Group gap={4} wrap="nowrap" justify="flex-end">
|
<Group gap={4} wrap="nowrap" justify="flex-end">
|
||||||
{showDetail ? (
|
{showDetail ? (
|
||||||
<Tooltip label="Manage wagons">
|
<Tooltip label="Manage wagons">
|
||||||
<ActionIcon variant="subtle" color="green" size="md" radius="md" onClick={handleDetail}>
|
<ActionIcon variant="subtle" color="edr-green" size="md" radius="md" onClick={handleDetail}>
|
||||||
<Truck size={16} />
|
<Truck size={16} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const FleetToolbar = ({
|
|||||||
/>
|
/>
|
||||||
{onAdd ? (
|
{onAdd ? (
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
size="sm"
|
size="sm"
|
||||||
fw={600}
|
fw={600}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function OverviewQuickLinks() {
|
|||||||
>
|
>
|
||||||
<Group justify="space-between" align="flex-start" wrap="nowrap">
|
<Group justify="space-between" align="flex-start" wrap="nowrap">
|
||||||
<Group align="flex-start" gap="sm" wrap="nowrap">
|
<Group align="flex-start" gap="sm" wrap="nowrap">
|
||||||
<ThemeIcon variant="light" color="green" size="lg" radius="md">
|
<ThemeIcon variant="light" color="edr-green" size="lg" radius="md">
|
||||||
<Icon size={18} />
|
<Icon size={18} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ const ManageRuleEngineOrderDialog = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={isLoading || isSaving}
|
disabled={isLoading || isSaving}
|
||||||
leftSection={
|
leftSection={
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ const RuleEngineFormDialog = ({
|
|||||||
checked={Boolean(values[field.name])}
|
checked={Boolean(values[field.name])}
|
||||||
onChange={(e) => setField(field.name, e.currentTarget.checked)}
|
onChange={(e) => setField(field.name, e.currentTarget.checked)}
|
||||||
size="md"
|
size="md"
|
||||||
color="green"
|
color="edr-green"
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
@@ -387,7 +387,7 @@ const RuleEngineFormDialog = ({
|
|||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
radius="md"
|
radius="md"
|
||||||
color="green"
|
color="edr-green"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
fw={600}
|
fw={600}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const RuleEngineToolbar = ({
|
|||||||
onChange={(value) => onViewModeChange(value as RuleEngineViewMode)}
|
onChange={(value) => onViewModeChange(value as RuleEngineViewMode)}
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
color="green"
|
color="edr-green"
|
||||||
data={[
|
data={[
|
||||||
{
|
{
|
||||||
value: "table",
|
value: "table",
|
||||||
@@ -101,7 +101,7 @@ const RuleEngineToolbar = ({
|
|||||||
leftSection={<Plus size={18} />}
|
leftSection={<Plus size={18} />}
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
color="green"
|
color="edr-green"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
fw={600}
|
fw={600}
|
||||||
style={{ whiteSpace: "nowrap" }}
|
style={{ whiteSpace: "nowrap" }}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const formatCell = (value: unknown, format?: ColumnFormat): ReactNode =>
|
|||||||
const active = Boolean(value);
|
const active = Boolean(value);
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge
|
||||||
color={active ? "green" : "gray"}
|
color={active ? "edr-green" : "gray"}
|
||||||
variant={active ? "filled" : "light"}
|
variant={active ? "filled" : "light"}
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="md"
|
radius="md"
|
||||||
@@ -53,7 +53,7 @@ export const formatCell = (value: unknown, format?: ColumnFormat): ReactNode =>
|
|||||||
const status = String(value);
|
const status = String(value);
|
||||||
const color =
|
const color =
|
||||||
status === "LIVE"
|
status === "LIVE"
|
||||||
? "green"
|
? "edr-green"
|
||||||
: status === "DRAFT"
|
: status === "DRAFT"
|
||||||
? "yellow"
|
? "yellow"
|
||||||
: status === "PENDING_APPROVAL"
|
: status === "PENDING_APPROVAL"
|
||||||
|
|||||||
@@ -447,27 +447,27 @@ export function AllocateBookingWizard({
|
|||||||
if (key === "bookings") {
|
if (key === "bookings") {
|
||||||
if (previewResult) {
|
if (previewResult) {
|
||||||
return (
|
return (
|
||||||
<Badge variant="light" color={previewResult.valid ? "green" : "red"} radius="sm">
|
<Badge variant="light" color={previewResult.valid ? "edr-green" : "red"} radius="sm">
|
||||||
{previewResult.valid ? "Plan valid" : "Has issues"}
|
{previewResult.valid ? "Plan valid" : "Has issues"}
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return allBookingIds.length ? (
|
return allBookingIds.length ? (
|
||||||
<Badge variant="light" color="green" radius="sm">
|
<Badge variant="light" color="edr-green" radius="sm">
|
||||||
{allBookingIds.length} selected
|
{allBookingIds.length} selected
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
if (key === "wagon" && displayWagonPlan.length) {
|
if (key === "wagon" && displayWagonPlan.length) {
|
||||||
return (
|
return (
|
||||||
<Badge variant="light" color="green" radius="sm">
|
<Badge variant="light" color="edr-green" radius="sm">
|
||||||
{displayWagonPlan.length} wagons
|
{displayWagonPlan.length} wagons
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (key === "container" && containerUnits.length) {
|
if (key === "container" && containerUnits.length) {
|
||||||
return (
|
return (
|
||||||
<Badge variant="light" color={containerComplete ? "green" : "yellow"} radius="sm">
|
<Badge variant="light" color={containerComplete ? "edr-green" : "yellow"} radius="sm">
|
||||||
{containerUnits.length} units
|
{containerUnits.length} units
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
@@ -577,7 +577,7 @@ export function AllocateBookingWizard({
|
|||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
radius="md"
|
radius="md"
|
||||||
leftSection={<Eye size={16} />}
|
leftSection={<Eye size={16} />}
|
||||||
loading={preview.isPending}
|
loading={preview.isPending}
|
||||||
@@ -648,7 +648,7 @@ export function AllocateBookingWizard({
|
|||||||
<Group>
|
<Group>
|
||||||
{!hasContainerStep ? (
|
{!hasContainerStep ? (
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
radius="md"
|
radius="md"
|
||||||
loading={assign.isPending || create.isPending}
|
loading={assign.isPending || create.isPending}
|
||||||
onClick={handleAssign}
|
onClick={handleAssign}
|
||||||
@@ -657,7 +657,7 @@ export function AllocateBookingWizard({
|
|||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
radius="md"
|
radius="md"
|
||||||
rightSection={<ContainerIcon size={16} />}
|
rightSection={<ContainerIcon size={16} />}
|
||||||
onClick={() => setActiveStep(2)}
|
onClick={() => setActiveStep(2)}
|
||||||
@@ -692,7 +692,7 @@ export function AllocateBookingWizard({
|
|||||||
)}
|
)}
|
||||||
<Group>
|
<Group>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
radius="md"
|
radius="md"
|
||||||
loading={assign.isPending || create.isPending}
|
loading={assign.isPending || create.isPending}
|
||||||
onClick={handleAssign}
|
onClick={handleAssign}
|
||||||
@@ -732,7 +732,7 @@ export function AllocateBookingWizard({
|
|||||||
style={{ background: scheduleBrand.softSurface, borderColor: scheduleBrand.mutedBorder }}
|
style={{ background: scheduleBrand.softSurface, borderColor: scheduleBrand.mutedBorder }}
|
||||||
>
|
>
|
||||||
<Group gap="md" align="flex-start" wrap="nowrap">
|
<Group gap="md" align="flex-start" wrap="nowrap">
|
||||||
<ThemeIcon size={44} radius="md" variant="light" color="green">
|
<ThemeIcon size={44} radius="md" variant="light" color="edr-green">
|
||||||
<CheckCircle2 size={22} />
|
<CheckCircle2 size={22} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Stack gap={2} style={{ flex: 1 }}>
|
<Stack gap={2} style={{ flex: 1 }}>
|
||||||
@@ -741,14 +741,14 @@ export function AllocateBookingWizard({
|
|||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
Booking {booking.reference} is scheduled on train{" "}
|
Booking {booking.reference} is scheduled on train{" "}
|
||||||
<Text span fw={600} c="green.7">
|
<Text span fw={600} c="edr-green.7">
|
||||||
{assignedSchedule?.trainSet?.locomotive?.code ?? "—"}
|
{assignedSchedule?.trainSet?.locomotive?.code ?? "—"}
|
||||||
</Text>
|
</Text>
|
||||||
.
|
.
|
||||||
</Text>
|
</Text>
|
||||||
<Group mt="sm">
|
<Group mt="sm">
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
radius="md"
|
radius="md"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose();
|
onClose();
|
||||||
@@ -777,14 +777,14 @@ export function AllocateBookingWizard({
|
|||||||
style={{ background: scheduleBrand.softSurface, borderColor: scheduleBrand.mutedBorder }}
|
style={{ background: scheduleBrand.softSurface, borderColor: scheduleBrand.mutedBorder }}
|
||||||
>
|
>
|
||||||
<Group gap="md" align="flex-start" wrap="nowrap">
|
<Group gap="md" align="flex-start" wrap="nowrap">
|
||||||
<ThemeIcon size={44} radius="md" variant="light" color="green">
|
<ThemeIcon size={44} radius="md" variant="light" color="edr-green">
|
||||||
<CheckCircle2 size={22} />
|
<CheckCircle2 size={22} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
<Text fw={600}>Ready to finalize</Text>
|
<Text fw={600}>Ready to finalize</Text>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
Finalizing locks the plan, moves the schedule to{" "}
|
Finalizing locks the plan, moves the schedule to{" "}
|
||||||
<Text span fw={600} c="green.7">
|
<Text span fw={600} c="edr-green.7">
|
||||||
SCHEDULED
|
SCHEDULED
|
||||||
</Text>
|
</Text>
|
||||||
, and completes the booking allocation.
|
, and completes the booking allocation.
|
||||||
@@ -794,7 +794,7 @@ export function AllocateBookingWizard({
|
|||||||
</Paper>
|
</Paper>
|
||||||
<Group>
|
<Group>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
size="md"
|
size="md"
|
||||||
radius="md"
|
radius="md"
|
||||||
leftSection={<CheckCircle2 size={18} />}
|
leftSection={<CheckCircle2 size={18} />}
|
||||||
@@ -852,7 +852,7 @@ export function AllocateBookingWizard({
|
|||||||
size={56}
|
size={56}
|
||||||
radius="lg"
|
radius="lg"
|
||||||
variant="white"
|
variant="white"
|
||||||
style={{ color: "var(--mantine-color-green-7)" }}
|
style={{ color: "var(--mantine-color-edr-green-7)" }}
|
||||||
>
|
>
|
||||||
<Train size={28} />
|
<Train size={28} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
@@ -884,7 +884,7 @@ export function AllocateBookingWizard({
|
|||||||
size="lg"
|
size="lg"
|
||||||
radius="sm"
|
radius="sm"
|
||||||
variant="white"
|
variant="white"
|
||||||
c={previewResult.valid ? "green.8" : "red.7"}
|
c={previewResult.valid ? "edr-green.8" : "red.7"}
|
||||||
leftSection={
|
leftSection={
|
||||||
<Box
|
<Box
|
||||||
w={8}
|
w={8}
|
||||||
@@ -892,7 +892,7 @@ export function AllocateBookingWizard({
|
|||||||
style={{
|
style={{
|
||||||
borderRadius: 999,
|
borderRadius: 999,
|
||||||
background: previewResult.valid
|
background: previewResult.valid
|
||||||
? "var(--mantine-color-green-6)"
|
? "var(--mantine-color-edr-green-6)"
|
||||||
: "var(--mantine-color-red-6)",
|
: "var(--mantine-color-red-6)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -941,7 +941,7 @@ export function AllocateBookingWizard({
|
|||||||
size={44}
|
size={44}
|
||||||
radius="md"
|
radius="md"
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
gradient={{ from: "green", to: "teal", deg: 135 }}
|
gradient={{ from: "edr-green", to: "teal", deg: 135 }}
|
||||||
>
|
>
|
||||||
<RouteIcon size={22} />
|
<RouteIcon size={22} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
@@ -959,9 +959,9 @@ export function AllocateBookingWizard({
|
|||||||
size={64}
|
size={64}
|
||||||
thickness={6}
|
thickness={6}
|
||||||
roundCaps
|
roundCaps
|
||||||
sections={[{ value: progressPct, color: "green" }]}
|
sections={[{ value: progressPct, color: "edr-green" }]}
|
||||||
label={
|
label={
|
||||||
<Text ta="center" size="xs" fw={700} c="green.7">
|
<Text ta="center" size="xs" fw={700} c="edr-green.7">
|
||||||
{progressPct}%
|
{progressPct}%
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export function ContainerPlacementGrid({
|
|||||||
value={progress}
|
value={progress}
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
color={issues.length ? "yellow" : "green"}
|
color={issues.length ? "yellow" : "edr-green"}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
@@ -135,7 +135,7 @@ export function ContainerPlacementGrid({
|
|||||||
</Stack>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
<Badge
|
<Badge
|
||||||
color="green"
|
color="edr-green"
|
||||||
variant="light"
|
variant="light"
|
||||||
size="sm"
|
size="sm"
|
||||||
w="fit-content"
|
w="fit-content"
|
||||||
@@ -163,7 +163,7 @@ export function ContainerPlacementGrid({
|
|||||||
{unit.label} · {unit.containerTypeCode} · {unit.grossWeightTons}T
|
{unit.label} · {unit.containerTypeCode} · {unit.grossWeightTons}T
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Badge size="sm" variant="light" color={isComplete ? "green" : "gray"}>
|
<Badge size="sm" variant="light" color={isComplete ? "edr-green" : "gray"}>
|
||||||
{isComplete ? "Ready" : "Pending"}
|
{isComplete ? "Ready" : "Pending"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -37,14 +37,14 @@ function EligibleBookingRow({
|
|||||||
p="sm"
|
p="sm"
|
||||||
style={{
|
style={{
|
||||||
border: `1px solid ${
|
border: `1px solid ${
|
||||||
selected ? "var(--mantine-color-green-3)" : "var(--mantine-color-gray-2)"
|
selected ? "var(--mantine-color-edr-green-3)" : "var(--mantine-color-gray-2)"
|
||||||
}`,
|
}`,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
background: selected ? "var(--mantine-color-green-0)" : "white",
|
background: selected ? "var(--mantine-color-edr-green-0)" : "white",
|
||||||
transition: "border-color 120ms ease, background 120ms ease",
|
transition: "border-color 120ms ease, background 120ms ease",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Checkbox checked={selected} onChange={onToggle} mt={4} color="green" />
|
<Checkbox checked={selected} onChange={onToggle} mt={4} color="edr-green" />
|
||||||
<Stack gap={4} style={{ flex: 1 }}>
|
<Stack gap={4} style={{ flex: 1 }}>
|
||||||
<Group gap="xs" wrap="wrap">
|
<Group gap="xs" wrap="wrap">
|
||||||
<Package size={14} />
|
<Package size={14} />
|
||||||
@@ -205,7 +205,7 @@ export function EligibleBookingsPanel({
|
|||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Group gap="xs" onClick={(e) => e.stopPropagation()}>
|
<Group gap="xs" onClick={(e) => e.stopPropagation()}>
|
||||||
<Badge variant="light" color="green">
|
<Badge variant="light" color="edr-green">
|
||||||
{selectedInBucket.length} selected
|
{selectedInBucket.length} selected
|
||||||
</Badge>
|
</Badge>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function FleetAvailabilitySummary({
|
|||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
<Badge variant="light" color={totalShortfall > 0 ? "yellow" : "green"}>
|
<Badge variant="light" color={totalShortfall > 0 ? "yellow" : "edr-green"}>
|
||||||
{fillRate}% fleet coverage
|
{fillRate}% fleet coverage
|
||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -59,7 +59,7 @@ export function FleetAvailabilitySummary({
|
|||||||
value={fillRate}
|
value={fillRate}
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
color={totalShortfall > 0 ? "yellow" : "green"}
|
color={totalShortfall > 0 ? "yellow" : "edr-green"}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -86,7 +86,7 @@ export function FleetAvailabilitySummary({
|
|||||||
{row.shortfall}
|
{row.shortfall}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : (
|
) : (
|
||||||
<Text size="sm" c="green">
|
<Text size="sm" c="edr-green">
|
||||||
0
|
0
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ export function RouteCorridorTrack({
|
|||||||
fw={passed ? 700 : 600}
|
fw={passed ? 700 : 600}
|
||||||
ta="center"
|
ta="center"
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
c={passed ? "green.8" : "dimmed"}
|
c={passed ? "edr-green.8" : "dimmed"}
|
||||||
>
|
>
|
||||||
{station.label}
|
{station.label}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -172,7 +172,7 @@ export function RouteCorridorTrack({
|
|||||||
<Button
|
<Button
|
||||||
size="compact-xs"
|
size="compact-xs"
|
||||||
radius="md"
|
radius="md"
|
||||||
color={isFinal ? "teal" : "green"}
|
color={isFinal ? "teal" : "edr-green"}
|
||||||
variant={isFinal ? "filled" : "light"}
|
variant={isFinal ? "filled" : "light"}
|
||||||
loading={loggingSeq === station.sequenceNo}
|
loading={loggingSeq === station.sequenceNo}
|
||||||
onClick={() => onLogCheckpoint?.(station.sequenceNo)}
|
onClick={() => onLogCheckpoint?.(station.sequenceNo)}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ interface ScheduleBatchPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const windowColor: Record<string, string> = {
|
const windowColor: Record<string, string> = {
|
||||||
OPEN: "green",
|
OPEN: "edr-green",
|
||||||
FULL: "orange",
|
FULL: "orange",
|
||||||
CLOSED: "gray",
|
CLOSED: "gray",
|
||||||
};
|
};
|
||||||
@@ -68,7 +68,7 @@ export function ScheduleBatchPanel({ schedule }: ScheduleBatchPanelProps) {
|
|||||||
<Paper radius="lg" withBorder p="lg" style={{ borderColor: "var(--mantine-color-gray-2)" }}>
|
<Paper radius="lg" withBorder p="lg" style={{ borderColor: "var(--mantine-color-gray-2)" }}>
|
||||||
<Group justify="space-between" align="center" mb="md" wrap="wrap">
|
<Group justify="space-between" align="center" mb="md" wrap="wrap">
|
||||||
<Group gap="sm">
|
<Group gap="sm">
|
||||||
<ThemeIcon size={36} radius="md" variant="light" color="green">
|
<ThemeIcon size={36} radius="md" variant="light" color="edr-green">
|
||||||
<Layers size={18} />
|
<Layers size={18} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<div>
|
<div>
|
||||||
@@ -90,7 +90,7 @@ export function ScheduleBatchPanel({ schedule }: ScheduleBatchPanelProps) {
|
|||||||
<Button
|
<Button
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
leftSection={<PlayCircle size={15} />}
|
leftSection={<PlayCircle size={15} />}
|
||||||
loading={actions.runBatch.isPending}
|
loading={actions.runBatch.isPending}
|
||||||
onClick={() => run(actions.runBatch.mutateAsync(schedule.id), "Batch fill run")}
|
onClick={() => run(actions.runBatch.mutateAsync(schedule.id), "Batch fill run")}
|
||||||
@@ -169,7 +169,7 @@ export function ScheduleBatchPanel({ schedule }: ScheduleBatchPanelProps) {
|
|||||||
<Button
|
<Button
|
||||||
size="compact-xs"
|
size="compact-xs"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
leftSection={<CheckCircle2 size={13} />}
|
leftSection={<CheckCircle2 size={13} />}
|
||||||
onClick={() => run(actions.markPaid.mutateAsync(b.id), "Marked paid")}
|
onClick={() => run(actions.markPaid.mutateAsync(b.id), "Marked paid")}
|
||||||
>
|
>
|
||||||
@@ -228,7 +228,7 @@ export function ScheduleBatchPanel({ schedule }: ScheduleBatchPanelProps) {
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
disabled={!moveTarget}
|
disabled={!moveTarget}
|
||||||
loading={actions.moveSchedule.isPending}
|
loading={actions.moveSchedule.isPending}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function ScheduleBookingsStep({
|
|||||||
defaultValue={assignedBookings.length ? "on-train" : "add"}
|
defaultValue={assignedBookings.length ? "on-train" : "add"}
|
||||||
radius="md"
|
radius="md"
|
||||||
variant="pills"
|
variant="pills"
|
||||||
color="green"
|
color="edr-green"
|
||||||
>
|
>
|
||||||
<Tabs.List mb="md">
|
<Tabs.List mb="md">
|
||||||
<Tabs.Tab
|
<Tabs.Tab
|
||||||
@@ -54,7 +54,7 @@ export function ScheduleBookingsStep({
|
|||||||
leftSection={<Train size={14} />}
|
leftSection={<Train size={14} />}
|
||||||
rightSection={
|
rightSection={
|
||||||
assignedBookings.length ? (
|
assignedBookings.length ? (
|
||||||
<Badge size="xs" variant="light" color="green" circle>
|
<Badge size="xs" variant="light" color="edr-green" circle>
|
||||||
{assignedBookings.length}
|
{assignedBookings.length}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : undefined
|
) : undefined
|
||||||
@@ -76,9 +76,9 @@ export function ScheduleBookingsStep({
|
|||||||
justify="space-between"
|
justify="space-between"
|
||||||
p="sm"
|
p="sm"
|
||||||
style={{
|
style={{
|
||||||
border: "1px solid var(--mantine-color-green-2)",
|
border: "1px solid var(--mantine-color-edr-green-2)",
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
background: "var(--mantine-color-green-0)",
|
background: "var(--mantine-color-edr-green-0)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
@@ -87,7 +87,7 @@ export function ScheduleBookingsStep({
|
|||||||
{booking.reference}
|
{booking.reference}
|
||||||
</Text>
|
</Text>
|
||||||
{booking.weightTons != null ? (
|
{booking.weightTons != null ? (
|
||||||
<Badge variant="outline" size="xs" color="green">
|
<Badge variant="outline" size="xs" color="edr-green">
|
||||||
{booking.weightTons}T
|
{booking.weightTons}T
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -97,7 +97,7 @@ export function ScheduleBookingsStep({
|
|||||||
Assigned to this consist
|
Assigned to this consist
|
||||||
</Text>
|
</Text>
|
||||||
<ArrowRight size={12} />
|
<ArrowRight size={12} />
|
||||||
<Text size="xs" c="green.7" fw={500}>
|
<Text size="xs" c="edr-green.7" fw={500}>
|
||||||
Ready for wagon plan
|
Ready for wagon plan
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function PreviewSummary({
|
|||||||
{ label: "Train length", value: `${summary.totalLengthMeters}m` },
|
{ label: "Train length", value: `${summary.totalLengthMeters}m` },
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<Paper p="md" radius="xl" withBorder bg="green.0">
|
<Paper p="md" radius="xl" withBorder bg="edr-green.0">
|
||||||
<Text size="sm" fw={600} mb="sm">
|
<Text size="sm" fw={600} mb="sm">
|
||||||
Plan summary
|
Plan summary
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function SchedulingWorkflowHeader({
|
|||||||
>
|
>
|
||||||
<Group justify="space-between" align="flex-start" wrap="wrap" gap="md">
|
<Group justify="space-between" align="flex-start" wrap="wrap" gap="md">
|
||||||
<Group gap="md" align="flex-start">
|
<Group gap="md" align="flex-start">
|
||||||
<ThemeIcon size={42} radius="xl" variant="gradient" gradient={{ from: "green", to: "teal", deg: 135 }}>
|
<ThemeIcon size={42} radius="xl" variant="gradient" gradient={{ from: "edr-green", to: "teal", deg: 135 }}>
|
||||||
<Icon size={20} />
|
<Icon size={20} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
@@ -63,7 +63,7 @@ export function SchedulingWorkflowHeader({
|
|||||||
) : null}
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
<Badge size="lg" variant="light" color="green">
|
<Badge size="lg" variant="light" color="edr-green">
|
||||||
Step {activeStep + 1} of {totalSteps}
|
Step {activeStep + 1} of {totalSteps}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -83,7 +83,7 @@ export function SchedulingWorkflowHeader({
|
|||||||
{progress}%
|
{progress}%
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Progress value={progress} size="sm" radius="xl" color="green" />
|
<Progress value={progress} size="sm" radius="xl" color="edr-green" />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -618,18 +618,18 @@ export function TrainCompositionDiagram({
|
|||||||
p="sm"
|
p="sm"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
background: "linear-gradient(135deg, var(--mantine-color-green-0), #F2FBF7)",
|
background: "linear-gradient(135deg, var(--mantine-color-edr-green-0), #F2FBF7)",
|
||||||
border: "1px solid var(--mantine-color-green-1)",
|
border: "1px solid var(--mantine-color-edr-green-1)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Group justify="space-between" mb={6}>
|
<Group justify="space-between" mb={6}>
|
||||||
<Group gap={6} wrap="nowrap">
|
<Group gap={6} wrap="nowrap">
|
||||||
<Gauge size={14} color={freightBrand.primary} />
|
<Gauge size={14} color={freightBrand.primary} />
|
||||||
<Text size="xs" fw={700} c="green.8">
|
<Text size="xs" fw={700} c="edr-green.8">
|
||||||
Locomotive load · {stats.totalWeight}T of {locomotive?.maxPullWeightTons}T
|
Locomotive load · {stats.totalWeight}T of {locomotive?.maxPullWeightTons}T
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Text size="sm" fw={800} c={stats.pullUtil > 95 ? "red.7" : "green.7"}>
|
<Text size="sm" fw={800} c={stats.pullUtil > 95 ? "red.7" : "edr-green.7"}>
|
||||||
{stats.pullUtil}%
|
{stats.pullUtil}%
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -639,7 +639,7 @@ export function TrainCompositionDiagram({
|
|||||||
radius="xl"
|
radius="xl"
|
||||||
striped={stats.pullUtil > 95}
|
striped={stats.pullUtil > 95}
|
||||||
animated={stats.pullUtil > 95}
|
animated={stats.pullUtil > 95}
|
||||||
color={stats.pullUtil > 95 ? "red" : stats.pullUtil > 80 ? "yellow" : "green"}
|
color={stats.pullUtil > 95 ? "red" : stats.pullUtil > 80 ? "yellow" : "edr-green"}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export function WagonPlanGrid({
|
|||||||
value={utilization}
|
value={utilization}
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
color={utilization > 95 ? "red" : utilization > 80 ? "yellow" : "green"}
|
color={utilization > 95 ? "red" : utilization > 80 ? "yellow" : "edr-green"}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -123,13 +123,13 @@ export function WorkflowStep({
|
|||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
fw={700}
|
fw={700}
|
||||||
c={isComplete || isActive ? "green.7" : "dimmed"}
|
c={isComplete || isActive ? "edr-green.7" : "dimmed"}
|
||||||
style={{ letterSpacing: 0.6 }}
|
style={{ letterSpacing: 0.6 }}
|
||||||
>
|
>
|
||||||
STEP {index + 1}
|
STEP {index + 1}
|
||||||
</Text>
|
</Text>
|
||||||
{isComplete ? (
|
{isComplete ? (
|
||||||
<Badge size="xs" variant="light" color="green" radius="sm">
|
<Badge size="xs" variant="light" color="edr-green" radius="sm">
|
||||||
Done
|
Done
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -155,10 +155,10 @@ export function WorkflowStep({
|
|||||||
height: 28,
|
height: 28,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: open
|
background: open
|
||||||
? "var(--mantine-color-green-0)"
|
? "var(--mantine-color-edr-green-0)"
|
||||||
: "var(--mantine-color-gray-1)",
|
: "var(--mantine-color-gray-1)",
|
||||||
color: open
|
color: open
|
||||||
? "var(--mantine-color-green-7)"
|
? "var(--mantine-color-edr-green-7)"
|
||||||
: "var(--mantine-color-gray-6)",
|
: "var(--mantine-color-gray-6)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -209,7 +209,7 @@ export function WorkflowRail({ children }: { children: ReactNode }) {
|
|||||||
bottom: 24,
|
bottom: 24,
|
||||||
width: 2,
|
width: 2,
|
||||||
background:
|
background:
|
||||||
"linear-gradient(180deg, var(--mantine-color-green-3) 0%, var(--mantine-color-gray-3) 100%)",
|
"linear-gradient(180deg, var(--mantine-color-edr-green-3) 0%, var(--mantine-color-gray-3) 100%)",
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
pointerEvents: "none",
|
pointerEvents: "none",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const PIPELINE_STAGES: ReadonlyArray<{
|
|||||||
{
|
{
|
||||||
key: "allocated",
|
key: "allocated",
|
||||||
label: "Allocated",
|
label: "Allocated",
|
||||||
color: "green",
|
color: "edr-green",
|
||||||
hint: "Assigned to wagons on the train",
|
hint: "Assigned to wagons on the train",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -137,7 +137,7 @@ export function BookingPipeline({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const WINDOW_META: Record<string, { color: string; label: string; pulse: boolean }> = {
|
const WINDOW_META: Record<string, { color: string; label: string; pulse: boolean }> = {
|
||||||
OPEN: { color: "green", label: "Window open", pulse: true },
|
OPEN: { color: "edr-green", label: "Window open", pulse: true },
|
||||||
FULL: { color: "orange", label: "Full", pulse: false },
|
FULL: { color: "orange", label: "Full", pulse: false },
|
||||||
CLOSED: { color: "gray", label: "Closed", pulse: false },
|
CLOSED: { color: "gray", label: "Closed", pulse: false },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const AssignedBookingsPanel = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Group gap={8} wrap="nowrap" align="flex-start">
|
<Group gap={8} wrap="nowrap" align="flex-start">
|
||||||
<ThemeIcon size={30} radius="md" variant="light" color="green">
|
<ThemeIcon size={30} radius="md" variant="light" color="edr-green">
|
||||||
<Package size={16} />
|
<Package size={16} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Box style={{ flex: 1, minWidth: 0 }}>
|
<Box style={{ flex: 1, minWidth: 0 }}>
|
||||||
@@ -107,7 +107,7 @@ export const AssignedBookingsPanel = ({
|
|||||||
<Badge
|
<Badge
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
leftSection={<TrainFront size={9} />}
|
leftSection={<TrainFront size={9} />}
|
||||||
>
|
>
|
||||||
{wagonCountByBooking.get(booking.id)}
|
{wagonCountByBooking.get(booking.id)}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function InfoRow({
|
|||||||
return (
|
return (
|
||||||
<Group justify="space-between" wrap="nowrap" gap="md">
|
<Group justify="space-between" wrap="nowrap" gap="md">
|
||||||
<Group gap={8} wrap="nowrap">
|
<Group gap={8} wrap="nowrap">
|
||||||
<ThemeIcon size={28} radius="md" variant="light" color="green">
|
<ThemeIcon size={28} radius="md" variant="light" color="edr-green">
|
||||||
{icon}
|
{icon}
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
@@ -146,7 +146,7 @@ export const BookingDetailModal = ({
|
|||||||
bookingWagons.length ? (
|
bookingWagons.length ? (
|
||||||
<Group gap={4} justify="flex-end">
|
<Group gap={4} justify="flex-end">
|
||||||
{bookingWagons.map((w) => (
|
{bookingWagons.map((w) => (
|
||||||
<Badge key={w.id} size="sm" variant="outline" color="green" radius="sm">
|
<Badge key={w.id} size="sm" variant="outline" color="edr-green" radius="sm">
|
||||||
#{w.sequenceNo}
|
#{w.sequenceNo}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ interface CompositionBookingTabsProps {
|
|||||||
type TabKey = "assigned" | "unassigned" | "payment" | "expired" | "removed";
|
type TabKey = "assigned" | "unassigned" | "payment" | "expired" | "removed";
|
||||||
|
|
||||||
const TAB_META: Record<TabKey, { label: string; icon: LucideIcon; color: string }> = {
|
const TAB_META: Record<TabKey, { label: string; icon: LucideIcon; color: string }> = {
|
||||||
assigned: { label: "Assigned to train", icon: PackageCheck, color: "green" },
|
assigned: { label: "Assigned to train", icon: PackageCheck, color: "edr-green" },
|
||||||
unassigned: { label: "Unassigned (ready to load)", icon: PackagePlus, color: "orange" },
|
unassigned: { label: "Unassigned (ready to load)", icon: PackagePlus, color: "orange" },
|
||||||
payment: { label: "Awaiting payment", icon: CreditCard, color: "orange" },
|
payment: { label: "Awaiting payment", icon: CreditCard, color: "orange" },
|
||||||
expired: { label: "Expired bookings", icon: XCircle, color: "red" },
|
expired: { label: "Expired bookings", icon: XCircle, color: "red" },
|
||||||
@@ -152,7 +152,7 @@ export const CompositionBookingTabs = ({
|
|||||||
value={tab}
|
value={tab}
|
||||||
onChange={(v) => v && setTab(v as TabKey)}
|
onChange={(v) => v && setTab(v as TabKey)}
|
||||||
variant="default"
|
variant="default"
|
||||||
color="green"
|
color="edr-green"
|
||||||
style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}
|
style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}
|
||||||
>
|
>
|
||||||
<Tabs.List grow>
|
<Tabs.List grow>
|
||||||
@@ -220,7 +220,7 @@ export const CompositionBookingTabs = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Group gap={5} wrap="nowrap">
|
<Group gap={5} wrap="nowrap">
|
||||||
<PackageCheck size={13} color="var(--mantine-color-green-7)" />
|
<PackageCheck size={13} color="var(--mantine-color-edr-green-7)" />
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{assignedCount} on train
|
{assignedCount} on train
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export const RemoveBookingConfirmModal = ({
|
|||||||
<Text fw={800} size="sm">
|
<Text fw={800} size="sm">
|
||||||
{target?.reference ?? "Booking"}
|
{target?.reference ?? "Booking"}
|
||||||
</Text>
|
</Text>
|
||||||
<Badge variant="light" color="green" leftSection={<TrainFront size={10} />}>
|
<Badge variant="light" color="edr-green" leftSection={<TrainFront size={10} />}>
|
||||||
{target?.wagonCount ?? 0} wagon{target?.wagonCount === 1 ? "" : "s"}
|
{target?.wagonCount ?? 0} wagon{target?.wagonCount === 1 ? "" : "s"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export const TrainConsistView = ({
|
|||||||
{selectedWagon ? (
|
{selectedWagon ? (
|
||||||
<Box>
|
<Box>
|
||||||
<Group gap={6} mb={6} wrap="nowrap">
|
<Group gap={6} mb={6} wrap="nowrap">
|
||||||
<Badge variant="light" color="green" radius="sm">
|
<Badge variant="light" color="edr-green" radius="sm">
|
||||||
Editing wagon #{selectedWagon.sequenceNo}
|
Editing wagon #{selectedWagon.sequenceNo}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
|
|||||||
@@ -48,18 +48,18 @@ const YardFleetBanner = ({ fleetAtOrigin }: { fleetAtOrigin: FleetAvailabilityRo
|
|||||||
py={6}
|
py={6}
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: "var(--mantine-color-green-0)",
|
background: "var(--mantine-color-edr-green-0)",
|
||||||
border: "1px solid var(--mantine-color-green-1)",
|
border: "1px solid var(--mantine-color-edr-green-1)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Group gap={5} wrap="nowrap">
|
<Group gap={5} wrap="nowrap">
|
||||||
<MapPin size={13} color="var(--mantine-color-green-7)" />
|
<MapPin size={13} color="var(--mantine-color-edr-green-7)" />
|
||||||
<Text size="xs" fw={700} c="green.8">
|
<Text size="xs" fw={700} c="edr-green.8">
|
||||||
Origin yard
|
Origin yard
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
{fleetAtOrigin.map((row) => (
|
{fleetAtOrigin.map((row) => (
|
||||||
<Badge key={row.wagonTypeId} size="sm" variant="light" color="green">
|
<Badge key={row.wagonTypeId} size="sm" variant="light" color="edr-green">
|
||||||
{row.wagonTypeCode}: {row.available}
|
{row.wagonTypeCode}: {row.available}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
@@ -148,7 +148,7 @@ export const UnassignedBookingsPanel = ({
|
|||||||
}
|
}
|
||||||
style={{
|
style={{
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
borderColor: isActive ? "var(--mantine-color-green-5)" : undefined,
|
borderColor: isActive ? "var(--mantine-color-edr-green-5)" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap={6}>
|
<Stack gap={6}>
|
||||||
@@ -162,7 +162,7 @@ export const UnassignedBookingsPanel = ({
|
|||||||
{booking.reference}
|
{booking.reference}
|
||||||
</Text>
|
</Text>
|
||||||
{booking.priorityScore ? (
|
{booking.priorityScore ? (
|
||||||
<Badge size="xs" color="green">
|
<Badge size="xs" color="edr-green">
|
||||||
P{booking.priorityScore}
|
P{booking.priorityScore}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -194,7 +194,7 @@ export const UnassignedBookingsPanel = ({
|
|||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
disabled={!fits}
|
disabled={!fits}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const WagonCard = ({
|
|||||||
<Card.Section withBorder inheritPadding py="xs" style={{ background: freightBrand.mutedBg }}>
|
<Card.Section withBorder inheritPadding py="xs" style={{ background: freightBrand.mutedBg }}>
|
||||||
<Group justify="space-between">
|
<Group justify="space-between">
|
||||||
<Group gap={6}>
|
<Group gap={6}>
|
||||||
<ThemeIcon size={28} radius="md" variant="white" color="green">
|
<ThemeIcon size={28} radius="md" variant="white" color="edr-green">
|
||||||
<TrainFront size={16} />
|
<TrainFront size={16} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<div>
|
<div>
|
||||||
@@ -55,7 +55,7 @@ export const WagonCard = ({
|
|||||||
<Text size="sm" fw={800}>
|
<Text size="sm" fw={800}>
|
||||||
Wagon #{wagon.sequenceNo}
|
Wagon #{wagon.sequenceNo}
|
||||||
</Text>
|
</Text>
|
||||||
<Badge size="xs" variant="light" color="green">
|
<Badge size="xs" variant="light" color="edr-green">
|
||||||
{wagonType}
|
{wagonType}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -142,7 +142,7 @@ export const WagonCard = ({
|
|||||||
</Group>
|
</Group>
|
||||||
<Progress
|
<Progress
|
||||||
value={Math.min(weightPercent, 100)}
|
value={Math.min(weightPercent, 100)}
|
||||||
color={weightPercent > 90 ? "red" : weightPercent > 75 ? "orange" : "green"}
|
color={weightPercent > 90 ? "red" : weightPercent > 75 ? "orange" : "edr-green"}
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="xl"
|
radius="xl"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { MantineTheme } from "@mantine/core";
|
|||||||
|
|
||||||
export const schedulingWorkflow = {
|
export const schedulingWorkflow = {
|
||||||
stepper: {
|
stepper: {
|
||||||
color: "green" as const,
|
color: "edr-green" as const,
|
||||||
iconSize: 32,
|
iconSize: 32,
|
||||||
size: "sm" as const,
|
size: "sm" as const,
|
||||||
},
|
},
|
||||||
@@ -12,11 +12,11 @@ export const schedulingWorkflow = {
|
|||||||
withBorder: true,
|
withBorder: true,
|
||||||
},
|
},
|
||||||
heroGradient: (theme: MantineTheme) =>
|
heroGradient: (theme: MantineTheme) =>
|
||||||
`linear-gradient(135deg, ${theme.colors.green[0]} 0%, ${theme.white} 55%, ${theme.colors.gray[0]} 100%)`,
|
`linear-gradient(135deg, ${theme.colors["edr-green"][0]} 0%, ${theme.white} 55%, ${theme.colors.gray[0]} 100%)`,
|
||||||
workflowGradient: (theme: MantineTheme) =>
|
workflowGradient: (theme: MantineTheme) =>
|
||||||
`linear-gradient(180deg, ${theme.white} 0%, ${theme.colors.gray[0]} 100%)`,
|
`linear-gradient(180deg, ${theme.white} 0%, ${theme.colors.gray[0]} 100%)`,
|
||||||
accentColor: "green" as const,
|
accentColor: "edr-green" as const,
|
||||||
successColor: "green" as const,
|
successColor: "edr-green" as const,
|
||||||
warningColor: "yellow" as const,
|
warningColor: "yellow" as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export function AssignWagonDialog({ trainId }: { trainId: string }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="edr-green"
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
leftSection={<Plus size={16} />}
|
leftSection={<Plus size={16} />}
|
||||||
@@ -91,7 +91,7 @@ export function AssignWagonDialog({ trainId }: { trainId: string }) {
|
|||||||
<Button variant="default" onClick={() => setOpen(false)}>
|
<Button variant="default" onClick={() => setOpen(false)}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="green" loading={assign.isPending} onClick={handleAssign}>
|
<Button color="edr-green" loading={assign.isPending} onClick={handleAssign}>
|
||||||
Assign
|
Assign
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const INVOICE_STATUS_COLOR: Record<WarehouseInvoiceStatus, string> = {
|
|||||||
DRAFT: 'gray',
|
DRAFT: 'gray',
|
||||||
ISSUED: 'orange',
|
ISSUED: 'orange',
|
||||||
PARTIALLY_PAID: 'yellow',
|
PARTIALLY_PAID: 'yellow',
|
||||||
PAID: 'green',
|
PAID: 'edr-green',
|
||||||
CANCELLED: 'gray',
|
CANCELLED: 'gray',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ export function FeePreviewModal({ opened, onClose, inventoryId }: FeePreviewModa
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
leftSection={<DoorOpen size={16} />}
|
leftSection={<DoorOpen size={16} />}
|
||||||
loading={gateClear.isPending}
|
loading={gateClear.isPending}
|
||||||
onClick={handleGateClearance}
|
onClick={handleGateClearance}
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ export function InspectionReportModal({ opened, onClose, inventoryId }: Inspecti
|
|||||||
<Button variant="default" onClick={onClose} disabled={submitting}>
|
<Button variant="default" onClick={onClose} disabled={submitting}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="green" onClick={handleSubmit} loading={submitting} disabled={!inventoryId}>
|
<Button color="edr-green" onClick={handleSubmit} loading={submitting} disabled={!inventoryId}>
|
||||||
Save report
|
Save report
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const actionColor: Record<InventoryAction, string> = {
|
|||||||
reserve: 'grape',
|
reserve: 'grape',
|
||||||
'ready-for-loading': 'cyan',
|
'ready-for-loading': 'cyan',
|
||||||
load: 'teal',
|
load: 'teal',
|
||||||
dispatch: 'green',
|
dispatch: 'edr-green',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WarehouseInventoryTable({
|
export function WarehouseInventoryTable({
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export function WarehouseTypeBadge({ type }: { type: WarehouseType }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function WarehouseStatusBadge({ status }: { status: WarehouseStatus }) {
|
export function WarehouseStatusBadge({ status }: { status: WarehouseStatus }) {
|
||||||
const color = status === 'ACTIVE' ? 'green' : 'gray';
|
const color = status === 'ACTIVE' ? 'edr-green' : 'gray';
|
||||||
return (
|
return (
|
||||||
<Badge color={color} variant="light" size="sm" radius="md" tt="uppercase" fw={600} style={badgeStyle}>
|
<Badge color={color} variant="light" size="sm" radius="md" tt="uppercase" fw={600} style={badgeStyle}>
|
||||||
{status}
|
{status}
|
||||||
@@ -39,7 +39,7 @@ const inventoryStatusColor: Record<InventoryStatus, string> = {
|
|||||||
RESERVED: 'grape',
|
RESERVED: 'grape',
|
||||||
READY_FOR_LOADING: 'cyan',
|
READY_FOR_LOADING: 'cyan',
|
||||||
LOADED: 'teal',
|
LOADED: 'teal',
|
||||||
DISPATCHED: 'green',
|
DISPATCHED: 'edr-green',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function InventoryStatusBadge({ status }: { status: InventoryStatus }) {
|
export function InventoryStatusBadge({ status }: { status: InventoryStatus }) {
|
||||||
|
|||||||
@@ -1,32 +1,31 @@
|
|||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Card,
|
||||||
|
Group,
|
||||||
|
Stack,
|
||||||
|
Tabs,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { ArrowRight, Calendar, Package, Search, User, X } from "lucide-react";
|
||||||
import { useCallback, useMemo, useRef, useState } from "react";
|
import { useCallback, useMemo, useRef, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { ArrowRight, Calendar, Package, Search, User, X } from "lucide-react";
|
|
||||||
import {
|
|
||||||
Stack,
|
|
||||||
Group,
|
|
||||||
Text,
|
|
||||||
Card,
|
|
||||||
TextInput,
|
|
||||||
ActionIcon,
|
|
||||||
Tabs,
|
|
||||||
} from "@mantine/core";
|
|
||||||
|
|
||||||
import { PageContainer } from "@/components/page";
|
import { BookingActionsMenu } from "@/components/bookings/BookingActionsMenu";
|
||||||
import Breadcrumbs from "@/components/ui/Breadcrumbs";
|
import { BookingApprovalProgressCell } from "@/components/bookings/BookingApprovalProgressCell";
|
||||||
import { BookingPriorityBadge } from "@/components/bookings/BookingPriorityBadge";
|
import { BookingPriorityBadge } from "@/components/bookings/BookingPriorityBadge";
|
||||||
|
import { BookingRequestsHeader } from "@/components/bookings/BookingRequestsHeader";
|
||||||
|
import { BookingStatusBadge } from "@/components/bookings/BookingStatusBadge";
|
||||||
import {
|
import {
|
||||||
BookingStatusTabs,
|
BookingStatusTabs,
|
||||||
type BookingStatusTabKey,
|
type BookingStatusTabKey,
|
||||||
} from "@/components/bookings/BookingStatusTabs";
|
} from "@/components/bookings/BookingStatusTabs";
|
||||||
import { BookingRequestsHeader } from "@/components/bookings/BookingRequestsHeader";
|
import { BookingTableEmpty } from "@/components/bookings/BookingTableEmpty";
|
||||||
import { BookingStatusBadge } from "@/components/bookings/BookingStatusBadge";
|
|
||||||
import { BookingApprovalProgressCell } from "@/components/bookings/BookingApprovalProgressCell";
|
|
||||||
import { AllocateBookingWizard } from "@/components/trainScheduling/AllocateBookingWizard";
|
|
||||||
import { BookingActionsMenu } from "@/components/bookings/BookingActionsMenu";
|
|
||||||
import { OperationsBookingQueue } from "@/components/bookings/OperationsBookingQueue";
|
import { OperationsBookingQueue } from "@/components/bookings/OperationsBookingQueue";
|
||||||
import { OperationsScheduledBookings } from "@/components/bookings/OperationsScheduledBookings";
|
import { OperationsScheduledBookings } from "@/components/bookings/OperationsScheduledBookings";
|
||||||
import { BookingTableEmpty } from "@/components/bookings/BookingTableEmpty";
|
|
||||||
import { bookingTable } from "@/components/bookings/booking-ui.styles";
|
import { bookingTable } from "@/components/bookings/booking-ui.styles";
|
||||||
|
import { PageContainer } from "@/components/page";
|
||||||
|
import { AllocateBookingWizard } from "@/components/trainScheduling/AllocateBookingWizard";
|
||||||
import { BOOKING_LIST_TABS } from "@/features/bookings/booking-status.config";
|
import { BOOKING_LIST_TABS } from "@/features/bookings/booking-status.config";
|
||||||
import { toBookingListRow } from "@/features/bookings/mapBookingListRow";
|
import { toBookingListRow } from "@/features/bookings/mapBookingListRow";
|
||||||
import {
|
import {
|
||||||
@@ -34,15 +33,15 @@ import {
|
|||||||
useBookingList,
|
useBookingList,
|
||||||
useBookingListSummary,
|
useBookingListSummary,
|
||||||
} from "@/hooks/bookings/useBookings";
|
} from "@/hooks/bookings/useBookings";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import type { BookingListFilter } from "@/services/bookings.service";
|
import type { BookingListFilter } from "@/services/bookings.service";
|
||||||
import type { BookingListRow } from "@/types/booking";
|
import type { BookingListRow } from "@/types/booking";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
import {
|
||||||
|
Badge,
|
||||||
DataTable,
|
DataTable,
|
||||||
DataTableFooter,
|
DataTableFooter,
|
||||||
type ColumnDef,
|
|
||||||
usePagination,
|
usePagination,
|
||||||
Badge,
|
type ColumnDef,
|
||||||
} from "@edr/ui-common";
|
} from "@edr/ui-common";
|
||||||
|
|
||||||
function getStatusesForTab(tab: BookingStatusTabKey): string | undefined {
|
function getStatusesForTab(tab: BookingStatusTabKey): string | undefined {
|
||||||
@@ -298,8 +297,6 @@ export default function BookingRequestsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Breadcrumbs items={[{ label: "Operations" }, { label: "Booking requests" }]} />
|
|
||||||
|
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<BookingRequestsHeader
|
<BookingRequestsHeader
|
||||||
metrics={metrics}
|
metrics={metrics}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ function FormSection({
|
|||||||
icon: Icon,
|
icon: Icon,
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
accent = "green",
|
accent = "edr-green",
|
||||||
right,
|
right,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
@@ -394,7 +394,7 @@ export default function NewBookingPage() {
|
|||||||
{/* LEFT — form */}
|
{/* LEFT — form */}
|
||||||
<Grid.Col span={{ base: 12, lg: 8 }}>
|
<Grid.Col span={{ base: 12, lg: 8 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
<FormSection icon={Layers} title="Booking type" subtitle="Who is booking and what kind of freight" accent="green">
|
<FormSection icon={Layers} title="Booking type" subtitle="Who is booking and what kind of freight" accent="edr-green">
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Switch
|
<Switch
|
||||||
label="Government booking"
|
label="Government booking"
|
||||||
@@ -745,7 +745,7 @@ export default function NewBookingPage() {
|
|||||||
<Box style={{ position: "sticky", top: 16 }}>
|
<Box style={{ position: "sticky", top: 16 }}>
|
||||||
<Paper radius="lg" withBorder p="lg" style={{ borderColor: "var(--mantine-color-gray-2)" }}>
|
<Paper radius="lg" withBorder p="lg" style={{ borderColor: "var(--mantine-color-gray-2)" }}>
|
||||||
<Group gap="sm" mb="md">
|
<Group gap="sm" mb="md">
|
||||||
<ThemeIcon size={34} radius="md" variant="light" color="green">
|
<ThemeIcon size={34} radius="md" variant="light" color="edr-green">
|
||||||
<Weight size={17} />
|
<Weight size={17} />
|
||||||
</ThemeIcon>
|
</ThemeIcon>
|
||||||
<Text fw={700}>Summary</Text>
|
<Text fw={700}>Summary</Text>
|
||||||
|
|||||||
@@ -751,7 +751,7 @@ export function WagonTypesCrudPage() {
|
|||||||
<MantineTable.Td>{type.lengthMeters}</MantineTable.Td>
|
<MantineTable.Td>{type.lengthMeters}</MantineTable.Td>
|
||||||
<MantineTable.Td>{type.supportedLoadTypes?.join(', ') || '-'}</MantineTable.Td>
|
<MantineTable.Td>{type.supportedLoadTypes?.join(', ') || '-'}</MantineTable.Td>
|
||||||
<MantineTable.Td>
|
<MantineTable.Td>
|
||||||
<MantineBadge color={type.isActive === false ? 'gray' : 'green'} variant="light">
|
<MantineBadge color={type.isActive === false ? 'gray' : 'edr-green'} variant="light">
|
||||||
{type.isActive === false ? 'Inactive' : 'Active'}
|
{type.isActive === false ? 'Inactive' : 'Active'}
|
||||||
</MantineBadge>
|
</MantineBadge>
|
||||||
</MantineTable.Td>
|
</MantineTable.Td>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const METHOD_OPTIONS: { value: PaymentMethod; label: string }[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const STATUS_COLORS: Record<string, string> = {
|
const STATUS_COLORS: Record<string, string> = {
|
||||||
success: "green",
|
success: "edr-green",
|
||||||
processing: "yellow",
|
processing: "yellow",
|
||||||
"action-required": "yellow",
|
"action-required": "yellow",
|
||||||
failed: "red",
|
failed: "red",
|
||||||
@@ -205,7 +205,7 @@ export default function PaymentsPage() {
|
|||||||
label: "Successful",
|
label: "Successful",
|
||||||
value: summary?.success ?? 0,
|
value: summary?.success ?? 0,
|
||||||
icon: CheckCircle2,
|
icon: CheckCircle2,
|
||||||
color: "green",
|
color: "edr-green",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Processing",
|
label: "Processing",
|
||||||
|
|||||||
@@ -516,12 +516,12 @@ export default function BatchBoardPage() {
|
|||||||
style={{
|
style={{
|
||||||
padding: "2px 8px",
|
padding: "2px 8px",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: "var(--mantine-color-green-0)",
|
background: "var(--mantine-color-edr-green-0)",
|
||||||
border: "1px solid var(--mantine-color-green-1)",
|
border: "1px solid var(--mantine-color-edr-green-1)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Package size={12} color="var(--mantine-color-green-7)" />
|
<Package size={12} color="var(--mantine-color-edr-green-7)" />
|
||||||
<Text size="xs" fw={700} c="green.8" lh={1.2}>
|
<Text size="xs" fw={700} c="edr-green.8" lh={1.2}>
|
||||||
{row.original.capacity.allocatedWagons}
|
{row.original.capacity.allocatedWagons}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="10px" c="dimmed" lh={1.2}>
|
<Text size="10px" c="dimmed" lh={1.2}>
|
||||||
|
|||||||
@@ -180,8 +180,8 @@ export default function TrainScheduleV2ListPage() {
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
borderRadius: 9,
|
borderRadius: 9,
|
||||||
background: "var(--mantine-color-green-0)",
|
background: "var(--mantine-color-edr-green-0)",
|
||||||
color: "var(--mantine-color-green-7)",
|
color: "var(--mantine-color-edr-green-7)",
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -576,13 +576,13 @@ function MetricChip({
|
|||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: subtle
|
background: subtle
|
||||||
? "var(--mantine-color-gray-1)"
|
? "var(--mantine-color-gray-1)"
|
||||||
: "var(--mantine-color-green-0)",
|
: "var(--mantine-color-edr-green-0)",
|
||||||
border: `1px solid ${
|
border: `1px solid ${
|
||||||
subtle ? "var(--mantine-color-gray-2)" : "var(--mantine-color-green-1)"
|
subtle ? "var(--mantine-color-gray-2)" : "var(--mantine-color-edr-green-1)"
|
||||||
}`,
|
}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text size="sm" fw={700} c={subtle ? "gray.7" : "green.8"} lh={1.2}>
|
<Text size="sm" fw={700} c={subtle ? "gray.7" : "edr-green.8"} lh={1.2}>
|
||||||
{value}
|
{value}
|
||||||
</Text>
|
</Text>
|
||||||
{label ? (
|
{label ? (
|
||||||
@@ -653,7 +653,7 @@ function ScheduleCard({
|
|||||||
<Group gap="xs" wrap="nowrap">
|
<Group gap="xs" wrap="nowrap">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
size="sm"
|
size="sm"
|
||||||
radius="md"
|
radius="md"
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import type { ArrivalQueueItem } from '@/types/warehouse';
|
|||||||
|
|
||||||
function inspectionBadge(status: string | null) {
|
function inspectionBadge(status: string | null) {
|
||||||
if (!status) return <Badge variant="light" color="gray" size="sm">Not inspected</Badge>;
|
if (!status) return <Badge variant="light" color="gray" size="sm">Not inspected</Badge>;
|
||||||
const color = status === 'PASSED' ? 'green' : status === 'FAILED' ? 'red' : 'orange';
|
const color = status === 'PASSED' ? 'edr-green' : status === 'FAILED' ? 'red' : 'orange';
|
||||||
return <Badge variant="light" color={color} size="sm">{status.replace(/_/g, ' ')}</Badge>;
|
return <Badge variant="light" color={color} size="sm">{status.replace(/_/g, ' ')}</Badge>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ export default function ArrivalQueuePage() {
|
|||||||
header: 'Status',
|
header: 'Status',
|
||||||
cell: ({ row }) =>
|
cell: ({ row }) =>
|
||||||
row.original.unloaded ? (
|
row.original.unloaded ? (
|
||||||
<Badge variant="light" color="green" size="sm">
|
<Badge variant="light" color="edr-green" size="sm">
|
||||||
{row.original.currentStatus ?? 'RECEIVED'}
|
{row.original.currentStatus ?? 'RECEIVED'}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : (
|
) : (
|
||||||
@@ -125,7 +125,7 @@ export default function ArrivalQueuePage() {
|
|||||||
<Button
|
<Button
|
||||||
size="compact-xs"
|
size="compact-xs"
|
||||||
variant="light"
|
variant="light"
|
||||||
color="green"
|
color="edr-green"
|
||||||
leftSection={<ClipboardList size={14} />}
|
leftSection={<ClipboardList size={14} />}
|
||||||
onClick={() => setInspectInventoryId(item.inventoryId)}
|
onClick={() => setInspectInventoryId(item.inventoryId)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const columns: ColumnDef<Loading>[] = [
|
|||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Badge
|
<Badge
|
||||||
variant="light"
|
variant="light"
|
||||||
color={row.original.inventory?.status === 'DISPATCHED' ? 'green' : 'teal'}
|
color={row.original.inventory?.status === 'DISPATCHED' ? 'edr-green' : 'teal'}
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
{row.original.inventory?.status ?? 'LOADED'}
|
{row.original.inventory?.status ?? 'LOADED'}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function LoadingQueuePage() {
|
|||||||
<Tabs.Tab
|
<Tabs.Tab
|
||||||
value="ready"
|
value="ready"
|
||||||
leftSection={
|
leftSection={
|
||||||
<Badge size="xs" color="green">
|
<Badge size="xs" color="edr-green">
|
||||||
{paidItems.length}
|
{paidItems.length}
|
||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const STATUS_COLOR: Record<WarehouseInvoiceStatus, string> = {
|
|||||||
DRAFT: 'gray',
|
DRAFT: 'gray',
|
||||||
ISSUED: 'orange',
|
ISSUED: 'orange',
|
||||||
PARTIALLY_PAID: 'yellow',
|
PARTIALLY_PAID: 'yellow',
|
||||||
PAID: 'green',
|
PAID: 'edr-green',
|
||||||
CANCELLED: 'gray',
|
CANCELLED: 'gray',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ function AllocationRules() {
|
|||||||
id: 'active',
|
id: 'active',
|
||||||
header: 'Active',
|
header: 'Active',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Badge color={row.original.isActive ? 'green' : 'gray'} variant="light">
|
<Badge color={row.original.isActive ? 'edr-green' : 'gray'} variant="light">
|
||||||
{row.original.isActive ? 'Yes' : 'No'}
|
{row.original.isActive ? 'Yes' : 'No'}
|
||||||
</Badge>
|
</Badge>
|
||||||
),
|
),
|
||||||
@@ -240,7 +240,7 @@ function FeeRules() {
|
|||||||
id: 'active',
|
id: 'active',
|
||||||
header: 'Active',
|
header: 'Active',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Badge color={row.original.isActive ? 'green' : 'gray'} variant="light">
|
<Badge color={row.original.isActive ? 'edr-green' : 'gray'} variant="light">
|
||||||
{row.original.isActive ? 'Yes' : 'No'}
|
{row.original.isActive ? 'Yes' : 'No'}
|
||||||
</Badge>
|
</Badge>
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user