Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.
Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.
Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.
Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat: add user locale preference to user_preferences table
- Introduced a new column 'locale' in the user_preferences table to store per-user UI language preferences.
- Added a CHECK constraint to ensure only supported locales ('sv', 'en') are allowed.
- Triggered a schema reload notification for the changes.
chore: declare CSS module support in TypeScript
- Added a declaration for CSS modules in globals.d.ts to enable TypeScript support for importing CSS files.
* feat: add Swish as an invoice payment method in company settings
* fix: MCP OAuth 303 redirect, send dialog auto-close, bank details null payload
- OAuth authorize: use 303 See Other instead of default 307, which
preserved POST method and caused Claude's callback to return 405
- SendInvoiceDialog: close dialog and show toast after email send
instead of leaving a success message that requires manual close
- BankDetailsSetupDialog: omit empty fields from payload instead of
sending null, which fails Zod validation on non-nullable schema fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: remove dead sentMessage state and fix stale comment
Remove sentMessage state, its success banner JSX, and the CheckCircle2
import — all unreachable after the dialog now auto-closes on email send.
Fix stale "to null" comment in BankDetailsSetupDialog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: event log, pending operations, and MCP staging
- Event log system: persist bus events to event_log table for external
automation platforms. Batch insert for transaction.synced. Daily
cleanup cron at 02:00 UTC.
- Pending operations: MCP write tools (categorize, create customer,
create invoice) now stage to pending_operations instead of executing
directly. Users review and commit/reject from /pending in the web UI.
- Granskning page: card-based review UI with expandable previews,
commit/reject dialogs. Only shown in nav when pending ops exist.
- Commit route re-executes using core lib functions (no extension
imports). Guards against stale state (double-commit, deleted entities).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: stage new MCP write tools after main merge
Add staging for 4 new write tools from #133:
- mark_invoice_paid, send_invoice, mark_invoice_sent,
match_transaction_invoice
- Expand pending_operations CHECK constraint
- Add commit executors with full execution logic
- Add UI labels and generic preview component
- Remove confirm parameter from categorize (single-call staging)
- Fix UUID in pending op title (fetch transaction description)
- Hide Granskning nav when no pending ops
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review feedback
- Fix TS build error: use `select('*, customer:customers(*)')` for
match_transaction_invoice to avoid array type inference
- Add status guard to commitSendInvoice (prevents duplicate sends)
- Replace auth.admin.getUserById with user email from session auth
- Restore optimistic lock check in commitMatchTransactionInvoice
- Fix tool description typo: expense_software → expense_office
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add support contact links and improve SIE import UX
Add a SupportLink component with a contact dialog throughout the app
(nav, help page, settings, MFA, error pages, empty states). Improve
SIE import flow with phased loading states, structured skip breakdowns,
and an elapsed-time counter. Fix MFA enroll stale factor cleanup and
URL encoding for settings return path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review — open redirect, XSS, test cleanup, fallback email
- Validate returnTo is a relative path in MFA enroll (prevents open redirect)
- Add afterEach import to event-log-handler tests (fixes handler leak)
- HTML-escape user-supplied subject and message in support email body
- Replace hardcoded personal email with support@gnubok.se fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: move bank details from onboarding to first invoice creation
Bank details (account, bankgiro, invoice prefix) are now collected
contextually when the user creates their first invoice, rather than
during onboarding where most users skip them. This ensures invoices
always have payment information on the PDF.
- Remove onboarding step 5 (bank details), simplify to 4 steps
- Delete Step6ConnectBank component
- Add BankDetailsSetupDialog with bank account, bankgiro (Luhn),
IBAN/BIC (collapsible), and invoice prefix fields
- Intercept at "Granska & skapa" for invoice document type only
(proforma and delivery notes pass through without bank details)
- Show soft info banner on invoice form when bank details are missing
- Add controlled mode (value/onChange) to BankNameCombobox for reuse
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review — null-check race, escape key, starting number
- Fix P1: use `hasBankDetails === false` instead of `!hasBankDetails`
to avoid treating null (loading) state as missing bank details
- Fix P2: remove onEscapeKeyDown override so keyboard users can
dismiss the dialog (WCAG AA compliance)
- Add starting invoice number field alongside prefix, so users can
choose e.g. starting at 14 instead of 1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: skip auto-categorization during bank sync when SIE overlap detected
Prevents double-booking when bank transactions are synced for a period
that already has journal entries from a SIE import. Reconciliation still
links transactions to existing GL lines; only new journal entry creation
is suppressed. A batch reconciliation sweep runs post-sync to catch
additional matches.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>