Files
accounted/components/transactions/InboxZeroState.tsx
T
Jakob Wennberg 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>
2026-03-18 10:42:57 +01:00

70 lines
2.5 KiB
TypeScript

'use client'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Check, Upload, Plus } from 'lucide-react'
import Link from 'next/link'
interface InboxZeroStateProps {
hasTransactions: boolean
onCreateTransaction: () => void
}
export default function InboxZeroState({ hasTransactions, onCreateTransaction }: InboxZeroStateProps) {
if (!hasTransactions) {
// No transactions at all
return (
<Card>
<CardContent className="flex flex-col items-center justify-center py-12">
<div className="p-5 rounded-full bg-muted mb-6">
<Upload className="h-8 w-8 text-muted-foreground" />
</div>
<h3 className="text-lg font-medium mb-2">Inga transaktioner</h3>
<p className="text-sm text-muted-foreground text-center max-w-sm mb-6">
Importera kontoutdrag från din bank eller lägg till transaktioner manuellt för att komma igång.
</p>
<div className="flex flex-col sm:flex-row gap-2 w-full sm:w-auto px-4 sm:px-0">
<Button asChild>
<Link href="/import">
<Upload className="mr-2 h-4 w-4" />
Importera transaktioner
</Link>
</Button>
<Button variant="outline" onClick={onCreateTransaction}>
<Plus className="mr-2 h-4 w-4" />
Lägg till manuellt
</Button>
</div>
</CardContent>
</Card>
)
}
// All transactions categorized - inbox zero!
return (
<Card>
<CardContent className="flex flex-col items-center justify-center py-12">
<div className="h-16 w-16 rounded-full bg-success/10 flex items-center justify-center mb-4">
<Check className="h-8 w-8 text-success" />
</div>
<h3 className="text-lg font-medium">Alla transaktioner bokförda!</h3>
<p className="text-muted-foreground text-center mt-1 max-w-sm">
Bra jobbat! Alla dina transaktioner är bokförda. Importera fler eller växla till historik.
</p>
<div className="flex gap-2 mt-6">
<Button asChild variant="outline">
<Link href="/import">
<Upload className="mr-2 h-4 w-4" />
Importera fler
</Link>
</Button>
<Button variant="outline" onClick={onCreateTransaction}>
<Plus className="mr-2 h-4 w-4" />
Ny transaktion
</Button>
</div>
</CardContent>
</Card>
)
}