The Anzeigen / Download / Löschen icons inside the documents modal
and the close X of every modal sit outside <table>, so the previous
"table button" reset didn't reach them — the global blue button
styling shone through.
- Introduce a generic `.icon-btn` class that mirrors the table reset
(transparent background, no padding, inherit colour). The reset
rule is now `table button, .icon-btn`.
- Tag the documents modal close button and the view/download/delete
buttons inside each document card with `icon-btn`. Tailwind classes
(`text-outline hover:text-primary` etc.) still win for colour.
- Rebuild the PDF viewer modal in Stitch style: clean white card,
light header with picture_as_pdf glyph + title, body on a soft grey
surface, footer with chevron navigation buttons and zoom icons.
Toggle hidden/flex instead of the legacy `.show` class. Removed the
matching .pdf-viewer-* / .pdf-nav-btn / .pdf-zoom-btn CSS that no
longer applies.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three follow-ups to the previous overview redesign:
- Filter bar: replace flex-grow inputs with fixed widths so the
Zurücksetzen button no longer ends up overlapping the second date
field. The date range gets its own 320px slot wide enough for two
flexible date pickers and the separator.
- Table icon buttons used to inherit the legacy global
`button { background, padding, color }` rule and rendered as solid
blue blocks. Add a `table button { background: transparent; … }`
reset; class-level utilities still win for buttons that opt back in
with bg-primary etc., so the "Neue Schaltung" CTA above the table
keeps its colour.
- Documents modal rewritten in Stitch style: clean white card with a
light header and footer separator, file rows as bordered cards with
a Material icon, primary "Dokumente hinzufügen" pill and outline
"Schließen". showDocuments / closeDocumentsModal toggle a Tailwind
hidden/flex pair instead of the legacy `.show` class. The matching
legacy CSS blocks (.documents-modal*, .document-info, .download-btn,
.view-btn, .no-documents*) are removed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Page header gains the headline / subtitle pair and a primary
"Neue Schaltung" CTA on the right that jumps back to the wizard.
- Filter bar moves into a bordered white card with label-caps labels,
compact 32px inputs, JetBrains Mono numeric inputs for ID/phone/date
and a "Zurücksetzen" pill on the right.
- Data table uses the Stitch shell: sticky surface-container-high
header row with label-caps column titles, 40px body rows, zebra
striping on the even rows and a hover highlight. The previously
separate Eingabe/Schaltungs-Datum columns are merged into a single
"Daten" cell (E:/A:), Anbieter renders as a primary pill, and the
Bearbeiten/Löschen/Dokumente buttons become icon-only actions.
- updateSchaltungenTable() now delegates to a single renderer used
by both the initial load and the client-side filter, so the
layout stays consistent after applying filters.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous reset set border-style: solid globally but kept the
browser-default border-width, which surfaced 1-2px outlines on
every <button>, <input>, <header> etc. Match the relevant slice of
Tailwind's Preflight by also resetting border-width to 0, so only
elements that explicitly opt in via a border utility (or a legacy
shorthand rule like `border: 2px solid #ddd`) draw a border.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small but visible regressions in the previous step:
1. Tailwind's "border" utility only sets the width; with preflight
disabled the style fell back to the browser default (none), so the
carrier cards in step 1 rendered without any visible outline. Add a
minimal *,::before,::after rule that restores border-style: solid
and a sensible default border-color, mirroring the relevant slice
of preflight without breaking legacy screens.
2. The active tab underline was a mix of Tailwind border-b-2 utility
classes on the button and a CSS border-bottom override. Because the
button used flex items-center inside an li, the underline floated
somewhere in the middle of the header instead of hugging the bottom.
Drop the Tailwind border-* classes and switch to a single inset
box-shadow at the bottom of the button, which always anchors to the
header's lower edge regardless of padding.
3. The carrier grid was 4 columns on lg, which made 6 providers wrap
awkwardly. Match the Stitch mockup: 2 cols on small screens, 3 on
lg. Card corner radius shrunk from rounded-lg back to rounded (4px)
to match the Stitch tool-style aesthetic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Foundation
- Pull in Tailwind via CDN with the full Stitch design tokens
(palette, typography, spacing, radius) inline in <head>. Preflight
disabled so the rest of the legacy inline CSS keeps applying until
each screen is migrated.
- Add Hanken Grotesk + JetBrains Mono + Material Symbols Outlined
via Google Fonts.
- Replace the page header with the Stitch dark-slate top bar: brand
on the left, segmented tab nav in the middle, user pill + logout
on the right. Floating userBadge removed; renderUserBadge() now
writes the display name into #headerUserName and wires the
existing logout button.
Login
- Single card mirroring the Stitch login mockup with icon-prefixed
inputs (person/lock), focus ring, error banner and a primary CTA
that includes the login glyph. Setup mode swaps the heading,
shows the orange info bar with admin_panel_settings glyph and
changes the button to "Administrator anlegen".
Wizard (Schaltung erfassen)
- Step 1: anbieter button grid in Stitch style — bordered cards
with a material icon, hover lift and primary-coloured outline.
- Step 2: white card with a "← Anbieter ändern" pill in the
header, two-column layout (Aktivierungsdaten / Vertragsdaten),
label-caps form labels, JetBrains-Mono numeric inputs for
dates/ID/phone and a dashed file-drop zone. Save button gains
the save glyph.
Other screens (Übersicht, Provisionen, Gelöschte, Administration,
Benutzer) still render in the legacy style — those migrations
follow in the next steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
action buttons on one line
- Wrap the Administration tab in a 260px sidebar + main grid. The
category buttons stack vertically inside the sidebar and stay
sticky while the content area scrolls. Below 900px viewports
the sidebar collapses back to a horizontal row.
- Compact button defaults for anything inside a table cell
(table button / td button) override the global 12px/24px padding
with 5px/10px and a smaller font. Marks every last-child <td> as
nowrap so the Bearbeiten/Löschen pair (and similar action sets)
stays on a single line instead of wrapping vertically.
- Adjacent action buttons get a 5px gap via "button + button"
without touching the existing inline margin-right declarations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New nullable anbieter.default_vo_id column (migration adds it to
existing DBs); deleting a VO that some provider points at clears
the reference automatically.
- GET /api/anbieter now includes default_vo_id + default_vo_nummer
+ default_vo_bezeichnung so the wizard can preselect without an
extra round-trip.
- New PUT /api/anbieter/:id/default-vo endpoint. Validates the VO
belongs to the given provider; accepts vo_id=null to clear.
- VO admin table grew a "Standard" column and an "Als Standard" /
"Standard entfernen" button on each row.
- When the user picks a provider in step 1 of the wizard, the VO
dropdown now preselects the default VO (falling back to the only
VO if there is exactly one), and immediately loads the
matching tariffs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The browser's native prompt() chain was awkward and made it
impossible to clear the bezeichnung in a single, predictable
flow. The new modal mirrors the user-management dialog: it shows
the provider read-only, lets you edit nummer and bezeichnung,
and exposes the aktiv flag explicitly. Empty bezeichnung now
correctly clears the field.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VOs (Vertriebspartner-Nummern)
- New vos table (one row per VO, scoped to a provider). Migrations
add tarife.vo_id and schaltungen.vo_id columns (nullable so legacy
rows survive).
- New /api/vos CRUD (list, create, update, delete with use-checks).
- Admin: new "VOs (Vertriebspartner)" sub-tab. List/create with
provider dropdown; edit via prompt; delete refuses while used.
- /api/tarife?vo_id=X filter for fetching tariffs of a specific VO.
Tariff editor
- Add-tariff form requires a VO; new dependent VO dropdown that
populates after the provider is chosen.
- Edit-tariff modal now shows the VO dropdown and a checkbox grid of
all anbieter-wide tarifoptionen. Saving the dialog applies the
selection via the existing tarifoptionen_tarife table.
- New GET /api/tarife/:id/tarifoptionen-multi returns the option set
scoped to the tariff's provider, each annotated with whether it is
currently assigned to this tariff.
- POST/PUT /api/tarife accept vo_id (required for create, validated
against the provider) and an optional tarifoption_ids array.
Schaltung wizard
- The Schaltung-erfassen tab is now a 2-step wizard:
step 1 picks a provider from a button row (mirrors the screenshot
the user shared); step 2 shows a 2-column form mirroring the
Aktivierungs-/Vertragsdaten layout.
- Step 2 cascades: VO -> tariffs filtered by VO -> tarifoptionen
filtered by tariff. Erfasser is autofilled from the logged-in user
(read-only).
- Removed customer fields that the user does not need (Kundennummer,
Kennwort/Service-PIN never existed; Rufnummer is now optional both
in the form and in the API validation).
- After saving, the wizard resets to step 1 ready for the next entry.
Schema
- tarife: vo_id added.
- schaltungen: vo_id added; rufnummer is nullable in new DBs.
Existing DBs may still have NOT NULL on rufnummer; the schaltungen
route now sends an empty string instead of NULL to remain
compatible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend
- New routes/users.js (admin-only via requireAdmin) with list, create,
update (incl. password reset) and delete endpoints.
- All user-facing API responses identify users by uuid, not the
numeric primary key — usernames can now be renamed without breaking
external references.
- Protection rules: cannot delete or demote the last admin; cannot
delete or demote your own account.
Schema / data
- users table gained a uuid TEXT column (unique). Existing rows are
backfilled with crypto.randomUUID() on startup. Setup endpoint and
the create-user CLI both generate a UUID for new users.
Frontend
- New "Benutzer" tab in nav-tabs, marked admin-only and shown only
after /api/auth/me reports isAdmin.
- Modal with create / edit forms (password optional on edit = leave
unchanged). XSS-safe rendering of the user list using the existing
escapeHtml helper and data-* attributes for action buttons.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>