update dark and ligth theme
This commit is contained in:
@@ -24,7 +24,7 @@ interface MockUserRecord extends AuthUser {
|
||||
const USERS: MockUserRecord[] = [
|
||||
{
|
||||
id: 'usr_owner_01',
|
||||
email: 'aravind@nealre.in',
|
||||
email: 'aravind@loyaly.in',
|
||||
name: 'Aravind',
|
||||
role: 'owner',
|
||||
organisation: 'Loyaly Retail Pvt Ltd',
|
||||
@@ -39,12 +39,20 @@ const USERS: MockUserRecord[] = [
|
||||
password: 'admin123',
|
||||
},
|
||||
{
|
||||
id: 'usr_mgr_01',
|
||||
email: 'priya.sharma@loyaly.ai',
|
||||
name: 'Priya Sharma',
|
||||
role: 'manager',
|
||||
id: 'usr_owner_03',
|
||||
email: 'fazul@loyaly.in',
|
||||
name: 'Fazul Ilahi',
|
||||
role: 'owner',
|
||||
organisation: 'Loyaly Retail Pvt Ltd',
|
||||
password: 'indiranagar@01',
|
||||
password: 'admin123',
|
||||
},
|
||||
{
|
||||
id: 'usr_mgr_01',
|
||||
email: 'suriya@loyaly.in',
|
||||
name: 'Suriya',
|
||||
role: 'owner',
|
||||
organisation: 'Loyaly Retail Pvt Ltd',
|
||||
password: 'admin123',
|
||||
},
|
||||
{
|
||||
id: 'usr_analyst_01',
|
||||
|
||||
@@ -18,8 +18,8 @@ export function CommerceRecentOrders() {
|
||||
<div className="rounded-xl border border-border bg-card p-4 space-y-3 shadow-sm">
|
||||
<div className="flex items-center justify-between border-b border-border pb-3">
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white flex items-center gap-2">
|
||||
<Icon icon={ICONS.purchases} size="sm" className="text-zinc-400" />
|
||||
<Heading level={4} className="text-base font-bold text-primary flex items-center gap-2">
|
||||
<Icon icon={ICONS.purchases} size="sm" className="text-secondary" />
|
||||
Recent Orders
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
@@ -29,21 +29,21 @@ export function CommerceRecentOrders() {
|
||||
<Badge variant="success" label="Live Feed" />
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-zinc-800/80">
|
||||
<div className="divide-y divide-border">
|
||||
{RECENT_ORDERS.map((ord) => (
|
||||
<div key={ord.id} className="py-2.5 first:pt-0 last:pb-0 flex items-center justify-between text-sm">
|
||||
<div>
|
||||
<div className="font-bold text-white flex items-center gap-2">
|
||||
<div className="font-bold text-primary flex items-center gap-2">
|
||||
{ord.code}
|
||||
<span className="text-xs text-zinc-400 font-normal">• {ord.customer}</span>
|
||||
<span className="text-xs text-secondary font-normal">• {ord.customer}</span>
|
||||
</div>
|
||||
<div className="text-xs text-zinc-400 font-medium">
|
||||
<div className="text-xs text-secondary font-medium">
|
||||
{ord.store} ({ord.method})
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="font-bold text-white tabular-nums">{ord.amount}</div>
|
||||
<div className="text-xs text-zinc-400">{ord.time}</div>
|
||||
<div className="font-bold text-primary tabular-nums">{ord.amount}</div>
|
||||
<div className="text-xs text-secondary">{ord.time}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -13,7 +13,7 @@ export function CommerceRevenueSummary() {
|
||||
return (
|
||||
<div className="rounded-xl border border-border bg-card p-4 space-y-4 shadow-sm">
|
||||
<div className="border-b border-border pb-3">
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Revenue Summary
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
|
||||
@@ -34,7 +34,7 @@ export function CommerceSalesInsights() {
|
||||
<div className="rounded-xl border border-border bg-card p-4 space-y-3 shadow-sm">
|
||||
<div className="flex items-center justify-between border-b border-border pb-3">
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white flex items-center gap-2">
|
||||
<Heading level={4} className="text-base font-bold text-primary flex items-center gap-2">
|
||||
<Icon icon={ICONS.ai} size="sm" className="text-amber-400" />
|
||||
Sales Insights
|
||||
</Heading>
|
||||
@@ -55,7 +55,7 @@ export function CommerceSalesInsights() {
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-bold text-white">{item.title}</span>
|
||||
<span className="text-xs font-bold text-primary">{item.title}</span>
|
||||
<Badge
|
||||
variant={item.intent === 'warning' ? 'warning' : 'success'}
|
||||
label={item.badge}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function CommerceWorkspace() {
|
||||
return (
|
||||
<VStack gap={5}>
|
||||
{/* Sticky Header matching Dashboard visual language */}
|
||||
<div className="sticky -top-5 z-40 -mx-5 px-5 pt-5 pb-4 bg-[#0F0F10] border-b border-[#2F2F2F] shadow-sm">
|
||||
<div className="sticky -top-5 z-40 -mx-5 px-5 pt-5 pb-4 bg-surface border-b border-border shadow-sm">
|
||||
<PageHeader
|
||||
eyebrow="Sales & Revenue Analytics"
|
||||
title="Commerce"
|
||||
|
||||
@@ -9,7 +9,7 @@ import {BUSINESS_ALERTS} from '../../services/commerceService';
|
||||
export function BusinessAlerts() {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Real-Time Business & Operational Alerts
|
||||
</Heading>
|
||||
|
||||
@@ -48,7 +48,7 @@ export function BusinessAlerts() {
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<span className="text-sm font-bold text-white block">{alert.title}</span>
|
||||
<span className="text-sm font-bold text-primary block">{alert.title}</span>
|
||||
<Text size="sm" color="secondary" className="mt-0.5">
|
||||
{alert.description}
|
||||
</Text>
|
||||
@@ -60,10 +60,10 @@ export function BusinessAlerts() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pt-2 border-t border-white/5 flex justify-end">
|
||||
<div className="pt-2 border-t border-border flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs font-bold text-white hover:text-amber-300 transition-colors flex items-center gap-1"
|
||||
className="text-xs font-bold text-primary hover:text-amber-300 transition-colors flex items-center gap-1"
|
||||
>
|
||||
{alert.actionText} →
|
||||
</button>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function OperationalFlowcharts() {
|
||||
return (
|
||||
<div className="rounded-xl border border-border bg-card p-5 space-y-6 shadow-sm overflow-hidden">
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Operational Flow Diagrams
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
@@ -48,12 +48,12 @@ export function OperationalFlowcharts() {
|
||||
<div className="flex items-center gap-2 min-w-[640px]">
|
||||
{ORDER_FLOW.map((node, i) => (
|
||||
<div key={node.step} className="flex items-center gap-2 flex-1">
|
||||
<div className="flex-1 rounded-lg border border-zinc-700 bg-zinc-900/90 p-2.5 text-center shadow-sm">
|
||||
<div className="text-xs font-bold text-white">{node.step}</div>
|
||||
<div className="text-[10px] text-zinc-400 font-medium mt-0.5">{node.detail}</div>
|
||||
<div className="flex-1 rounded-lg border border-border-strong bg-card p-2.5 text-center shadow-sm">
|
||||
<div className="text-xs font-bold text-primary">{node.step}</div>
|
||||
<div className="text-[10px] text-secondary font-medium mt-0.5">{node.detail}</div>
|
||||
</div>
|
||||
{i < ORDER_FLOW.length - 1 ? (
|
||||
<span className="text-zinc-500 font-bold text-sm shrink-0">→</span>
|
||||
<span className="text-disabled font-bold text-sm shrink-0">→</span>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
@@ -72,7 +72,7 @@ export function OperationalFlowcharts() {
|
||||
<div key={node.step} className="flex items-center gap-2 flex-1">
|
||||
<div className="flex-1 rounded-lg border border-emerald-950/60 bg-emerald-950/20 p-2.5 text-center shadow-sm">
|
||||
<div className="text-xs font-bold text-emerald-300">{node.step}</div>
|
||||
<div className="text-[10px] text-zinc-400 font-medium mt-0.5">{node.detail}</div>
|
||||
<div className="text-[10px] text-secondary font-medium mt-0.5">{node.detail}</div>
|
||||
</div>
|
||||
{i < REVENUE_FLOW.length - 1 ? (
|
||||
<span className="text-emerald-600 font-bold text-sm shrink-0">→</span>
|
||||
@@ -94,7 +94,7 @@ export function OperationalFlowcharts() {
|
||||
<div key={node.step} className="flex items-center gap-2 flex-1">
|
||||
<div className="flex-1 rounded-lg border border-indigo-950/60 bg-indigo-950/20 p-2.5 text-center shadow-sm">
|
||||
<div className="text-xs font-bold text-indigo-300">{node.step}</div>
|
||||
<div className="text-[10px] text-zinc-400 font-medium mt-0.5">{node.detail}</div>
|
||||
<div className="text-[10px] text-secondary font-medium mt-0.5">{node.detail}</div>
|
||||
</div>
|
||||
{i < INVENTORY_FLOW.length - 1 ? (
|
||||
<span className="text-indigo-600 font-bold text-sm shrink-0">→</span>
|
||||
|
||||
@@ -15,14 +15,14 @@ export function OrdersChart() {
|
||||
<div className="rounded-xl border border-border bg-card p-4 space-y-3 overflow-hidden shadow-sm">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Orders Volume Trend
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
Completed order count grouped by hourly buckets
|
||||
</Text>
|
||||
</div>
|
||||
<span className="text-xs font-bold text-white bg-zinc-800 px-2.5 py-1 rounded-full border border-zinc-700">
|
||||
<span className="text-xs font-bold text-primary bg-muted px-2.5 py-1 rounded-full border border-border-strong">
|
||||
{totalOrders.toLocaleString()} Orders
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ export function PaymentBreakdown() {
|
||||
<div className="rounded-xl border border-border bg-card p-5 space-y-4 shadow-sm overflow-hidden">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Payment Methods Breakdown
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
@@ -34,8 +34,8 @@ export function PaymentBreakdown() {
|
||||
onClick={() => setSelectedMethod(null)}
|
||||
className={`px-2.5 py-1 rounded-md text-xs font-bold transition-all ${
|
||||
selectedMethod === null
|
||||
? 'bg-zinc-700 text-white shadow-sm'
|
||||
: 'bg-zinc-900/80 text-zinc-400 hover:text-white border border-zinc-800'
|
||||
? 'bg-muted text-primary shadow-sm'
|
||||
: 'bg-card text-secondary hover:text-primary border border-border'
|
||||
}`}
|
||||
>
|
||||
All
|
||||
@@ -49,8 +49,8 @@ export function PaymentBreakdown() {
|
||||
}
|
||||
className={`px-2.5 py-1 rounded-md text-xs font-bold transition-all flex items-center gap-1.5 ${
|
||||
selectedMethod === item.method
|
||||
? 'text-white border shadow-sm'
|
||||
: 'bg-zinc-900/80 text-zinc-400 hover:text-white border border-zinc-800'
|
||||
? 'text-primary border shadow-sm'
|
||||
: 'bg-card text-secondary hover:text-primary border border-border'
|
||||
}`}
|
||||
style={{
|
||||
borderColor: selectedMethod === item.method ? item.color : undefined,
|
||||
@@ -68,7 +68,7 @@ export function PaymentBreakdown() {
|
||||
</div>
|
||||
|
||||
{/* Visual Ring / Stack Bar */}
|
||||
<div className="h-4 w-full rounded-full bg-zinc-800 overflow-hidden flex shadow-inner p-0.5">
|
||||
<div className="h-4 w-full rounded-full bg-muted overflow-hidden flex shadow-inner p-0.5">
|
||||
{paymentData.map((item) => {
|
||||
const isSelected = selectedMethod === null || selectedMethod === item.method;
|
||||
return (
|
||||
@@ -101,10 +101,10 @@ export function PaymentBreakdown() {
|
||||
}
|
||||
className={`flex items-center justify-between p-3 rounded-xl cursor-pointer transition-all border ${
|
||||
isSelected
|
||||
? 'bg-zinc-800/90 shadow-md ring-1'
|
||||
? 'bg-muted shadow-md ring-1'
|
||||
: selectedMethod !== null
|
||||
? 'bg-zinc-900/40 border-zinc-800/60 opacity-60 hover:opacity-100'
|
||||
: 'bg-zinc-900/60 border-zinc-800 hover:border-zinc-700'
|
||||
? 'bg-card border-border opacity-60 hover:opacity-100'
|
||||
: 'bg-card border-border hover:border-border-strong'
|
||||
}`}
|
||||
style={{
|
||||
borderColor: isSelected ? item.color : undefined,
|
||||
@@ -116,17 +116,17 @@ export function PaymentBreakdown() {
|
||||
style={{backgroundColor: item.color}}
|
||||
/>
|
||||
<div>
|
||||
<span className="text-xs font-bold text-white block truncate">
|
||||
<span className="text-xs font-bold text-primary block truncate">
|
||||
{item.method}
|
||||
</span>
|
||||
<span className="text-[10px] font-semibold text-zinc-400">
|
||||
<span className="text-[10px] font-semibold text-secondary">
|
||||
{item.percentage}% share
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-right shrink-0">
|
||||
<div className="text-xs font-bold text-white tabular-nums">
|
||||
<div className="text-xs font-bold text-primary tabular-nums">
|
||||
{item.amount}
|
||||
</div>
|
||||
{isSelected ? (
|
||||
@@ -140,21 +140,21 @@ export function PaymentBreakdown() {
|
||||
|
||||
{/* Selected Channel Deep-Dive Banner */}
|
||||
{activeItem ? (
|
||||
<div className="p-3 rounded-lg bg-zinc-900/90 border border-zinc-700 flex items-center justify-between text-xs animate-fadeIn">
|
||||
<div className="p-3 rounded-lg bg-card border border-border-strong flex items-center justify-between text-xs animate-fadeIn">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="size-2.5 rounded-full"
|
||||
style={{backgroundColor: activeItem.color}}
|
||||
/>
|
||||
<span className="font-bold text-white">{activeItem.method} Channel Details:</span>
|
||||
<span className="text-zinc-300">
|
||||
<span className="font-bold text-primary">{activeItem.method} Channel Details:</span>
|
||||
<span className="text-secondary">
|
||||
{activeItem.amount} collected across selected scope terminals.
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedMethod(null)}
|
||||
className="text-zinc-400 hover:text-white font-bold text-xs"
|
||||
className="text-secondary hover:text-primary font-bold text-xs"
|
||||
>
|
||||
Clear Filter ✕
|
||||
</button>
|
||||
|
||||
@@ -13,7 +13,7 @@ export function RevenueChart() {
|
||||
<div className="rounded-xl border border-border bg-card p-4 space-y-3 overflow-hidden shadow-sm">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Revenue Trend
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
|
||||
@@ -9,7 +9,7 @@ export function StorePerformanceChart() {
|
||||
return (
|
||||
<div className="rounded-xl border border-border bg-card p-5 space-y-4 shadow-sm overflow-hidden">
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Top Performing Outlets
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
@@ -23,12 +23,14 @@ export function StorePerformanceChart() {
|
||||
return (
|
||||
<div key={store.store} className="space-y-1">
|
||||
<div className="flex justify-between items-center text-xs font-semibold">
|
||||
<span className="text-white font-bold">{store.store}</span>
|
||||
<span className="text-primary font-bold">{store.store}</span>
|
||||
<span className="text-emerald-400 font-bold tabular-nums">
|
||||
₹{(store.Revenue / 100000).toFixed(1)}L
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-6 w-full rounded-md bg-zinc-900/80 p-0.5 border border-zinc-800">
|
||||
{/* bg-muted, not bg-card: the track sits INSIDE a card, so it
|
||||
needs the next surface step up to read as a track at all. */}
|
||||
<div className="h-6 w-full rounded-md bg-muted p-0.5 border border-border">
|
||||
<div
|
||||
className="h-full rounded bg-gradient-to-r from-emerald-600 to-emerald-400 transition-all duration-500"
|
||||
style={{width: `${widthPercent}%`}}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function TopProductsTable() {
|
||||
return (
|
||||
<div className="rounded-xl border border-border bg-card overflow-hidden shadow-sm space-y-3">
|
||||
<div className="p-4 border-b border-border bg-card">
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
Top Selling Products Leaderboard
|
||||
</Heading>
|
||||
<Text size="sm" color="secondary">
|
||||
@@ -23,7 +23,7 @@ export function TopProductsTable() {
|
||||
<div className="overflow-x-auto w-full">
|
||||
<table className="w-full text-left text-sm border-collapse">
|
||||
<thead>
|
||||
<tr className="border-b border-zinc-800 bg-zinc-900/60 text-white font-bold">
|
||||
<tr className="border-b border-border bg-card text-primary font-bold">
|
||||
<th className="px-4 py-3 text-left">Product Name</th>
|
||||
<th className="px-4 py-3 text-left">Category</th>
|
||||
<th className="px-4 py-3 text-right">Units Sold</th>
|
||||
@@ -32,13 +32,13 @@ export function TopProductsTable() {
|
||||
<th className="px-4 py-3 text-center">Stock Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-zinc-800/60">
|
||||
<tbody className="divide-y divide-border">
|
||||
{products.map((prod) => (
|
||||
<tr key={prod.id} className="hover:bg-white/[0.02] transition-colors">
|
||||
<td className="px-4 py-3.5 text-white font-semibold">{prod.name}</td>
|
||||
<td className="px-4 py-3.5 text-zinc-300 font-medium">{prod.category}</td>
|
||||
<td className="px-4 py-3.5 text-right text-white font-bold tabular-nums">{prod.unitsSold.toLocaleString()}</td>
|
||||
<td className="px-4 py-3.5 text-right text-white font-bold tabular-nums">{prod.revenue}</td>
|
||||
<tr key={prod.id} className="hover:bg-overlay-hover transition-colors">
|
||||
<td className="px-4 py-3.5 text-primary font-semibold">{prod.name}</td>
|
||||
<td className="px-4 py-3.5 text-secondary font-medium">{prod.category}</td>
|
||||
<td className="px-4 py-3.5 text-right text-primary font-bold tabular-nums">{prod.unitsSold.toLocaleString()}</td>
|
||||
<td className="px-4 py-3.5 text-right text-primary font-bold tabular-nums">{prod.revenue}</td>
|
||||
<td className="px-4 py-3.5 text-right font-bold tabular-nums">
|
||||
<span className={prod.growthDirection === 'up' ? 'text-emerald-400' : 'text-rose-400'}>
|
||||
{prod.growth}
|
||||
|
||||
@@ -29,8 +29,8 @@ export function CommerceFilterToolbar({
|
||||
onClick={() => onFilterChange({...filters, dateRange: r})}
|
||||
className={`px-2.5 py-1 rounded-md transition-colors capitalize ${
|
||||
filters.dateRange === r
|
||||
? 'bg-zinc-800 text-white font-bold'
|
||||
: 'text-zinc-400 hover:text-white'
|
||||
? 'bg-muted text-primary font-bold'
|
||||
: 'text-secondary hover:text-primary'
|
||||
}`}
|
||||
>
|
||||
{r === 'today' ? 'Today' : r === '7d' ? '7 Days' : r === '30d' ? '30 Days' : '90 Days'}
|
||||
@@ -42,7 +42,7 @@ export function CommerceFilterToolbar({
|
||||
<select
|
||||
value={filters.storeId}
|
||||
onChange={(e) => onFilterChange({...filters, storeId: e.target.value})}
|
||||
className="h-8 rounded-lg border border-border bg-card px-2.5 text-xs font-semibold text-white outline-none focus:border-zinc-500"
|
||||
className="h-8 rounded-lg border border-border bg-card px-2.5 text-xs font-semibold text-primary outline-none focus:border-border-strong"
|
||||
>
|
||||
<option value="all">All Active Stores</option>
|
||||
<option value="indiranagar">Indiranagar Flagship</option>
|
||||
|
||||
@@ -33,15 +33,15 @@ import {ICONS} from '@/shared/utils/icons';
|
||||
const MAX_HEIGHT_PX = 132;
|
||||
|
||||
const PILL = [
|
||||
'rounded-[28px] bg-zinc-900/90 border border-zinc-700/80 shadow-md',
|
||||
'rounded-[28px] bg-card border border-border-strong shadow-md',
|
||||
'transition-colors duration-150',
|
||||
'focus-within:border-zinc-500 focus-within:ring-1 focus-within:ring-zinc-500',
|
||||
'focus-within:border-border-strong focus-within:ring-1 focus-within:ring-border-strong',
|
||||
'pl-4 pr-2 py-1.5',
|
||||
].join(' ');
|
||||
|
||||
const FIELD = [
|
||||
'flex-1 min-w-0 resize-none bg-transparent border-0 outline-none',
|
||||
'text-sm font-medium text-white placeholder:text-zinc-400 placeholder:font-normal',
|
||||
'text-sm font-medium text-primary placeholder:text-secondary placeholder:font-normal',
|
||||
'py-2 leading-6 overflow-y-auto [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden',
|
||||
].join(' ');
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ export function ChartBlock({charts}: {charts: ChartPartSpec[]}) {
|
||||
className="rounded-xl border border-border bg-card p-4 shadow-sm space-y-3 overflow-hidden"
|
||||
>
|
||||
<div>
|
||||
<Heading level={4} className="text-base font-bold text-white">
|
||||
<Heading level={4} className="text-base font-bold text-primary">
|
||||
{c.title}
|
||||
</Heading>
|
||||
{c.subtitle ? (
|
||||
<p className="text-sm font-medium text-zinc-300 mt-0.5">{c.subtitle}</p>
|
||||
<p className="text-sm font-medium text-secondary mt-0.5">{c.subtitle}</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -57,13 +57,13 @@ export function InsightCardsBlock({insights}: {insights: ReportInsight[]}) {
|
||||
<div className="flex flex-wrap items-start justify-between gap-x-2 gap-y-1.5">
|
||||
<div className="flex items-start gap-2 min-w-0">
|
||||
<span className="text-lg shrink-0">{meta.icon}</span>
|
||||
<span className="text-base font-bold text-white whitespace-normal break-words min-w-0">
|
||||
<span className="text-base font-bold text-primary whitespace-normal break-words min-w-0">
|
||||
{insight.title}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
{insight.confidence ? (
|
||||
<span className="text-sm font-semibold text-zinc-300 font-mono tabular-nums whitespace-nowrap">
|
||||
<span className="text-sm font-semibold text-secondary font-mono tabular-nums whitespace-nowrap">
|
||||
{insight.confidence}% confidence
|
||||
</span>
|
||||
) : null}
|
||||
@@ -71,14 +71,14 @@ export function InsightCardsBlock({insights}: {insights: ReportInsight[]}) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-medium text-zinc-200 leading-relaxed whitespace-normal break-words">
|
||||
<p className="text-sm font-medium text-primary leading-relaxed whitespace-normal break-words">
|
||||
{insight.explanation}
|
||||
</p>
|
||||
|
||||
{insight.recommendedAction ? (
|
||||
<div className="pt-2 flex flex-wrap items-baseline gap-x-2 gap-y-1 text-sm font-semibold">
|
||||
<span className="text-amber-300 font-bold shrink-0">Action:</span>
|
||||
<span className="text-white whitespace-normal break-words min-w-0">
|
||||
<span className="text-primary whitespace-normal break-words min-w-0">
|
||||
{insight.recommendedAction}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,7 @@ export function KpiGridBlock({kpis}: {kpis: ReportKpi[]}) {
|
||||
so a long token can still break as a last resort but ordinary
|
||||
prose never breaks mid-word.
|
||||
*/}
|
||||
<span className="text-xs font-semibold text-zinc-300 whitespace-normal break-words min-w-0 flex-1">
|
||||
<span className="text-xs font-semibold text-secondary whitespace-normal break-words min-w-0 flex-1">
|
||||
{kpi.title}
|
||||
</span>
|
||||
{kpi.iconName ? (
|
||||
@@ -98,14 +98,14 @@ export function KpiGridBlock({kpis}: {kpis: ReportKpi[]}) {
|
||||
width decides whether there is room for the larger setting,
|
||||
which is the question `sm:` was answering with the window's.
|
||||
*/}
|
||||
<span className="text-xl @xs/report:text-2xl font-bold tracking-tight text-white tabular-nums whitespace-nowrap shrink-0">
|
||||
<span className="text-xl @xs/report:text-2xl font-bold tracking-tight text-primary tabular-nums whitespace-nowrap shrink-0">
|
||||
{kpi.value}
|
||||
</span>
|
||||
{kpi.trend ? (
|
||||
<span
|
||||
title={kpi.trend}
|
||||
className={`text-xs font-bold truncate max-w-full min-w-0 ${
|
||||
isUp ? 'text-emerald-400' : isDown ? 'text-rose-400' : 'text-zinc-300'
|
||||
isUp ? 'text-emerald-400' : isDown ? 'text-rose-400' : 'text-secondary'
|
||||
}`}
|
||||
>
|
||||
{kpi.trend}
|
||||
|
||||
@@ -16,7 +16,7 @@ export function TableBlock({tables}: {tables: TablePartSpec[]}) {
|
||||
>
|
||||
{t.title ? (
|
||||
<div className="p-4 border-b border-border bg-card">
|
||||
<Heading level={4} className="text-base font-bold text-white whitespace-normal break-words">
|
||||
<Heading level={4} className="text-base font-bold text-primary whitespace-normal break-words">
|
||||
{t.title}
|
||||
</Heading>
|
||||
</div>
|
||||
@@ -38,11 +38,11 @@ export function TableBlock({tables}: {tables: TablePartSpec[]}) {
|
||||
className="w-full min-w-full table-auto text-left text-sm border-collapse"
|
||||
>
|
||||
<thead>
|
||||
<tr className="border-b border-zinc-700 bg-zinc-800/80 text-white font-bold">
|
||||
<tr className="border-b border-border-strong bg-muted text-primary font-bold">
|
||||
{t.columns.map((col) => (
|
||||
<th
|
||||
key={col.key}
|
||||
className={`px-3.5 py-3.5 whitespace-nowrap text-sm font-bold text-white ${
|
||||
className={`px-3.5 py-3.5 whitespace-nowrap text-sm font-bold text-primary ${
|
||||
col.align === 'end'
|
||||
? 'text-right'
|
||||
: col.align === 'center'
|
||||
@@ -55,9 +55,9 @@ export function TableBlock({tables}: {tables: TablePartSpec[]}) {
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-zinc-800/80">
|
||||
<tbody className="divide-y divide-border">
|
||||
{t.rows.map((row, rIdx) => (
|
||||
<tr key={rIdx} className="hover:bg-white/[0.04] transition-colors">
|
||||
<tr key={rIdx} className="hover:bg-overlay-hover transition-colors">
|
||||
{t.columns.map((col) => {
|
||||
const val = row[col.key];
|
||||
const valStr = String(val ?? '');
|
||||
@@ -66,7 +66,7 @@ export function TableBlock({tables}: {tables: TablePartSpec[]}) {
|
||||
return (
|
||||
<td
|
||||
key={col.key}
|
||||
className={`px-3.5 py-3.5 text-white font-semibold align-middle text-sm ${
|
||||
className={`px-3.5 py-3.5 text-primary font-semibold align-middle text-sm ${
|
||||
col.align === 'end'
|
||||
? 'text-right tabular-nums whitespace-nowrap'
|
||||
: col.align === 'center'
|
||||
|
||||
@@ -8,9 +8,11 @@ import {Button} from '@astryxdesign/core/Button';
|
||||
import {Text} from '@astryxdesign/core/Text';
|
||||
import {useToast} from '@astryxdesign/core/Toast';
|
||||
import {StaticPanel} from '@/shared/components/patterns/PanelCard';
|
||||
import {useThemeMode} from '@/shared/providers/ThemeModeProvider';
|
||||
import type {ThemeMode} from '@/shared/theme/themeMode';
|
||||
|
||||
/** Theme mode is deliberately absent — it lives in ThemeModeProvider. */
|
||||
export interface SystemPreferences {
|
||||
theme: 'dark' | 'light' | 'system';
|
||||
currency: 'INR' | 'USD' | 'EUR' | 'GBP';
|
||||
language: 'en' | 'hi' | 'kn' | 'ta';
|
||||
dateFormat: 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY-MM-DD';
|
||||
@@ -18,7 +20,6 @@ export interface SystemPreferences {
|
||||
}
|
||||
|
||||
const DEFAULT_PREFS: SystemPreferences = {
|
||||
theme: 'dark',
|
||||
currency: 'INR',
|
||||
language: 'en',
|
||||
dateFormat: 'DD/MM/YYYY',
|
||||
@@ -27,6 +28,14 @@ const DEFAULT_PREFS: SystemPreferences = {
|
||||
|
||||
export function PreferencesForm() {
|
||||
const toast = useToast();
|
||||
/**
|
||||
* Theme is the one preference that does NOT go through the save cycle.
|
||||
* It repaints the workspace the instant it changes and persists itself to
|
||||
* the theme-mode cookie, so there is nothing left for Save to commit — and
|
||||
* a mode you can see but have not "saved" would be the confusing state.
|
||||
* The rest of the form is still mock local state.
|
||||
*/
|
||||
const {mode: themeMode, setMode: setThemeMode} = useThemeMode();
|
||||
const [prefs, setPrefs] = useState<SystemPreferences>(DEFAULT_PREFS);
|
||||
const [savedPrefs, setSavedPrefs] = useState<SystemPreferences>(DEFAULT_PREFS);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
@@ -80,8 +89,8 @@ export function PreferencesForm() {
|
||||
<FormLayout direction="horizontal">
|
||||
<Selector
|
||||
label="Theme Interface Mode"
|
||||
value={prefs.theme}
|
||||
onChange={(v) => set('theme', v as SystemPreferences['theme'])}
|
||||
value={themeMode}
|
||||
onChange={(v) => setThemeMode(v as ThemeMode)}
|
||||
options={[
|
||||
{label: 'Dark Mode (Monochrome Premium)', value: 'dark'},
|
||||
{label: 'Light Mode', value: 'light'},
|
||||
|
||||
Reference in New Issue
Block a user