From 9686b54b41cdf9084ff0b9b412f9626845923e1d Mon Sep 17 00:00:00 2001 From: Jakob Wennberg Date: Fri, 14 Aug 2026 08:55:37 +0200 Subject: [PATCH] refactor(design): lock the border-radius ladder, one radius per role (#1607) Seven radii were in circulation (4/5/6/8/12/16px + pill) with no rule for which went where; one toolbar row on /transactions mixed four shape languages. This locks a 4-tier ladder (design.md convention 16): - pill: interactive toolbar controls (buttons, chips, pickers, segmented controls, toolbar search, count nubs) - rounded-xl (12px): overlay tier: page panel, dialogs, slide-overs - rounded-lg (8px): cards, form fields, popover/menu content, boxes - rounded-sm (4px): nested leaves (menu items, checkboxes, kbd/code nubs) Changes: - New SegmentedControl primitive (pill-in-pill tablist, h-8) replaces the hand-rolled bg-muted/70 tablist copied across 11 files - New ToolbarSearch primitive (pill, h-8) adopted on 9 page toolbars; dialog/picker searches keep the rounded-lg Input - dialog.tsx 8px -> 12px, matching SettingsModal/slide-over/CommandPalette - ContextPicker chips at the shared h-8 toolbar height - ~300 rounded-md / bare rounded call sites remapped by role; auth icon tiles and the mobile nav sheet come down from 16px to 12px - rounded-md, bare rounded, rounded-2xl and rounded-[Npx] are dead vocabulary, enforced by a new off-ladder-radius check in check:guards Verified: lint 0 errors, 14422 unit tests pass, check:guards green, tsc clean on all changed files, sandbox screenshots of transactions/ bookkeeping/granskning toolbars and the Ny verifikation dialog. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 --- .claude/rules/design.md | 17 ++- DECISIONS.md | 3 + app/(auth)/login/login-client.tsx | 4 +- app/(auth)/mfa/enroll/page.tsx | 6 +- app/(auth)/mfa/verify/page.tsx | 2 +- app/(auth)/register/page.tsx | 4 +- app/(auth)/reset-password/page.tsx | 2 +- app/(dashboard)/articles/page.tsx | 25 ++--- app/(dashboard)/assets/[id]/dispose/page.tsx | 8 +- app/(dashboard)/bookkeeping/[id]/page.tsx | 6 +- .../bookkeeping/periodiseringar/page.tsx | 39 ++----- .../year-end/periodisering/page.tsx | 12 +-- app/(dashboard)/customers/page.tsx | 24 ++--- app/(dashboard)/import/page.tsx | 38 +++---- app/(dashboard)/invoices/[id]/page.tsx | 2 +- app/(dashboard)/invoices/page.tsx | 24 ++--- app/(dashboard)/loading.tsx | 6 +- app/(dashboard)/pending/page.tsx | 40 +++---- .../runs/[id]/employees/[employeeId]/page.tsx | 6 +- app/(dashboard)/salary/runs/[id]/page.tsx | 2 +- app/(dashboard)/skattekonto/page.tsx | 2 +- .../supplier-invoices/[id]/page.tsx | 4 +- app/(dashboard)/supplier-invoices/page.tsx | 19 ++-- app/(dashboard)/suppliers/page.tsx | 24 ++--- app/(dashboard)/transactions/page.tsx | 101 +++++------------- app/global-error.tsx | 2 +- app/payslip/[token]/page.tsx | 2 +- components/agent-knowledge/LedgerGraph.tsx | 2 +- components/agent/AgentChat.tsx | 4 +- components/agent/AgentSessionList.tsx | 6 +- components/agent/AgentSheet.tsx | 16 +-- components/agent/ApprovalCard.tsx | 8 +- components/agent/ChatConversationView.tsx | 2 +- components/agent/ChatSidebar.tsx | 18 ++-- components/bookkeeping/AccountCombobox.tsx | 6 +- .../bookkeeping/AccrualPeriodControl.tsx | 2 +- .../bookkeeping/ActivateAccountsDialog.tsx | 4 +- .../bookkeeping/AttachDocumentPreview.tsx | 4 +- .../bookkeeping/ChartOfAccountsManager.tsx | 58 +++------- components/bookkeeping/CorrectionChain.tsx | 2 +- components/bookkeeping/DocumentUploadZone.tsx | 2 +- components/bookkeeping/DocumentViewerPane.tsx | 2 +- components/bookkeeping/EditAccountDialog.tsx | 2 +- .../bookkeeping/InboxDocumentPicker.tsx | 2 +- .../bookkeeping/JournalEntryAttachments.tsx | 2 +- components/bookkeeping/JournalEntryForm.tsx | 2 +- components/bookkeeping/JournalEntryList.tsx | 55 +++------- components/bookkeeping/TemplateBookDialog.tsx | 2 +- .../bookkeeping/assets/CreateAssetDialog.tsx | 8 +- .../bookkeeping/assets/EditAssetDialog.tsx | 4 +- .../bookkeeping/year-end/AccrualsStep.tsx | 4 +- .../year-end/DepreciationPanel.tsx | 6 +- .../year-end/EfDeclarationSection.tsx | 2 +- components/common/ContextPicker.tsx | 4 +- components/common/ReportDateRange.tsx | 2 +- components/dashboard/AttGoraSection.tsx | 4 +- components/dashboard/CompanySwitcher.tsx | 4 +- components/dashboard/DashboardNav.tsx | 6 +- components/dashboard/SandboxBanner.tsx | 4 +- components/dashboard/UserMenu.tsx | 6 +- components/deadlines/DeadlineGroupCard.tsx | 2 +- components/deadlines/DeadlineList.tsx | 36 ++----- components/deadlines/DeadlineRow.tsx | 2 +- components/dimensions/DimensionCombobox.tsx | 2 +- components/dimensions/DimensionsManager.tsx | 60 +++++------ components/docs/DocsLayout.tsx | 2 +- .../general/ArcimMigrationWorkspace.tsx | 16 +-- .../extensions/general/BookDirectlyDialog.tsx | 2 +- .../general/BulkBookInboxDialog.tsx | 4 +- .../general/InvoiceInboxWorkspace.tsx | 8 +- .../extensions/general/WhatsAppLinkPanel.tsx | 2 +- .../shared/MockDataImportDialog.tsx | 4 +- components/import/ArticlesEditStep.tsx | 6 +- .../import/BankFileColumnMappingStep.tsx | 4 +- components/import/BankFilePreviewStep.tsx | 2 +- components/import/BankFileResultStep.tsx | 2 +- components/import/CustomersEditStep.tsx | 6 +- .../OpeningBalanceColumnMappingStep.tsx | 2 +- components/import/OpeningBalanceRowEditor.tsx | 4 +- .../import/RegisterColumnMappingStep.tsx | 2 +- components/import/SuppliersEditStep.tsx | 6 +- components/inbox/TransactionMatchPicker.tsx | 4 +- components/invoices/RotRutPayoutDialog.tsx | 2 +- components/kpi/KPISettingsDialog.tsx | 4 +- components/onboarding/NewUserChecklist.tsx | 2 +- components/reports/DeclarationRutaRow.tsx | 4 +- components/reports/ReportLibrary.tsx | 2 +- components/reports/ReportRowExpansion.tsx | 4 +- components/reports/views/index.tsx | 45 +++----- components/salary/AGIPanel.tsx | 24 ++--- components/salary/EmployeeBenefitsPanel.tsx | 2 +- components/salary/EmployeeTaxCard.tsx | 8 +- components/salary/MunicipalityCombobox.tsx | 2 +- components/salary/PaymentFilePanel.tsx | 8 +- components/salary/SalaryCalendar.tsx | 16 +-- components/salary/run/RunHeader.tsx | 2 +- components/settings/AgentMemoryPanel.tsx | 2 +- components/settings/AgentSkillsPanel.tsx | 2 +- components/settings/ApiKeysPanel.tsx | 12 +-- components/settings/BackupDownloadForm.tsx | 4 +- components/settings/BankNameCombobox.tsx | 4 +- .../settings/InvoiceEmailTextsSettings.tsx | 2 +- components/settings/OAuthClientsPanel.tsx | 2 +- components/settings/SettingsRows.tsx | 2 +- components/settings/TemplateForm.tsx | 2 +- .../sections/BankingSettingsContent.tsx | 2 +- .../skattekonto/SkattekontoMatchDialog.tsx | 2 +- .../NewSupplierInvoiceForm.tsx | 8 +- .../supplier-invoices/PaymentFileDialog.tsx | 2 +- .../transactions/BankSyncSinceLastVisit.tsx | 2 +- .../transactions/BankSyncStatusChip.tsx | 6 +- .../transactions/DuplicateBookingDialog.tsx | 2 +- .../transactions/InvoiceMatchDialog.tsx | 2 +- .../TransactionAttachDocumentDialog.tsx | 2 +- .../transactions/TransactionBookingDialog.tsx | 2 +- .../transactions/TransactionHistoryList.tsx | 25 ++--- .../transactions/VatTreatmentSelect.tsx | 2 +- components/ui/checkbox.tsx | 2 +- components/ui/dialog.tsx | 2 +- components/ui/dropdown-menu.tsx | 4 +- components/ui/open-in-new-tab.tsx | 2 +- components/ui/segmented-control.tsx | 68 ++++++++++++ components/ui/select.tsx | 2 +- components/ui/skeleton.tsx | 2 +- components/ui/split-button.tsx | 2 +- components/ui/tabs.tsx | 4 +- components/ui/tag-input.tsx | 6 +- components/ui/toast.tsx | 6 +- components/ui/toolbar-search.tsx | 35 ++++++ scripts/checks/no-new-antipatterns.mjs | 79 +++++++++++++- 130 files changed, 633 insertions(+), 652 deletions(-) create mode 100644 components/ui/segmented-control.tsx create mode 100644 components/ui/toolbar-search.tsx diff --git a/.claude/rules/design.md b/.claude/rules/design.md index ce17ea3c..612dda72 100644 --- a/.claude/rules/design.md +++ b/.claude/rules/design.md @@ -54,6 +54,7 @@ Decided during the 2026-07 concept work (dev_docs/ui_migration_plan.md); they ap 13. **Overlays**: centered modal for create/confirm (template, assistant, confirmations, settings); right slide-over for reviewing an object (Granskning detail). Both with veil, Esc, and click-outside. 14. **Manual base, AI as opt-in.** Base flows work without AI; AI entry points are clearly labeled discrete choices (e.g. "Skapa med assistenten") and no AI suggestion posts without Granskning. 15. **Settings speak "Fönster"** (founder-chosen 2026-07-25, applies to every settings tab). Settings content is flat hairline rows, never boxed cards: `SettingsGroup` / `SettingsRow` / `SettingsSectionHeader` from `components/settings/SettingsRows.tsx`. Toggles are switches, not checkboxes. Saving is a sticky bar that fades in only when the form is dirty (`components/settings/SettingsFormWrapper.tsx`); no always-visible save button. The settings surface is a 920x680 modal (`components/settings/SettingsModal.tsx`); switching tabs inside it updates the URL shallowly via `history.replaceState` (`SettingsRail.tsx`), never `router.replace`, which would remount the modal. +16. **One radius per role (the radius ladder, 2026-08-13).** Four tiers, tied to what a thing IS: pills for interactive toolbar controls (buttons, chips, pickers, segmented controls, toolbar search, count badges), `rounded-xl` (12px) for the overlay/panel tier (page panel, dialogs, slide-overs, command palette), `rounded-lg` (8px) for surfaces on the panel (cards, form fields, textareas, popover/dropdown/menu content, bordered boxes), `rounded-sm` (4px) for leaf elements nested inside 8px surfaces (menu items, checkboxes, kbd/code nubs, skeleton text lines). Toolbar controls share one height: `h-8`. `rounded-md`, bare `rounded`, `rounded-2xl` and arbitrary `rounded-[Npx]` are dead vocabulary, enforced by `check:guards` (off-ladder-radius). Nesting is concentric: pill-in-pill by construction, `rounded-sm` items inside `rounded-lg` content with ~4px inset. ## Design System Tokens @@ -72,6 +73,17 @@ Decided during the 2026-07 concept work (dev_docs/ui_migration_plan.md); they ap Compact metric cards (e.g. dashboard tiles, salary KPI row) use `p-4`. Detail cards use `p-6`. Never mix `p-5`. +**Radius ladder** (convention 16; enforced by `check:guards` off-ladder-radius). Radius communicates role, and every role has exactly one: + +| Tier | Class | Use for | +|---|---|---| +| pill | `rounded-full` | Buttons, chips, badges, context pickers, segmented controls, toolbar search, count nubs, dots, avatars | +| 12px | `rounded-xl` | Page panel, dialogs, slide-overs, command palette, hero surfaces | +| 8px | `rounded-lg` | Cards, form inputs, textareas, popover/dropdown/menu content, bordered boxes, toasts | +| 4px | `rounded-sm` | Menu items, checkboxes, kbd/code/account-number nubs, skeleton text lines, small nested leaves | + +**Forbidden radii:** `rounded-md`, bare `rounded`, `rounded-2xl`+, `rounded-[Npx]`. A search field is a pill in a toolbar (`ToolbarSearch`) and `rounded-lg` in a form or dialog (`Input`): the row it sits in decides, and mixing shapes in one row is the bug the ladder exists to prevent. + **Layout (frame layout).** - The dashboard wrapper is `bg-frame` (`--frame: 40 18% 96%` light, `0 0% 5%` dark). `
` is the page panel: `bg-background rounded-xl border border-border`, 10px margin against the frame, own inner scroll (`md:h-[calc(100vh-20px)] md:overflow-y-auto`). Defined once as `MAIN_PANEL_CLASS` in `app/(dashboard)/layout.tsx`; never restyle per page. - The panel is the desktop scroll container: `position: sticky` binds to it automatically; never assume `window` scroll on dashboard pages. Scroll reset on navigation lives in `MainContainer`. @@ -91,6 +103,8 @@ Compact metric cards (e.g. dashboard tiles, salary KPI row) use `p-4`. Detail ca | No-data state | `components/ui/empty-state.tsx` `EmptyState` | Don't hand-roll `
…
`. Preset variants exist (`EmptyInvoices`, `EmptyCustomers`, `EmptyTransactions`, etc.). | | Loading placeholder | `components/ui/skeleton.tsx` `` | Don't hand-roll `bg-muted rounded animate-pulse` divs. | | Inline help / formulas | `components/ui/info-tooltip.tsx` `InfoTooltip` | Hover-revealed; don't use always-visible info buttons. | +| View/mode switcher in a toolbar | `components/ui/segmented-control.tsx` `SegmentedControl` | Pill-in-pill tablist at the shared `h-8` toolbar height; `options` take an optional `count` for the standard count chip. Never hand-roll the `bg-muted/70` tablist div. | +| Search in a page toolbar | `components/ui/toolbar-search.tsx` `ToolbarSearch` | Pill search at `h-8`, matching the chips and pickers beside it. Searches inside dialogs/pickers keep the regular `Input`. | | Fiscal year picker | `components/common/FyPicker.tsx` | The chip-dropdown context picker of convention 8 (wraps `ContextPicker`). `FiscalYearSelector` is the legacy pre-frame control: don't add new uses. Never use a raw ` { - setSearchTerm(e.target.value) - setVisibleCount(INITIAL_VISIBLE_ROWS) - }} - className="h-9 pl-10" - /> - + { + setSearchTerm(e.target.value) + setVisibleCount(INITIAL_VISIBLE_ROWS) + }} + /> {/* A single-currency register needs no scope: the chip would be a control with one meaningful position. */} {availableCurrencies.length > 1 && ( diff --git a/app/(dashboard)/assets/[id]/dispose/page.tsx b/app/(dashboard)/assets/[id]/dispose/page.tsx index 3d127b62..708ea116 100644 --- a/app/(dashboard)/assets/[id]/dispose/page.tsx +++ b/app/(dashboard)/assets/[id]/dispose/page.tsx @@ -325,7 +325,7 @@ export default function DisposeAssetPage({ params }: { params: Promise<{ id: str {VAT_TREATMENTS.map((value) => {t(`vat_${value}`)})} -
+
@@ -351,20 +351,20 @@ export default function DisposeAssetPage({ params }: { params: Promise<{ id: str
)} {jamkningAssessment && ( -
+
{jamkningAssessment.capped &&

{t('adjustment_capped')}

}
)} {disposalType === 'business_transfer' && ( -
+
)} {disposalType === 'business_transfer' && transferNeedsDocument && ( -
+
diff --git a/app/(dashboard)/bookkeeping/[id]/page.tsx b/app/(dashboard)/bookkeeping/[id]/page.tsx index 86d15fce..68604b8f 100644 --- a/app/(dashboard)/bookkeeping/[id]/page.tsx +++ b/app/(dashboard)/bookkeeping/[id]/page.tsx @@ -842,7 +842,7 @@ export default function JournalEntryDetailPage({ params }: { params: Promise<{ i - ))} -
+ {isLoading ? (
diff --git a/app/(dashboard)/bookkeeping/year-end/periodisering/page.tsx b/app/(dashboard)/bookkeeping/year-end/periodisering/page.tsx index 91abfc6c..d7896b08 100644 --- a/app/(dashboard)/bookkeeping/year-end/periodisering/page.tsx +++ b/app/(dashboard)/bookkeeping/year-end/periodisering/page.tsx @@ -522,7 +522,7 @@ function VacationStep({ {proposal ? ( -
+

{proposal.label}

{proposal.description}

@@ -590,7 +590,7 @@ function AuditStep({
{state.enabled && ( -
+
{ - setSearchTerm(e.target.value) - setVisibleCount(INITIAL_VISIBLE_ROWS) - }} - className="h-9 pl-10" - /> -
+ { + setSearchTerm(e.target.value) + setVisibleCount(INITIAL_VISIBLE_ROWS) + }} + />
{isLoading ? ( diff --git a/app/(dashboard)/import/page.tsx b/app/(dashboard)/import/page.tsx index c1b4a2da..7ee5ff77 100644 --- a/app/(dashboard)/import/page.tsx +++ b/app/(dashboard)/import/page.tsx @@ -10,6 +10,7 @@ import { Badge } from '@/components/ui/badge' import { PageHeader } from '@/components/ui/page-header' import { HelpPopover } from '@/components/ui/help-popover' import { AttnLine } from '@/components/ui/attn-line' +import { SegmentedControl } from '@/components/ui/segmented-control' import { Dialog, DialogContent, @@ -2048,7 +2049,7 @@ function CSVDataImportWizard() { onClick={() => setEntity(opt.value)} aria-pressed={selected} className={cn( - 'relative h-9 rounded-md border px-4 text-sm font-medium transition-colors', + 'relative h-9 rounded-lg border px-4 text-sm font-medium transition-colors', selected ? 'border-foreground bg-foreground text-background' : 'border-border bg-card text-foreground hover:border-foreground/30 hover:bg-muted', @@ -2203,29 +2204,14 @@ export default function ImportPage() { {isSandbox && {t('sandbox_disabled')}} {/* Importera / Exportera as separate tabs (house seg), like before */} -
- {( - [ - { key: 'import', label: t('tab_import') }, - { key: 'export', label: t('tab_export') }, - ] as const - ).map(({ key, label }) => ( - - ))} -
+ {view === 'import' ? (
@@ -2370,7 +2356,7 @@ export default function ImportPage() {
{['w-40', 'w-48', 'w-36', 'w-44'].map((w, i) => ( @@ -80,7 +80,7 @@ export default function DashboardLoading() { key={i} className="flex items-start gap-3 border-b border-border px-1 py-3.5" > - +
diff --git a/app/(dashboard)/pending/page.tsx b/app/(dashboard)/pending/page.tsx index 8364706e..cef8a5f9 100644 --- a/app/(dashboard)/pending/page.tsx +++ b/app/(dashboard)/pending/page.tsx @@ -8,6 +8,7 @@ import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { DataListEmpty, DataListLoading } from '@/components/ui/data-list' import { ContextPicker } from '@/components/common/ContextPicker' +import { SegmentedControl } from '@/components/ui/segmented-control' import { HOVER_REVEAL_CLASS, QUIET_LINK_CLASS } from '@/components/ui/dry-table' import { SlideOver, @@ -229,7 +230,7 @@ function useAccountNamesSource(): Record { function PeriodLockBanner({ period }: { period: PeriodStatusShape }) { const lockedThrough = period.lock_date ? formatDate(period.lock_date) : null return ( -
+

@@ -704,7 +705,7 @@ export default function PendingOperationsPage() {

{conversationFilter && ( -
+
@@ -733,30 +734,15 @@ export default function PendingOperationsPage() { {/* Toolbar (concept): status seg left, source picker (convention 8) far right. The count chip rides only on Väntar: it is the queue. */}
-
- {SEG_TABS.map(({ tab, labelKey }) => ( - - ))} -
+ ({ + value: tab, + label: t(labelKey), + count: tab === 'pending' ? pendingCount ?? 0 : undefined, + }))} + />

{t('bulk_confirm_intro')}

-
    +
      {selectedBreakdown.map(({ type, count }) => (
    • {bulkActionLabel(type, count, t)} diff --git a/app/(dashboard)/salary/runs/[id]/employees/[employeeId]/page.tsx b/app/(dashboard)/salary/runs/[id]/employees/[employeeId]/page.tsx index 63596a8c..dcca5495 100644 --- a/app/(dashboard)/salary/runs/[id]/employees/[employeeId]/page.tsx +++ b/app/(dashboard)/salary/runs/[id]/employees/[employeeId]/page.tsx @@ -163,7 +163,7 @@ export default function SalaryRunEmployeeDetailPage({ > {t('back_to_run')} -
      +
      {error ?? t('error_load_employee')}
      @@ -321,7 +321,7 @@ export default function SalaryRunEmployeeDetailPage({ function SummaryCard({ label, value, accent, overridden }: { label: string; value: number; accent?: boolean; overridden?: boolean }) { const t = useTranslations('salary_run_employee') return ( -
      +
      {label} {/* The override is an exception, so it is a chip, not a second ring: @@ -337,7 +337,7 @@ function SummaryCard({ label, value, accent, overridden }: { label: string; valu function AbsenceCount({ label, days }: { label: string; days: number }) { const t = useTranslations('salary_run_employee') return ( -
      +
      {label}
      {t('days_count', { days })}
      diff --git a/app/(dashboard)/salary/runs/[id]/page.tsx b/app/(dashboard)/salary/runs/[id]/page.tsx index 3a8840a3..5e94fa47 100644 --- a/app/(dashboard)/salary/runs/[id]/page.tsx +++ b/app/(dashboard)/salary/runs/[id]/page.tsx @@ -896,7 +896,7 @@ export default function SalaryRunPage({ params }: { params: Promise<{ id: string {approveOverride && approveOverride.length > 0 && ( -
      +
        {approveOverride.map((reason, i) => ( diff --git a/app/(dashboard)/skattekonto/page.tsx b/app/(dashboard)/skattekonto/page.tsx index fb446ad9..fc30a09e 100644 --- a/app/(dashboard)/skattekonto/page.tsx +++ b/app/(dashboard)/skattekonto/page.tsx @@ -888,7 +888,7 @@ function MatchDialog({ )} {!loading && candidates && candidates.length > 0 && ( -
        +
        diff --git a/app/(dashboard)/supplier-invoices/[id]/page.tsx b/app/(dashboard)/supplier-invoices/[id]/page.tsx index 68f15615..af350814 100644 --- a/app/(dashboard)/supplier-invoices/[id]/page.tsx +++ b/app/(dashboard)/supplier-invoices/[id]/page.tsx @@ -1080,7 +1080,7 @@ export default function SupplierInvoiceDetailPage() { onChange={(e) => updateEditLine(i, { description: e.target.value })} placeholder="Beskrivning" /> -
        +
        - - {/* Review tab exists only while suggestions do (or while the user is - standing in it after emptying the list): a permanent third tab - would advertise a migrator surface most companies never need. */} - {(suggestionItems.length > 0 || mode === 'review') && ( - - )} -
        -
        - - setSearchTerm(e.target.value)} - className="h-9 pl-10" - /> -
        + 0 || mode === 'review' + ? [{ value: 'review' as const, label: t('mode_review'), count: suggestionItems.length }] + : []), + ]} + /> + setSearchTerm(e.target.value)} + /> {/* Far-right context group, shared by both view modes: period scope (rakenskapsar chip) and the account chooser (concept scene 10). Approved deviation from convention 8's single chip: booking is @@ -3276,12 +3231,12 @@ export default function TransactionsPage() { {[1, 2, 3].map((i) => (
        - +
        - - + +
        - +
        ))}
        @@ -3857,7 +3812,7 @@ export default function TransactionsPage() { fakturan istället för att bokföra direkt på leverantörsskuldskontot, annars skapas en dubblerad verifikation som måste stornas (BFL 5 kap 5 §).

        -
        +
        {siMatchSuggestion?.candidates.map((c) => (
        @@ -3915,7 +3870,7 @@ export default function TransactionsPage() { istället för att bokföra direkt mot kundfordringskontot, annars skapas en dubblerad verifikation som måste stornas (BFL 5 kap 5 §).

        -
        +
        {ciMatchSuggestion?.candidates.map((c) => (
        diff --git a/app/global-error.tsx b/app/global-error.tsx index e1638e3c..611295b2 100644 --- a/app/global-error.tsx +++ b/app/global-error.tsx @@ -78,7 +78,7 @@ export default function GlobalError({

        diff --git a/app/payslip/[token]/page.tsx b/app/payslip/[token]/page.tsx index 9c956b15..1dbd0f43 100644 --- a/app/payslip/[token]/page.tsx +++ b/app/payslip/[token]/page.tsx @@ -134,7 +134,7 @@ export default async function PayslipPage({ Ladda ner PDF (lönespecifikation) diff --git a/components/agent-knowledge/LedgerGraph.tsx b/components/agent-knowledge/LedgerGraph.tsx index 4f76c138..7bd8b34d 100644 --- a/components/agent-knowledge/LedgerGraph.tsx +++ b/components/agent-knowledge/LedgerGraph.tsx @@ -329,7 +329,7 @@ export function LedgerGraph({ deep, companyName }: { deep: DeepLedgerContext; co @@ -135,7 +135,7 @@ export default function AgentSessionList({ activeConversationId, onSelect }: Pro placeholder="Namnge konversationen…" maxLength={200} aria-label="Nytt namn på konversationen" - className="w-full rounded-md border border-border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-ring" + className="w-full rounded-lg border border-border bg-background px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-ring" />
        ) : ( diff --git a/components/agent/AgentSheet.tsx b/components/agent/AgentSheet.tsx index 5e63dec5..23faec84 100644 --- a/components/agent/AgentSheet.tsx +++ b/components/agent/AgentSheet.tsx @@ -617,7 +617,7 @@ export default function AgentSheet({ >
        {preview != null && typeof preview === 'object' && ( -
        +
        setConfirmText(e.target.value)} disabled={isBusy} - className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring" + className="w-full rounded-lg border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring" autoComplete="off" aria-label="Bekräfta med ordet godkänn" /> @@ -384,7 +384,7 @@ export default function ApprovalCard({ {errorMessage &&

        {errorMessage}

        } {showRejectForm ? ( -
        +

        Vad är fel?

        setSearchQuery(e.target.value)} - className="h-9 pl-10" - /> -
        + + setSearchQuery(e.target.value)} + containerClassName="min-w-[190px] max-w-xs flex-1" + /> {view === 'my-accounts' && (