From 36098374c254b672e90f66702c3ef6a6c6ea8cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Sun, 7 Jun 2026 07:31:09 +0200 Subject: [PATCH] move stuff to docs --- STATUSES.md => docs/STATUSES.md | 0 docs/form-overhaul.md | 398 ++++++++++++++++++++++++++++++++ 2 files changed, 398 insertions(+) rename STATUSES.md => docs/STATUSES.md (100%) create mode 100644 docs/form-overhaul.md diff --git a/STATUSES.md b/docs/STATUSES.md similarity index 100% rename from STATUSES.md rename to docs/STATUSES.md diff --git a/docs/form-overhaul.md b/docs/form-overhaul.md new file mode 100644 index 0000000..a59a237 --- /dev/null +++ b/docs/form-overhaul.md @@ -0,0 +1,398 @@ +# Form Overhaul Plan + +> Last updated: 2026-05-12 +> Status: Decided — awaiting implementation +> +> **Decisions made:** +> - All forms (simple and complex) get section headers for consistency +> - Two-column layout uses **flexbox** (auto-reflow on different screen sizes) +> - `cotton/layouts/add.html` enhanced with **Option A**: `c-section` component slots +> - `add_purchase.html` dual-submit **simplified** — remove ``, use same `c-button` pattern as `add_game.html` +> - GameStatusChange delete confirmation **converted to modal** (via HTMX trigger) + +## Goal + +Modernize all forms and form-like elements to align with Flowbite design, improve visual consistency, and adopt responsive multi-column layouts for complex forms. + +--- + +## Current State Analysis + +### Form Pages (add/edit) + +All use `cotton/layouts/add.html` — single column, `max-w-xl`, `form.as_div`: + +| Page | Form | Fields | Complexity | +|---|---|---|---| +| Game | `GameForm` | 7 fields: name, sort_name, platform, year, year_orig, status, mastered, wikidata | Medium | +| Purchase | `PurchaseForm` | 11 fields: games, platform, dates, price, currency, type, ownership, related, infinite, name | High | +| Session | `SessionForm` | 8 fields: game, timestamps, duration, emulated, device, note, checkbox (custom rendering) | High | +| Platform | `PlatformForm` | 3 fields: name, icon, group | Low | +| Device | `DeviceForm` | 2 fields: name, type | Low | +| PlayEvent | `PlayEventForm` | 5 fields: game, dates, note, checkbox | Low | +| GameStatusChange | `GameStatusChangeForm` | 4 fields | Low | + +### Other Form-Like Elements + +| Element | Template | Notes | +|---|---|---| +| Login | `registration/login.html` | Flowbite card, already good | +| Search | `cotton/search_field.html` | Reusable, already good | +| Delete Game | `partials/delete_game_confirmation.html` | Inline modal, inconsistent button layout | +| Delete PlayEvent | `gamestatuschange_confirm_delete.html` | Full-page form, no modal | +| Refund Purchase | `partials/refund_purchase_confirmation.html` | Inline modal, inconsistent button layout | +| Stats Year Select | `stats.html` | Manual ``** has no Flowbite styling +9. **Search field** is not wrapped in `
` — no native clear-on-Enter behavior + +### P2: Styling Consistency + +10. **Status/device selectors** use old Tailwind v3 patterns (`rounded-sm`, `shadow-2xs`, `border-gray-200` without explicit color) +11. **`navbar.html` buttons** use `rounded-sm` instead of `rounded-base` +12. **`simple_table.html` pagination buttons** use `rounded-s-lg`/`rounded-e-lg` — could be simplified + +--- + +## Proposed Improvements + +### 1. Two-Column Layout for Complex Forms (Flexbox) + +**Scope**: `GameForm`, `PurchaseForm`, `PlayEventForm`, `SessionForm` + +Use **flexbox** with wrap behavior so fields auto-reflow on different screen sizes. No fixed column count — fields sit side-by-side on `md:`+ and wrap naturally on smaller screens. + +#### GameForm Layout +``` +┌──────────────────────────────────┐ +│ Game Details │ +│ ┌──────────────────┬───────────┐ │ +│ │ Name │ Platform │ │ +│ │ Sort Name │ Year │ │ +│ │ Original Year │ Wikidata │ │ +│ └──────────────────┴───────────┘ │ +│ Status │ +│ ┌──────────────────┬───────────┐ │ +│ │ Status │ Mastered │ │ +│ └──────────────────┴───────────┘ │ +│ [Submit] │ +└──────────────────────────────────┘ +``` + +#### PurchaseForm Layout (simplified) +``` +┌──────────────────────────────────────────┐ +│ Purchase Details │ +│ ┌──────────────────────┬───────────────┐ │ +│ │ Games (multi-select) │ Platform │ │ +│ │ Type │ Ownership │ │ +│ │ Name │ Related Purch │ │ +│ └──────────────────────┴───────────────┘ │ +│ Dates │ Price │ +│ ┌───────────────┬──────┴───────────────┐ │ +│ │ Date Purch │ Price Curr │ │ +│ │ Date Refund │ Infinite [ ] │ │ +│ └───────────────┴──────────────────────┘ │ +│ [Submit] [Submit + Session] │ +└──────────────────────────────────────────┘ +``` + +**Implementation**: `c-section` component accepts `columns="2"` (or `"3"`) which applies `flex flex-wrap gap-4 [&>div]:w-[calc(50%-0.5rem)]` on md+ screens. Each field wraps in a `
` inside the section slot. + +**Decision**: Dual-submit in `add_purchase.html` simplified — remove ``, use same `` pattern as `add_game.html`. + +### 2. Field Grouping with Card Sections + +**Decision**: ALL forms get section headers for consistency (not just complex forms). + +Group related fields with section headings and subtle borders/backgrounds: + +```html + + {{ form.name }} + {{ form.platform }} + {{ form.sort_name }} + {{ form.year_released }} + +``` + +Each section renders as: +```html +
+

