From cb0203c5e284137be08eca7e2043a5b8860b1150 Mon Sep 17 00:00:00 2001 From: James Morton Date: Fri, 30 Oct 2026 20:45:47 +0000 Subject: [PATCH 1/4] feat(design): the feed's default sort comes from the design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the design engine the feedback page sorts by the design's default (trending, top or new) until the visitor picks one: the loader, the feed, its loading state and the container all read it, and clearing a search returns to it. A legacy portal sorts by trending as before. Appearance sets it under Sections › Feed, and the preview loads with the draft's sort. Review and publish names the page's changes as the controls do: the headline, the subtitle, the welcome message and its text. --- .../appearance/__tests__/change-list.test.ts | 22 ++++++++++++ .../settings/appearance/appearance-view.tsx | 1 + .../admin/settings/appearance/change-list.tsx | 5 +++ .../settings/appearance/sections-panel.tsx | 24 +++++++++++++ .../settings/branding/portal-preview.tsx | 12 +++++-- .../public/feedback/feedback-container.tsx | 5 ++- .../public/feedback/portal-feed-skeleton.tsx | 13 +++++-- .../lib/shared/design/__tests__/view.test.ts | 35 ++++++++++++++++--- apps/web/src/lib/shared/design/design-view.ts | 1 + apps/web/src/lib/shared/design/view.ts | 10 ++++++ .../__tests__/root.document-renders.test.tsx | 2 +- apps/web/src/routes/_portal/index.tsx | 23 ++++++++---- 12 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 apps/web/src/components/admin/settings/appearance/__tests__/change-list.test.ts diff --git a/apps/web/src/components/admin/settings/appearance/__tests__/change-list.test.ts b/apps/web/src/components/admin/settings/appearance/__tests__/change-list.test.ts new file mode 100644 index 0000000000..1f9b923fdd --- /dev/null +++ b/apps/web/src/components/admin/settings/appearance/__tests__/change-list.test.ts @@ -0,0 +1,22 @@ +/** + * Review and publish names each change the way the Appearance controls do. + */ +import { describe, expect, it } from 'vitest' +import { changeLabel } from '../change-list' + +describe('changeLabel', () => { + it.each([ + ['brand.light.accent', 'Accent'], + ['brand.dark.ink', 'Dark text'], + ['brand.fonts.heading.id', 'Heading font'], + ['layout.nav', 'Menu style'], + ['pages.feedback.defaultSort', 'Default sort'], + ['pages.feedback.headline.title.en', 'Headline'], + ['pages.feedback.headline', 'Headline'], + ['pages.feedback.headline.subtitle.de', 'Subtitle'], + ['pages.feedback.welcome.show', 'Welcome message'], + ['pages.feedback.welcome.body.content', 'Welcome message text'], + ])('%s is %s', (path, label) => { + expect(changeLabel(path)).toBe(label) + }) +}) diff --git a/apps/web/src/components/admin/settings/appearance/appearance-view.tsx b/apps/web/src/components/admin/settings/appearance/appearance-view.tsx index ec36cbda32..4a7a6ffe10 100644 --- a/apps/web/src/components/admin/settings/appearance/appearance-view.tsx +++ b/apps/web/src/components/admin/settings/appearance/appearance-view.tsx @@ -115,6 +115,7 @@ export function AppearanceView({ draftDirty design={draft.preview} scaleToDesktop + sort={draft.design.pages.feedback.defaultSort} viewport={viewport} workspaceName={workspaceName} faviconUrl={faviconUrl} diff --git a/apps/web/src/components/admin/settings/appearance/change-list.tsx b/apps/web/src/components/admin/settings/appearance/change-list.tsx index 553f61a4e7..0b70a418bb 100644 --- a/apps/web/src/components/admin/settings/appearance/change-list.tsx +++ b/apps/web/src/components/admin/settings/appearance/change-list.tsx @@ -24,6 +24,7 @@ const LABELS: Record = { 'layout.density': 'Density', 'layout.width': 'Page width', 'layout.filters': 'Filters side', + 'pages.feedback.defaultSort': 'Default sort', } /** A change's name as the Appearance controls call it. */ @@ -33,6 +34,10 @@ export function changeLabel(path: string): string { const name = COLOURS[colour[2]] ?? colour[2] return colour[1] === 'dark' ? `Dark ${name.toLowerCase()}` : name } + if (path.startsWith('pages.feedback.welcome.body')) return 'Welcome message text' + if (path === 'pages.feedback.welcome.show') return 'Welcome message' + const headline = path.match(/^pages\.feedback\.headline(?:\.(title|subtitle))?/) + if (headline) return headline[1] === 'subtitle' ? 'Subtitle' : 'Headline' const font = path.match(/^(brand\.fonts\.(?:heading|body))\./) return LABELS[font ? font[1] : path] ?? path } diff --git a/apps/web/src/components/admin/settings/appearance/sections-panel.tsx b/apps/web/src/components/admin/settings/appearance/sections-panel.tsx index b8993f43bb..e7751f81a3 100644 --- a/apps/web/src/components/admin/settings/appearance/sections-panel.tsx +++ b/apps/web/src/components/admin/settings/appearance/sections-panel.tsx @@ -3,6 +3,7 @@ import { Switch } from '@/components/ui/switch' import { SettingRow } from '@/components/admin/settings/setting-row' import { WelcomeBodyEditor } from '@/components/admin/settings/branding/welcome-body-editor' import type { TiptapContent } from '@/lib/shared/db-types' +import { ArrowTrendingUpIcon, ClockIcon, FireIcon } from '@heroicons/react/24/solid' import { DESIGN_OPTIONS } from '@/lib/shared/design/options' import type { PortalDesign } from '@/lib/shared/design/schema' import { DesignTiles } from './design-tiles' @@ -131,6 +132,29 @@ export function SectionsPanel({ design, update }: { design: PortalDesign; update drawing: , }))} /> + update((draft) => void (draft.pages.feedback.defaultSort = sort))} + options={[ + { + value: 'trending', + label: 'Trending', + drawing: , + }, + { + value: 'top', + label: 'Top', + drawing: , + }, + { + value: 'new', + label: 'New', + drawing: , + }, + ]} + /> (null) const [boxWidth, setBoxWidth] = useState(0) @@ -171,7 +177,7 @@ export function PortalPreview({