mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
always-on calendar, currency cards
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { Box, Text } from "@mantine/core";
|
||||
import { Check } from "lucide-react";
|
||||
|
||||
export interface CurrencySelectorProps {
|
||||
/** Selected currency code, or "" when none picked yet. */
|
||||
value: string;
|
||||
onChange: (currency: "USD" | "ETB") => void;
|
||||
disabled?: boolean;
|
||||
/** Validation error shown under the cards. */
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const OPTIONS = [
|
||||
{
|
||||
code: "USD",
|
||||
symbol: "$",
|
||||
name: "US Dollar",
|
||||
hint: "As quoted on the contract",
|
||||
},
|
||||
{
|
||||
code: "ETB",
|
||||
symbol: "Br",
|
||||
name: "Ethiopian Birr",
|
||||
hint: "Converted from the USD total",
|
||||
},
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Card-style USD/ETB billing-currency picker. Renders unselected when `value`
|
||||
* is "" so a required choice never looks pre-made.
|
||||
*/
|
||||
export function CurrencySelector({
|
||||
value,
|
||||
onChange,
|
||||
disabled = false,
|
||||
error,
|
||||
}: CurrencySelectorProps) {
|
||||
return (
|
||||
<Box>
|
||||
<Box
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
{OPTIONS.map((o) => {
|
||||
const selected = value === o.code;
|
||||
return (
|
||||
<button
|
||||
key={o.code}
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
aria-pressed={selected}
|
||||
onClick={() => onChange(o.code)}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 12,
|
||||
textAlign: "left",
|
||||
padding: "12px 14px",
|
||||
borderRadius: 12,
|
||||
border: selected
|
||||
? "1.5px solid #12B981"
|
||||
: error
|
||||
? "1px solid #FCA5A5"
|
||||
: "1px solid #E6ECF2",
|
||||
background: selected ? "#F6FBF8" : "#fff",
|
||||
cursor: disabled ? "default" : "pointer",
|
||||
opacity: disabled && !selected ? 0.55 : 1,
|
||||
transition: "border-color 120ms ease, background 120ms ease",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
width: 38,
|
||||
height: 38,
|
||||
flexShrink: 0,
|
||||
borderRadius: 11,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: 15,
|
||||
fontWeight: 800,
|
||||
background: selected ? "#ECF6F1" : "#F1F4F7",
|
||||
color: selected ? "#0A6F4D" : "#6B7C8E",
|
||||
}}
|
||||
>
|
||||
{o.symbol}
|
||||
</Box>
|
||||
<Box style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text fz={14} fw={700} c="#10202F" lh={1.25}>
|
||||
{o.code}
|
||||
<Text component="span" fz={12.5} fw={500} c="dimmed">
|
||||
{" "}
|
||||
· {o.name}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text fz={11.5} c="dimmed" lh={1.35}>
|
||||
{o.hint}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box
|
||||
style={{
|
||||
width: 20,
|
||||
height: 20,
|
||||
flexShrink: 0,
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
border: selected ? "none" : "1.5px solid #D4DDE5",
|
||||
background: selected ? "#12B981" : "transparent",
|
||||
}}
|
||||
>
|
||||
{selected && <Check size={12} color="#fff" strokeWidth={3.5} />}
|
||||
</Box>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
{error && (
|
||||
<Text fz={12.5} c="red.6" mt={6}>
|
||||
{error}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CurrencySelector;
|
||||
@@ -0,0 +1,2 @@
|
||||
export { CurrencySelector, default } from "./CurrencySelector";
|
||||
export type { CurrencySelectorProps } from "./CurrencySelector";
|
||||
@@ -18,6 +18,8 @@ export interface OperationDatePickerProps {
|
||||
onChange: (date: string) => void;
|
||||
/** Stretch to the full width of the parent container. */
|
||||
fullWidth?: boolean;
|
||||
/** Text shown under the grid when no days are available. */
|
||||
emptyMessage?: string;
|
||||
}
|
||||
|
||||
/** `yyyy-MM-dd` for a local date. */
|
||||
@@ -58,6 +60,7 @@ export function OperationDatePicker({
|
||||
value,
|
||||
onChange,
|
||||
fullWidth = false,
|
||||
emptyMessage = "No scheduled departures found for this route yet.",
|
||||
}: OperationDatePickerProps) {
|
||||
const [month, setMonth] = useState(() => {
|
||||
const now = new Date();
|
||||
@@ -270,7 +273,7 @@ export function OperationDatePicker({
|
||||
)}
|
||||
{departureDays.size === 0 && (
|
||||
<Text fz="12px" c="orange.7" mt="sm" ta="center">
|
||||
No scheduled departures found for this route yet.
|
||||
{emptyMessage}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -26,6 +26,8 @@ export { OperationDatePicker } from "./components/OperationDatePicker";
|
||||
export type { OperationDatePickerProps } from "./components/OperationDatePicker";
|
||||
export { ExportTrainPicker } from "./components/ExportTrainPicker";
|
||||
export type { ExportTrainPickerProps } from "./components/ExportTrainPicker";
|
||||
export { CurrencySelector } from "./components/CurrencySelector";
|
||||
export type { CurrencySelectorProps } from "./components/CurrencySelector";
|
||||
|
||||
export { CountdownTimer } from "./components/CountdownTimer";
|
||||
export type { CountdownTimerProps } from "./components/CountdownTimer";
|
||||
|
||||
Reference in New Issue
Block a user