Section Title

+
+ +
+
+``` + +Each section gets: +- Subtle background (`bg-neutral-primary-soft/30`) +- Top border with spacing (`border-t border-default-medium`) +- Section heading (`text-sm font-medium text-heading uppercase mb-4`) +- Flexbox gap for responsive field reflow + +### 1b. `c-section` Component Specification + +New cotton component for the `cotton/` directory: + +```python +# games/templates/cotton/section.py (or inline in components.py) +from common.components import Div + +def Section(title: str = "", columns: str = "1", children: str = "") -> SafeText: + """Renders a form field section with optional multi-column flexbox layout. + + Args: + title: Section heading (renders as uppercase label) + columns: "1" (default), "2", or "3" — target column count on md+ screens + children: Field markup (each field wrapped in
for flex wrapping) + """ + col_class = { + "1": "flex flex-col", + "2": "flex flex-wrap gap-4 [&>div]:w-[calc(50%-0.5rem)]", + "3": "flex flex-wrap gap-4 [&>div]:w-[calc(33.333%-0.67rem)]", + }.get(columns, "flex flex-col") + + return Div( + cls=f"form-section p-5 border-t border-default-medium bg-neutral-primary-soft/30 first-of-type:border-t-0 first-of-type:pt-0", + children=f""" +

{title}

