fix(frontend): finish the token migration — raw grey classes -> theme tokens

UI_REDESIGN chunk 4 was never finished: 39 app/component files still used
raw Tailwind greys, kept readable in light mode only by the !important
override sheet. In dark mode text-gray-600 is ~2.3:1 on surface, and the
light override maps it to #94a3b8 (~2.6:1 on white) — unreadable in both.

Mechanical mapping (886 classes): gray-100/200 -> ink, 300/400 -> ink-2,
500/600/700 -> ink-muted, bg 950/900/800 -> page/surface/raised,
border 800 -> line, 700/600 -> line-strong, plus hover/divide/placeholder.
Alpha suffixes kept (tokens are color-mix, so /60 works). bg-gray-700,
text-white and marketing pages left alone. text-[9px] -> text-[10px].

Frontend unverified: no Node on the authoring box (#207).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-10-25 21:29:13 -04:00
co-authored by Claude Opus 5.5
parent 3968b49b80
commit bf206334e3
39 changed files with 655 additions and 655 deletions
+72 -72
View File
@@ -141,7 +141,7 @@ function PlaceSearch({
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && (e.preventDefault(), search())}
placeholder="Search a place…"
className="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="flex-1 bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
<button
type="button"
@@ -153,7 +153,7 @@ function PlaceSearch({
</button>
</div>
{results.length > 0 && (
<div className="bg-gray-800 border border-gray-700 rounded-lg overflow-hidden divide-y divide-gray-700">
<div className="bg-raised border border-line-strong rounded-lg overflow-hidden divide-y divide-line-strong">
{results.map((p) => (
<button
key={p.place_id}
@@ -162,7 +162,7 @@ function PlaceSearch({
className="w-full text-left px-3 py-2 hover:bg-gray-700 transition-colors"
>
<p className="text-white text-sm font-medium">{p.name}</p>
<p className="text-gray-400 text-xs">{p.address}</p>
<p className="text-ink-2 text-xs">{p.address}</p>
{p.rating && (
<p className="text-yellow-400 text-xs">★ {p.rating}</p>
)}
@@ -240,60 +240,60 @@ function AddEventModal({
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
<form
onSubmit={handleSubmit}
className="bg-gray-900 border border-gray-700 rounded-xl p-6 w-full max-w-lg space-y-4 max-h-[90vh] overflow-y-auto"
className="bg-surface border border-line-strong rounded-xl p-6 w-full max-w-lg space-y-4 max-h-[90vh] overflow-y-auto"
>
<h2 className="text-white font-bold text-lg">{isEdit ? "Edit Event" : "Add Event"}</h2>
<div>
<label className="text-xs text-gray-400 block mb-1">Title</label>
<label className="text-xs text-ink-2 block mb-1">Title</label>
<input
required value={title} onChange={(e) => setTitle(e.target.value)}
placeholder="Dinner at the bar"
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
<div className="grid grid-cols-3 gap-3">
<div>
<label className="text-xs text-gray-400 block mb-1">Date</label>
<label className="text-xs text-ink-2 block mb-1">Date</label>
<input
required type="date"
min={trip.start_date} max={trip.end_date}
value={date} onChange={(e) => setDate(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
<div>
<label className="text-xs text-gray-400 block mb-1">Start</label>
<label className="text-xs text-ink-2 block mb-1">Start</label>
<input
type="time" value={startTime} onChange={(e) => setStartTime(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
<div>
<label className="text-xs text-gray-400 block mb-1">End</label>
<label className="text-xs text-ink-2 block mb-1">End</label>
<input
type="time" value={endTime} onChange={(e) => setEndTime(e.target.value)}
min={startTime}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
</div>
<div>
<label className="text-xs text-gray-400 block mb-1">Find a place</label>
<label className="text-xs text-ink-2 block mb-1">Find a place</label>
<PlaceSearch near={trip.location} onSelect={handlePlaceSelect} />
</div>
<div>
<label className="text-xs text-gray-400 block mb-1">
<label className="text-xs text-ink-2 block mb-1">
Location{" "}
<span className="text-gray-600">(auto-filled from search, or type manually)</span>
<span className="text-ink-muted">(auto-filled from search, or type manually)</span>
</label>
<input
value={location} onChange={(e) => setLocation(e.target.value)}
placeholder={`Inherits: ${trip.location}`}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
@@ -307,16 +307,16 @@ function AddEventModal({
)}
<div>
<label className="text-xs text-gray-400 block mb-1">Notes (optional)</label>
<label className="text-xs text-ink-2 block mb-1">Notes (optional)</label>
<textarea
value={notes} onChange={(e) => setNotes(e.target.value)} rows={2}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none resize-none"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none resize-none"
/>
</div>
{availableTags.length > 0 && (
<div>
<label className="text-xs text-gray-400 block mb-1">Tags</label>
<label className="text-xs text-ink-2 block mb-1">Tags</label>
<div className="flex flex-wrap gap-1.5">
{availableTags.map((tag) => {
const active = tags.includes(tag);
@@ -326,7 +326,7 @@ function AddEventModal({
type="button"
onClick={() => setTags((prev) => active ? prev.filter((t) => t !== tag) : [...prev, tag])}
className={`text-xs rounded-full px-2.5 py-1 border transition-colors ${
active ? tagColor(tag, availableTags) : "bg-gray-800 text-gray-500 border-gray-700 hover:border-gray-600 hover:text-gray-400"
active ? tagColor(tag, availableTags) : "bg-raised text-ink-muted border-line-strong hover:border-line-strong hover:text-ink-2"
}`}
>
{tag}
@@ -340,7 +340,7 @@ function AddEventModal({
{error && <p className="text-red-400 text-xs">{error}</p>}
<div className="flex gap-3 justify-end pt-1">
<button type="button" onClick={onClose} className="text-sm text-gray-400 hover:text-gray-200 px-4 py-2">
<button type="button" onClick={onClose} className="text-sm text-ink-2 hover:text-ink px-4 py-2">
Cancel
</button>
<button
@@ -389,7 +389,7 @@ function DayTimeline({
if (timed.length === 0 && untimed.length === 0) {
return (
<p className="text-gray-600 text-sm font-mono py-8 text-center">
<p className="text-ink-muted text-sm font-mono py-8 text-center">
No events on this day yet.
</p>
);
@@ -424,10 +424,10 @@ function DayTimeline({
style={{ top: (h * 60 - rangeStart) * PX_PER_MIN }}
className="absolute inset-x-0 flex items-center pointer-events-none"
>
<span className="w-14 text-right pr-3 text-xs text-gray-600 font-mono shrink-0 -translate-y-2">
<span className="w-14 text-right pr-3 text-xs text-ink-muted font-mono shrink-0 -translate-y-2">
{h === 0 ? "12 AM" : h < 12 ? `${h} AM` : h === 12 ? "12 PM" : `${h - 12} PM`}
</span>
<div className="flex-1 border-t border-gray-800/70" />
<div className="flex-1 border-t border-line/70" />
</div>
))}
@@ -444,7 +444,7 @@ function DayTimeline({
{e.end_time && (
<div
style={{ top, height, left: 60, right: 0 }}
className="absolute bg-gray-800/30 border-l-2 border-gray-600/30 z-0 pointer-events-none"
className="absolute bg-raised/30 border-l-2 border-line-strong/30 z-0 pointer-events-none"
/>
)}
{/* Dashed marker line + label */}
@@ -452,15 +452,15 @@ function DayTimeline({
style={{ top, left: 60, right: 0 }}
className="absolute z-10 flex items-center gap-2"
>
<div className="flex-1 border-t border-dashed border-gray-600/40" />
<span className="text-gray-500 text-[10px] font-mono shrink-0 pr-1">
<div className="flex-1 border-t border-dashed border-line-strong/40" />
<span className="text-ink-muted text-[10px] font-mono shrink-0 pr-1">
{fmtTime(e.start_time)}
</span>
<span className="text-gray-500 text-[10px] truncate max-w-[120px] shrink-0">{e.title}</span>
<span className="text-ink-muted text-[10px] truncate max-w-[120px] shrink-0">{e.title}</span>
{isAdmin && (
<div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity shrink-0">
<button onClick={() => onEdit(e)} className="text-gray-600 hover:text-indigo-400 text-[10px]">Edit</button>
<button onClick={() => onDelete(e.event_id)} className="text-gray-600 hover:text-red-400 text-xs leading-none">×</button>
<button onClick={() => onEdit(e)} className="text-ink-muted hover:text-indigo-400 text-[10px]">Edit</button>
<button onClick={() => onDelete(e.event_id)} className="text-ink-muted hover:text-red-400 text-xs leading-none">×</button>
</div>
)}
</div>
@@ -498,10 +498,10 @@ function DayTimeline({
</p>
)}
{height >= 60 && !e.location_inherited && e.location && (
<p className="text-gray-500 text-xs mt-0.5 truncate">{e.location}</p>
<p className="text-ink-muted text-xs mt-0.5 truncate">{e.location}</p>
)}
{height >= 60 && e.notes && (
<p className="text-gray-600 text-xs mt-0.5 italic truncate">{e.notes}</p>
<p className="text-ink-muted text-xs mt-0.5 italic truncate">{e.notes}</p>
)}
{e.tags?.length > 0 && (
<div className="flex flex-wrap gap-1 mt-1">
@@ -525,13 +525,13 @@ function DayTimeline({
<>
<button
onClick={() => onEdit(e)}
className="text-gray-600 hover:text-indigo-400 text-xs opacity-0 group-hover:opacity-100 transition-opacity"
className="text-ink-muted hover:text-indigo-400 text-xs opacity-0 group-hover:opacity-100 transition-opacity"
>
Edit
</button>
<button
onClick={() => onDelete(e.event_id)}
className="text-gray-600 hover:text-red-400 text-base leading-none opacity-0 group-hover:opacity-100 transition-opacity"
className="text-ink-muted hover:text-red-400 text-base leading-none opacity-0 group-hover:opacity-100 transition-opacity"
>
×
</button>
@@ -543,9 +543,9 @@ function DayTimeline({
{drive && (
<div
style={{ top: top + height + 2, left: 60 }}
className="absolute text-xs text-gray-600 font-mono flex items-center gap-1 z-20"
className="absolute text-xs text-ink-muted font-mono flex items-center gap-1 z-20"
>
<span className="text-gray-700">↓</span> {drive} drive
<span className="text-ink-muted">↓</span> {drive} drive
</div>
)}
</div>
@@ -557,18 +557,18 @@ function DayTimeline({
{/* Untimed / all-day events */}
{untimed.length > 0 && (
<div className="space-y-1.5">
<p className="text-xs font-mono text-gray-600 uppercase tracking-wider">Unscheduled</p>
<p className="text-xs font-mono text-ink-muted uppercase tracking-wider">Unscheduled</p>
{untimed.map((e) => (
<div
key={e.event_id}
className="bg-gray-900 border border-gray-800 rounded-lg px-4 py-2.5 flex items-center justify-between group"
className="bg-surface border border-line rounded-lg px-4 py-2.5 flex items-center justify-between group"
>
<div className="min-w-0">
<p className="text-white text-sm">{e.title}</p>
{!e.location_inherited && e.location && (
<p className="text-gray-500 text-xs mt-0.5">{e.location}</p>
<p className="text-ink-muted text-xs mt-0.5">{e.location}</p>
)}
{e.notes && <p className="text-gray-600 text-xs italic mt-0.5">{e.notes}</p>}
{e.notes && <p className="text-ink-muted text-xs italic mt-0.5">{e.notes}</p>}
{e.tags?.length > 0 && (
<div className="flex flex-wrap gap-1 mt-1">
{e.tags.map((t) => <TagPill key={t} tag={t} availableTags={availableTags} />)}
@@ -585,11 +585,11 @@ function DayTimeline({
{isAdmin && (
<>
<button onClick={() => onEdit(e)}
className="text-xs text-gray-600 hover:text-indigo-400 opacity-0 group-hover:opacity-100 transition-opacity">
className="text-xs text-ink-muted hover:text-indigo-400 opacity-0 group-hover:opacity-100 transition-opacity">
Edit
</button>
<button onClick={() => onDelete(e.event_id)}
className="text-gray-600 hover:text-red-400 text-sm opacity-0 group-hover:opacity-100 transition-opacity">
className="text-ink-muted hover:text-red-400 text-sm opacity-0 group-hover:opacity-100 transition-opacity">
×
</button>
</>
@@ -728,17 +728,17 @@ function AssistantPanel({
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="px-4 py-3 border-b border-gray-800 shrink-0 flex items-start justify-between gap-2">
<div className="px-4 py-3 border-b border-line shrink-0 flex items-start justify-between gap-2">
<div>
<p className="text-white text-sm font-semibold">Trip Assistant</p>
<p className="text-gray-500 text-xs mt-0.5">
<p className="text-ink-muted text-xs mt-0.5">
Tell me what you want to do — I can search places and suggest events.
</p>
</div>
{messages.length > 0 && (
<button
onClick={clearChat}
className="text-xs text-gray-600 hover:text-gray-400 transition-colors shrink-0 mt-0.5"
className="text-xs text-ink-muted hover:text-ink-2 transition-colors shrink-0 mt-0.5"
>
Clear
</button>
@@ -758,7 +758,7 @@ function AssistantPanel({
<button
key={prompt}
onClick={() => { setInput(prompt); }}
className="w-full text-left bg-gray-800/60 hover:bg-gray-800 border border-gray-700/50 rounded-lg px-3 py-2 text-gray-400 text-xs transition-colors"
className="w-full text-left bg-raised/60 hover:bg-raised border border-line-strong/50 rounded-lg px-3 py-2 text-ink-2 text-xs transition-colors"
>
{prompt}
</button>
@@ -772,7 +772,7 @@ function AssistantPanel({
className={`rounded-xl px-3 py-2 text-sm max-w-[90%] ${
msg.role === "user"
? "bg-indigo-600 text-white"
: "bg-gray-800 text-gray-200"
: "bg-raised text-ink"
}`}
>
{msg.role === "user" ? msg.content : (
@@ -804,11 +804,11 @@ function AssistantPanel({
className={`w-full border rounded-xl p-3 space-y-1.5 transition-all ${
s.added
? "border-green-800/50 bg-green-950/30"
: "border-gray-700 bg-gray-900"
: "border-line-strong bg-surface"
}`}
>
<p className="text-white text-sm font-semibold">{s.title}</p>
<div className="text-xs text-gray-400 space-y-0.5">
<div className="text-xs text-ink-2 space-y-0.5">
{s.date && (
<p>
{fmtDate(s.date)}
@@ -817,7 +817,7 @@ function AssistantPanel({
</p>
)}
{s.location && <p className="truncate">{s.location}</p>}
{s.notes && <p className="text-gray-500 italic">{s.notes}</p>}
{s.notes && <p className="text-ink-muted italic">{s.notes}</p>}
{s.tags && s.tags.length > 0 && (
<div className="flex flex-wrap gap-1 pt-0.5">
{s.tags.map((t) => <TagPill key={t} tag={t} availableTags={trip.available_tags ?? []} />)}
@@ -840,7 +840,7 @@ function AssistantPanel({
</button>
<button
onClick={() => dismissSuggestion(msg.id, idx)}
className="px-3 text-gray-500 hover:text-gray-300 text-xs transition-colors"
className="px-3 text-ink-muted hover:text-ink text-xs transition-colors"
>
Skip
</button>
@@ -856,7 +856,7 @@ function AssistantPanel({
{loading && (
<div className="flex items-start">
<div className="bg-gray-800 rounded-xl px-3 py-2 text-gray-400 text-sm">
<div className="bg-raised rounded-xl px-3 py-2 text-ink-2 text-sm">
<span className="animate-pulse">Thinking…</span>
</div>
</div>
@@ -865,7 +865,7 @@ function AssistantPanel({
</div>
{/* Input */}
<div className="px-3 py-3 border-t border-gray-800 shrink-0">
<div className="px-3 py-3 border-t border-line shrink-0">
<div className="flex gap-2">
<textarea
ref={inputRef}
@@ -875,7 +875,7 @@ function AssistantPanel({
placeholder="What do you want to do?"
disabled={loading}
rows={1}
className="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500 disabled:opacity-50 resize-none overflow-hidden"
className="flex-1 bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500 disabled:opacity-50 resize-none overflow-hidden"
/>
<button
onClick={send}
@@ -1055,8 +1055,8 @@ export default function TripDetailPage() {
setTrip((prev) => prev ? { ...prev, overlap_tags: overlap } : prev);
}
if (loading) return <p className="text-gray-500 text-sm font-mono">Loading…</p>;
if (!trip) return <p className="text-gray-500 text-sm font-mono">Trip not found.</p>;
if (loading) return <p className="text-ink-muted text-sm font-mono">Loading…</p>;
if (!trip) return <p className="text-ink-muted text-sm font-mono">Trip not found.</p>;
const days = dateRange(trip.start_date, trip.end_date);
const attendees = Object.values(trip.attendees ?? {});
@@ -1070,7 +1070,7 @@ export default function TripDetailPage() {
<div className="space-y-4">
<button
onClick={() => router.push("/trips")}
className="text-sm font-mono text-gray-500 hover:text-gray-300 transition-colors"
className="text-sm font-mono text-ink-muted hover:text-ink transition-colors"
>
← Trips
</button>
@@ -1078,7 +1078,7 @@ export default function TripDetailPage() {
<div className="flex items-start justify-between gap-4 flex-wrap">
<div className="space-y-1">
<h1 className="text-white text-2xl font-bold">{trip.name}</h1>
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-gray-400">
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-ink-2">
<span>{trip.location}</span>
{trip.maps_link && (
<a href={trip.maps_link} target="_blank" rel="noopener noreferrer"
@@ -1086,11 +1086,11 @@ export default function TripDetailPage() {
Maps
</a>
)}
<span className="text-gray-700">•</span>
<span className="text-ink-muted">•</span>
<span className="font-mono text-xs">{fmtDate(trip.start_date)} — {fmtDate(trip.end_date)}</span>
{attendees.length > 0 && (
<>
<span className="text-gray-700">•</span>
<span className="text-ink-muted">•</span>
<span className="text-xs">{attendees.join(", ")}</span>
</>
)}
@@ -1114,7 +1114,7 @@ export default function TripDetailPage() {
</button>
<button
onClick={handleToggleVisibility}
className="text-xs text-gray-500 hover:text-gray-300 transition-colors border border-gray-700 rounded-lg px-3 py-2"
className="text-xs text-ink-muted hover:text-ink transition-colors border border-line-strong rounded-lg px-3 py-2"
>
{trip.visibility === "private" ? "Make public" : "Make private"}
</button>
@@ -1159,9 +1159,9 @@ export default function TripDetailPage() {
onChange={(e) => setTagInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && (e.preventDefault(), handleAddTag())}
placeholder="Add tag…"
className="bg-transparent border border-gray-700 rounded-full px-2.5 py-0.5 text-xs text-gray-400 placeholder-gray-600 focus:outline-none focus:border-gray-500 w-24"
className="bg-transparent border border-line-strong rounded-full px-2.5 py-0.5 text-xs text-ink-2 placeholder-ink-muted focus:outline-none focus:border-gray-500 w-24"
/>
<button onClick={handleAddTag} className="text-xs text-gray-500 hover:text-gray-300 transition-colors">+</button>
<button onClick={handleAddTag} className="text-xs text-ink-muted hover:text-ink transition-colors">+</button>
</div>
)}
</div>
@@ -1170,13 +1170,13 @@ export default function TripDetailPage() {
{/* Invite management — admin only, only when private */}
{isAdmin && trip.visibility === "private" && (
<div className="space-y-2">
<p className="text-xs text-gray-500 uppercase tracking-wider font-mono">Invited</p>
<p className="text-xs text-ink-muted uppercase tracking-wider font-mono">Invited</p>
<div className="flex flex-wrap items-center gap-2">
{(trip.invited_discord_ids ?? []).length === 0 && (
<span className="text-xs text-gray-600">No invites yet</span>
<span className="text-xs text-ink-muted">No invites yet</span>
)}
{(trip.invited_discord_ids ?? []).map((discordId) => (
<span key={discordId} className="inline-flex items-center gap-1 text-xs bg-gray-800 border border-gray-700 rounded-full px-2.5 py-0.5 text-gray-300">
<span key={discordId} className="inline-flex items-center gap-1 text-xs bg-raised border border-line-strong rounded-full px-2.5 py-0.5 text-ink-2">
<span className="font-mono">{discordId}</span>
<button
onClick={() => handleRevokeInvite(discordId)}
@@ -1192,9 +1192,9 @@ export default function TripDetailPage() {
onChange={(e) => setInviteInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && (e.preventDefault(), handleInvite())}
placeholder="Discord user ID…"
className="bg-transparent border border-gray-700 rounded-full px-2.5 py-0.5 text-xs text-gray-400 placeholder-gray-600 focus:outline-none focus:border-gray-500 w-36"
className="bg-transparent border border-line-strong rounded-full px-2.5 py-0.5 text-xs text-ink-2 placeholder-ink-muted focus:outline-none focus:border-gray-500 w-36"
/>
<button onClick={handleInvite} className="text-xs text-gray-500 hover:text-gray-300 transition-colors">+</button>
<button onClick={handleInvite} className="text-xs text-ink-muted hover:text-ink transition-colors">+</button>
</div>
</div>
</div>
@@ -1218,13 +1218,13 @@ export default function TripDetailPage() {
className={`flex flex-col items-center px-3 py-2 rounded-xl text-xs transition-colors relative ${
day === selectedDay
? "bg-indigo-600 text-white"
: "bg-gray-900 border border-gray-800 text-gray-400 hover:border-gray-700 hover:text-gray-200"
: "bg-surface border border-line text-ink-2 hover:border-line-strong hover:text-ink"
}`}
>
<span className="font-mono font-bold">{short}</span>
<span className="font-mono">{date}</span>
{count > 0 && (
<span className={`text-xs mt-0.5 ${day === selectedDay ? "text-indigo-200" : "text-gray-600"}`}>
<span className={`text-xs mt-0.5 ${day === selectedDay ? "text-indigo-200" : "text-ink-muted"}`}>
{count}
</span>
)}
@@ -1238,7 +1238,7 @@ export default function TripDetailPage() {
{/* Selected day */}
<div className="space-y-4">
<p className="text-sm font-mono text-gray-400 uppercase tracking-wider">
<p className="text-sm font-mono text-ink-2 uppercase tracking-wider">
{fmtDayHeading(selectedDay)}
</p>
<DayTimeline
@@ -1254,7 +1254,7 @@ export default function TripDetailPage() {
</div>
{/* Right: AI Assistant — sticky */}
<div className="w-80 xl:w-96 shrink-0 sticky top-20 h-[calc(100vh-240px)] flex flex-col bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
<div className="w-80 xl:w-96 shrink-0 sticky top-20 h-[calc(100vh-240px)] flex flex-col bg-surface border border-line rounded-xl overflow-hidden">
<AssistantPanel trip={trip} onAddEvent={handleAssistantAddEvent} />
</div>
</div>
+21 -21
View File
@@ -29,25 +29,25 @@ function TripCard({ trip, isAdmin, onDelete }: {
return (
<div
className="bg-gray-900 border border-gray-800 rounded-xl p-5 cursor-pointer hover:border-gray-700 transition-colors"
className="bg-surface border border-line rounded-xl p-5 cursor-pointer hover:border-line-strong transition-colors"
onClick={() => router.push(`/trips/${trip.trip_id}`)}
>
<div className="flex items-start justify-between gap-4">
<div className="min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className={`text-xs font-mono px-2 py-0.5 rounded-full ${
upcoming ? "bg-indigo-900 text-indigo-300" : "bg-gray-800 text-gray-500"
upcoming ? "bg-indigo-900 text-indigo-300" : "bg-raised text-ink-muted"
}`}>
{upcoming ? "Upcoming" : "Past"}
</span>
</div>
<h3 className="text-white font-semibold text-sm leading-snug">{trip.name}</h3>
<p className="text-gray-400 text-xs mt-1">{trip.location}</p>
<p className="text-gray-500 text-xs font-mono mt-1">
<p className="text-ink-2 text-xs mt-1">{trip.location}</p>
<p className="text-ink-muted text-xs font-mono mt-1">
{fmtDate(trip.start_date)} — {fmtDate(trip.end_date)}
</p>
{attendeeCount > 0 && (
<p className="text-gray-500 text-xs mt-1">
<p className="text-ink-muted text-xs mt-1">
{attendeeCount} going
</p>
)}
@@ -102,59 +102,59 @@ function CreateModal({ onClose, onCreate }: {
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
<form
onSubmit={handleSubmit}
className="bg-gray-900 border border-gray-700 rounded-xl p-6 w-full max-w-md space-y-4 max-h-[90vh] overflow-y-auto"
className="bg-surface border border-line-strong rounded-xl p-6 w-full max-w-md space-y-4 max-h-[90vh] overflow-y-auto"
>
<h2 className="text-white font-bold">New Trip</h2>
<div>
<label className="text-xs text-gray-400 block mb-1">Name</label>
<label className="text-xs text-ink-2 block mb-1">Name</label>
<input
required value={name} onChange={(e) => setName(e.target.value)}
placeholder="Road trip to Nashville"
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
<div>
<label className="text-xs text-gray-400 block mb-1">Location</label>
<label className="text-xs text-ink-2 block mb-1">Location</label>
<input
required value={location} onChange={(e) => setLocation(e.target.value)}
placeholder="Nashville, TN"
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-gray-400 block mb-1">Start date</label>
<label className="text-xs text-ink-2 block mb-1">Start date</label>
<input
required type="date" value={start} onChange={(e) => setStart(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
<div>
<label className="text-xs text-gray-400 block mb-1">End date</label>
<label className="text-xs text-ink-2 block mb-1">End date</label>
<input
required type="date" value={end} onChange={(e) => setEnd(e.target.value)}
min={start}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
</div>
<div>
<label className="text-xs text-gray-400 block mb-1">Google Maps link (optional)</label>
<label className="text-xs text-ink-2 block mb-1">Google Maps link (optional)</label>
<input
type="url" value={mapsLink} onChange={(e) => setMapsLink(e.target.value)}
placeholder="https://maps.google.com/…"
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
className="w-full bg-raised border border-line-strong rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500"
/>
</div>
{error && <p className="text-red-400 text-xs">{error}</p>}
<div className="flex gap-3 justify-end">
<button type="button" onClick={onClose} className="text-sm text-gray-400 hover:text-gray-200 px-4 py-2">
<button type="button" onClick={onClose} className="text-sm text-ink-2 hover:text-ink px-4 py-2">
Cancel
</button>
<button
@@ -198,12 +198,12 @@ export default function TripsPage() {
</div>
{loading ? (
<p className="text-gray-500 text-sm font-mono">Loading…</p>
<p className="text-ink-muted text-sm font-mono">Loading…</p>
) : (
<>
{upcoming.length > 0 && (
<section>
<h2 className="text-sm font-mono text-gray-400 uppercase tracking-wider mb-3">Upcoming</h2>
<h2 className="text-sm font-mono text-ink-2 uppercase tracking-wider mb-3">Upcoming</h2>
<div className="space-y-3">
{upcoming.map((t) => (
<TripCard key={t.trip_id} trip={t} isAdmin={isAdmin} onDelete={handleDelete} />
@@ -214,7 +214,7 @@ export default function TripsPage() {
{past.length > 0 && (
<section>
<h2 className="text-sm font-mono text-gray-400 uppercase tracking-wider mb-3">Past</h2>
<h2 className="text-sm font-mono text-ink-2 uppercase tracking-wider mb-3">Past</h2>
<div className="space-y-3">
{past.map((t) => (
<TripCard key={t.trip_id} trip={t} isAdmin={isAdmin} onDelete={handleDelete} />
@@ -224,7 +224,7 @@ export default function TripsPage() {
)}
{trips.length === 0 && (
<p className="text-gray-600 text-sm font-mono">No trips yet.</p>
<p className="text-ink-muted text-sm font-mono">No trips yet.</p>
)}
</>
)}