style(ui): system-wide UX/UI polish pass — design-system conformance + copy cleanup (#835)
* style(ui): system-wide UX/UI polish pass — design-system conformance + copy cleanup Multi-agent scan of all 404 UI files against the locked design system, then 141 verified surgical fixes across 109 files (net -32 lines): - Remove forbidden elevation/motion: shadow-* and rounded-xl on cards, active:scale bounce, hover:shadow on list items, transition-all -> transition-colors. - Drop font-medium from single-weight Hedvig display headings/numerals. - Replace raw rainbow Tailwind status colors with Badge variants / brand tokens / neutral surfaces (achromatic chrome, semantic colors stay data-only). - Route raw dates through formatDate(), hand-rolled currency through formatCurrency(), add tabular-nums to financial figures; text-gray-* -> text-foreground tokens. - Swap hand-rolled skeletons for the Skeleton primitive; off-scale spacing -> token scale. - Fix copy: mislabeled "Leverantörsfakturor" -> "Utgifter" on bank-import outflow total, collapse no-op identical-branch ternaries, broken Swedish diacritics (mojibake), correct mismatch-password toast, correct supplier currency-field label. - Remove PII-leaking debug console.log on register, stray console.logs. Verified: tsc clean on all changed files, eslint clean, production build passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(auth): sanitize residual error logs in register flow Follow-up to PR review (compliance swarm V16 / GDPR Art.5(1)(f)): the remaining console.error calls in the register flow passed raw error objects, which Supabase may populate with PII (email) in nested fields. Log only sanitized message strings instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a8072f6423
commit
b800dcd403
@@ -102,13 +102,13 @@ export function AccountDangerZone() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="space-y-4 border-t border-border/8 pt-8">
|
||||
<section className="space-y-4 border-t border-border pt-8">
|
||||
<h2 className="text-sm font-medium uppercase tracking-wider text-destructive/80">
|
||||
{t('heading')}
|
||||
</h2>
|
||||
|
||||
{hasBlockers && (
|
||||
<div className="rounded-lg border border-border/60 bg-muted/30 p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-muted/30 p-4 space-y-3">
|
||||
<p className="text-sm font-medium">{t('blockers_title')}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('blockers_description')}
|
||||
@@ -117,7 +117,7 @@ export function AccountDangerZone() {
|
||||
{blockers.map((b) => (
|
||||
<li
|
||||
key={b.id}
|
||||
className="flex items-center justify-between rounded-md border border-border/40 bg-background px-3 py-2"
|
||||
className="flex items-center justify-between rounded-md border border-border bg-background px-3 py-2"
|
||||
>
|
||||
<span className="text-sm font-medium">{b.name}</span>
|
||||
<Button variant="ghost" size="sm" asChild>
|
||||
|
||||
@@ -187,7 +187,7 @@ function CopyBlock({ text, copyAriaLabel }: { text: string; copyAriaLabel: strin
|
||||
aria-label={copyAriaLabel}
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-3.5 w-3.5 text-green-600" />
|
||||
<Check className="h-3.5 w-3.5 text-success" />
|
||||
) : (
|
||||
<Copy className="h-3.5 w-3.5" />
|
||||
)}
|
||||
@@ -700,7 +700,7 @@ export function ApiKeysPanel() {
|
||||
onClick={handleCopy}
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-4 w-4 text-green-600" />
|
||||
<Check className="h-4 w-4 text-success" />
|
||||
) : (
|
||||
<Copy className="h-4 w-4" />
|
||||
)}
|
||||
|
||||
@@ -243,7 +243,7 @@ export function BackupDownloadForm() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border border-border/60 bg-muted/30 p-3 text-sm">
|
||||
<div className="rounded-md border border-border bg-muted/30 p-3 text-sm">
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Info className="h-3.5 w-3.5" />
|
||||
{isLoadingEstimate ? (
|
||||
|
||||
@@ -105,7 +105,7 @@ export function BankIdSettings() {
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-base">
|
||||
{identity ? (
|
||||
<ShieldCheck className="h-4 w-4 text-green-600" />
|
||||
<ShieldCheck className="h-4 w-4 text-success" />
|
||||
) : (
|
||||
<Shield className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
|
||||
@@ -161,7 +161,7 @@ export function BankNameCombobox({ defaultValue = '', value: controlledValue, on
|
||||
role="option"
|
||||
aria-selected={highlightedIndex === i}
|
||||
className={cn(
|
||||
'flex items-center gap-2.5 px-3 py-2 text-sm cursor-pointer transition-colors',
|
||||
'flex items-center gap-2 px-3 py-2 text-sm cursor-pointer transition-colors',
|
||||
highlightedIndex === i && 'bg-accent text-accent-foreground',
|
||||
)}
|
||||
onMouseEnter={() => setHighlightedIndex(i)}
|
||||
|
||||
@@ -81,7 +81,7 @@ export function CompanyDangerZone() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="space-y-4 border-t border-border/8 pt-8">
|
||||
<section className="space-y-4 border-t border-border pt-8">
|
||||
<h2 className="text-sm font-medium uppercase tracking-wider text-destructive/80">
|
||||
{t('danger_heading')}
|
||||
</h2>
|
||||
|
||||
@@ -21,8 +21,8 @@ function formatDate(iso: string | null) {
|
||||
}
|
||||
|
||||
function confidenceColor(c: number): string {
|
||||
if (c >= 0.8) return 'text-emerald-600 dark:text-emerald-400'
|
||||
if (c >= 0.5) return 'text-amber-600 dark:text-amber-400'
|
||||
if (c >= 0.8) return 'text-success'
|
||||
if (c >= 0.5) return 'text-warning'
|
||||
return 'text-muted-foreground'
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ export function FiscalPeriodEditor() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="space-y-4 border-t border-border/8 pt-8">
|
||||
<section className="space-y-4 border-t border-border pt-8">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{t('fp_heading')}
|
||||
@@ -286,7 +286,7 @@ function BlockedState({
|
||||
: t('fp_blocked_reason_posted', { count: postedCount })
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border/60 bg-muted/30 p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-muted/30 p-4 space-y-3">
|
||||
<div className="flex gap-2">
|
||||
<Lock className="h-4 w-4 text-muted-foreground flex-shrink-0 mt-0.5" />
|
||||
<div className="space-y-1">
|
||||
|
||||
@@ -117,7 +117,7 @@ export function LogoUpload({ logoUrl, onUpdate }: LogoUploadProps) {
|
||||
|
||||
{preview ? (
|
||||
<div className="space-y-3">
|
||||
<div className="inline-block rounded-lg border border-border/60 bg-muted/30 p-4">
|
||||
<div className="inline-block rounded-lg border border-border bg-muted/30 p-4">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={preview}
|
||||
@@ -157,7 +157,7 @@ export function LogoUpload({ logoUrl, onUpdate }: LogoUploadProps) {
|
||||
onDragLeave={handleDragLeave}
|
||||
disabled={isUploading}
|
||||
className={`flex flex-col items-center justify-center w-full max-w-xs rounded-lg border-2 border-dashed py-8 px-4 text-center transition-colors disabled:opacity-50 ${
|
||||
isDragging ? 'border-foreground bg-muted/40' : 'border-border/60 hover:border-border hover:bg-muted/20'
|
||||
isDragging ? 'border-foreground bg-muted/40' : 'border-border hover:border-border hover:bg-muted/20'
|
||||
}`}
|
||||
>
|
||||
{isUploading ? (
|
||||
|
||||
@@ -272,11 +272,11 @@ export function SecuritySettings() {
|
||||
</div>
|
||||
) : hasMfa ? (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3 p-4 rounded-lg border bg-green-50 dark:bg-green-950/20 border-green-200 dark:border-green-900">
|
||||
<ShieldCheck className="h-5 w-5 text-green-600 dark:text-green-500" />
|
||||
<div className="flex items-center gap-3 p-4 rounded-lg border border-border bg-secondary">
|
||||
<ShieldCheck className="h-5 w-5 text-success" />
|
||||
<div>
|
||||
<p className="font-medium text-green-900 dark:text-green-100">{t('mfa_active_title')}</p>
|
||||
<p className="text-sm text-green-700 dark:text-green-400">
|
||||
<p className="font-medium text-foreground">{t('mfa_active_title')}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('mfa_active_description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -67,7 +67,7 @@ export function SettingsRail({ variant, display, activeId }: SettingsRailProps)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav aria-label={t('aria_label')} className="space-y-5">
|
||||
<nav aria-label={t('aria_label')} className="space-y-6">
|
||||
{groups.map((g) => (
|
||||
<div key={g.key} className="space-y-1">
|
||||
<p className="px-3 text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
|
||||
@@ -108,7 +108,7 @@ export function SkatteverketConnectPanel() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{status?.disabled && (
|
||||
<div className="flex gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-100">
|
||||
<div className="flex gap-2 rounded-md border border-border bg-secondary/40 p-3 text-sm text-foreground">
|
||||
<ShieldAlert className="h-4 w-4 mt-0.5 shrink-0" />
|
||||
<p>{t('disabled_message')}</p>
|
||||
</div>
|
||||
@@ -211,7 +211,7 @@ export function SkatteverketConnectPanel() {
|
||||
</div>
|
||||
|
||||
{status.disabled && (
|
||||
<div className="flex gap-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-100">
|
||||
<div className="flex gap-2 rounded-md border border-border bg-secondary/40 p-3 text-sm text-foreground">
|
||||
<ShieldAlert className="h-4 w-4 mt-0.5 shrink-0" />
|
||||
<p>{t('disabled_filings_message')}</p>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
|
||||
</section>
|
||||
|
||||
{/* F-skatt */}
|
||||
<section className="border-t border-border/8 pt-8 space-y-4">
|
||||
<section className="border-t border-border pt-8 space-y-4">
|
||||
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{t('tax_vat_heading')}
|
||||
</h2>
|
||||
@@ -140,7 +140,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
|
||||
</section>
|
||||
|
||||
{/* Tax contact — required for SKV-filings */}
|
||||
<section className="border-t border-border/8 pt-8 space-y-4">
|
||||
<section className="border-t border-border pt-8 space-y-4">
|
||||
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{t('tax_contact_heading')}
|
||||
</h2>
|
||||
@@ -181,7 +181,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
|
||||
</section>
|
||||
|
||||
{/* Fiscal year & salaries */}
|
||||
<section className="border-t border-border/8 pt-8 space-y-4">
|
||||
<section className="border-t border-border pt-8 space-y-4">
|
||||
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{t('fiscal_year_salaries_heading')}
|
||||
</h2>
|
||||
@@ -235,7 +235,7 @@ export function TaxSettingsForm({ settings }: TaxSettingsFormProps) {
|
||||
</section>
|
||||
|
||||
{/* Preliminary tax */}
|
||||
<section className="border-t border-border/8 pt-8 space-y-4">
|
||||
<section className="border-t border-border pt-8 space-y-4">
|
||||
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{t('preliminary_tax_heading')}
|
||||
</h2>
|
||||
|
||||
@@ -164,7 +164,7 @@ export function AccountSettingsContent() {
|
||||
key={value}
|
||||
type="button"
|
||||
onClick={() => setTheme(value)}
|
||||
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-2.5 text-sm font-medium transition-colors ${
|
||||
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-3 text-sm font-medium transition-colors ${
|
||||
theme === value
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-border hover:border-primary/40'
|
||||
@@ -193,7 +193,7 @@ export function AccountSettingsContent() {
|
||||
type="button"
|
||||
onClick={() => handleLocaleChange(value)}
|
||||
disabled={savingLocale}
|
||||
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-2.5 text-sm font-medium transition-colors disabled:opacity-50 ${
|
||||
className={`flex items-center gap-2 rounded-lg border-2 px-4 py-3 text-sm font-medium transition-colors disabled:opacity-50 ${
|
||||
activeLocale === value
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-border hover:border-primary/40'
|
||||
|
||||
Reference in New Issue
Block a user