Commit Graph
11 Commits
Author SHA1 Message Date
MattssonandClaude Opus 4.7 064fb7f7a9 Add/white label (#381)
* feat(branding): add BrandingService with default-preserving env layer

Introduce lib/branding/service.ts mirroring lib/email/service.ts. Defaults
match current gnubok values exactly, so production behaviour is unchanged
unless an env var (NEXT_PUBLIC_BRANDING_*, BRANDING_*) or extension override
(via registerBrandingService) is set.

Resolution order: defaults < env vars < extension override.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(branding): route root layout, manifest, and PWA assets through branding service

- app/layout.tsx now reads title, description, themeColor, and apple-touch-icon
  from getBranding() instead of hardcoded values.
- public/manifest.json replaced by dynamic app/manifest.ts so PWA name,
  short_name, description, theme_color, background_color, and icon paths
  are resolved at request time.

The manifest now serves at /manifest.webmanifest (Next.js convention for
the metadata file route). The previous /manifest.json URL is no longer
populated; nothing in core references it after this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(branding): route email service and templates through branding service

- resend-service.ts: From line uses getBranding().appName instead of
  hardcoded "Gnubok" in both the with-fromName and bare cases.
- invite-templates.ts: subject, HTML header, body, plain text, and the
  team-invite variants all read from branding (sentence case in prose,
  uppercased for the styled <p> header).
- consent-notification-templates.ts: signature fallback (companyName ||
  branding) for both HTML and plain text variants.

Defaults preserve the exact current strings ("Gnubok", "GNUBOK", "gnubok"
in their respective contexts) so no email content changes for production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(branding): route OAuth consent page through branding service

The MCP OAuth consent page rendered for Claude Desktop / Claude.ai
connector flows now reads the app name from getBranding() for both the
HTML <title> and the body copy. Default still produces "gnubok" in
lowercase prose, matching current behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(branding): route auth, dashboard, and onboarding text through branding service

Replace user-visible "gnubok" / "Gnubok" references with calls to
getBranding(). Touches:

- Auth pages (login, register, mfa/enroll): logo src/alt, MFA TOTP
  friendlyName.
- Onboarding (companies/new, invite, sandbox, WelcomeOnboarding,
  Step2CompanyDetails, NewUserChecklist, BankIdCompanyPicker,
  ArcimMigrationWorkspace): logo, headings, error/help text.
- Dashboard fallback (companyName="gnubok") and settings (backup copy,
  ApiKeysPanel MCP connector name + login note, CompanyDangerZone,
  retention-notice).
- API routes (support contact subject prefix, enable-banking consent
  email companyName fallback, AI inbox receipt-request appUrl,
  pain001 messageId prefix).
- MCP server "open the gnubok web app" review message.
- Salary/reports filings (AGI Programnamn, KU10 Programnamn,
  payslip footer, full-archive system metadata, SRU #PROGRAM line).

Internal identifiers (cookie names gnubok-company-id /
gnubok-invite-token, API key prefix gnubok_sk_, invite token prefix
gnubok_inv_, MCP tool names, npm package gnubok-mcp, GNUBOK_API_KEY
env name) are deliberately left unchanged — they're stable contracts
that whitelabels must not break.

Defaults match current behaviour exactly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(branding): support legal page field-level swaps for entity and contact

Privacy and DPA pages now interpolate appName, legalEntity, and
privacyEmail from the branding service instead of hardcoding "Gnubok",
"Arcim", and "privacy@gnubok.se". Page metadata uses generateMetadata()
so titles also reflect the brand.

lib/support.ts now falls back to getBranding().supportEmail when
SUPPORT_RECIPIENT_EMAIL is unset, so a single BRANDING_SUPPORT_EMAIL
env var configures both the support form recipient and the displayed
support address.

Whitelabels with a different legal jurisdiction or entirely different
DPA text should override the page route from an extension. Phase 1
intentionally only supports field-level swaps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(branding): add WHITELABEL.md and example branding extension

WHITELABEL.md: fork checklist, env var reference, the "do not change"
list (cookies, API key prefixes, invite token prefixes, MCP tool names,
gnubok-mcp npm package, GNUBOK_API_KEY env name), out-of-scope items,
the upstream sync workflow YAML to copy into a fork, conflict avoidance
guidance, and a verification checklist.

extensions/general/_example-branding/: copy-paste starter extension with
index.ts (commented placeholder values for registerBrandingService),
manifest.json, and README.md. Disabled by default (not added to
extensions.config.json); whitelabels cp the folder, edit, and enable.

sectors.test.ts: bumped expected extension count 12 -> 13 to account
for the new starter extension on disk. The generated registry is
unchanged because the example is disabled.

The sync workflow YAML is documented inline in WHITELABEL.md rather
than checked in as a workflow file. It's only meaningful in a fork --
gnubok itself has nothing to sync from.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(branding): address PR review — lazy support email + escape brand in HTML/XML

Three issues from code review:

P1 — lib/support.ts: SUPPORT_RECIPIENT_EMAIL was a module-level const,
evaluated at import time before extensions register branding overrides
via ensureInitialized(). Convert to getSupportRecipientEmail() lazy
accessor; update the only caller in app/api/support/contact/route.ts.
Extension-supplied supportEmail values now route correctly.

P2 — app/api/mcp-oauth/authorize/route.ts: appName was interpolated
into the consent page HTML without escapeHtml(), inconsistent with
the existing escaping of companyName. Wrap appName.toLowerCase() in
escapeHtml() at use sites in <title> and the body paragraph.

P2 — lib/salary/agi/xml-generator.ts and lib/salary/ku/ku10-generator.ts:
appName placed inside <gem:Programnamn> / <Programnamn> XML elements
without escapeXml(), the helper already used for other admin-controlled
fields in the same files. Wrap accordingly to prevent malformed XML if
a brand name contains XML reserved characters.

All admin-controlled inputs only — no user-exploitable path. Defense in
depth, not a known incident.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(branding): security follow-up — lazy metadata, SRU/email header sanitization

Self-audit after the PR review surfaced four more concerns. Fixes them
with the same defense-in-depth posture as the prior review fixes.

1. app/layout.tsx — same eager-evaluation class as P1 support.ts. The
   module-level `const branding = getBranding()` froze branding before
   extensions registered, so extension-based overrides for title,
   description, themeColor, and apple-touch-icon silently never applied.
   - Convert to generateMetadata() / generateViewport() (lazy, run per
     request, see extension-registered overrides).
   - Inline getBranding() inside RootLayout for the apple-touch-icon
     href so it picks up overrides too.
   - Add ensureInitialized() at module level so extensions are loaded
     before the first metadata call. Mirrors the API route pattern.

2. app/manifest.ts — same class. The dynamic manifest function reads
   getBranding() per request, but if the manifest is requested before
   any other module has triggered ensureInitialized(), extensions are
   still unloaded. Add ensureInitialized() at module level.

3. lib/reports/ink2/sru-generator.ts — appName interpolated into the
   SRU `#PROGRAM` directive without sanitization. SRU's reserved char
   is `#` (directive marker) and CRLF injects new directives. Wrap in
   the existing sanitizeString() helper to match the pattern used for
   other admin-controlled fields in this file (#NAMN, #ADRESS, etc.).

4. extensions/general/email/lib/resend-service.ts — appName and the
   user-controlled fromName both flow into the From header. Resend's
   API does its own validation, but defense in depth: strip CRLF and
   angle brackets via a small sanitizeHeaderPart() helper before
   building the header string. fromName was a pre-existing surface;
   appName is new with this whitelabel work.

All four are admin-controlled inputs (env vars or extension code),
not user-exploitable. No known incidents — defense in depth, and
correctness for extension-based whitelabels.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:32:26 +02:00
Mattsson d5410a518f Mobile view fixes (#188)
* fix: make new user checklist mobile responsive

Adjust spacing, padding, typography, and indentation for small screens
so the onboarding welcome screen works well on mobile devices.

* fix: make expenses and bookkeeping verification views mobile responsive

- Expenses/new: stack form grids on mobile, replace line items table with
  card layout, full-width action buttons
- Expenses/[id]: stack header and actions, card layout for line items and
  payments, single-column info grid on mobile
- JournalEntryList: unify verification lines into card layout for all
  screen sizes, hide redundant line descriptions that duplicate account
  name or entry description, stack filter bar and action buttons on mobile

* fix: remove stray )); in provider_consents migration

Syntax error on line 35 caused MIGRATIONS_FAILED on the Supabase staging branch.

* fix: enhance mobile responsiveness for invoices page and update dialog styles
2026-04-07 15:53:30 +02:00
Mattsson fa7c742ee3 Instruction copy (#141)
* feat: enhance import page UI with improved card layout and new icons

* feat: update NewUserChecklist for improved onboarding experience with revised text and enhanced layout
2026-03-26 16:28:40 +01:00
Jakob WennbergandClaude Opus 4.6 38b658205d feat: BFL-compliant descriptions, cancelled status, and setup gate (#58)
* fix: include reversed entries in all reports (general ledger, trial balance, VAT, SIE, NE, INK2)

Reversed entries (storno) must appear alongside their original posted entries
in reports for a complete audit trail. Previously, filtering by status='posted'
excluded them, causing discrepancies when corrections had been made.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: semi-manual invoice payment booking with editable journal lines

When marking an invoice as paid, users now see a dialog where they can:
- Choose which bank/cash account the payment goes to (1910, 1920, 1930, etc.)
- Review and edit the proposed journal entry lines before committing
- The happy path remains fast — lines are pre-filled correctly

Implementation:
- Pure proposePaymentLines() function for line computation (accrual + cash)
- PaymentBookingDialog with AccountCombobox, balance validation, date picker
- API accepts optional custom lines, falls back to auto-generation without them
- 18 tests (8 unit + 10 API) all passing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — validation fallback, balance check, error handling

- P1: Return 400 on invalid body instead of silently falling back to
  auto-generated lines (split JSON parse from schema validation)
- P1: Add server-side balance check for custom lines before committing
  (debit must equal credit, totalDebit > 0)
- P2: Wrap PaymentBookingDialog init() in try/catch with toast on
  failure and auto-close instead of silent empty state
- Add 2 new tests: unbalanced lines → 400, invalid schema → 400

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: OAuth callback redirect for local dev and timeout resilience

- Pass redirectUri dynamically from NEXT_PUBLIC_APP_URL so OAuth
  callbacks work on localhost (not just production)
- Encode consentId/provider in OAuth state (base64url JSON) so the
  callback doesn't depend on session storage
- Add skipAuth flag to extension API routes for OAuth callbacks
  (external provider redirects have no user session cookie)
- Wrap AbortError in descriptive timeout messages in arcim-client
- Make preview endpoint resilient to partial failures (company info
  and SIE fetch are individually non-blocking)
- Simplify login page (remove unused magic link auth mode)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: create journal entry before marking invoice as paid

Move journal entry creation before the invoice status update so that
if accounting fails, the invoice is not permanently marked paid without
a corresponding entry. Previously the error was silently swallowed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update mark-paid tests for journal-first ordering

Reorder mock queue to match new flow (settings before update), update
failure test to expect 500 instead of silent success, add try-catch
with proper error response in route handler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add reverse charge VAT (ruta 20-32) and improve mobile UX across dashboard

Add full reverse charge (omvänd skattskyldighet) support to the VAT declaration:
- Map accounts 2614/2624/2634 to ruta 30/31/32 for self-assessed output VAT
- Calculate purchase bases (ruta 20-24) from supplier invoices by supplier type
- Include ruta 30-32 in ruta 49 formula and totalOutputVat summary
- Display reverse charge section in reports UI and composition chart
- Add comprehensive test coverage for all reverse charge scenarios

Improve mobile UX across the app:
- Convert nav drawer to bottom sheet with drag handle and safe area padding
- Add mobile card layout for PaymentBookingDialog journal lines
- Replace settings tab pills with dropdown selector on mobile
- Make wizard step indicators responsive (collapsed on mobile)
- Ensure all dialog footers stack buttons full-width on mobile
- Add 44px minimum touch targets throughout
- Make onboarding buttons full-width on mobile

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — indentation, query efficiency, tab dedup

- Fix misleading try-block indentation in mark-paid route
- Filter reversed entries at DB level (.eq('status', 'posted')) instead
  of fetching then discarding in memory
- Extract shared settingsTabs array so mobile Select and desktop
  TabsList stay in sync automatically

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add resilience fallbacks, Arcim retry logic, and client tests

Add FallbackPrompt component and integrate it across banking and migration
error states so users always have a manual import escape hatch. Add retry
with exponential backoff to Arcim API client for transient failures (429,
502, 503, 504) and timeouts. Expand import page deep-linking with ?mode=
parameter. Add persistent error banner on settings page for bank connection
failures. Include 18 new tests for the Arcim client covering retry, backoff,
pagination, timeout, env validation, and singleton resource unwrapping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — setActiveTab, test cleanup, redundant clearTimeout

- Add missing setActiveTab('banking') when handling bank_error query
  param so the error banner is actually visible (P1)
- Guard env-var cleanup with try/finally in arcim-client tests to
  prevent state leakage on assertion failure (P2)
- Only mock retry-range setTimeout delays in backoff test, letting
  AbortController timers pass through real setTimeout (P2)
- Remove redundant clearTimeout in catch block — finally handles it (P2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add BFL-compliant counterparty names to journal descriptions and cancelled entry status

Journal descriptions now include customer/supplier names for traceability
(e.g. "Kundfaktura 1001, Foretag AB"). Failed draft entries are marked as
'cancelled' instead of deleted, respecting immutability constraints.
Includes DB migration for the new journal_entries status value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — Swedish typos, missing source type, trigger and reversal cleanup

- Fix Swedish spelling: leverantor → leverantör in all supplier description prefixes
- Add supplier_credit_note to supplierSourceTypes in VAT declaration so credit
  notes correctly reduce reverse-charge bases (ruta 20–24)
- Mark orphaned concurrent reversals as cancelled instead of attempting deletion
  that the immutability trigger blocks
- Allow posted → cancelled transition in trigger for orphaned reversal cleanup
- Restrict cancelled entry line trigger to DELETE-only (block INSERT/UPDATE)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use main's Step3TaxRegistration (onboarding restructured in PR #54)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: retrigger Greptile review

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add TIC company lookup extension, extension nav items, and legacy toggle fallback

Introduces the TIC (Bolagsuppgifter) extension for automatic company data lookup
via org number during onboarding. Adds dynamic extension nav items in the sidebar,
legacy general extension fallback for toggle checks, and company lookup type
definitions in core.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — restore push-notifications, filter nav by toggles, fix timeout error name

- Restore push-notifications to LEGACY_GENERAL_EXTENSIONS (was silently
  dropped when extracting the shared constant)
- Remove tic and arcim-migration from legacy defaults (new extensions
  should not default to enabled for all users)
- Filter getExtensionNavItems() against user's enabled extensions so
  disabled extensions don't appear in the sidebar
- Fix AbortSignal.timeout() error name check — Node.js throws
  TimeoutError, not AbortError

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add all bundled extensions to legacy defaults (email, arcim-migration, tic)

Bundled extensions configured in extensions.config.json should default
to enabled. Adds email, arcim-migration, and tic alongside the
existing legacy defaults so they are accessible without explicit
toggle rows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: replace welcome checklist with full-page setup gate

New users now see a full-page onboarding gate instead of a small
dismissible card. The gate enforces a clear two-step sequential flow:
(1) migrate bookkeeping from Fortnox/Bokio/Visma or import SIE file,
(2) connect bank account. The gate blocks all dashboard content until
the user has imported data or explicitly chooses "fresh start".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clean up journal lines after cancelling failed reversal entries

When a reversal fails at the line-insert, post, or CAS stage, the entry
is correctly marked as cancelled. Now also delete the attached lines to
prevent stale data accumulation — the migration trigger allows DELETE on
lines of cancelled entries for this purpose.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:05:39 +01:00
Jakob WennbergandClaude Opus 4.6 98cd253bce feat: enable banking hardening, arcim inference, SIE fixes, onboarding (#32)
* feat: import system improvements, INK2 fix, and Swedish text corrections

- SIE parser: Windows-1252 and CP437 encoding detection and decoding
- Bank file parser: add Nordea Business (Företag) CSV format
- Bank file parser: improve format detection for SEB, Länsförsäkringar, generic CSV
- INK2 engine: calculate årets resultat (7222) from income statement for open fiscal years
- Dashboard: parallel Supabase queries, simplified dashboard page
- Fix Swedish characters (å, ä, ö) in BAS data descriptions, validation messages, AI consent disclosures
- Import wizard UI improvements across all steps
- Migration: add 'bas_range' match type to sie_account_mappings constraint
- Extensive new tests for SIE parser encoding and bank file parser

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: arcim migration wizard UX fixes, Sentry setup, and extension scaffolding

Arcim migration wizard improvements:
- Progress bar now excludes non-interactive steps (migrating/result)
- Fix OAuth text to match target="_blank" behavior (new tab, not redirect)
- Display month names instead of "Månad X" in preview
- Fix Swedish typo "förifylla" in no-company-info message
- Replace native checkboxes with shadcn Switch in options step
- Add ConfirmationDialog before starting migration
- Show progress percentage during migration
- Add "Nästa steg" guidance and navigation links in result step
- Add "Försök igen" button in error state (returns to options)
- Add Bokio company ID help text (GUID from URL)
- Add Fortnox integration add-on hint on connection failure

Also includes: SIE import system improvements, INK2 fixes, Swedish text
corrections, Sentry error tracking setup, and arcim-migration extension
scaffolding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback

- Fix OAuth error recovery blank page (restore provider from URL params)
- Pass real userId to MigrationWizard instead of empty string
- Remove ~50 debug console.log statements from sie-import.ts
- Fix comment referencing account 3740 → 3741

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: comprehensive UI design audit and normalization

Dashboard audit:
- Fix muted-foreground contrast (4.31:1 → 5.08:1) for WCAG AA
- Add prefers-reduced-motion media query for all animations
- Replace border-l-2 accent anti-pattern with subtle full-border colors
- Add aria-expanded to toggle buttons, role="status" to live counters
- Fix touch targets on deadline buttons (28px → 36px)
- Vary section spacing for rhythm (mb-12/mb-10/mb-8)
- Remove unused imports and dead code

Transactions audit + hardening:
- Add pagination (200 per page) with "Ladda fler" button
- Replace height animation with transform-only exit animation
- Show batch progress in floating action bar during processing
- Fix batch bar mobile overlap (bottom-20 on mobile)
- Replace clickable badges with proper button elements
- Add safe area padding to fullscreen swipe view
- Add response.ok check to suggestion fetch
- Add truncation to invoice number buttons

Invoicing audit:
- Remove border-l-4 accent pattern from invoice cards
- Replace string concatenation with cn() utility

Systemic sweep (34 files):
- All page headings: font-bold → font-display font-medium (Fraunces)
- All stat numbers: font-bold → font-display font-medium tabular-nums
- All hard-coded blue/amber/emerald colors → design tokens
- Remove all dark mode overrides (tokens handle automatically)
- Tint pure white card background to 99%

Design context added to CLAUDE.md with brand personality,
aesthetic direction, and 5 design principles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: bookkeeping flow audit — design system, accessibility, UX

- Replace raw <select> with shadcn Select component (JournalEntryForm)
- Add confirmation dialog for account deletion (ChartOfAccountsManager)
- Remove console.error from production code (JournalEntryList, JournalEntryForm)
- Fix contradictory h-7/min-h-[44px] button sizing → h-10 (ChartOfAccountsManager)
- Increase BAS catalog "Lägg till" touch target h-7 → h-9
- Improve loading state with spinner (JournalEntryList)
- Improve empty state with icon, description, and guidance (JournalEntryList)
- Add response.ok check on journal entry fetch
- Add aria-expanded to entry expand buttons
- Add tabular-nums to desktop debit/credit columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: onboarding and empty state improvements

Onboarding:
- Replace font-serif with font-display (Fraunces) for brand consistency
- Remove console.error calls from production code

Empty states:
- Fix broken /transactions/new link in EmptyTransactions (route doesn't exist)
- Add actionHref fallback to EmptyCustomers when no onAction prop provided
- Improve EmptyTransactions description copy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clarify Swedish UX copy — terminology, errors, descriptions

Terminology consistency:
- "Försenad" → "Förfallen" for overdue invoices (customers/[id])
- "bokföringsorder" → actionable description in bookkeeping page
- "verifikation har bifogats" → "underlag har bifogats" in doc warning
- "Fortsätt ändå" → "Bokför utan underlag" (specific action)

Error messages — replace generic "Fel" + "Något gick fel" with specific:
- "Något gick fel vid bokföring" → "Transaktionen kunde inte bokföras"
- "Något gick fel vid matchning" → "Transaktionen kunde inte matchas"
- "Kunde inte hämta X" → "Kunde inte ladda X" + recovery hint
- Add "Försök igen" guidance to all error toasts

Page descriptions — replace redundant with actionable:
- Invoices: "Skapa och hantera" → "Skicka, följ betalningar, skapa kreditnotor"
- Bookkeeping: list of features → actionable description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: design critique — dashboard affordance, reports description

Dashboard:
- Add ChevronRight indicator to clickable summary cards
  (Att få betalt, Koppla bank) to distinguish from static cards
- Add cursor-pointer to linked cards

Reports:
- Replace feature list description with actionable guidance
  "Huvudbok, grundbok..." → "Generera skattedeklarationer..."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace generic "Fel" error toasts with specific messages

Deadlines: 5 generic "Fel" → specific per-action titles
  (create, toggle, edit, delete, load)
Expenses detail: 5 generic "Fel" → specific per-action titles
  (load, approve, pay, credit, delete)
Expenses new: 3 generic "Fel" → instructional validation messages
  (supplier name, supplier selection, invoice number)
Customers: 1 generic "Fel" → specific load error with recovery hint

All error toasts now follow pattern:
  title = what failed, description = how to recover

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace all remaining generic "Fel" error toasts (37 instances)

Systematic sweep across 12 dashboard pages replacing generic
title: 'Fel' with context-specific error titles:

- Load errors: "Kunde inte ladda [resurs]"
- Action errors: "[Åtgärd] misslyckades"
- Validation: "[Fält] saknas"

Every error toast now tells the user what failed without needing
to read the description. Recovery hints added where missing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: import flow — normalize stat typography, remove console.warn

- Replace font-bold with font-display font-medium on 13 stat numbers
  across SIEPreviewStep, BankFilePreviewStep, BankFileConfirmStep,
  ImportResultStep (missed by systemic sweep since these are in
  components/import/, not app/(dashboard)/)
- Add tabular-nums to stat numbers displaying counts/currency
- Remove console.warn in ArcimMigrationWorkspace

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: final cleanup — console statements, remaining font-bold stats

Remove production console statements:
- Step1EntityType: remove debug console.warn (dead code after onNext)
- TransactionBookingDialog: remove console.error on doc link failure
- JournalEntryAttachments: remove 3 console.error calls

Normalize remaining font-bold stat displays:
- SwipeCategorizationView: 3 instances (completion, amount displays)
- NEDeclarationView: yearly result heading + value

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review feedback

loadMoreTransactions: add inbox item enrichment matching fetchTransactions
- Paginated transactions now fetch invoice_inbox_items in parallel
- Fixes missing document indicator, template suggestions, and inbox
  match card for transactions loaded via "Ladda fler"

fetchAllPages: add maxPages guard (default 500) to prevent infinite loop
- If Arcim gateway returns hasMore:true indefinitely, the loop now
  exits after 500 pages instead of running forever

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: minimize CLAUDE.md — remove derivable content, fix stale data

Remove ~230 lines (51% reduction) of content that duplicates what's
already in the source code (directory tree, function tables, type
definitions, migration lists). Update migration count (63→65), add
missing test helpers, fix cron job list. Keep all high-value sections:
accounting guard rails, BAS accounts, VAT rutor, design context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: enable banking hardening, arcim entity inference, SIE import fixes, and onboarding improvements

- Enable Banking: OAuth CSRF state tokens, JWT caching, retry with timeouts, raw PSD2 response archival (BFL 7 kap), expired/error connection UI, consent expiry notifications, pagination safety limits
- Arcim migration: Smarter entity type inference from org numbers, VAT prefixes, company name suffixes (GmbH, Ltd, etc.), and country codes
- SIE import: Parser and import fixes with new migration
- BAS accounts: Added vehicle accounts (1241, 1242, 1249, 1259)
- Dashboard: New SIE import and stale uncategorized transaction queries
- Onboarding: Enhanced NewUserChecklist
- Period service: Improvements with updated tests
- Transaction ingest: Updated logic and tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — credit note type, EU country codes, notification thresholds, migration timestamps

- Fix dead ternary: credit notes now correctly stored as 'credit_note' instead of 'invoice'
- Add 'GR' (Greece ISO 3166-1) to EU_COUNTRIES alongside 'EL' (VAT prefix)
- Fix consent notification condition: fire at exactly 7 days or ≤3 days, not every day in 7-day window
- Deduplicate migration timestamps: rename SIE migration to 20260316120100

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 14:21:32 +01:00
Jakob WennbergandClaude Opus 4.6 a7be1aab17 feat: comprehensive UI design audit and normalization (#28)
* feat: import system improvements, INK2 fix, and Swedish text corrections

- SIE parser: Windows-1252 and CP437 encoding detection and decoding
- Bank file parser: add Nordea Business (Företag) CSV format
- Bank file parser: improve format detection for SEB, Länsförsäkringar, generic CSV
- INK2 engine: calculate årets resultat (7222) from income statement for open fiscal years
- Dashboard: parallel Supabase queries, simplified dashboard page
- Fix Swedish characters (å, ä, ö) in BAS data descriptions, validation messages, AI consent disclosures
- Import wizard UI improvements across all steps
- Migration: add 'bas_range' match type to sie_account_mappings constraint
- Extensive new tests for SIE parser encoding and bank file parser

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: arcim migration wizard UX fixes, Sentry setup, and extension scaffolding

Arcim migration wizard improvements:
- Progress bar now excludes non-interactive steps (migrating/result)
- Fix OAuth text to match target="_blank" behavior (new tab, not redirect)
- Display month names instead of "Månad X" in preview
- Fix Swedish typo "förifylla" in no-company-info message
- Replace native checkboxes with shadcn Switch in options step
- Add ConfirmationDialog before starting migration
- Show progress percentage during migration
- Add "Nästa steg" guidance and navigation links in result step
- Add "Försök igen" button in error state (returns to options)
- Add Bokio company ID help text (GUID from URL)
- Add Fortnox integration add-on hint on connection failure

Also includes: SIE import system improvements, INK2 fixes, Swedish text
corrections, Sentry error tracking setup, and arcim-migration extension
scaffolding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback

- Fix OAuth error recovery blank page (restore provider from URL params)
- Pass real userId to MigrationWizard instead of empty string
- Remove ~50 debug console.log statements from sie-import.ts
- Fix comment referencing account 3740 → 3741

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: comprehensive UI design audit and normalization

Dashboard audit:
- Fix muted-foreground contrast (4.31:1 → 5.08:1) for WCAG AA
- Add prefers-reduced-motion media query for all animations
- Replace border-l-2 accent anti-pattern with subtle full-border colors
- Add aria-expanded to toggle buttons, role="status" to live counters
- Fix touch targets on deadline buttons (28px → 36px)
- Vary section spacing for rhythm (mb-12/mb-10/mb-8)
- Remove unused imports and dead code

Transactions audit + hardening:
- Add pagination (200 per page) with "Ladda fler" button
- Replace height animation with transform-only exit animation
- Show batch progress in floating action bar during processing
- Fix batch bar mobile overlap (bottom-20 on mobile)
- Replace clickable badges with proper button elements
- Add safe area padding to fullscreen swipe view
- Add response.ok check to suggestion fetch
- Add truncation to invoice number buttons

Invoicing audit:
- Remove border-l-4 accent pattern from invoice cards
- Replace string concatenation with cn() utility

Systemic sweep (34 files):
- All page headings: font-bold → font-display font-medium (Fraunces)
- All stat numbers: font-bold → font-display font-medium tabular-nums
- All hard-coded blue/amber/emerald colors → design tokens
- Remove all dark mode overrides (tokens handle automatically)
- Tint pure white card background to 99%

Design context added to CLAUDE.md with brand personality,
aesthetic direction, and 5 design principles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: bookkeeping flow audit — design system, accessibility, UX

- Replace raw <select> with shadcn Select component (JournalEntryForm)
- Add confirmation dialog for account deletion (ChartOfAccountsManager)
- Remove console.error from production code (JournalEntryList, JournalEntryForm)
- Fix contradictory h-7/min-h-[44px] button sizing → h-10 (ChartOfAccountsManager)
- Increase BAS catalog "Lägg till" touch target h-7 → h-9
- Improve loading state with spinner (JournalEntryList)
- Improve empty state with icon, description, and guidance (JournalEntryList)
- Add response.ok check on journal entry fetch
- Add aria-expanded to entry expand buttons
- Add tabular-nums to desktop debit/credit columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: onboarding and empty state improvements

Onboarding:
- Replace font-serif with font-display (Fraunces) for brand consistency
- Remove console.error calls from production code

Empty states:
- Fix broken /transactions/new link in EmptyTransactions (route doesn't exist)
- Add actionHref fallback to EmptyCustomers when no onAction prop provided
- Improve EmptyTransactions description copy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clarify Swedish UX copy — terminology, errors, descriptions

Terminology consistency:
- "Försenad" → "Förfallen" for overdue invoices (customers/[id])
- "bokföringsorder" → actionable description in bookkeeping page
- "verifikation har bifogats" → "underlag har bifogats" in doc warning
- "Fortsätt ändå" → "Bokför utan underlag" (specific action)

Error messages — replace generic "Fel" + "Något gick fel" with specific:
- "Något gick fel vid bokföring" → "Transaktionen kunde inte bokföras"
- "Något gick fel vid matchning" → "Transaktionen kunde inte matchas"
- "Kunde inte hämta X" → "Kunde inte ladda X" + recovery hint
- Add "Försök igen" guidance to all error toasts

Page descriptions — replace redundant with actionable:
- Invoices: "Skapa och hantera" → "Skicka, följ betalningar, skapa kreditnotor"
- Bookkeeping: list of features → actionable description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: design critique — dashboard affordance, reports description

Dashboard:
- Add ChevronRight indicator to clickable summary cards
  (Att få betalt, Koppla bank) to distinguish from static cards
- Add cursor-pointer to linked cards

Reports:
- Replace feature list description with actionable guidance
  "Huvudbok, grundbok..." → "Generera skattedeklarationer..."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace generic "Fel" error toasts with specific messages

Deadlines: 5 generic "Fel" → specific per-action titles
  (create, toggle, edit, delete, load)
Expenses detail: 5 generic "Fel" → specific per-action titles
  (load, approve, pay, credit, delete)
Expenses new: 3 generic "Fel" → instructional validation messages
  (supplier name, supplier selection, invoice number)
Customers: 1 generic "Fel" → specific load error with recovery hint

All error toasts now follow pattern:
  title = what failed, description = how to recover

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace all remaining generic "Fel" error toasts (37 instances)

Systematic sweep across 12 dashboard pages replacing generic
title: 'Fel' with context-specific error titles:

- Load errors: "Kunde inte ladda [resurs]"
- Action errors: "[Åtgärd] misslyckades"
- Validation: "[Fält] saknas"

Every error toast now tells the user what failed without needing
to read the description. Recovery hints added where missing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: import flow — normalize stat typography, remove console.warn

- Replace font-bold with font-display font-medium on 13 stat numbers
  across SIEPreviewStep, BankFilePreviewStep, BankFileConfirmStep,
  ImportResultStep (missed by systemic sweep since these are in
  components/import/, not app/(dashboard)/)
- Add tabular-nums to stat numbers displaying counts/currency
- Remove console.warn in ArcimMigrationWorkspace

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: final cleanup — console statements, remaining font-bold stats

Remove production console statements:
- Step1EntityType: remove debug console.warn (dead code after onNext)
- TransactionBookingDialog: remove console.error on doc link failure
- JournalEntryAttachments: remove 3 console.error calls

Normalize remaining font-bold stat displays:
- SwipeCategorizationView: 3 instances (completion, amount displays)
- NEDeclarationView: yearly result heading + value

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review feedback

loadMoreTransactions: add inbox item enrichment matching fetchTransactions
- Paginated transactions now fetch invoice_inbox_items in parallel
- Fixes missing document indicator, template suggestions, and inbox
  match card for transactions loaded via "Ladda fler"

fetchAllPages: add maxPages guard (default 500) to prevent infinite loop
- If Arcim gateway returns hasMore:true indefinitely, the loop now
  exits after 500 pages instead of running forever

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 14:48:40 +01:00
Jakob WennbergandClaude Opus 4.6 2ad8731dc9 feat: arcim migration wizard UX, import fixes, Sentry setup (#22)
* feat: import system improvements, INK2 fix, and Swedish text corrections

- SIE parser: Windows-1252 and CP437 encoding detection and decoding
- Bank file parser: add Nordea Business (Företag) CSV format
- Bank file parser: improve format detection for SEB, Länsförsäkringar, generic CSV
- INK2 engine: calculate årets resultat (7222) from income statement for open fiscal years
- Dashboard: parallel Supabase queries, simplified dashboard page
- Fix Swedish characters (å, ä, ö) in BAS data descriptions, validation messages, AI consent disclosures
- Import wizard UI improvements across all steps
- Migration: add 'bas_range' match type to sie_account_mappings constraint
- Extensive new tests for SIE parser encoding and bank file parser

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: arcim migration wizard UX fixes, Sentry setup, and extension scaffolding

Arcim migration wizard improvements:
- Progress bar now excludes non-interactive steps (migrating/result)
- Fix OAuth text to match target="_blank" behavior (new tab, not redirect)
- Display month names instead of "Månad X" in preview
- Fix Swedish typo "förifylla" in no-company-info message
- Replace native checkboxes with shadcn Switch in options step
- Add ConfirmationDialog before starting migration
- Show progress percentage during migration
- Add "Nästa steg" guidance and navigation links in result step
- Add "Försök igen" button in error state (returns to options)
- Add Bokio company ID help text (GUID from URL)
- Add Fortnox integration add-on hint on connection failure

Also includes: SIE import system improvements, INK2 fixes, Swedish text
corrections, Sentry error tracking setup, and arcim-migration extension
scaffolding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback

- Fix OAuth error recovery blank page (restore provider from URL params)
- Pass real userId to MigrationWizard instead of empty string
- Remove ~50 debug console.log statements from sie-import.ts
- Fix comment referencing account 3740 → 3741

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:24:54 +01:00
Jakob WennbergandClaude Opus 4.6 66a4027f1e feat: BAS data overhaul, currency revaluation, expenses, UI polish, and cleanup
- Update BAS account catalog with comprehensive SRU codes and K2 flags
- Add currency revaluation service with tests and API route
- Add expenses page and account deletion API
- Enhance booking templates with new patterns and improved tests
- Improve transaction categorization with template picker and description matching
- Polish dashboard, onboarding, import, and transaction UIs
- Refactor year-end service for multi-step closing
- Move SRU generator to ne-bilaga, remove standalone SRU export
- Remove unused dev docs, mock data, and extension hooks
- Add invoice delivery note sequences migration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:19:56 +01:00
Jakob WennbergandClaude Opus 4.6 885f362a29 feat: add bank file import as core, move Enable Banking to extension
Replace PSD2 bank integration as the default with file-based bank
import (CSV/XML), which better suits Swedish sole traders and small
companies. Enable Banking is now an opt-in extension.

- Phase 1: Extract generic transaction ingestion service (ingest.ts)
  with dedup, auto-categorization, and OCR-based invoice matching
- Phase 2: Bank file parser library supporting Nordea, SEB, Swedbank,
  Handelsbanken CSV formats and ISO 20022 camt.053 XML
- Phase 3: Database migration adding import_source, reference columns
  and bank_file_imports tracking table
- Phase 4: Import wizard UI (5-step flow) and API routes for parse/execute
- Phase 5: Move Enable Banking to extensions/enable-banking/ with
  commented-out loader entry for opt-in activation
- Phase 6: 104 new tests (ingestion + all parser formats), fixing
  Nordea detection overlap and camt.053 XML tag collision bugs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:21:28 +01:00
Jakob WennbergandClaude Opus 4.6 0aecf5b41c Refactor to generic ERP base
Remove influencer-specific features (campaigns, TikTok, gifts, shadow ledger,
contracts, briefings) and consolidate into a clean ERP foundation with core
bookkeeping, invoicing, receipts, tax reporting, and calendar functionality.
Reorganize database migrations into a clean numbered sequence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:39:13 +01:00
Jakob Wennberg a25d10a528 Initial copy from influencer-biz 2026-02-13 14:00:46 +01:00