# Client feedback implementation task list This plan translates the client feedback from `Feedback.xlsx` into implementation tasks for the current Payload CMS and Next.js codebase. ## Confirmed decisions - Unpublishing any draft-enabled CMS document must remove it from the anonymous public frontend. - Authenticated draft/live preview may continue to show unpublished content intentionally. - The CMS cleanup is focused: remove obsolete or non-rendered controls, keep page-specific content on its page, and keep content shared by multiple pages in Globals. - `/netzwerk` receives its own CMS video upload instead of reusing the homepage video. - Images and videos introduced by this work must be selectable through Payload's Media collection. - Sponsor hierarchy needs a responsive design checkpoint before implementation. ## Definition of done - An anonymous visitor cannot see an unpublished page, winner, jury member, partner, event, or press article in a list, relationship-driven section, direct detail route, sitemap, or fallback UI. - An editor can still preview drafts through Payload preview/live preview. - Payload exposes only controls that affect the current frontend or an explicitly retained global contract. - New press and Netzwerk media can be selected and replaced in Payload without developer assistance. - All visual feedback is verified at desktop, tablet, and mobile widths. - Every schema change has a committed migration tested against a copy of the current SQLite database. ## Recommended implementation order ### 1. Establish the publication contract This is the highest-priority work because later CMS cleanup must not hide the underlying visibility problem. - [ ] Reproduce unpublishing from the Payload admin for every draft-enabled collection: - [ ] `pages` - [ ] `preistraeger` - [ ] `jury-mitglieder` - [ ] `partners` - [ ] `events` - [ ] `posts` - [ ] For every reproduction, record: - [ ] the document ID and `_status` before and after clicking **Unpublish** - [ ] whether the admin request actually saves `_status = draft` - [ ] behavior in an anonymous/private browser session - [ ] behavior in Payload preview or a browser carrying a draft-mode cookie - [ ] behavior on the collection/list page and direct detail URL - [ ] Confirm that the reported problem is not only draft preview doing what it is designed to do. - [ ] Add a table-driven integration test that queries each collection as an anonymous public request and proves that only `_status = published` documents are returned. Acceptance criteria: - [ ] Directly unpublishing a document changes its stored `_status` to `draft`. - [ ] Anonymous Local API reads enforce access control with `overrideAccess: false` and `draft: false`. - [ ] Draft preview is the only frontend context allowed to request unpublished documents. ### 2. Fix public route hydration and relationship resolution The public route currently loads the primary document plus supporting collections in `src/app/(frontend)/[[...slug]]/page.tsx`. Make the public/draft boundary explicit rather than relying on implicit parameter combinations. - [ ] Split public and preview query options into a small, testable helper: - [ ] public: `draft: false`, `overrideAccess: false` - [ ] preview: drafts enabled only when Next draft mode is active - [ ] Apply the same contract to: - [ ] the route document - [ ] support pages - [ ] winners - [ ] events - [ ] press articles - [ ] partners - [ ] jury members - [ ] Ensure relationship-populated objects cannot bypass the filtered public collection. - [ ] Update `resolveCmsRelationship(...)` and/or the consuming selection helpers so a relationship ID missing from the published collection resolves to nothing, never to the embedded relationship object. - [ ] Keep the existing jury page selection authoritative, but silently omit selected jury members that are unpublished. - [ ] Add regression tests for: - [ ] unpublished jury member selected on `/netzwerk` - [ ] unpublished winner selected on the homepage or `/der-bmp` - [ ] unpublished partner previously present in the homepage ticker - [ ] unpublished event previously present on `/presse` - [ ] unpublished press article previously present on `/presse` Acceptance criteria: - [ ] Unpublishing one related item removes only that item and preserves the remaining published selection order. - [ ] Republishing the item restores it without requiring a page document edit. ### 3. Remove collection fallbacks that resurrect unpublished content Fallbacks for a missing image or missing optional field may remain. Entire fallback records must not replace intentionally empty CMS collections. - [ ] Change `/presse` so an empty published `events` collection renders an intentional empty state or no event rows instead of `pressIndex.events.fallbackItems`. - [ ] Change `/presse` so an empty published `posts` collection renders an intentional empty state or no news cards instead of `pressIndex.news.fallbackItems`. - [ ] Remove the event and news fallback-record arrays from `pressIndexFields.ts` after the collection-backed render path is authoritative. - [ ] Remove their preload/seed plumbing from `preload-press-index-page-cms.ts` and fresh-database setup. - [ ] Audit winner, jury, and partner surfaces for the same pattern. - [ ] Preserve only presentation fallbacks that cannot make a removed CMS record reappear, such as a placeholder image on an otherwise published document. Acceptance criteria: - [ ] Unpublishing the final event, article, or related record produces an honest empty state. - [ ] No demo/default record appears merely because the CMS collection became empty. ### 4. Complete the focused Payload CMS cleanup Create a field-to-renderer inventory before deleting schema. Every retained field must have a current frontend consumer or a documented global purpose. #### Startseite - [ ] Keep the page-specific hero, winners, quick check, intro, testimonials, benefits, and homepage video controls. - [ ] Remove the hidden legacy `home.application` group now owned by the `application-form` Global. - [ ] Remove the hidden legacy `home.form` group now owned by the `application-form` Global. #### Teilnahme - [ ] Keep hero, process, eligibility, application paths, dates, and awards. - [ ] Remove the hidden legacy `participation.applicationForm` group. - [ ] Remove the hidden legacy `participation.form` group. #### Der BMP - [ ] Remove the hidden legacy `about.highlights` group because the rendered winners section reuses the homepage source of truth. #### Presse - [ ] Remove obsolete press-kit controls that no longer render: - [ ] `kitLabel` - [ ] `kitMeta` - [ ] `kitFile` - [ ] `kitUrl` - [ ] `kitDownloadName` - [ ] Remove the complete non-rendered `pressIndex.accreditation` group. - [ ] Remove the fallback-record arrays covered by Task 3. - [ ] Keep the rendered section headings, descriptions, status labels, missing-image controls, and press-contact copy. #### Netzwerk - [ ] Remove confirmed non-rendered fields after the field-to-renderer audit, including current candidates: - [ ] `jury.note` - [ ] `partners.premiumLabel` - [ ] `partners.defaultLogoColor` - [ ] sponsoring membership-footnote fields - [ ] Do not remove partner tier labels, partner modal copy, jury selection, or sponsoring form copy that still renders. #### Global ownership - [ ] Keep shared application content in **Formulare > Bewerbungsformular**. - [ ] Keep phase state in the **Application Phase** Global. - [ ] Keep phase-dependent newsletter content in the **Newsletter-Formular** Global. - [ ] Keep Header, Footer, and site-wide settings in their existing Website Globals. - [ ] Do not create a generic “Sections” content type unless a second real consumer justifies it. #### Schema completion - [ ] Generate a narrow migration that removes the obsolete live-table and version-table columns/child tables. - [ ] Update `payload-types.ts`. - [ ] Update preload, seed, and fresh-database scripts. - [ ] Validate migration upgrade and rollback on a copied SQLite database. Acceptance criteria: - [ ] The Payload admin contains no hidden legacy groups or controls with no current frontend effect. - [ ] Shared content is edited once in a Global and is not duplicated on individual pages. - [ ] Page editors contain only page-specific content plus standard page metadata/SEO controls. ### 5. Give `/netzwerk` its own Ilse Aigner video - [ ] Add a video upload field under the Netzwerk patronage/greeting section. - [ ] Filter the upload relationship to video MIME types. - [ ] Add page-specific video button label, title, description, and close-label fields if those strings need to differ from the homepage. - [ ] Replace the `HomeVideoCms`/homepage lookup in `Netzwerk.tsx` with the Netzwerk page fields. - [ ] Keep the existing homepage video and modal completely independent. - [ ] Update preload/fresh-database code without copying the existing homepage media relationship into the new field unless the client explicitly wants a temporary placeholder. - [ ] Let the client upload and select the supplied Netzwerk video after deployment. Acceptance criteria: - [ ] Changing the homepage video does not change `/netzwerk`. - [ ] Changing the Netzwerk video does not change the homepage. - [ ] Empty Netzwerk video selection hides the video action cleanly. ### 6. Add the CMS-controlled press-contact image - [ ] Add `contactImage` and `contactImageAlt` to the rendered Presse contact/material section. - [ ] Use a Payload upload relationship filtered to image MIME types. - [ ] Restore the desktop contact section to a two-column composition with copy on the left and the selected image on the right. - [ ] Stack image and copy in a deliberate order on mobile. - [ ] Define a safe empty-image layout so the section still works before the client selects an asset. - [ ] Update the preload script and migration. Acceptance criteria: - [ ] The client can select, replace, or clear the image in Payload. - [ ] No code edit is required to change the press image. ### 7. Repair the `/teilnahme` “Schritt für Schritt” timeline The current desktop implementation uses a `300vh` scroll container and produces overlapping/off-screen cards. - [ ] Replace the current per-card transform calculation with a single deterministic horizontal-track transform, or replace scroll-jacking with an explicit horizontal carousel/stepper. - [ ] Ensure steps 1–4 are all reachable at normal wheel/trackpad speed. - [ ] Remove the unused vertical space after the final step. - [ ] Make the progress indicator derive from actual scroll/active-step state instead of fixed copy. - [ ] Preserve the existing stacked mobile presentation unless testing finds a separate defect. - [ ] Respect `prefers-reduced-motion` by providing a non-scroll-jacked interaction. - [ ] Add a browser regression test that proves every step becomes visible and the next section follows without a large blank gap. Acceptance criteria: - [ ] Steps 1, 2, 3, and 4 can each be read in full. - [ ] Cards never overlap at supported desktop/tablet widths. - [ ] The eligibility section begins promptly after the timeline finishes. ### 8. Design and approve a responsive sponsor hierarchy Recommended direction: preserve a hierarchy through tier bands and decreasing visual weight rather than forcing lower tiers into empty higher-tier slots. Mixing tiers within one row weakens the pyramid and produces inconsistent card widths. #### Proposed responsive behavior - [ ] Desktop (`>= 1200px`): - [ ] Tier 1: up to 2 large cards per centered row - [ ] Tier 2: up to 3 medium cards per centered row - [ ] Tier 3: up to 4 compact cards per centered row - [ ] Center incomplete rows rather than filling them with a different tier - [ ] Tablet (`768–1199px`): - [ ] Tier 1: 2 columns - [ ] Tier 2: 2 columns - [ ] Tier 3: 3 columns where space allows, otherwise 2 - [ ] Mobile (`< 768px`): - [ ] one clearly labeled section per tier - [ ] Tier 1 rendered as full-width feature cards - [ ] Tier 2 rendered as medium full-width cards - [ ] Tier 3 rendered as compact cards, using two columns only when logos remain readable - [ ] Use card size, logo area, spacing, and tier heading—not color alone—to communicate rank accessibly. - [ ] Keep `partner.tier` and `partner.sortOrder` as the CMS source of truth. Approval gate: - [ ] Produce desktop, tablet, and mobile mockups using the current partner counts. - [ ] Review the hierarchy with the client before implementing the final grid. - [ ] After approval, implement the chosen breakpoints without changing the partner data model unless the mockup reveals a real need. Acceptance criteria: - [ ] Hierarchy is immediately understandable at all three breakpoint classes. - [ ] Adding or removing a partner does not create broken gaps or stretch a card unpredictably. - [ ] Logo aspect ratios remain intact. ### 9. Make sponsor ordering explicit and manageable - [ ] Confirm that `sortOrder` remains authoritative within each tier. - [ ] Show `tier`, `sortOrder`, publication status, and active state in useful Partners admin columns. - [ ] Add validation preventing unsupported tier values outside 1–3. - [ ] Document the current order for client review: 1. RSM Ebner Stolz 2. WWK 3. Radio Gong 96.3 4. münchen.tv 5. METZLER 6. Dr. Wieselhuber & Partner 7. Fristads 8. New Edge 9. Moodtalk 10. Deutsche Bank 11. Bionorica 12. Primus - [ ] Treat an order change as a CMS content operation, not a frontend code change. ### 10. Slow the homepage sponsor ticker - [ ] Replace the fixed `22s` duration with a slower content-aware duration or an agreed slower fixed baseline. - [ ] Keep perceived speed stable as the number of logos changes. - [ ] Pause or substantially reduce motion on hover/focus and respect `prefers-reduced-motion`. - [ ] Verify that duplicating the track still produces a seamless loop. Acceptance criteria: - [ ] The ticker feels calm enough to read individual logos. - [ ] No jump is visible at the loop boundary. ### 11. Correct the homepage phase-heading line break - [ ] Confirm the client copy belongs to the homepage status/phase module. - [ ] Make phase headlines honor explicit CMS newlines. - [ ] Store/render the target split as: ```text Ein weiteres erfolgreiches Jahr ``` - [ ] Preserve responsive wrapping when translated or edited copy is longer. - [ ] Do not hard-code this one sentence in the component. ### 12. Adjust the homepage bottom application/newsletter split - [ ] Widen the blue left panel on the homepage so the boundary with the gold form panel feels more balanced. - [ ] Scope the ratio change through the existing `source="homepage"` prop unless the client separately approves the same change on `/teilnahme`. - [ ] Recheck text measure, benefit rows, form width, and the image area after changing the ratio. - [ ] Verify both open-application and newsletter phase variants. ### 13. Verify the shared application/newsletter background color The current component already uses dark blue `#24366A` instead of black. - [ ] Compare the deployed site with the current source. - [ ] If production is still black, deploy the existing dark-blue implementation or identify the production content/style override. - [ ] Make no additional palette change if production already matches the requested dark blue. Verification record (2026-07-31): The canonical production deployment at `https://srv1.bayerischer-mittelstandspreis.de` was checked read-only on `/` and `/teilnahme`. Both routes were serving the newsletter variant, and the rendered `#bewerben` section computed to `rgb(36, 54, 106)` (`#24366A`), with no black or content/style override. The shared source owner is `src/spa/components/forms/ApplicationFormSection.tsx`; its unconditional outer section background applies to both the application-open and newsletter variants. No palette or deployment change was needed. ### 14. Improve `/teilnahme` eligibility hierarchy - [ ] Keep the existing content and CTAs. - [ ] Introduce clearer grouping within the white eligibility area through restrained section tinting, dividers, spacing, or row containers. - [ ] Avoid turning every criterion into an unrelated floating card. - [ ] Make the visual transition into “Wichtige Termine” clear without redesigning the rest of the page. - [ ] Verify mobile scan order and contrast. ### 15. Adjust `/der-bmp` “Relevanz & Zielsetzung” - [ ] Increase the desktop share of the left image so more of it is visible. - [ ] Shift the right-hand copy farther into the viewport and give it a centered content measure within its panel. - [ ] Tune the image `object-position` rather than cropping important subjects. - [ ] Keep mobile image-first ordering and readable padding. ### 16. Correct `/der-bmp` “Was ist der Preis?” facts and caption - [ ] Center the three key facts within the text panel instead of anchoring each value to the left edge. - [ ] Keep separators and equal visual weight across all three facts. - [ ] Improve the image-caption legibility with a controlled background/overlay or by moving it into a dedicated caption area. - [ ] Verify long CMS labels do not collide. ### 17. Fix sitemap route generation Sitemap support already exists, but current output needs correction. - [ ] Generate the homepage URL as `/` when the page slug is either `home` or `startseite`, or prefer the stored `spaPath` directly. - [ ] Generate press article URLs under `/presse/blog/` instead of `/posts/`. - [ ] Add event detail URLs under `/presse/events/`. - [ ] Add winner detail URLs under `/preistraeger/`. - [ ] Keep unpublished documents out by using public access and `_status = published`. - [ ] Ensure generated sitemap and robots URLs use the production base URL during deployment, never committed localhost/example URLs. - [ ] Add route-level tests for root, page, event, article, and winner entries. Acceptance criteria: - [ ] Every sitemap URL returns `200` on production. - [ ] Unpublished entries disappear from the sitemap immediately or after the documented cache/revalidation window. ### 18. Regression, migration, and deployment verification - [ ] Run focused unit/integration tests for publication filtering, relationship resolution, and sitemap paths. - [ ] Run TypeScript without incremental cache. - [ ] Run the relevant Vitest suite. - [ ] Build against a migrated copy of `content.db`. - [ ] Visually verify at approximately `1440px`, `1024px`, `768px`, and `390px` widths. - [ ] Test anonymous public browsing separately from authenticated draft preview. - [ ] Back up the production SQLite database. - [ ] Deploy the schema-changing commit with explicit migrations enabled. - [ ] Upload/select the client-supplied Netzwerk video and the selected press image in production Payload. - [ ] Smoke-test all affected routes after deployment: - [ ] `/` - [ ] `/teilnahme` - [ ] `/der-bmp` - [ ] `/netzwerk` - [ ] `/presse` - [ ] representative event/article/winner detail routes - [ ] sitemap and robots endpoints - [ ] Perform a production unpublish/republish smoke test using disposable or approved test records. ## Feedback traceability | Workbook feedback | Planned work | | --- | --- | | CMS unpublish button does not work | Tasks 1–3 and 18 | | `/teilnahme` steps 2–4 do not open; excess space | Task 7 | | Separate Ilse Aigner video from homepage | Task 5 | | Remove press-kit CMS controls | Task 4 | | Keep Pages focused and shared content in Globals | Task 4 | | Remove accreditation form copy | Task 4 | | Shared 2027-interest section should be dark blue | Task 13; current source already appears compliant | | `/der-bmp` relevance text/image alignment | Task 15 | | Partner tier hierarchy and responsive layout | Tasks 8–9 | | Add image beside press contact | Task 6 | | Add/fix sitemap in code | Task 17 | | Sponsor ticker feels too fast | Task 10 | | Homepage success heading line break | Task 11 | | Homepage bottom blue panel should be wider | Task 12 | | Participation eligibility area lacks structure | Task 14 | | `/der-bmp` facts and caption are misaligned/hard to read | Task 16 | | Confirm sponsor order | Task 9 | | “Aktuelles” looks good | No change required |