Files
Marijn 404c0dcc26 Summer cleanup: XLSX export, WIDM-style quiz UI, CSS fixes (#162)
* Add CLAUDE.md, replace Makefile with Justfile, remove .junie

- Add CLAUDE.md with project overview, commands, architecture, and domain entity docs
- Remove Makefile in favour of the existing Justfile
- Remove .junie/AGENTS.md (knowledge transferred to CLAUDE.md)
- Update .gitignore: drop .junie/ entries, add .claude/settings.local.json
- Minor doc fixes in config/reference.php (typo, type correction)

* Clean up templates and CSS

- season.html.twig: remove dead empty column, drop redundant flex-row
- tab_overview.html.twig: extract Twig macro for confirm modals, fix duplicate aria-labelledby IDs
- tab_result.html.twig: remove dead comment, replace inline widths with CSS classes, simplify nested row/col forms to d-flex gap-1
- backoffice.scss: add col-result-xs/sm/md column width classes
- quiz.scss: replace broken display:grid + justify-self:center with flexbox centering

* Implement quizToXlsx() export and add export button

- QuizSpreadsheetService: implement quizToXlsx() as the inverse of
  fillQuizFromArray() — writes quiz questions and answers to XLSX using
  the same column layout as the import template
- BackofficeController: add exportQuiz() action at GET /backoffice/quiz/{quiz}/export
- tab_overview.html.twig: add Export to XLSX button in Quick actions

* Add unit tests for QuizSpreadsheetService

7 tests covering generateTemplate(), quizToXlsx(), and xlsxToQuiz():
- valid XLSX output and MIME type
- template without example reimports as empty
- template example data survives a reimport
- round-trip (export → reimport) preserves questions, answers, and correct flags
- empty quiz exports and reimports cleanly
- invalid MIME type throws InvalidArgumentException
- question with no answers throws SpreadsheetDataException with error list

* Fix quiz page vertical centering regression

The CSS cleanup broke vertical centering: flex on body causes main to
stretch full-width; place-items:center on a grid body only centers
items within their auto-sized track (not the track within body).

Fix: move background/color to html (full-viewport grid that centers
body), give body height:100% + display:grid + align-content:center
(centers the content track within full-height body) + justify-self:center
(shrink-wraps body width). Matches production behavior exactly.

* Improve quiz page layouts: WIDM-style answers and responsive centering

- Add green square answer buttons styled after the TV show
- Two-column answer grid for 6+ answers, single column on mobile
- fit-content centering for question pages so block matches question width
- Narrow fixed-width centering for form pages (enter name, select season)

* Use HeaderUtils::makeDisposition() for safe Content-Disposition filename

* Fix quizToXlsx to support unlimited answers and add header count tests

- Replace hardcoded 6-column arrays with dynamic Coordinate arithmetic
- Write data rows first to determine max answer count, write headers last
- Replace try/catch ErrorException in fillQuizFromArray with array_key_exists
- Add data-provider test covering 2, 6, 7, and 10 answers
- Add cross-question max-header and 7-answer round-trip tests

* Fix Sass healthcheck

* Improve quiz layout: add fixed topbar, include navigation, and clean up unused elements

- Add `.quiz-topbar` with fixed positioning and spacing in `quiz.scss`
- Update `base.html.twig` to include `quiz/nav.html.twig` in a new `nav` block
- Remove unused "Manage Quiz" button from `select_season.html.twig`

* Refactor generateTemplate to reuse quizToXlsx and add second example question

- generateTemplate now builds an in-memory Quiz entity and delegates to
  quizToXlsx, eliminating duplicate spreadsheet-building logic
- Adds a second example question "Wie is de mol?" with 10 Dutch names
  (5 male, 5 female) to better illustrate the import format
- Updates tests to assert both example questions and adds a test for the
  blank-row halt behaviour in fillQuizFromArray (achieving 100% coverage)

* Move PHPUnit cache to /tmp to avoid writing into the mounted volume

* Update src/Service/QuizSpreadsheetService.php

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-07-02 20:25:02 +00:00

133 lines
2.3 KiB
Plaintext

/frankenphp/data
### Generated by gibo (https://github.com/simonwhitaker/gibo)
### https://raw.github.com/github/gitignore/6eeebe6f49678aacd8311ce079842c971b3ebe96/Symfony.gitignore
# Cache and logs (Symfony2)
/app/cache/*
/app/logs/*
!app/cache/.gitkeep
!app/logs/.gitkeep
# Email spool folder
/app/spool/*
# Cache, session files and logs (Symfony3)
/var/cache/*
/var/logs/*
/var/sessions/*
!var/cache/.gitkeep
!var/logs/.gitkeep
!var/sessions/.gitkeep
# Logs (Symfony4)
/var/log/*
!var/log/.gitkeep
# Parameters
/app/config/parameters.yml
/app/config/parameters.ini
# Managed by Composer
/app/bootstrap.php.cache
/var/bootstrap.php.cache
/bin/*
!bin/console
!bin/symfony_requirements
# Assets and user uploads
/web/bundles/
/web/uploads/
# PHPUnit
/app/phpunit.xml
# Build data
/build/
# Composer PHAR
/composer.phar
# Backup entities generated with doctrine:generate:entities command
**/Entity/*~
# Embedded web-server pid file
/.web-server-pid
### Generated by gibo (https://github.com/simonwhitaker/gibo)
### https://raw.github.com/github/gitignore/6eeebe6f49678aacd8311ce079842c971b3ebe96/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# IDEs
/.idea/
/.vscode/
# Claude Code
/.claude/settings.local.json
# Windows
Thumbs.db
Desktop.ini
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###
###> phpstan/phpstan ###
phpstan.neon
###< phpstan/phpstan ###
###> friendsofphp/php-cs-fixer ###
/.php-cs-fixer.php
/.php-cs-fixer.cache
###< friendsofphp/php-cs-fixer ###
###> phpunit/phpunit ###
/phpunit.xml
/.phpunit.cache/
###< phpunit/phpunit ###
###> vincentlanglet/twig-cs-fixer ###
/.twig-cs-fixer.cache
###< vincentlanglet/twig-cs-fixer ###
###> symfony/phpunit-bridge ###
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> symfony/asset-mapper ###
/public/assets/
/assets/vendor/
###< symfony/asset-mapper ###