feat(admin): apply design system to Stops pages
- Stops list, new, and detail pages use new PageHeader with Operations eyebrow, Stops & Routes title, and editorial subtitle. - StopsCalendarClient: replace hardcoded amber (#f59e0b) and brown (#92400e) draft markers with --admin-warning / --admin-warning-soft tokens. Calendar structure unchanged (drag/click/edit preserved). - Add/Edit/StopDetail modals: replace hardcoded red rgba with --admin-danger-soft and color-mix derived borders. - StopsLocationsTabs: swap hardcoded emerald-* for --admin-primary / --admin-primary-soft tokens. - StopMessagingForm: align with design system (ha-field, ha-eyebrow, tokens). - StopProductAssignment error surface: tokens instead of red rgba. - StopsHeaderActions already tokenized via AdminButton; no change. TS clean. Calendar functionality unchanged.
This commit is contained in:
@@ -4,9 +4,17 @@ import StopProductAssignment from "@/components/admin/StopProductAssignment";
|
|||||||
import MessageCustomersSection from "@/components/admin/MessageCustomersSection";
|
import MessageCustomersSection from "@/components/admin/MessageCustomersSection";
|
||||||
import { getAdminUser } from "@/lib/admin-permissions";
|
import { getAdminUser } from "@/lib/admin-permissions";
|
||||||
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
|
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
|
||||||
|
import { PageHeader } from "@/components/admin/design-system";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const StopIcon = () => (
|
||||||
|
<svg className="h-5 w-5 sm:h-6 sm:w-6 text-current" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/>
|
||||||
|
<circle cx="12" cy="10" r="3"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
type StopDetailPageProps = {
|
type StopDetailPageProps = {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
id: string;
|
id: string;
|
||||||
@@ -68,19 +76,31 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
|
|
||||||
if (!stopRow) {
|
if (!stopRow) {
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen px-6 py-12" style={{ backgroundColor: "var(--admin-bg)" }}>
|
<main className="min-h-screen bg-[var(--admin-bg)]">
|
||||||
|
<div className="px-4 sm:px-6 md:px-8 py-6 sm:py-8">
|
||||||
<div className="mx-auto max-w-4xl">
|
<div className="mx-auto max-w-4xl">
|
||||||
<h1 className="text-3xl font-bold text-red-600">Stop not found</h1>
|
<p className="ha-eyebrow mb-2">Operations</p>
|
||||||
<pre className="mt-4 rounded-xl bg-white border border-stone-200 p-4 text-sm text-stone-600">
|
<PageHeader
|
||||||
|
title="Stop not found"
|
||||||
|
subtitle="This stop may have been removed or you don't have access."
|
||||||
|
icon={<StopIcon />}
|
||||||
|
/>
|
||||||
|
<div className="rounded-2xl border border-[var(--admin-border)] bg-white p-5">
|
||||||
|
<pre className="rounded-xl bg-[var(--admin-bg-subtle)] border border-[var(--admin-border-light)] p-4 text-sm text-[var(--admin-text-secondary)] overflow-auto">
|
||||||
{id}
|
{id}
|
||||||
</pre>
|
</pre>
|
||||||
<Link
|
<Link
|
||||||
href="/admin/stops"
|
href="/admin/stops"
|
||||||
className="mt-4 inline-block text-stone-500 hover:text-stone-700"
|
className="ha-btn-ghost mt-4 inline-flex"
|
||||||
>
|
>
|
||||||
← Back to Stops
|
<svg className="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.2}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||||
|
</svg>
|
||||||
|
Back to Stops
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -148,33 +168,50 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const detailSubtitle = stop.brands?.name
|
||||||
|
? `${stop.brands.name} · ${stop.location}`
|
||||||
|
: stop.location;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen px-6 py-12" style={{ backgroundColor: "var(--admin-bg)" }}>
|
<main className="min-h-screen bg-[var(--admin-bg)]">
|
||||||
|
<div className="px-4 sm:px-6 md:px-8 pt-4 sm:pt-6">
|
||||||
|
<p className="ha-eyebrow mb-2">Operations</p>
|
||||||
|
<PageHeader
|
||||||
|
title={`${stop.city}, ${stop.state}`}
|
||||||
|
subtitle={detailSubtitle}
|
||||||
|
icon={<StopIcon />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-4 sm:px-6 md:px-8 pb-8 sm:pb-10">
|
||||||
<div className="mx-auto max-w-4xl">
|
<div className="mx-auto max-w-4xl">
|
||||||
<Link
|
<Link
|
||||||
href="/admin/stops"
|
href="/admin/stops"
|
||||||
className="text-sm text-stone-500 hover:text-stone-700"
|
className="ha-btn-ghost mb-4 inline-flex"
|
||||||
>
|
>
|
||||||
← Back to Stops
|
<svg className="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.2}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||||
|
</svg>
|
||||||
|
Back to Stops
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="mt-6 rounded-2xl bg-white p-8 border border-stone-200 shadow-lg">
|
<div className="rounded-2xl bg-white p-8 border border-[var(--admin-border)] shadow-sm">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-semibold uppercase tracking-wide text-stone-500">
|
<p className="text-sm font-semibold uppercase tracking-wide text-[var(--admin-text-muted)]">
|
||||||
{stop.brands?.name}
|
{stop.brands?.name}
|
||||||
</p>
|
</p>
|
||||||
<h1 className="mt-2 text-4xl font-bold text-stone-950">
|
<h1 className="mt-2 text-4xl font-bold text-[var(--admin-text-primary)]">
|
||||||
{stop.city}, {stop.state}
|
{stop.city}, {stop.state}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-2 text-lg text-stone-600">{stop.location}</p>
|
<p className="mt-2 text-lg text-[var(--admin-text-secondary)]">{stop.location}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className={`shrink-0 rounded-full px-4 py-2 text-sm font-medium ${
|
className={`shrink-0 rounded-full px-4 py-2 text-sm font-medium ${
|
||||||
stop.active
|
stop.active
|
||||||
? "bg-emerald-100 text-emerald-700"
|
? "bg-[var(--admin-success-soft)] text-[var(--admin-success)] border border-[var(--admin-success)]/30"
|
||||||
: "bg-stone-200 text-stone-500"
|
: "bg-[var(--admin-bg-subtle)] text-[var(--admin-text-muted)] border border-[var(--admin-border)]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{stop.active ? "Active" : "Inactive"}
|
{stop.active ? "Active" : "Inactive"}
|
||||||
@@ -183,21 +220,21 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
|
|
||||||
<div className="mt-8 grid grid-cols-2 gap-6">
|
<div className="mt-8 grid grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-stone-500">Date</p>
|
<p className="text-sm font-medium text-[var(--admin-text-muted)]">Date</p>
|
||||||
<p className="mt-1 text-lg font-semibold text-stone-950">
|
<p className="mt-1 text-lg font-semibold text-[var(--admin-text-primary)]">
|
||||||
{stop.date}
|
{stop.date}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-stone-500">Time</p>
|
<p className="text-sm font-medium text-[var(--admin-text-muted)]">Time</p>
|
||||||
<p className="mt-1 text-lg font-semibold text-stone-950">
|
<p className="mt-1 text-lg font-semibold text-[var(--admin-text-primary)]">
|
||||||
{stop.time}
|
{stop.time}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{stop.address && (
|
{stop.address && (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-stone-500">Address</p>
|
<p className="text-sm font-medium text-[var(--admin-text-muted)]">Address</p>
|
||||||
<p className="mt-1 text-lg font-semibold text-stone-950">
|
<p className="mt-1 text-lg font-semibold text-[var(--admin-text-primary)]">
|
||||||
{stop.address}
|
{stop.address}
|
||||||
{stop.zip ? `, ${stop.zip}` : ""}
|
{stop.zip ? `, ${stop.zip}` : ""}
|
||||||
</p>
|
</p>
|
||||||
@@ -205,8 +242,8 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
)}
|
)}
|
||||||
{stop.cutoff_date && (
|
{stop.cutoff_date && (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-stone-500">Cutoff</p>
|
<p className="text-sm font-medium text-[var(--admin-text-muted)]">Cutoff</p>
|
||||||
<p className="mt-1 text-lg font-semibold text-stone-950">
|
<p className="mt-1 text-lg font-semibold text-[var(--admin-text-primary)]">
|
||||||
{new Date(stop.cutoff_date + "T00:00:00").toLocaleDateString()}
|
{new Date(stop.cutoff_date + "T00:00:00").toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -217,17 +254,17 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
<div className="mt-4 flex justify-end">
|
<div className="mt-4 flex justify-end">
|
||||||
<a
|
<a
|
||||||
href={`/admin/stops/new?duplicate=${stop.id}`}
|
href={`/admin/stops/new?duplicate=${stop.id}`}
|
||||||
className="rounded-xl border border-stone-300 bg-white px-4 py-2 text-sm font-medium text-stone-700 hover:bg-stone-50"
|
className="ha-btn-ghost"
|
||||||
>
|
>
|
||||||
Duplicate Stop
|
Duplicate Stop
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 rounded-2xl bg-white p-8 border border-stone-200 shadow-lg">
|
<div className="mt-6 rounded-2xl bg-white p-8 border border-[var(--admin-border)] shadow-sm">
|
||||||
<h2 className="text-2xl font-bold text-stone-950">
|
<h2 className="text-2xl font-bold text-[var(--admin-text-primary)]">
|
||||||
Assigned Products
|
Assigned Products
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-1 text-stone-600">
|
<p className="mt-1 text-[var(--admin-text-secondary)]">
|
||||||
Manage which products are available at this stop.
|
Manage which products are available at this stop.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -241,9 +278,9 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 rounded-2xl bg-white p-8 border border-stone-200 shadow-lg">
|
<div className="mt-6 rounded-2xl bg-white p-8 border border-[var(--admin-border)] shadow-sm">
|
||||||
<h2 className="text-2xl font-bold text-stone-950">Edit Stop</h2>
|
<h2 className="text-2xl font-bold text-[var(--admin-text-primary)]">Edit Stop</h2>
|
||||||
<p className="mt-1 text-stone-600">
|
<p className="mt-1 text-[var(--admin-text-secondary)]">
|
||||||
Update stop details, location, and availability.
|
Update stop details, location, and availability.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -269,9 +306,9 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Message Customers */}
|
{/* Message Customers */}
|
||||||
<div className="mt-6 rounded-2xl bg-white p-8 border border-stone-200 shadow-lg">
|
<div className="mt-6 rounded-2xl bg-white p-8 border border-[var(--admin-border)] shadow-sm">
|
||||||
<h2 className="text-2xl font-bold text-stone-950">Message Customers</h2>
|
<h2 className="text-2xl font-bold text-[var(--admin-text-primary)]">Message Customers</h2>
|
||||||
<p className="mt-1 text-stone-600">
|
<p className="mt-1 text-[var(--admin-text-secondary)]">
|
||||||
Send updates to customers with pending pickups at this stop.
|
Send updates to customers with pending pickups at this stop.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -280,6 +317,7 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,17 @@ import NewStopForm from "@/components/admin/NewStopForm";
|
|||||||
import StopProductAssignment from "@/components/admin/StopProductAssignment";
|
import StopProductAssignment from "@/components/admin/StopProductAssignment";
|
||||||
import { getAdminUser } from "@/lib/admin-permissions";
|
import { getAdminUser } from "@/lib/admin-permissions";
|
||||||
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
|
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
|
||||||
|
import { PageHeader } from "@/components/admin/design-system";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const StopIcon = () => (
|
||||||
|
<svg className="h-5 w-5 sm:h-6 sm:w-6 text-current" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/>
|
||||||
|
<circle cx="12" cy="10" r="3"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
type Stop = {
|
type Stop = {
|
||||||
city: string;
|
city: string;
|
||||||
state: string;
|
state: string;
|
||||||
@@ -62,32 +70,39 @@ export default async function NewStopPage({
|
|||||||
[brandId]
|
[brandId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const pageTitle = duplicateFrom ? "Duplicate Stop" : "Create Stop";
|
||||||
|
const pageSubtitle = duplicateFrom
|
||||||
|
? `Pre-filled from ${duplicateFrom.city}, ${duplicateFrom.state}. Edit and save to create.`
|
||||||
|
: "Add a new tour stop for Tuxedo Corn or Indian River Direct.";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen px-6 py-12" style={{ backgroundColor: "var(--admin-bg)" }}>
|
<main className="min-h-screen bg-[var(--admin-bg)]">
|
||||||
<div className="mx-auto max-w-4xl">
|
<div className="px-4 sm:px-6 md:px-8 pt-4 sm:pt-6">
|
||||||
<div className="mb-8">
|
<p className="ha-eyebrow mb-2">Operations</p>
|
||||||
<Link
|
<PageHeader
|
||||||
href="/admin/stops"
|
title={pageTitle}
|
||||||
className="text-sm text-stone-500 hover:text-stone-700"
|
subtitle={pageSubtitle}
|
||||||
>
|
icon={<StopIcon />}
|
||||||
← Back to Stops
|
/>
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl bg-white p-8 border border-stone-200 shadow-lg">
|
<div className="px-4 sm:px-6 md:px-8 pb-8 sm:pb-10">
|
||||||
<h1 className="text-3xl font-bold text-stone-950">
|
<div className="mx-auto max-w-4xl">
|
||||||
{duplicateFrom ? "Duplicate Stop" : "Create Stop"}
|
<Link
|
||||||
</h1>
|
href="/admin/stops"
|
||||||
|
className="ha-btn-ghost mb-4 inline-flex"
|
||||||
<p className="mt-2 text-stone-600">
|
>
|
||||||
{duplicateFrom
|
<svg className="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.2}>
|
||||||
? `Pre-filled from ${duplicateFrom.city}, ${duplicateFrom.state}. Edit and save to create.`
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||||
: "Add a new tour stop for Tuxedo Corn or Indian River Direct."}
|
</svg>
|
||||||
</p>
|
Back to Stops
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="rounded-2xl bg-white p-8 border border-[var(--admin-border)] shadow-sm">
|
||||||
<NewStopForm duplicateFrom={duplicateFrom} />
|
<NewStopForm duplicateFrom={duplicateFrom} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import { getAdminUser } from "@/lib/admin-permissions";
|
|||||||
import { getActiveBrandId } from "@/lib/brand-scope";
|
import { getActiveBrandId } from "@/lib/brand-scope";
|
||||||
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
|
import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
|
||||||
import StopTableClient from "@/components/admin/StopTableClient";
|
import StopTableClient from "@/components/admin/StopTableClient";
|
||||||
|
import { PageHeader } from "@/components/admin/design-system";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
const StopIcon = () => (
|
const StopIcon = () => (
|
||||||
@@ -75,23 +76,26 @@ export default async function AdminStopsPage({ searchParams }: PageProps) {
|
|||||||
<main className="min-h-screen bg-[var(--admin-bg)]">
|
<main className="min-h-screen bg-[var(--admin-bg)]">
|
||||||
<div className="px-4 sm:px-6 md:px-8 py-6 sm:py-8">
|
<div className="px-4 sm:px-6 md:px-8 py-6 sm:py-8">
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<nav className="flex items-center gap-2 text-xs sm:text-sm mb-6">
|
<p className="ha-eyebrow mb-2">Operations</p>
|
||||||
<a href="/admin" className="text-[var(--admin-text-muted)] hover:text-[var(--admin-text-secondary)]">Admin</a>
|
<PageHeader
|
||||||
<span className="text-[var(--admin-text-muted)]">/</span>
|
title="Stops & Routes"
|
||||||
<span className="text-[var(--admin-text-primary)] font-medium">Stops & Routes</span>
|
subtitle="Schedule and manage pickup locations and dates."
|
||||||
</nav>
|
icon={<StopIcon />}
|
||||||
<h1 className="text-2xl sm:text-3xl font-black text-red-600 tracking-tight">Error loading stops</h1>
|
/>
|
||||||
<pre className="mt-4 rounded-xl bg-white border border-[var(--admin-border)] p-4 text-sm text-[var(--admin-text-secondary)] overflow-auto">
|
<div className="rounded-2xl border border-[var(--admin-danger)]/30 bg-[var(--admin-danger-soft)] p-5">
|
||||||
|
<h2 className="text-lg font-semibold text-[var(--admin-danger)]">Error loading stops</h2>
|
||||||
|
<pre className="mt-3 rounded-xl bg-white border border-[var(--admin-border)] p-4 text-sm text-[var(--admin-text-secondary)] overflow-auto">
|
||||||
{error}
|
{error}
|
||||||
</pre>
|
</pre>
|
||||||
<div className="mt-4 p-4 rounded-xl bg-stone-100 text-sm">
|
<div className="mt-4 p-4 rounded-xl bg-[var(--admin-bg-subtle)] text-sm text-[var(--admin-text-secondary)]">
|
||||||
<p className="font-semibold">Debug info:</p>
|
<p className="font-semibold text-[var(--admin-text-primary)]">Debug info:</p>
|
||||||
<p>adminUser.brand_id: {adminUser.brand_id ?? "null"}</p>
|
<p>adminUser.brand_id: {adminUser.brand_id ?? "null"}</p>
|
||||||
<p>adminUser.role: {adminUser.role}</p>
|
<p>adminUser.role: {adminUser.role}</p>
|
||||||
<p>adminUser.email: {adminUser.email}</p>
|
<p>adminUser.email: {adminUser.email}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -115,7 +119,15 @@ export default async function AdminStopsPage({ searchParams }: PageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[var(--admin-bg)]">
|
<div className="min-h-screen bg-[var(--admin-bg)]">
|
||||||
<div className="px-4 sm:px-6 md:px-8 py-6 sm:py-8">
|
<div className="px-4 sm:px-6 md:px-8 pt-4 sm:pt-6">
|
||||||
|
<p className="ha-eyebrow mb-2">Operations</p>
|
||||||
|
<PageHeader
|
||||||
|
title="Stops & Routes"
|
||||||
|
subtitle="Schedule and manage pickup locations and dates."
|
||||||
|
icon={<StopIcon />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="px-4 sm:px-6 md:px-8 pb-6 sm:pb-8">
|
||||||
<StopTableClient stops={stopsForClient} />
|
<StopTableClient stops={stopsForClient} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export default function AddStopModal({ isOpen, onClose, brandId, duplicateFrom,
|
|||||||
<div
|
<div
|
||||||
role="alert"
|
role="alert"
|
||||||
className="flex items-start gap-2 rounded-lg px-3 py-2 text-xs text-[var(--admin-danger)]"
|
className="flex items-start gap-2 rounded-lg px-3 py-2 text-xs text-[var(--admin-danger)]"
|
||||||
style={{ background: "rgba(220, 38, 38, 0.06)", border: "1px solid rgba(220, 38, 38, 0.15)" }}
|
style={{ background: "var(--admin-danger-soft)", border: "1px solid color-mix(in srgb, var(--admin-danger) 25%, transparent)" }}
|
||||||
>
|
>
|
||||||
<svg className="h-3.5 w-3.5 mt-0.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
<svg className="h-3.5 w-3.5 mt-0.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
||||||
<circle cx="12" cy="12" r="10" />
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
|||||||
@@ -185,8 +185,8 @@ export default function EditStopModal({ isOpen, onClose, brandId, stop, onSucces
|
|||||||
{error && (
|
{error && (
|
||||||
<div
|
<div
|
||||||
role="alert"
|
role="alert"
|
||||||
className="flex items-start gap-2 rounded-lg px-3 py-2 text-xs text-red-700"
|
className="flex items-start gap-2 rounded-lg px-3 py-2 text-xs text-[var(--admin-danger)]"
|
||||||
style={{ background: "rgba(220, 38, 38, 0.06)", border: "1px solid rgba(220, 38, 38, 0.15)" }}
|
style={{ background: "var(--admin-danger-soft)", border: "1px solid color-mix(in srgb, var(--admin-danger) 25%, transparent)" }}
|
||||||
>
|
>
|
||||||
<svg className="h-3.5 w-3.5 mt-0.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
<svg className="h-3.5 w-3.5 mt-0.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
||||||
<circle cx="12" cy="12" r="10" />
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
|||||||
@@ -93,19 +93,25 @@ export default function StopDetailModal({ stopId, onDuplicate, onClose }: Props)
|
|||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="h-4 w-1/3 animate-pulse rounded bg-stone-200" />
|
<div className="h-4 w-1/3 animate-pulse rounded bg-[var(--admin-bg-subtle)]" />
|
||||||
<div className="h-4 w-2/3 animate-pulse rounded bg-stone-200" />
|
<div className="h-4 w-2/3 animate-pulse rounded bg-[var(--admin-bg-subtle)]" />
|
||||||
<div className="h-4 w-1/2 animate-pulse rounded bg-stone-200" />
|
<div className="h-4 w-1/2 animate-pulse rounded bg-[var(--admin-bg-subtle)]" />
|
||||||
</div>
|
</div>
|
||||||
) : loadError ? (
|
) : loadError ? (
|
||||||
<div className="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
|
<div
|
||||||
|
className="rounded-xl border px-4 py-3 text-sm text-[var(--admin-danger)]"
|
||||||
|
style={{
|
||||||
|
background: "var(--admin-danger-soft)",
|
||||||
|
borderColor: "color-mix(in srgb, var(--admin-danger) 25%, transparent)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
{loadError}
|
{loadError}
|
||||||
</div>
|
</div>
|
||||||
) : stop ? (
|
) : stop ? (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-1 rounded-xl border border-[var(--admin-border)] bg-stone-50 p-1"
|
className="flex items-center gap-1 rounded-xl border border-[var(--admin-border)] bg-[var(--admin-bg-subtle)] p-1"
|
||||||
role="tablist"
|
role="tablist"
|
||||||
>
|
>
|
||||||
<TabButton active={tab === "details"} onClick={() => setTab("details")}>
|
<TabButton active={tab === "details"} onClick={() => setTab("details")}>
|
||||||
@@ -223,8 +229,8 @@ function DetailsPanel({
|
|||||||
<span
|
<span
|
||||||
className={`shrink-0 rounded-full px-3 py-1 text-xs font-medium ${
|
className={`shrink-0 rounded-full px-3 py-1 text-xs font-medium ${
|
||||||
stop.active
|
stop.active
|
||||||
? "bg-emerald-100 text-emerald-700"
|
? "bg-[var(--admin-success-soft)] text-[var(--admin-success)]"
|
||||||
: "bg-stone-200 text-stone-500"
|
: "bg-[var(--admin-bg-subtle)] text-[var(--admin-text-muted)]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{stop.active ? "Active" : "Inactive"}
|
{stop.active ? "Active" : "Inactive"}
|
||||||
@@ -265,14 +271,14 @@ function DetailsPanel({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onDuplicate(stop.id)}
|
onClick={() => onDuplicate(stop.id)}
|
||||||
className="rounded-xl border border-[var(--admin-border)] bg-white px-3 py-1.5 text-xs font-medium text-[var(--admin-text-secondary)] hover:bg-stone-50"
|
className="rounded-xl border border-[var(--admin-border)] bg-white px-3 py-1.5 text-xs font-medium text-[var(--admin-text-secondary)] hover:bg-[var(--admin-bg-subtle)]"
|
||||||
>
|
>
|
||||||
Duplicate Stop
|
Duplicate Stop
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<a
|
<a
|
||||||
href={`/admin/stops/new?duplicate=${stop.id}`}
|
href={`/admin/stops/new?duplicate=${stop.id}`}
|
||||||
className="rounded-xl border border-[var(--admin-border)] bg-white px-3 py-1.5 text-xs font-medium text-[var(--admin-text-secondary)] hover:bg-stone-50"
|
className="rounded-xl border border-[var(--admin-border)] bg-white px-3 py-1.5 text-xs font-medium text-[var(--admin-text-secondary)] hover:bg-[var(--admin-bg-subtle)]"
|
||||||
>
|
>
|
||||||
Duplicate Stop
|
Duplicate Stop
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { getStopPendingCustomers, type StopCustomer } from "@/actions/stops/get-stop-customers";
|
import { getStopPendingCustomers, type StopCustomer } from "@/actions/stops/get-stop-customers";
|
||||||
import { sendStopBlast } from "@/actions/communications/stop-blast";
|
import { sendStopBlast } from "@/actions/communications/stop-blast";
|
||||||
|
import { AdminButton } from "@/components/admin/design-system";
|
||||||
|
|
||||||
const quickMessages = [
|
const quickMessages = [
|
||||||
{ label: "Truck running late", value: "Heads up — the truck is running about 15 minutes behind schedule. Thanks for your patience!" },
|
{ label: "Truck running late", value: "Heads up — the truck is running about 15 minutes behind schedule. Thanks for your patience!" },
|
||||||
@@ -84,10 +85,10 @@ export default function StopMessagingForm({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-zinc-100">
|
<h2 className="ha-display text-2xl text-[var(--admin-text-primary)]">
|
||||||
Send Message
|
Send Message
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-1 text-zinc-400">
|
<p className="mt-1 text-sm text-[var(--admin-text-muted)]">
|
||||||
Notify all customers with pending pickups at this stop.
|
Notify all customers with pending pickups at this stop.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -96,18 +97,21 @@ export default function StopMessagingForm({
|
|||||||
<button
|
<button
|
||||||
onClick={loadCustomers}
|
onClick={loadCustomers}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full rounded-xl border-2 border-dashed border-zinc-600 px-6 py-4 text-lg font-medium text-zinc-500 disabled:opacity-50"
|
className="w-full rounded-xl border-2 border-dashed border-[var(--admin-border)] px-6 py-4 text-lg font-medium text-[var(--admin-text-secondary)] hover:border-[var(--admin-primary)] hover:text-[var(--admin-primary)] transition-colors disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{loading ? "Loading customers..." : "Load Pending Customers"}
|
{loading ? "Loading customers..." : "Load Pending Customers"}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{customers.length === 0 ? (
|
{customers.length === 0 ? (
|
||||||
<div className="rounded-xl bg-slate-50 p-6 text-center text-zinc-500">
|
<div className="rounded-xl bg-[var(--admin-bg-subtle)] p-6 text-center text-[var(--admin-text-muted)]">
|
||||||
No pending orders for this stop yet.
|
No pending orders for this stop yet.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-xl bg-green-900/30 p-4 text-sm text-green-400">
|
<div
|
||||||
|
className="rounded-xl p-4 text-sm text-[var(--admin-success)]"
|
||||||
|
style={{ background: "var(--admin-success-soft)" }}
|
||||||
|
>
|
||||||
{customers.length} pending order{customers.length !== 1 ? "s" : ""} found
|
{customers.length} pending order{customers.length !== 1 ? "s" : ""} found
|
||||||
{recipients.length !== customers.length && (
|
{recipients.length !== customers.length && (
|
||||||
<span> — {recipients.length} with contact info</span>
|
<span> — {recipients.length} with contact info</span>
|
||||||
@@ -117,21 +121,21 @@ export default function StopMessagingForm({
|
|||||||
|
|
||||||
{/* Channel */}
|
{/* Channel */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-2 block text-sm font-medium text-zinc-300">
|
<label className="ha-field-label mb-2">
|
||||||
Send via
|
<span>Send via</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-2">
|
||||||
{(["sms", "email", "both"] as const).map((ch) => (
|
{(["sms", "email", "both"] as const).map((ch) => (
|
||||||
<button
|
<button
|
||||||
key={ch}
|
key={ch}
|
||||||
onClick={() => setChannel(ch)}
|
onClick={() => setChannel(ch)}
|
||||||
className={`flex-1 rounded-xl px-4 py-3 text-sm font-medium transition-colors ${
|
className={`flex-1 rounded-xl px-4 py-3 text-sm font-medium transition-colors ${
|
||||||
channel === ch
|
channel === ch
|
||||||
? "bg-slate-900 text-white"
|
? "bg-[var(--admin-primary)] text-white"
|
||||||
: "bg-zinc-950 text-zinc-400 hover:bg-slate-200"
|
: "bg-[var(--admin-bg-subtle)] text-[var(--admin-text-secondary)] hover:bg-[var(--admin-primary-soft)] hover:text-[var(--admin-primary)]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{ch === "sms" ? "📱 SMS" : ch === "email" ? "✉️ Email" : "📱+✉️ Both"}
|
{ch === "sms" ? "SMS" : ch === "email" ? "Email" : "Both"}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -139,8 +143,8 @@ export default function StopMessagingForm({
|
|||||||
|
|
||||||
{/* Quick messages */}
|
{/* Quick messages */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-2 block text-sm font-medium text-zinc-300">
|
<label className="ha-field-label mb-2">
|
||||||
Quick messages
|
<span>Quick messages</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{quickMessages.map((qm) => (
|
{quickMessages.map((qm) => (
|
||||||
@@ -149,8 +153,8 @@ export default function StopMessagingForm({
|
|||||||
onClick={() => applyQuickMessage(qm.value)}
|
onClick={() => applyQuickMessage(qm.value)}
|
||||||
className={`rounded-full px-3 py-1 text-sm font-medium transition-colors ${
|
className={`rounded-full px-3 py-1 text-sm font-medium transition-colors ${
|
||||||
message === qm.value
|
message === qm.value
|
||||||
? "bg-slate-900 text-white"
|
? "bg-[var(--admin-primary)] text-white"
|
||||||
: "bg-zinc-950 text-zinc-400 hover:bg-slate-200"
|
: "bg-[var(--admin-bg-subtle)] text-[var(--admin-text-secondary)] hover:bg-[var(--admin-primary-soft)] hover:text-[var(--admin-primary)]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{qm.label}
|
{qm.label}
|
||||||
@@ -161,8 +165,8 @@ export default function StopMessagingForm({
|
|||||||
|
|
||||||
{/* Message preview */}
|
{/* Message preview */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-2 block text-sm font-medium text-zinc-300">
|
<label className="ha-field-label mb-2">
|
||||||
Message
|
<span>Message</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={message}
|
value={message}
|
||||||
@@ -171,37 +175,44 @@ export default function StopMessagingForm({
|
|||||||
setCustomMessage(e.target.value);
|
setCustomMessage(e.target.value);
|
||||||
}}
|
}}
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full rounded-xl border border-zinc-600 px-4 py-3 text-base outline-none focus:border-slate-900"
|
className="ha-field-textarea"
|
||||||
placeholder="Type your message..."
|
placeholder="Type your message..."
|
||||||
/>
|
/>
|
||||||
<p className="mt-1 text-xs text-slate-400">
|
<p className="mt-1 text-xs text-[var(--admin-text-muted)] tabular-nums">
|
||||||
{message.length} characters
|
{message.length} characters
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="rounded-xl bg-red-900/30 p-4 text-red-400 text-sm">
|
<div
|
||||||
|
role="alert"
|
||||||
|
className="rounded-xl p-4 text-sm text-[var(--admin-danger)]"
|
||||||
|
style={{ background: "var(--admin-danger-soft)" }}
|
||||||
|
>
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{sent > 0 && (
|
{sent > 0 && (
|
||||||
<div className="rounded-xl bg-green-900/30 p-4 text-green-400 text-sm">
|
<div
|
||||||
|
className="rounded-xl p-4 text-sm text-[var(--admin-success)]"
|
||||||
|
style={{ background: "var(--admin-success-soft)" }}
|
||||||
|
>
|
||||||
Message sent to {sent} customer{sent !== 1 ? "s" : ""}!
|
Message sent to {sent} customer{sent !== 1 ? "s" : ""}!
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Recipients */}
|
{/* Recipients */}
|
||||||
{recipients.length > 0 && message && (
|
{recipients.length > 0 && message && (
|
||||||
<div className="rounded-xl border border-zinc-800 p-4">
|
<div className="rounded-xl border border-[var(--admin-border)] bg-white p-4">
|
||||||
<p className="mb-3 text-sm font-medium text-zinc-300">
|
<p className="mb-3 text-sm font-medium text-[var(--admin-text-secondary)]">
|
||||||
Recipients ({recipients.length}):
|
Recipients ({recipients.length}):
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{recipients.map((c) => (
|
{recipients.map((c) => (
|
||||||
<div key={c.id} className="flex justify-between text-sm">
|
<div key={c.id} className="flex justify-between text-sm">
|
||||||
<span className="text-zinc-300">{c.customer_name}</span>
|
<span className="text-[var(--admin-text-primary)]">{c.customer_name}</span>
|
||||||
<span className="text-slate-400">
|
<span className="text-[var(--admin-text-muted)] tabular-nums">
|
||||||
{c.customer_phone ?? c.customer_email ?? "no contact"}
|
{c.customer_phone ?? c.customer_email ?? "no contact"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -210,15 +221,18 @@ export default function StopMessagingForm({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<AdminButton
|
||||||
|
variant="primary"
|
||||||
|
size="lg"
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={!message || recipients.length === 0 || sending}
|
disabled={!message || recipients.length === 0 || sending}
|
||||||
className="w-full rounded-xl bg-slate-900 px-6 py-4 text-lg font-bold text-white disabled:opacity-50"
|
isLoading={sending}
|
||||||
|
className="w-full"
|
||||||
>
|
>
|
||||||
{sending
|
{sending
|
||||||
? "Sending..."
|
? "Sending..."
|
||||||
: `Send to ${recipients.length} customer${recipients.length !== 1 ? "s" : ""}`}
|
: `Send to ${recipients.length} customer${recipients.length !== 1 ? "s" : ""}`}
|
||||||
</button>
|
</AdminButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -203,8 +203,8 @@ export default function StopProductAssignment({
|
|||||||
role="alert"
|
role="alert"
|
||||||
className="flex items-start gap-2 rounded-lg px-3 py-2 text-xs text-[var(--admin-danger)]"
|
className="flex items-start gap-2 rounded-lg px-3 py-2 text-xs text-[var(--admin-danger)]"
|
||||||
style={{
|
style={{
|
||||||
background: "rgba(220, 38, 38, 0.06)",
|
background: "var(--admin-danger-soft)",
|
||||||
border: "1px solid rgba(220, 38, 38, 0.15)",
|
border: "1px solid color-mix(in srgb, var(--admin-danger) 25%, transparent)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg className="h-3.5 w-3.5 mt-0.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
<svg className="h-3.5 w-3.5 mt-0.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ export default function StopsCalendarClient({ stops }: Props) {
|
|||||||
Active
|
Active
|
||||||
</span>
|
</span>
|
||||||
<span className="ha-calendar-legend-item">
|
<span className="ha-calendar-legend-item">
|
||||||
<span className="ha-calendar-legend-swatch" style={{ background: "#f59e0b" }} />
|
<span className="ha-calendar-legend-swatch" style={{ background: "var(--admin-warning)" }} />
|
||||||
Draft
|
Draft
|
||||||
</span>
|
</span>
|
||||||
<span className="ha-calendar-legend-item">
|
<span className="ha-calendar-legend-item">
|
||||||
@@ -513,7 +513,7 @@ function EventPopover({
|
|||||||
className="inline-block w-1.5 h-1.5 rounded-full"
|
className="inline-block w-1.5 h-1.5 rounded-full"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
status === "active" ? "var(--admin-accent)" : status === "draft" ? "#f59e0b" : "var(--admin-text-muted)",
|
status === "active" ? "var(--admin-accent)" : status === "draft" ? "var(--admin-warning)" : "var(--admin-text-muted)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{statusLabel(status)} · {brandName(stop)}
|
{statusLabel(status)} · {brandName(stop)}
|
||||||
@@ -682,19 +682,19 @@ function DayRouteDrawer({
|
|||||||
status === "active"
|
status === "active"
|
||||||
? "var(--admin-accent-light)"
|
? "var(--admin-accent-light)"
|
||||||
: status === "draft"
|
: status === "draft"
|
||||||
? "rgba(245, 158, 11, 0.1)"
|
? "var(--admin-warning-soft)"
|
||||||
: "var(--admin-bg-subtle)",
|
: "var(--admin-bg-subtle)",
|
||||||
color:
|
color:
|
||||||
status === "active"
|
status === "active"
|
||||||
? "var(--admin-accent-text)"
|
? "var(--admin-accent-text)"
|
||||||
: status === "draft"
|
: status === "draft"
|
||||||
? "#92400e"
|
? "var(--admin-warning)"
|
||||||
: "var(--admin-text-secondary)",
|
: "var(--admin-text-secondary)",
|
||||||
borderColor:
|
borderColor:
|
||||||
status === "active"
|
status === "active"
|
||||||
? "var(--admin-accent)"
|
? "var(--admin-accent)"
|
||||||
: status === "draft"
|
: status === "draft"
|
||||||
? "rgba(245, 158, 11, 0.3)"
|
? "var(--admin-warning)"
|
||||||
: "var(--admin-border-light)",
|
: "var(--admin-border-light)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ export default function StopsLocationsTabs({
|
|||||||
group relative flex items-center gap-2.5 rounded-xl px-3.5 py-2
|
group relative flex items-center gap-2.5 rounded-xl px-3.5 py-2
|
||||||
text-sm font-semibold transition-all duration-200
|
text-sm font-semibold transition-all duration-200
|
||||||
${isActive
|
${isActive
|
||||||
? "bg-white text-emerald-700 border border-emerald-200 shadow-sm"
|
? "bg-white text-[var(--admin-primary)] border border-[var(--admin-primary)]/30 shadow-sm"
|
||||||
: "text-stone-600 border border-transparent hover:text-emerald-700 hover:bg-emerald-50/40"
|
: "text-[var(--admin-text-secondary)] border border-transparent hover:text-[var(--admin-primary)] hover:bg-[var(--admin-primary-soft)]/40"
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`flex-shrink-0 transition-colors ${isActive ? "text-emerald-600" : "text-stone-400 group-hover:text-emerald-500"}`}
|
className={`flex-shrink-0 transition-colors ${isActive ? "text-[var(--admin-primary)]" : "text-[var(--admin-text-muted)] group-hover:text-[var(--admin-primary)]"}`}
|
||||||
aria-hidden
|
aria-hidden
|
||||||
>
|
>
|
||||||
{t.icon}
|
{t.icon}
|
||||||
@@ -85,8 +85,8 @@ export default function StopsLocationsTabs({
|
|||||||
inline-flex h-5 min-w-[1.25rem] items-center justify-center rounded-full px-1.5
|
inline-flex h-5 min-w-[1.25rem] items-center justify-center rounded-full px-1.5
|
||||||
text-[11px] font-bold tabular-nums
|
text-[11px] font-bold tabular-nums
|
||||||
${isActive
|
${isActive
|
||||||
? "bg-emerald-600 text-white"
|
? "bg-[var(--admin-primary)] text-white"
|
||||||
: "bg-stone-200/70 text-stone-600 group-hover:bg-emerald-100 group-hover:text-emerald-700"
|
: "bg-[var(--admin-bg-subtle)] text-[var(--admin-text-secondary)] group-hover:bg-[var(--admin-primary-soft)] group-hover:text-[var(--admin-primary)]"
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
@@ -94,7 +94,7 @@ export default function StopsLocationsTabs({
|
|||||||
</span>
|
</span>
|
||||||
{t.sublabel && (
|
{t.sublabel && (
|
||||||
<span
|
<span
|
||||||
className={`hidden sm:inline text-[11px] font-medium tabular-nums ${isActive ? "text-stone-500" : "text-stone-400"}`}
|
className={`hidden sm:inline text-[11px] font-medium tabular-nums ${isActive ? "text-[var(--admin-text-muted)]" : "text-[var(--admin-text-muted)]/80"}`}
|
||||||
>
|
>
|
||||||
· {t.sublabel}
|
· {t.sublabel}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user