bb855d2ddcfffc67093a4d95ced0162769a96a14
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
8fd3f112f8 |
fix: surface active TIC companies + block duplicate org numbers (#344)
* fix: surface active TIC companies + block duplicate org numbers
Three fixes from live-prod testing:
1. Enrichment filter hid the user's directorships. Now accepts both
Completed and PartiallyCompleted status from TIC (tenants without
CompanyRoles enabled still get SPAR) and the /select-company role
filter no longer requires companyStatus === 'Aktivt' — real TIC
payloads have been observed with different values, and positionEnd
alone is the authoritative "currently a director" signal. Added
PII-free diagnostic logs so the next shape-mismatch is debuggable
from Vercel logs without a round trip.
2. Manual wizard silently allowed duplicate org numbers. Added:
- findExistingCompanyByOrgNumber helper in actions.ts (service role,
bypasses RLS to see cross-tenant rows)
- Server-side guard in createCompanyFromOnboarding — returns
'org_number_exists' before the create RPC so we don't leave ghost
companies
- New /api/company/check-org-number endpoint for debounced client
checks
- Warning + disabled submit in Step2CompanyDetails
- Friendly error toasts in WelcomeOnboarding + BankIdCompanyPicker
- Mirror cleaned org_number onto companies.org_number on creation so
future duplicate checks and lookups are reliable
3. /onboarding ignored ?org_number= when the picker routed there as a
fallback. Now reads searchParams and pre-fills settings; also fixed
a latent bug where Step1's entity-type change wiped the pre-fill on
*first* selection (it should only reset on a genuine change).
Tests: duplicate-org guard (with formatted-input normalization),
check-org-number route (auth + 400 + exists true/false +
normalization).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address PR review feedback on duplicate-org guard
Greptile P1 findings + swedish-compliance feedback:
- findExistingCompanyByOrgNumber now throws on Supabase error instead
of silently returning null. Previously a DB outage or RLS
misconfiguration would bypass the entire duplicate guard and allow
duplicates through.
- createCompanyFromOnboarding catches the throw and returns a
user-facing error ("Kunde inte verifiera organisationsnummer"),
failing closed instead of open.
- companies.update({ org_number }) error is now checked and triggers a
rollback. Silent failure would leave the company without an
org_number, breaking all future duplicate checks for that entity.
- New normalizeOrgNumber helper validates 10- or 12-digit input,
strips the century prefix for 12-digit personnummer form, and
rejects anything else. Malformed input would have corrupted SIE4
(#ORGNR) and SRU (INFO.SRU) exports downstream.
- /select-company now uses loose `== null` for positionEnd — TIC has
been observed returning `undefined` for open-ended positions, which
strict `=== null` would silently filter out. Documented the two
downstream isCeased guards so future maintainers don't remove one
without the other.
- createCompanyFromTicRole refuses to provision when lookup.isCeased
(BFL 2 kap — bokföringsskyldighet ends at avregistrering).
BankIdCompanyPicker surfaces this client-side too.
- WelcomeOnboarding + BankIdCompanyPicker recognise new error codes:
org_number_invalid, company_ceased.
Tests: +4 cases covering malformed input rejection, fail-closed
behaviour on DB error, 12-digit personnummer normalization, and the
ceased-company refusal path. Full suite: 2306 passing.
Out of scope for this PR (follow-up):
- Partial unique index on companies(org_number) WHERE archived_at IS
NULL. Closes the race-condition window but needs a migration plus
any existing-duplicate cleanup — too risky for this hotfix.
- Rate limiting on /api/company/check-org-number. Endpoint is
auth-gated so not an immediate concern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: add Luhn validation and extract org-number normalization
Third round of PR review feedback (swedish-compliance):
- Add Luhn-10 check-digit validation to normalizeOrgNumber. Rejects
structurally invalid org_numbers (wrong check digit) at the boundary
instead of letting them propagate into SIE4 #ORGNR and SRU INFO.SRU,
where Skatteverket and receiving accounting systems would reject
them later anyway. Reuses the existing luhnValidate helper from
lib/bankgiro/luhn.ts (Bankgirot 10-modulen — same algorithm applies
to both Bolagsverket org numbers and Swedish personnummer).
- Extract normalizeOrgNumber into lib/company-lookup/normalize-org-number.ts
so the server action and /api/company/check-org-number use the same
rule. Previously the API route only stripped hyphens/spaces, so a
12-digit input would miss a stored 10-digit duplicate and mislead the
client debounce check ("not a duplicate" → submit → server rejects).
- /api/company/check-org-number now returns exists=false for
Luhn-invalid input rather than querying the DB. The submit-time
server action surfaces org_number_invalid, which is the right place
for the error.
Test coverage: dedicated normalize-org-number.test.ts (10 cases
covering both-lengths, Luhn, whitespace tolerance, garbage). Updated
existing tests to use Luhn-valid numbers (real Volvo 5560125790,
synthetic personnummer 8001011231). New failing-Luhn test in
actions.test.ts. New 12-digit-normalization and
luhn-invalid-returns-false tests in route.test.ts.
Full suite: 2315 passing.
Not fixed (out of scope for this hotfix):
- 10↔12 digit round-trip fragility for personnummer born 2000+. This
is a codebase-wide architectural choice (see lib/skatteverket/format.ts
which uses a two-digit-year heuristic to choose 19/20 at export).
Migrating to 12-digit storage is a separate refactor.
- Server-side re-fetch of TIC /lookup for isCeased. The trust boundary
here is user-to-their-own-onboarding, not adversarial; doubling TIC
API cost isn't proportionate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6d75b9a1bf |
feat: BankID authentication via TIC Identity API (#192)
* feat: add BankID authentication via TIC Identity API Integrate BankID as a login/signup method using the TIC Identity API. Users can authenticate with BankID QR codes (desktop) or deep links (mobile), link BankID to existing accounts, and skip TOTP MFA when BankID is linked. Removes Step 0 (role choice) from onboarding for all users. Adds enrichment data support for pre-filling company details from Bolagsverket during signup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review — server-side rate limit, unlink clears MFA bypass - Add per-IP rate limit (5s cooldown) on /bankid/start to prevent unbounded billable TIC sessions from unauthenticated callers - Add /bankid/unlink endpoint that deletes bankid_identities AND clears app_metadata.bankid_linked so MFA enforcement resumes after unlink - Update BankIdSettings to call server-side unlink instead of client-side delete Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: move rate limiter to module scope, add BankID logo and year-end skill Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
e18b8dc789 |
feat: BFL-compliant descriptions, cancelled status, and TIC company lookup (#57)
* 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> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
ceec8c02a8 |
feat: reverse charge VAT (ruta 20-32) + mobile UX improvements (#50)
* 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> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
c7672e538a |
Logging (#30)
* Added loggin for the onboarding flow. * Logging for potential errors * feat: enhance onboarding error logging with server-side capture * Update app/(onboarding)/onboarding/page.tsx Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> |
||
|
|
a1ca96453c |
feat: mobile UX improvements across dashboard (#9)
* feat: mobile UX improvements across dashboard pages - Responsive layouts with stacked mobile forms and grid desktop views - Safe area handling (viewportFit cover, bottom nav insets, main padding) - Touch-friendly dialogs, tabs, and page headers - Mobile nav drawer open/close animation - Dashboard layout parallel Supabase queries - SIE parser: handle quoted VER/TRANS fields - Skip account override for template-based transaction booking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback from Greptile - Fix nav drawer close timer race condition with useRef + clearTimeout - Show locked VAT rate as read-only label on mobile instead of hiding it - Remove dead bottom-16 class overridden by inline style Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
091d043c85 |
feat: UI polish, lint fixes, onboarding redesign, help page expansion, and test improvements
Broad update across dashboard pages, components, extensions, and lib code. Includes ESLint config additions, onboarding flow redesign, settings page refactor, help page content expansion, dead code removal, and test mock fixes. Adds dev docs and public assets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
f3ec634a46 |
feat: open-source under AGPL-3.0, redesign UI to grayscale palette, add uncategorize API, fix VAT account names
Add LICENSE (AGPL-3.0-or-later), CONTRIBUTING.md, SECURITY.md, DCO, and NOTICE files. Rewrite README for open-source audience with self-hosting instructions. Redesign color palette to grayscale chrome theme across all components. Add transaction uncategorize API route with tests. Fix VAT account name mismatches in migration 052. Improve import page with SIE file support and loading skeleton. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
a25d10a528 | Initial copy from influencer-biz |