mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 04:08:11 +00:00
style: booking detail pages fix
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
|||||||
X,
|
X,
|
||||||
XCircle,
|
XCircle,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import { useMemo, useRef, useState } from "react";
|
import { useMemo, useRef, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
@@ -136,22 +137,17 @@ export function DraftBookingView({
|
|||||||
<PageHeader
|
<PageHeader
|
||||||
booking={booking}
|
booking={booking}
|
||||||
actions={
|
actions={
|
||||||
<>
|
<HeaderButton
|
||||||
<HeaderButton
|
dark
|
||||||
icon={<Download size={16} />}
|
icon={<Pencil size={16} />}
|
||||||
label="Documents"
|
label="Continue editing"
|
||||||
onClick={() =>
|
onClick={() => navigate(`/bookings/${booking.id}/edit`)}
|
||||||
documentsRef.current?.scrollIntoView({ behavior: "smooth" })
|
/>
|
||||||
}
|
|
||||||
/>
|
|
||||||
<HeaderButton
|
|
||||||
dark
|
|
||||||
icon={<Pencil size={16} />}
|
|
||||||
label="Continue editing"
|
|
||||||
onClick={() => navigate(`/bookings/${booking.id}/edit`)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
menuActions={{
|
||||||
|
onCancel: () => setCancelDialogOpen(true),
|
||||||
|
onSupport: () => navigate("/support"),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MutationErrors
|
<MutationErrors
|
||||||
@@ -249,7 +245,7 @@ export function DraftBookingView({
|
|||||||
<NoticeBanner
|
<NoticeBanner
|
||||||
tone="red"
|
tone="red"
|
||||||
icon={<AlertCircle size={16} />}
|
icon={<AlertCircle size={16} />}
|
||||||
style={{ marginBottom: 12 }}
|
className="mb-3"
|
||||||
>
|
>
|
||||||
{docError}
|
{docError}
|
||||||
</NoticeBanner>
|
</NoticeBanner>
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ export function ReadonlyBookingView({ booking }: { booking: Freight.IBooking })
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
menuActions={{
|
||||||
|
onViewContract: booking.signedByCeoAt ? () => {} : undefined,
|
||||||
|
onRebook: () => navigate("/bookings/new"),
|
||||||
|
onSupport: () => navigate("/support"),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isNegative(status) ? (
|
{isNegative(status) ? (
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ export function NoticeBanner({
|
|||||||
icon,
|
icon,
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
style,
|
className,
|
||||||
}: {
|
}: {
|
||||||
tone: "amber" | "red";
|
tone: "amber" | "red";
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
title?: string;
|
title?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
style?: React.CSSProperties;
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
const palette =
|
const palette =
|
||||||
tone === "amber"
|
tone === "amber"
|
||||||
@@ -21,33 +21,26 @@ export function NoticeBanner({
|
|||||||
: { border: "#F3C8C1", bg: "#FBEAE7", color: "#A93226" };
|
: { border: "#F3C8C1", bg: "#FBEAE7", color: "#A93226" };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<div
|
||||||
align="flex-start"
|
className={`flex items-start gap-3 rounded-[14px] border p-4 ${className ?? ""}`}
|
||||||
gap={12}
|
|
||||||
wrap="nowrap"
|
|
||||||
p="md"
|
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 14,
|
borderColor: palette.border,
|
||||||
border: `1px solid ${palette.border}`,
|
|
||||||
backgroundColor: palette.bg,
|
backgroundColor: palette.bg,
|
||||||
color: palette.color,
|
color: palette.color,
|
||||||
...style,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box component="span" mt={2} style={{ flexShrink: 0 }}>
|
<span className="mt-0.5 shrink-0">{icon}</span>
|
||||||
{icon}
|
<div className="min-w-0">
|
||||||
</Box>
|
|
||||||
<Box miw={0}>
|
|
||||||
{title && (
|
{title && (
|
||||||
<Text fz="13.5px" fw={800} c={palette.color}>
|
<Text fz="13.5px" fw={800} c={palette.color}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Text fz="13px" c={palette.color} style={{ lineHeight: 1.45 }}>
|
<Text fz="13px" c={palette.color} className="leading-[1.45]">
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</div>
|
||||||
</Group>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,54 +54,35 @@ export function ActionRequiredBanner({
|
|||||||
onAction?: () => void;
|
onAction?: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Group
|
<div
|
||||||
justify="space-between"
|
className="flex flex-wrap items-center justify-between gap-6 rounded-2xl border border-[#F4D9A8] px-[22px] py-5"
|
||||||
align="center"
|
style={{ backgroundColor: "#FDF3E0" }}
|
||||||
wrap="wrap"
|
|
||||||
gap={24}
|
|
||||||
px={22}
|
|
||||||
py={20}
|
|
||||||
style={{
|
|
||||||
borderRadius: 16,
|
|
||||||
backgroundColor: "#FDF3E0",
|
|
||||||
border: "1px solid #F4D9A8",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Group gap={16} align="center" wrap="nowrap" miw={0} flex={1}>
|
<div className="flex min-w-0 flex-1 items-center gap-4">
|
||||||
<Box
|
<div
|
||||||
style={{
|
className="flex shrink-0 items-center justify-center rounded-xl border border-[#F4D9A8]"
|
||||||
flexShrink: 0,
|
style={{ width: 44, height: 44, backgroundColor: "#fff", color: "#C77F12" }}
|
||||||
width: 44,
|
|
||||||
height: 44,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
borderRadius: 12,
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
border: "1px solid #F4D9A8",
|
|
||||||
color: "#C77F12",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<AlertTriangle size={23} />
|
<AlertTriangle size={23} />
|
||||||
</Box>
|
</div>
|
||||||
<Box miw={0}>
|
<div className="min-w-0">
|
||||||
<Text
|
<Text
|
||||||
fz="10.5px"
|
fz="10.5px"
|
||||||
fw={800}
|
fw={800}
|
||||||
c="#B07A2A"
|
c="#B07A2A"
|
||||||
tt="uppercase"
|
tt="uppercase"
|
||||||
style={{ letterSpacing: 0.6 }}
|
className="tracking-[0.6px]"
|
||||||
>
|
>
|
||||||
Action required · You
|
Action required · You
|
||||||
</Text>
|
</Text>
|
||||||
<Text mt={5} fz="15.5px" fw={700} c="#10202F">
|
<Text mt={5} fz="15.5px" fw={700} c="#10202F">
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text mt={2} fz="13px" c="#7A6A4E" style={{ lineHeight: 1.45 }}>
|
<Text mt={2} fz="13px" c="#7A6A4E" className="leading-[1.45]">
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</div>
|
||||||
</Group>
|
</div>
|
||||||
{onAction && (
|
{onAction && (
|
||||||
<Button
|
<Button
|
||||||
onClick={onAction}
|
onClick={onAction}
|
||||||
@@ -123,7 +97,7 @@ export function ActionRequiredBanner({
|
|||||||
Review & Resubmit
|
Review & Resubmit
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,44 +119,24 @@ export function CancelledBanner({
|
|||||||
radius={16}
|
radius={16}
|
||||||
p={20}
|
p={20}
|
||||||
bg="#FCEEEA"
|
bg="#FCEEEA"
|
||||||
style={{ border: "1px solid #F3C6BB" }}
|
className="border border-[#F3C6BB]"
|
||||||
>
|
>
|
||||||
<Stack gap={14}>
|
<Stack gap={14}>
|
||||||
<Group justify="space-between" align="center" wrap="wrap" gap={20}>
|
<Group justify="space-between" align="center" wrap="wrap" gap={20}>
|
||||||
<Group gap={16} align="center" wrap="nowrap" miw={0}>
|
<Group gap={16} align="center" wrap="nowrap" miw={0}>
|
||||||
<Box
|
<div
|
||||||
style={{
|
className="flex shrink-0 items-center justify-center rounded-[13px] border border-[#F3C6BB]"
|
||||||
flexShrink: 0,
|
style={{ width: 46, height: 46, backgroundColor: "#fff", color: "#D93C15" }}
|
||||||
width: 46,
|
|
||||||
height: 46,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
borderRadius: 13,
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
border: "1px solid #F3C6BB",
|
|
||||||
color: "#D93C15",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<XCircle size={24} />
|
<XCircle size={24} />
|
||||||
</Box>
|
</div>
|
||||||
<Box miw={0}>
|
<Box miw={0}>
|
||||||
<Box
|
<span
|
||||||
component="span"
|
className="inline-flex rounded-full px-[10px] py-1 text-[10.5px] font-extrabold uppercase tracking-[0.3px] text-white"
|
||||||
style={{
|
style={{ backgroundColor: "#D93C15" }}
|
||||||
display: "inline-flex",
|
|
||||||
borderRadius: 999,
|
|
||||||
backgroundColor: "#D93C15",
|
|
||||||
padding: "4px 10px",
|
|
||||||
fontSize: 10.5,
|
|
||||||
fontWeight: 800,
|
|
||||||
letterSpacing: 0.3,
|
|
||||||
color: "#fff",
|
|
||||||
textTransform: "uppercase",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{pillLabel}
|
{pillLabel}
|
||||||
</Box>
|
</span>
|
||||||
<Text mt={6} fz="15.5px" fw={700} c="#10202F">
|
<Text mt={6} fz="15.5px" fw={700} c="#10202F">
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -206,33 +160,26 @@ export function CancelledBanner({
|
|||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
{reason && (
|
{reason && (
|
||||||
<Group
|
<div
|
||||||
gap={11}
|
className="flex items-start gap-[11px] rounded-xl border border-[#F3C6BB] p-[14px]"
|
||||||
align="flex-start"
|
style={{ backgroundColor: "#fff" }}
|
||||||
wrap="nowrap"
|
|
||||||
p={14}
|
|
||||||
style={{
|
|
||||||
borderRadius: 12,
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
border: "1px solid #F3C6BB",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<StickyNote size={20} color="#B0341A" style={{ flexShrink: 0 }} />
|
<StickyNote size={20} color="#B0341A" className="shrink-0" />
|
||||||
<Box miw={0}>
|
<div className="min-w-0">
|
||||||
<Text
|
<Text
|
||||||
fz="10.5px"
|
fz="10.5px"
|
||||||
fw={700}
|
fw={700}
|
||||||
c="#B0341A"
|
c="#B0341A"
|
||||||
tt="uppercase"
|
tt="uppercase"
|
||||||
style={{ letterSpacing: 0.5 }}
|
className="tracking-[0.5px]"
|
||||||
>
|
>
|
||||||
Reason for cancellation
|
Reason for cancellation
|
||||||
</Text>
|
</Text>
|
||||||
<Text mt={3} fz="13.5px" c="#3D2A26" style={{ lineHeight: 1.45 }}>
|
<Text mt={3} fz="13.5px" c="#3D2A26" className="leading-[1.45]">
|
||||||
{reason}
|
{reason}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</div>
|
||||||
</Group>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -1,17 +1,36 @@
|
|||||||
import { Box, Button, Group, Stack, Text } from "@mantine/core";
|
import { ActionIcon, Button, Group, Menu, Stack, Text } from "@mantine/core";
|
||||||
import { ArrowDownLeft, ArrowUpRight } from "lucide-react";
|
import {
|
||||||
|
ArrowDownLeft,
|
||||||
|
ArrowUpRight,
|
||||||
|
Edit2,
|
||||||
|
FileText,
|
||||||
|
HelpCircle,
|
||||||
|
MoreHorizontal,
|
||||||
|
RefreshCw,
|
||||||
|
XCircle,
|
||||||
|
} from "lucide-react";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import type { Freight } from "@edr/types";
|
import type { Freight } from "@edr/types";
|
||||||
|
|
||||||
import { bookingSubtitle, isDraftLike, isNegative } from "../utils";
|
import { bookingSubtitle, isDraftLike, isNegative } from "../utils";
|
||||||
|
|
||||||
|
export interface PageHeaderMenuActions {
|
||||||
|
onViewContract?: () => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
onEdit?: () => void;
|
||||||
|
onSupport?: () => void;
|
||||||
|
onRebook?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
export function PageHeader({
|
export function PageHeader({
|
||||||
booking,
|
booking,
|
||||||
actions,
|
actions,
|
||||||
|
menuActions,
|
||||||
}: {
|
}: {
|
||||||
booking: Freight.IBooking;
|
booking: Freight.IBooking;
|
||||||
actions: ReactNode;
|
actions?: ReactNode;
|
||||||
|
menuActions?: PageHeaderMenuActions;
|
||||||
}) {
|
}) {
|
||||||
const status = booking.status as string;
|
const status = booking.status as string;
|
||||||
const negative = isNegative(status);
|
const negative = isNegative(status);
|
||||||
@@ -23,6 +42,8 @@ export function PageHeader({
|
|||||||
const pillText = negative ? "#A93226" : draft ? "#475569" : "#0A6F4D";
|
const pillText = negative ? "#A93226" : draft ? "#475569" : "#0A6F4D";
|
||||||
const isExport = booking.tradeDirection === "EXPORT";
|
const isExport = booking.tradeDirection === "EXPORT";
|
||||||
|
|
||||||
|
const hasMenu = menuActions && Object.values(menuActions).some(Boolean);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group justify="space-between" align="flex-start" wrap="wrap" gap="md">
|
<Group justify="space-between" align="flex-start" wrap="wrap" gap="md">
|
||||||
<Stack gap={8} miw={0}>
|
<Stack gap={8} miw={0}>
|
||||||
@@ -30,58 +51,90 @@ export function PageHeader({
|
|||||||
<Text fz="26px" fw={800} c="#10202F">
|
<Text fz="26px" fw={800} c="#10202F">
|
||||||
{booking.reference}
|
{booking.reference}
|
||||||
</Text>
|
</Text>
|
||||||
<Group
|
|
||||||
component="span"
|
<span
|
||||||
gap={7}
|
className="inline-flex items-center gap-[7px] rounded-full px-3 py-1.5 text-xs font-bold"
|
||||||
align="center"
|
style={{ backgroundColor: pillBg, border: `1px solid ${pillBorder}`, color: pillText }}
|
||||||
wrap="nowrap"
|
|
||||||
style={{
|
|
||||||
display: "inline-flex",
|
|
||||||
borderRadius: 999,
|
|
||||||
padding: "6px 12px",
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 700,
|
|
||||||
backgroundColor: pillBg,
|
|
||||||
border: `1px solid ${pillBorder}`,
|
|
||||||
color: pillText,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Box
|
<span
|
||||||
component="span"
|
className="shrink-0 rounded-full"
|
||||||
style={{
|
style={{ width: 7, height: 7, backgroundColor: dotColor }}
|
||||||
width: 7,
|
|
||||||
height: 7,
|
|
||||||
borderRadius: 999,
|
|
||||||
backgroundColor: dotColor,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{status.replace(/_/g, " ").replace(/\b\w/g, (m) => m.toUpperCase())}
|
{status.replace(/_/g, " ").replace(/\b\w/g, (m) => m.toUpperCase())}
|
||||||
</Group>
|
</span>
|
||||||
<Group
|
|
||||||
component="span"
|
<span className="inline-flex items-center gap-[6px] rounded-full bg-[#F1F4F7] px-[11px] py-1.5 text-xs font-bold text-[#475569]">
|
||||||
gap={6}
|
|
||||||
align="center"
|
|
||||||
wrap="nowrap"
|
|
||||||
style={{
|
|
||||||
display: "inline-flex",
|
|
||||||
borderRadius: 999,
|
|
||||||
backgroundColor: "#F1F4F7",
|
|
||||||
padding: "6px 11px",
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 700,
|
|
||||||
color: "#475569",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isExport ? <ArrowUpRight size={14} /> : <ArrowDownLeft size={14} />}
|
{isExport ? <ArrowUpRight size={14} /> : <ArrowDownLeft size={14} />}
|
||||||
{isExport ? "Export" : "Import"}
|
{isExport ? "Export" : "Import"}
|
||||||
</Group>
|
</span>
|
||||||
</Group>
|
</Group>
|
||||||
<Text fz="14px" c="#6B7C8E">
|
<Text fz="14px" c="#6B7C8E">
|
||||||
{bookingSubtitle(booking)}
|
{bookingSubtitle(booking)}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Group gap={12} wrap="wrap">
|
|
||||||
|
<Group gap={8} wrap="nowrap" align="center">
|
||||||
{actions}
|
{actions}
|
||||||
|
{hasMenu && (
|
||||||
|
<Menu shadow="md" radius={12} position="bottom-end">
|
||||||
|
<Menu.Target>
|
||||||
|
<ActionIcon
|
||||||
|
variant="default"
|
||||||
|
size={42}
|
||||||
|
radius={10}
|
||||||
|
aria-label="More options"
|
||||||
|
>
|
||||||
|
<MoreHorizontal size={18} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Menu.Target>
|
||||||
|
<Menu.Dropdown miw={210}>
|
||||||
|
{menuActions!.onViewContract && (
|
||||||
|
<Menu.Item
|
||||||
|
leftSection={<FileText size={15} />}
|
||||||
|
onClick={menuActions!.onViewContract}
|
||||||
|
>
|
||||||
|
View contract
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{menuActions!.onEdit && (
|
||||||
|
<Menu.Item
|
||||||
|
leftSection={<Edit2 size={15} />}
|
||||||
|
onClick={menuActions!.onEdit}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{menuActions!.onSupport && (
|
||||||
|
<Menu.Item
|
||||||
|
leftSection={<HelpCircle size={15} />}
|
||||||
|
onClick={menuActions!.onSupport}
|
||||||
|
>
|
||||||
|
Contact customer support
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{menuActions!.onRebook && (
|
||||||
|
<Menu.Item
|
||||||
|
leftSection={<RefreshCw size={15} />}
|
||||||
|
onClick={menuActions!.onRebook}
|
||||||
|
>
|
||||||
|
Rebook similar schedule
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{menuActions!.onCancel && (
|
||||||
|
<>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
color="red"
|
||||||
|
leftSection={<XCircle size={15} />}
|
||||||
|
onClick={menuActions!.onCancel}
|
||||||
|
>
|
||||||
|
Cancel booking
|
||||||
|
</Menu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Menu.Dropdown>
|
||||||
|
</Menu>
|
||||||
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -43,20 +43,12 @@ export function StatusHero({
|
|||||||
<SectionCard p={28}>
|
<SectionCard p={28}>
|
||||||
<Group justify="space-between" align="center" wrap="wrap" gap="md">
|
<Group justify="space-between" align="center" wrap="wrap" gap="md">
|
||||||
<Group gap={16} align="center" wrap="nowrap">
|
<Group gap={16} align="center" wrap="nowrap">
|
||||||
<Box
|
<div
|
||||||
style={{
|
className="flex items-center justify-center rounded-2xl shrink-0"
|
||||||
width: 56,
|
style={{ width: 56, height: 56, backgroundColor: tileBg, color: tileFg }}
|
||||||
height: 56,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
borderRadius: 16,
|
|
||||||
backgroundColor: tileBg,
|
|
||||||
color: tileFg,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<HeroIcon size={26} />
|
<HeroIcon size={26} />
|
||||||
</Box>
|
</div>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="21px" fw={800} c="#10202F">
|
<Text fz="21px" fw={800} c="#10202F">
|
||||||
{cfg.title}
|
{cfg.title}
|
||||||
@@ -72,11 +64,7 @@ export function StatusHero({
|
|||||||
wrap="nowrap"
|
wrap="nowrap"
|
||||||
px="md"
|
px="md"
|
||||||
py="sm"
|
py="sm"
|
||||||
style={{
|
className="rounded-[14px] border border-[#E6ECF2] bg-[#F7FAFC]"
|
||||||
borderRadius: 14,
|
|
||||||
border: "1px solid #E6ECF2",
|
|
||||||
backgroundColor: "#F7FAFC",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<History size={22} color="#64748B" />
|
<History size={22} color="#64748B" />
|
||||||
<Box>
|
<Box>
|
||||||
@@ -85,7 +73,7 @@ export function StatusHero({
|
|||||||
fw={700}
|
fw={700}
|
||||||
c="#9AA8B5"
|
c="#9AA8B5"
|
||||||
tt="uppercase"
|
tt="uppercase"
|
||||||
style={{ letterSpacing: 0.6 }}
|
className="tracking-[0.6px]"
|
||||||
>
|
>
|
||||||
{chipLabel}
|
{chipLabel}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -124,104 +112,97 @@ function ProgressTracker({
|
|||||||
const activeSub = tone === "ink" ? "#475569" : "#0A6F4D";
|
const activeSub = tone === "ink" ? "#475569" : "#0A6F4D";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group align="flex-start" gap={0} wrap="nowrap" w="100%">
|
/* Scrollable on mobile so 5 stages never overflow */
|
||||||
{PROGRESS_STAGES.map((stage, idx) => {
|
<Box
|
||||||
const state =
|
className="overflow-x-auto"
|
||||||
idx < current ? "done" : idx === current ? "active" : "idle";
|
style={{ scrollbarWidth: "none", WebkitOverflowScrolling: "touch" } as React.CSSProperties}
|
||||||
const Icon = stage.icon;
|
>
|
||||||
const reachedLeft = current >= idx && current >= 0;
|
<div className="flex items-start" style={{ minWidth: 440 }}>
|
||||||
const reachedRight = current > idx && current >= 0;
|
{PROGRESS_STAGES.map((stage, idx) => {
|
||||||
|
const state =
|
||||||
|
idx < current ? "done" : idx === current ? "active" : "idle";
|
||||||
|
const Icon = stage.icon;
|
||||||
|
const reachedLeft = current >= idx && current >= 0;
|
||||||
|
const reachedRight = current > idx && current >= 0;
|
||||||
|
|
||||||
const circleStyle: React.CSSProperties =
|
const circleBg =
|
||||||
state === "idle"
|
state === "idle"
|
||||||
? { backgroundColor: "#EEF2F6", border: "1px solid #E1E7EE" }
|
? "#EEF2F6"
|
||||||
: {
|
: state === "active"
|
||||||
backgroundColor: state === "active" ? activeFill : "#0EA371",
|
? activeFill
|
||||||
boxShadow:
|
: "#0EA371";
|
||||||
state === "active" ? `0 0 0 4px ${activeRing}` : undefined,
|
const circleBorder = state === "idle" ? "1px solid #E1E7EE" : undefined;
|
||||||
};
|
const circleShadow =
|
||||||
|
state === "active" ? `0 0 0 4px ${activeRing}` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<div
|
||||||
key={stage.label}
|
key={stage.label}
|
||||||
flex={1}
|
className="flex flex-1 flex-col items-center gap-[10px]"
|
||||||
style={{
|
>
|
||||||
display: "flex",
|
<div className="flex w-full items-center">
|
||||||
flexDirection: "column",
|
{/* left connector */}
|
||||||
alignItems: "center",
|
<div
|
||||||
gap: 10,
|
className="flex-1 rounded-full"
|
||||||
}}
|
style={{
|
||||||
>
|
height: 3,
|
||||||
<Box style={{ display: "flex", alignItems: "center", width: "100%" }}>
|
background:
|
||||||
<Box
|
idx === 0 ? "transparent" : reachedLeft ? "#0EA371" : "#E1E7EE",
|
||||||
flex={1}
|
}}
|
||||||
style={{
|
/>
|
||||||
height: 3,
|
{/* stage circle */}
|
||||||
borderRadius: 999,
|
<div
|
||||||
background:
|
className="flex items-center justify-center rounded-full shrink-0"
|
||||||
idx === 0
|
style={{
|
||||||
? "transparent"
|
width: 40,
|
||||||
: reachedLeft
|
height: 40,
|
||||||
? "#0EA371"
|
backgroundColor: circleBg,
|
||||||
: "#E1E7EE",
|
border: circleBorder,
|
||||||
}}
|
boxShadow: circleShadow,
|
||||||
/>
|
}}
|
||||||
<Box
|
>
|
||||||
style={{
|
{state === "done" ? (
|
||||||
width: 40,
|
<Check size={16} color="#fff" />
|
||||||
height: 40,
|
) : state === "active" ? (
|
||||||
display: "flex",
|
<Icon size={16} color="#fff" />
|
||||||
alignItems: "center",
|
) : null}
|
||||||
justifyContent: "center",
|
</div>
|
||||||
borderRadius: 999,
|
{/* right connector */}
|
||||||
...circleStyle,
|
<div
|
||||||
}}
|
className="flex-1 rounded-full"
|
||||||
|
style={{
|
||||||
|
height: 3,
|
||||||
|
background:
|
||||||
|
idx === last ? "transparent" : reachedRight ? "#0EA371" : "#E1E7EE",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Text
|
||||||
|
fz="13.5px"
|
||||||
|
fw={state === "active" ? 800 : 700}
|
||||||
|
ta="center"
|
||||||
|
c={state === "idle" ? "#9AA8B5" : "#10202F"}
|
||||||
>
|
>
|
||||||
{state === "done" ? (
|
{stage.label}
|
||||||
<Check size={16} color="#fff" />
|
</Text>
|
||||||
) : state === "active" ? (
|
<Text
|
||||||
<Icon size={16} color="#fff" />
|
fz="11.5px"
|
||||||
) : null}
|
fw={state === "active" ? 700 : 500}
|
||||||
</Box>
|
ta="center"
|
||||||
<Box
|
c={state === "active" ? activeSub : "#9AA8B5"}
|
||||||
flex={1}
|
>
|
||||||
style={{
|
{state === "done"
|
||||||
height: 3,
|
? "Completed"
|
||||||
borderRadius: 999,
|
: state === "active"
|
||||||
background:
|
? negative
|
||||||
idx === last
|
? "Stopped"
|
||||||
? "transparent"
|
: "In progress"
|
||||||
: reachedRight
|
: "Pending"}
|
||||||
? "#0EA371"
|
</Text>
|
||||||
: "#E1E7EE",
|
</div>
|
||||||
}}
|
);
|
||||||
/>
|
})}
|
||||||
</Box>
|
</div>
|
||||||
<Text
|
</Box>
|
||||||
fz="13.5px"
|
|
||||||
fw={state === "active" ? 800 : 700}
|
|
||||||
ta="center"
|
|
||||||
c={state === "idle" ? "#9AA8B5" : "#10202F"}
|
|
||||||
>
|
|
||||||
{stage.label}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
fz="11.5px"
|
|
||||||
fw={state === "active" ? 700 : 500}
|
|
||||||
ta="center"
|
|
||||||
c={state === "active" ? activeSub : "#9AA8B5"}
|
|
||||||
>
|
|
||||||
{state === "done"
|
|
||||||
? "Completed"
|
|
||||||
: state === "active"
|
|
||||||
? negative
|
|
||||||
? "Stopped"
|
|
||||||
: "In progress"
|
|
||||||
: "Pending"}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Group>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ export function BodyGrid({ left, right }: { left: ReactNode; right: ReactNode })
|
|||||||
align={{ base: "stretch", lg: "flex-start" }}
|
align={{ base: "stretch", lg: "flex-start" }}
|
||||||
gap={24}
|
gap={24}
|
||||||
>
|
>
|
||||||
<Box flex={1} miw={0} display="flex" style={{ flexDirection: "column", gap: 24 }}>
|
{/* Left column: capped at 700px on tablet so it doesn't bleed edge-to-edge */}
|
||||||
|
<Box flex={1} miw={0} maw={700} className="flex flex-col gap-6 lg:max-w-none">
|
||||||
{left}
|
{left}
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
w={360}
|
w={360}
|
||||||
maw="100%"
|
maw="100%"
|
||||||
display="flex"
|
className="flex flex-col gap-6 shrink-0"
|
||||||
style={{ flexDirection: "column", gap: 24, flexShrink: 0 }}
|
|
||||||
>
|
>
|
||||||
{right}
|
{right}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user