* Implement company and account deletion features - Add event types for company and account deletion to CoreEvent. - Enhance Supabase middleware to handle company context resolution and cookie management for archived companies. - Create API routes for deleting accounts and companies, including necessary validations and event emissions. - Implement tests for account and company deletion endpoints to ensure proper functionality and error handling. - Add retention notice component to inform users about bookkeeping data retention during destructive actions. - Create database migrations to support soft deletion of companies and anonymization of user accounts, ensuring compliance with retention laws. * feat: enhance account deletion process and update user notifications * Add service client for onboarding completion check and update escape hatch visibility * Enhance invite flow and email handling for company members * Refactor company context and RLS policies for active company isolation - Update `switchCompany` to remove unnecessary revalidation as client handles navigation. - Revise `getActiveCompanyId` to prioritize `user_preferences` and validate against non-archived memberships. - Modify `setActiveCompany` to ensure `user_preferences` is the authoritative source while maintaining cookie compatibility. - Enhance middleware to resolve active company using `user_preferences` and fallback to first non-archived membership. - Introduce new API route `/api/company/current` to fetch the active company ID for cross-tab synchronization. - Implement `CompanyTabSync` component for real-time active company enforcement across tabs. - Create migration for RLS policies to enforce single-active-company isolation using `current_active_company_id()`. * feat: implement viewer role enforcement for write permissions - Added `useCanWrite` hook to determine if the current user has write permissions based on their role in the active company. - Updated various components (JournalEntryForm, CustomerForm, DeadlineForm, etc.) to disable write actions and show a lock icon with a tooltip for users without write permissions. - Introduced `requireWritePermission` function to enforce write permissions at the API level, returning a 403 response for viewers. - Created tests to verify the behavior of the viewer role and write permissions. - Added database migration to enforce read-only access for viewers at the database level.
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
'use client'
|
||
|
||
import { AlertTriangle } from 'lucide-react'
|
||
import { cn } from '@/lib/utils'
|
||
|
||
interface RetentionNoticeProps {
|
||
variant: 'company' | 'account'
|
||
className?: string
|
||
}
|
||
|
||
/**
|
||
* Shared BFL retention notice shown before destructive actions that
|
||
* affect bookkeeping data. Keeps the legal copy consistent between the
|
||
* account danger zone and the company danger zone.
|
||
*
|
||
* Swedish Bokföringslagen (BFL) 7 kap. 2§ requires räkenskapsinformation
|
||
* to be retained for 7 years. gnubok is the system of record, so deleting
|
||
* a company or an account does not remove the underlying data — it only
|
||
* hides it from the UI and anonymizes PII where applicable.
|
||
*/
|
||
export function RetentionNotice({ variant, className }: RetentionNoticeProps) {
|
||
const copy =
|
||
variant === 'company'
|
||
? {
|
||
title: 'Bokföringen behålls i 7 år',
|
||
body:
|
||
'Enligt bokföringslagen (BFL 7 kap. 2§) sparas räkenskapsinformation i 7 år. ' +
|
||
'När du raderar företaget döljs det i gnubok, men verifikationer, dokument och ' +
|
||
'bokföring behålls säkert tills lagkravet löpt ut.',
|
||
}
|
||
: {
|
||
title: 'Kontoraderingen är permanent',
|
||
body:
|
||
'Ditt konto avidentifieras och du loggas ut från alla enheter. Räkenskapsinformation ' +
|
||
'från företag du ägt behålls säkert i 7 år enligt BFL 7 kap. 2§. Du kan inte skapa ett ' +
|
||
'nytt konto med samma e-postadress — kontakta support om du vill återaktivera kontot ' +
|
||
'i framtiden. Ladda gärna ner ett fullständigt arkiv innan du fortsätter.',
|
||
}
|
||
|
||
return (
|
||
<div
|
||
className={cn(
|
||
'rounded-lg border border-destructive/30 bg-destructive/5 p-4',
|
||
className
|
||
)}
|
||
>
|
||
<div className="flex gap-3">
|
||
<AlertTriangle className="h-5 w-5 text-destructive shrink-0 mt-0.5" />
|
||
<div className="space-y-1 text-sm">
|
||
<p className="font-medium text-destructive">{copy.title}</p>
|
||
<p className="text-muted-foreground">{copy.body}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|