| 1 | /** |
| 2 | * The landing page's pictures of Home (routes/workspace/home.tsx), drawn |
| 3 | * from the same parts and words as the real cards in components/home.tsx |
| 4 | * and components/home-ask.tsx: What changed and Landed, for coming back to |
| 5 | * what happened; Settled for you, Where you're needed and Start here, |
| 6 | * for what still needs you. They are pictures, so nothing in them takes |
| 7 | * focus or a click (`inert`), and the page reads them as one image. |
| 8 | */ |
| 9 | import { ArrowRight, ArrowUp, CheckCheck, GitCommitHorizontal, GitMerge, GitPullRequest, RotateCcw, Rocket, Sparkles } from "lucide-react"; |
| 10 | import type { ReactNode } from "react"; |
| 11 | |
| 12 | import { MarkGlyph } from "./logo"; |
| 13 | import { Badge } from "./ui/badge"; |
| 14 | import { Button } from "./ui/button"; |
| 15 | import { Card } from "./ui/card"; |
| 16 | |
| 17 | /** One of Home's cards, as components/home.tsx draws it: a title, a few words under it, then its rows. */ |
| 18 | function HomeCard({ title, hint, children }: { title: ReactNode; hint?: string; children: ReactNode }) { |
| 19 | return ( |
| 20 | <Card tone="bg" className="overflow-hidden rounded-2xl text-left shadow-sm"> |
| 21 | <header className="px-4 pt-3.5 pb-2.5"> |
| 22 | <p className="text-sm font-semibold text-fg">{title}</p> |
| 23 | {hint && <p className="mt-0.5 text-xs text-muted">{hint}</p>} |
| 24 | </header> |
| 25 | {children} |
| 26 | </Card> |
| 27 | ); |
| 28 | } |
| 29 | |
| 30 | const ROW = "flex items-center gap-3 border-t border-line px-4 py-2.5"; |
| 31 | |
| 32 | /** A picture, not a control: kept out of the tab order and read as one image. */ |
| 33 | function Picture({ label, children }: { label: string; children: ReactNode }) { |
| 34 | return ( |
| 35 | <div role="img" aria-label={label} className="space-y-3"> |
| 36 | <div inert className="space-y-3"> |
| 37 | {children} |
| 38 | </div> |
| 39 | </div> |
| 40 | ); |
| 41 | } |
| 42 | |
| 43 | /** Coming back: the Ask box, What changed in plain English, and Landed with whether each change is live. */ |
| 44 | export function WhatChangedArt() { |
| 45 | return ( |
| 46 | <Picture label="Home: What are we building today? over a box that asks g1t, What changed summarizing in plain English what landed, and Landed with whether each change is live"> |
| 47 | <div className="text-center"> |
| 48 | <p className="text-lg font-semibold tracking-tight text-fg">What are we building today?</p> |
| 49 | <div className="mt-3 flex items-center gap-2 rounded-2xl border border-line bg-bg p-1.5 pl-3.5 text-left shadow-sm"> |
| 50 | <MarkGlyph className="h-3.5 shrink-0" /> |
| 51 | <span className="min-w-0 grow truncate text-[0.8125rem] text-faint">Ask g1t what changed, what's blocked, or what to do next</span> |
| 52 | <Button type="button" variant="accent" size="icon-xs" tabIndex={-1} aria-hidden="true"> |
| 53 | <ArrowUp size={13} /> |
| 54 | </Button> |
| 55 | </div> |
| 56 | <div className="mt-2.5 flex flex-wrap justify-center gap-1.5"> |
| 57 | <Badge tone="accent" size="md" className="whitespace-normal"> |
| 58 | Summarize what changed since I was last here |
| 59 | </Badge> |
| 60 | <Badge size="md" className="hidden sm:inline-flex"> |
| 61 | What needs me first? |
| 62 | </Badge> |
| 63 | </div> |
| 64 | </div> |
| 65 | |
| 66 | <HomeCard title="What changed" hint="In plain English, since I was last here"> |
| 67 | <div className="space-y-2 px-4 pb-3.5 text-[0.8125rem] leading-relaxed text-fg-soft"> |
| 68 | <p> |
| 69 | Two changes landed in <span className="font-medium text-fg">acme/web</span>. CSV exports now stream in batches of 1,000 rows, so the |
| 70 | largest accounts no longer time out; it went live 9 minutes after the merge queue merged it. Jordan pinned the build image's cache key, which fixed last night's failed deploy. |
| 71 | </p> |
| 72 | <div className="flex flex-wrap items-center gap-1.5 border-t border-line pt-2.5"> |
| 73 | <Button type="button" variant="soft" size="xs" tabIndex={-1}> |
| 74 | <MarkGlyph className="h-2.5" /> |
| 75 | Ask a follow-up |
| 76 | </Button> |
| 77 | <Button type="button" variant="ghost" size="xs" tabIndex={-1}> |
| 78 | <RotateCcw size={12} /> |
| 79 | Summarize again |
| 80 | </Button> |
| 81 | </div> |
| 82 | </div> |
| 83 | </HomeCard> |
| 84 | |
| 85 | <HomeCard title="Landed" hint="1 pull request merged · 1 push"> |
| 86 | {[ |
| 87 | { title: "Stream CSV exports in batches", detail: "web#431 · by priya", push: false, ship: <Badge tone="success"><Rocket size={10} aria-hidden="true" />Live 9 min after merge</Badge> }, |
| 88 | { title: "Pin the build image's cache key", detail: "web · 1 push by jordan", push: true, ship: <Badge tone="success"><Rocket size={10} aria-hidden="true" />Live 4 min after push</Badge> }, |
| 89 | ].map((row) => ( |
| 90 | <div key={row.title} className={ROW}> |
| 91 | {row.push ? <GitCommitHorizontal size={15} className="shrink-0 text-muted" aria-hidden="true" /> : <GitMerge size={15} className="shrink-0 text-merged" aria-hidden="true" />} |
| 92 | <div className="min-w-0 grow"> |
| 93 | <p className="truncate text-sm text-fg">{row.title}</p> |
| 94 | <div className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1"> |
| 95 | <span className="truncate text-xs text-muted">{row.detail}</span> |
| 96 | {row.ship} |
| 97 | </div> |
| 98 | </div> |
| 99 | <span className="flex size-7 shrink-0 items-center justify-center rounded-md text-accent"> |
| 100 | <Sparkles size={13} aria-hidden="true" /> |
| 101 | </span> |
| 102 | </div> |
| 103 | ))} |
| 104 | </HomeCard> |
| 105 | </Picture> |
| 106 | ); |
| 107 | } |
| 108 | |
| 109 | /** What still needs you: what settled by itself, Where you're needed, and Start here. */ |
| 110 | export function NeedsYouArt() { |
| 111 | return ( |
| 112 | <Picture label="Home: Settled for you lists notifications that settled themselves and why, Where you're needed lists a pull request ready to merge and a review asked of you, and Start here opens the one to do first"> |
| 113 | <HomeCard title="Settled for you" hint="3 finished by themselves"> |
| 114 | <div className="flex flex-wrap gap-1.5 px-4 pb-3"> |
| 115 | <Badge tone="success" size="md">3 notifications settled</Badge> |
| 116 | <Badge tone="warn" size="md">2 things still need you</Badge> |
| 117 | </div> |
| 118 | {[ |
| 119 | { title: "CI failed on main", why: "CI passed in run 330" }, |
| 120 | { title: "Review requested: Cache the dependency graph", why: "Merged by @ana" }, |
| 121 | ].map((row) => ( |
| 122 | <div key={row.title} className={ROW}> |
| 123 | <CheckCheck size={15} className="shrink-0 text-success" aria-hidden="true" /> |
| 124 | <div className="min-w-0 grow"> |
| 125 | <p className="truncate text-sm text-fg">{row.title}</p> |
| 126 | <p className="truncate text-xs text-fg-soft">{row.why}</p> |
| 127 | </div> |
| 128 | </div> |
| 129 | ))} |
| 130 | </HomeCard> |
| 131 | |
| 132 | <HomeCard title="Where you're needed"> |
| 133 | {[ |
| 134 | { icon: <GitMerge size={15} className="shrink-0 text-success" aria-hidden="true" />, title: "Retry webhook deliveries with backoff", detail: "acme/web #433 · Ready to land when you merge it.", stake: "Ready to merge", action: "Open" }, |
| 135 | { icon: <GitPullRequest size={15} className="shrink-0 text-info" aria-hidden="true" />, title: "Add audit log export", detail: "acme/web #435 · Your review", stake: "+212 −40", action: "Review" }, |
| 136 | ].map((row) => ( |
| 137 | <div key={row.title} className={ROW}> |
| 138 | {row.icon} |
| 139 | <div className="min-w-0 grow"> |
| 140 | <p className="truncate text-sm font-semibold text-fg">{row.title}</p> |
| 141 | <p className="truncate text-xs text-muted">{row.detail}</p> |
| 142 | </div> |
| 143 | <span className="hidden shrink-0 text-xs font-medium text-fg-soft sm:inline">{row.stake}</span> |
| 144 | <Button type="button" variant="outline" size="xs" tabIndex={-1} className="border-line-strong px-2.5 text-fg"> |
| 145 | {row.action} |
| 146 | </Button> |
| 147 | </div> |
| 148 | ))} |
| 149 | </HomeCard> |
| 150 | |
| 151 | <HomeCard |
| 152 | title={ |
| 153 | <> |
| 154 | Start here <span className="font-normal text-faint">· picked by g1t</span> |
| 155 | </> |
| 156 | } |
| 157 | > |
| 158 | <div className="px-4 pb-4"> |
| 159 | <p className="text-sm font-semibold text-fg">Retry webhook deliveries with backoff</p> |
| 160 | <p className="mt-2.5 rounded-lg bg-raised/60 px-3 py-2 text-xs leading-relaxed text-fg-soft"> |
| 161 | <span className="font-medium text-fg">Why this one: </span> |
| 162 | Everything it needs is met; it lands when you merge it. |
| 163 | </p> |
| 164 | <Button type="button" variant="accent" size="sm" tabIndex={-1} className="mt-3"> |
| 165 | Open pull request |
| 166 | <ArrowRight size={13} /> |
| 167 | </Button> |
| 168 | </div> |
| 169 | </HomeCard> |
| 170 | </Picture> |
| 171 | ); |
| 172 | } |