Feature: Home screen As a logged-in RenewCMS owner I want /home to summarize how many articles I have, how many are online or drafts, and my five most recently updated articles, all from the single GET /v1/article/ call the route already makes So that I land somewhere useful after logging in, instead of an empty dashboard or a screen that silently fails Background: Given /home is a verified route (meta.auth "verified") rendered as HomePage inside DashboardShell, per docs/design/routes.md And HomePage calls GET /v1/article/ once per navigation; there is no separate stats endpoint, so the three stat tiles and the recent list are both derived from that one response And the PageHeader shows breadcrumb "Home" (mobile title "Home") and a primary action "New article" opening NewArticleModal And a "Connect your website" card is always rendered on the page, in every state (Loading, Default, Empty and Error alike): icon plug, title "Connect your website", text "Your site reads published articles from RenewCMS. Share this guide with whoever builds it.", and a link "Open the guide" navigating to /integration And a greeting reading "{greeting}, {name}" sits above the subtitle in every state (Loading, Default, Empty and Error alike); name is the session's display name when it is not empty and the session's username otherwise; it is computed from that name and the device's local hour via Date.getHours(), needs no request of its own since the session is already resolved by the app-boot flow before HomePage ever renders, and so never has its own loading or error state And hours 0 through 11 give the greeting "Good morning", hours 12 through 17 give "Good afternoon", and hours 18 through 23 give "Good evening" And once the list resolves with at least one article, the subtitle reads "Here's where things stand on your site.", three stat tiles show the total article count labelled "articles", the online count labelled "online" and the draft count labelled "drafts", and a recent list shows the 5 most recently updated articles, each with its title, an ArticleStatusBadge (online or draft) and its updated date, linking to /article/:id/edit And while the list request is in flight, skeleton placeholders replace the stat tiles and the recent list rows, but only after 150 ms, so a fast response never flashes a skeleton And when the list resolves with zero articles, the subtitle reads "Welcome to RenewCMS.", and the stat tiles and recent list are replaced by an empty state: icon file-text, title "Write your first article", text "It starts as a draft, so nothing appears on your site until you publish it.", and a single action "New article" that opens NewArticleModal And when the list request fails with a 500 or a network error, the breadcrumb, greeting and subtitle stay visible, the subtitle stays "Here's where things stand on your site.", and the stat tiles and recent list are replaced by an inline role="alert" banner: icon circle-x, title "Couldn't load your articles", text "Check your connection and try again.", and a single action "Try again" Scenario: Skeletons appear only after 150 ms, never for a fast response Given the user navigates to /home for the first time in this session When GET /v1/article/ has not resolved 150 ms after the navigation Then skeleton placeholders replace the three stat tiles and the five recent-list rows And the recent list container is aria-busy="true" When GET /v1/article/ then resolves 200 with a non-empty list of articles Then the skeletons are replaced by the Default state's stat tiles and recent list Given a second, separate navigation to /home When GET /v1/article/ resolves in under 150 ms with a non-empty list of articles Then no skeleton is ever shown, and the Default state's content renders directly Scenario: Default — the list loaded with a mix of online and draft articles Given GET /v1/article/ resolves 200 with 6 articles, 4 online and 2 drafts, most recently updated first: "How we price a bathroom renovation" (online), "Winter checklist for timber windows" (online), "Choosing tiles for a small bathroom" (draft), "A guide to underfloor heating" (online), "When to reseal a stone worktop" (draft), and "Small bathroom lighting ideas" (online, least recently updated) When the user navigates to /home Then the subtitle reads "Here's where things stand on your site." And the stat tiles read "6" "articles", "4" "online" and "2" "drafts" And the recent list shows exactly the first 5 articles above, in that order, each with its ArticleStatusBadge and updated date, and each row links to its /article/:id/edit And "Small bathroom lighting ideas" does not appear in the recent list And the "Connect your website" card's "Open the guide" link navigates to /integration, and the header's "New article" action opens NewArticleModal Scenario: Empty, first run — zero articles Given GET /v1/article/ resolves 200 with zero articles When the user navigates to /home Then the subtitle reads "Welcome to RenewCMS." And no stat tiles are shown And an empty state is shown in place of the recent list: icon file-text, title "Write your first article", text "It starts as a draft, so nothing appears on your site until you publish it.", and a single action "New article" When the user activates "New article" Then NewArticleModal opens Scenario Outline: Error — a 500 or a network failure shows the same inline alert Given GET /v1/article/ When the user navigates to /home Then the breadcrumb, greeting and subtitle remain visible, and the subtitle stays "Here's where things stand on your site." And no stat tiles or recent list are shown And an inline alert with role="alert" is shown in their place: icon circle-x, title "Couldn't load your articles", text "Check your connection and try again.", and a single action "Try again" Examples: | failure | | fails with a 500 response | | never reaches the server | Scenario: Retry re-issues the request and recovers into the Default state Given the user is on /home in the Error state, because GET /v1/article/ failed When the user activates "Try again" Then GET /v1/article/ is called again When it resolves 200 with 6 articles, 4 online and 2 drafts Then the Alert is replaced by the stat tiles reading "6" "articles", "4" "online", "2" "drafts", and the recent list Scenario: The list is fetched exactly once per navigation Given the user navigates to /home Then GET /v1/article/ is called exactly once And both the stat tiles and the recent list are derived from that single response, with no second request made for either Scenario Outline: The greeting matches the hour of day Given the session's username is "marta" and it has no display name And the device's local hour is When the user views /home Then the greeting above the subtitle reads ", marta" Examples: | hour | greeting | | 0 | Good morning | | 11 | Good morning | | 12 | Good afternoon | | 17 | Good afternoon | | 18 | Good evening | | 23 | Good evening |