+
{children}
+ """ + ) +``` + +**Template usage:** +```django +{# add_game.html #} + + +
{{ form.name }}
+
{{ form.platform }}
+
{{ form.sort_name }}
+
{{ form.year_released }}
+
{{ form.original_year_released }}
+
{{ form.wikidata }}
+
+ +
{{ form.status }}
+
{{ form.mastered }}
+
+
+``` + +**`cotton/layouts/add.html` changes:** +- Remove hardcoded `{{ form.as_div }}` rendering +- Accept optional `sections` variable (list of rendered `c-section` output) +- If `sections` provided, render them; otherwise fall back to `{{ form.as_div }}` for simple forms +- Keep `additional_row` slot for dual-submit buttons + +### 3. CSS/Style Fixes + +#### `input.css` changes: +```css +/* Update errorlist */ +.errorlist { + @apply mt-4 mb-1 pl-3 py-2 bg-red-600 text-slate-200 w-full max-w-xl; /* was w-[300px] */ +} + +/* Remove: #button-container, .basic-button — unused legacy */ + +/* Remove: .flowbite-input — custom class is code smell with Tailwind */ +/* Remove: flowbite-input @apply block (line 229-234) */ + +/* Add Flowbite styling for select in stats */ +#yearSelect { + @apply bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand; +} +``` + +**Important**: The styling previously provided by `.flowbite-input` must be preserved. The element-level `@apply` rules for `input`, `select`, and `textarea` in `input.css` (lines 209-219) already provide equivalent styling. These rules automatically apply to all form inputs without needing custom classes: +- `input:not([type="checkbox"])` — background, border, text, radius, focus ring, padding +- `select` — same base styling as inputs +- `textarea` — same base styling with adjusted padding + +**Files to clean up:** +- `common/input.css`: Remove `.flowbite-input` class entirely (lines 229-234) +- `games/forms.py`: Remove `flowbite_input_widget` and `flowbite_password_widget` (lines 22-23) +- `games/forms.py`: Remove `widget=` from `LoginForm` fields (lines 28, 32) — login template uses explicit Tailwind classes already + +#### Rewrite `modal.html`: +- Remove stray `` tag and restructure as a proper cotton component +- New `c-modal` component with: `modal_id`, `title`, `size="xl"`, `backdrop_close` variables +- `{{ slot }}` (cotton default slot) for body content — passed as children of ``, no block tags needed +- `{{ footer }}` (optional named slot via ``) for non-form buttons +- Reusable `cotton/close_button.html` via `` +- Size mapping via inline `{% if %}`: `{% if size == 'sm' %}max-w-sm{% elif size == 'lg' %}max-w-lg{% else %}max-w-xl{% endif %}` +- Horizontal centering: `mx-auto` on inner container (matching old modal pattern) +- Click-to-dismiss backdrop with `event.stopPropagation()` on inner container +- Flowbite-style styling: `rounded-lg shadow`, `bg-white dark:bg-gray-800`, `sm:p-5` + +### 4. Unify Delete Confirmations (All Modal) + +**Decision**: GameStatusChange delete confirmation converted from full-page to modal. All three use the same modal pattern. + +**Target**: All confirmation modals use the same pattern: + +```html +
+
+

Confirm Action

+

Are you sure...?

+ {% if details %} +
    +
  • {{ detail }}
  • +
+ {% endif %} +

This action cannot be undone.

+
+ Delete + Cancel +
+
+
+``` + +- **Delete Game** (`partials/delete_game_confirmation.html`): Update template to match standard pattern +- **Delete StatusChange** (`gamestatuschange_confirm_delete.html` → `partials/statuschange_delete_confirmation.html`): Adopt the same 2-view pattern as delete-game. + - Add `delete_statuschange_confirmation` view (GET → renders modal partial) + URL before the delete URL + - Update `partials/history.html` — add `hx-get="{% url 'games:delete_statuschange_confirmation' change.id %}" hx-target="#global-modal-container"` to the Delete link + - Create new `partials/statuschange_delete_confirmation.html` using ``, same structure as `delete_game_confirmation.html` (detail list, red warning text, same button layout, `` badge for old status) + - Modify `GameStatusChangeDeleteView` to only handle POST (remove its GET-rendered template) + - Delete old `gamestatuschange_confirm_delete.html` after migration +- **Refund Purchase** (`partials/refund_purchase_confirmation.html`): Update template to match standard pattern + +### 5. Search Form Enhancement + +Wrap `search_field.html` in proper `
`: + +```html + + +
+``` + +This enables: +- Native form submission on Enter +- Potential for "clear all" functionality +- Proper browser form autofill behavior + +### 6. Status/Device Selector Styling + +Update Alpine.js dropdowns to use consistent button classes: +- Replace `rounded-lg` with `rounded-base` +- Replace `shadow-2xs` with `shadow-xs` +- Standardize border colors with `border-default` +- Use `text-heading` / `text-body` for dark mode compatibility + +--- + +## Templates That Need Changes + +| Template | Change | Effort | +|---|---|---| +| `cotton/layouts/add.html` | Add `c-section` component support (title, columns, fields slots) | Medium | +| `add_game.html` | Multi-column flexbox layout, section headers | Medium | +| `add_purchase.html` | Multi-column flexbox layout, simplify dual-submit, section headers | High | +| `add_session.html` | Flexbox layout for timestamps+duration, section headers | Low | +| `add_playevent.html` | Flexbox layout, section headers | Low | +| `add_platform.html` | Section headers (was flat single-column) | Low | +| `add_device.html` | Section headers (was flat single-column) | Low | +| `partials/delete_game_confirmation.html` | Standardize to shared modal pattern | Low | +| `partials/refund_purchase_confirmation.html` | Standardize to shared modal pattern | Low | +| `partials/statuschange_delete_confirmation.html` | New — adopt same 2-view pattern as delete-game (modal, ``, HTMX triggers) | Medium | +| `gamestatuschange_confirm_delete.html` | Delete (replaced by new partial) | Trivial | +| `cotton/modal.html` | Fix missing `
` tag | Low | +| `stats.html` | Add Flowbite select styling | Low | +| `partials/gamestatus_selector.html` | Update button classes | Low | +| `partials/sessiondevice_selector.html` | Update button classes | Low | +| `cotton/search_field.html` | Wrap in `` | Low | +| `common/input.css` | Remove legacy, fix errorlist, add select styles | Low | + +--- + +## Implementation Order + +### Phase 1: Quick Wins (low risk, no breaking changes) + +1. **CSS fixes** (`input.css`) — fix errorlist width, remove legacy `.basic-button` / `#button-container`, add select styles +2. ~~**`modal.html` rewrite**~~ — add missing `` tag, conditional form wrapper ✓ Implemented (uses `{{ slot }}` cotton default slot, no `{% partial %}` tags; `size` defaults to `"xl"` with inline `{% if %}` mapping) +3. **Delete confirmation standardization** — 3 templates → all modal, same pattern (including GameStatusChange: full-page → modal) +4. **Search field enhancement** — wrap in `` +5. **Stats select styling** — add Flowbite select classes +6. **Selector styling updates** — gamestatus + sessiondevice selectors, consistent classes + +### Phase 2: `c-section` Component + +7. **Create `c-section` component** — title, columns, fields slots +8. **Update `cotton/layouts/add.html`** — support `sections` variable, fallback to `form.as_div` + +### Phase 3: Form Layout Overhaul (largest change) + +9. **`GameForm`** — section headers + 2-col flexbox (`add_game.html`) +10. **`PlayEventForm`** — section headers + 2-col flexbox +11. **`PurchaseForm`** — section headers + 2/3-col flexbox + simplify dual-submit (`add_purchase.html`) +12. **`SessionForm`** — section headers + flexbox for timestamps+duration (custom rendering already exists) +13. **Simple forms** — `add_platform.html`, `add_device.html` get section headers (single column) + +--- + +## Testing Strategy + +- Run `make test` after Phase 1 changes to verify nothing broke +- `tests/test_paths_return_200.py` — URL-level smoke tests (186 tests). All views must have a `test_*_returns_200` test. Adding new views requires a corresponding test to prevent `TemplateDoesNotExist` regressions. +- CSS changes do not require test changes (no test coverage for rendering), but visual verification is recommended + +--- + +## Open Questions + +- [x] Simple forms section headers? → **All forms get section headers** for consistency +- [x] CSS Grid or Flexbox? → **Flexbox** — auto-reflow on different screen sizes +- [x] add.html layout variable? → **Option A** — `c-section` cotton component with `title` and `columns` slots +- [x] add_purchase.html dual-submit? → **Simplify** — remove ``, use same `` pattern as `add_game.html` +- [x] GameStatusChange modal or full-page? → **Modal** — trigger via HTMX, same pattern as delete-game +- [x] .flowbite-input class? → **Remove entirely** — rely on element-level `@apply` in `input.css` + +## Decision Summary + +| Question | Decision | +|---|---| +| Section headers on simple forms | Yes, all forms get them | +| Layout approach for multi-column | Flexbox with wrap | +| Layout mechanism in add.html | Option A: `c-section` cotton component | +| Purchase dual-submit | Simplify — single submit button, same as Game | +| GameStatusChange delete | Convert to modal (HTMX-triggered) | +| .flowbite-input class | Remove — preserve styling via element-level `@apply` in `input.css` | +| `modal.html` component | Rewrite with form wrapping, body + footer slots, reusable close button ✓ Implemented + +## Build Step + +After any CSS changes to `common/input.css`, the compiled output must be rebuilt: + +- **`make css`** — one-shot build: `npx @tailwindcss/cli -i ./common/input.css -o ./games/static/base.css` +- **`make dev`** — watch mode: Tailwind rebuilds automatically on every `input.css` save + +Running `make dev` is sufficient for development since it concurrently runs Django and the CSS watcher. +Only use `make css` if you only want to rebuild CSS without starting the dev server. + +**Important**: Legacy CSS removals (`.basic-button`, `#button-container`, `.flowbite-input`) will only take effect in the browser after a rebuild. The old compiled `base.css` will still contain them until rebuilt.