a1a816b4a5
* 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.
130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
'use client'
|
|
|
|
import { Button } from '@/components/ui/button'
|
|
import { Badge } from '@/components/ui/badge'
|
|
import Link from 'next/link'
|
|
import { Upload, Wand, Plus, CheckSquare, FileText, Lock } from 'lucide-react'
|
|
import { useCanWrite } from '@/lib/hooks/use-can-write'
|
|
import type { ViewMode } from './transaction-types'
|
|
|
|
interface TransactionStatusBarProps {
|
|
uncategorizedCount: number
|
|
invoiceMatchCount: number
|
|
mode: ViewMode
|
|
onModeChange: (mode: ViewMode) => void
|
|
onOpenSwipeView: () => void
|
|
onOpenCreateDialog: () => void
|
|
isLoadingSuggestions: boolean
|
|
isBatchMode: boolean
|
|
onToggleBatchMode: () => void
|
|
}
|
|
|
|
export default function TransactionStatusBar({
|
|
uncategorizedCount,
|
|
invoiceMatchCount,
|
|
mode,
|
|
onModeChange,
|
|
onOpenSwipeView,
|
|
onOpenCreateDialog,
|
|
isLoadingSuggestions,
|
|
isBatchMode,
|
|
onToggleBatchMode,
|
|
}: TransactionStatusBarProps) {
|
|
const { canWrite } = useCanWrite()
|
|
return (
|
|
<div className="space-y-4">
|
|
{/* Header with title + actions */}
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
|
<div>
|
|
<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">Transaktioner</h1>
|
|
{uncategorizedCount > 0 && mode === 'inbox' && (
|
|
<p className="text-muted-foreground mt-1">
|
|
<span className="text-foreground font-semibold">{uncategorizedCount}</span> att bokföra
|
|
{invoiceMatchCount > 0 && (
|
|
<span className="ml-2">
|
|
· <FileText className="inline h-3.5 w-3.5 text-primary" />{' '}
|
|
<span className="text-foreground font-semibold">{invoiceMatchCount} fakturamatchningar</span>
|
|
</span>
|
|
)}
|
|
</p>
|
|
)}
|
|
{mode === 'history' && (
|
|
<p className="text-muted-foreground">Alla dina transaktioner</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
<Button variant="outline" size="sm" asChild>
|
|
<Link href="/import">
|
|
<Upload className="mr-2 h-4 w-4" />
|
|
Importera
|
|
</Link>
|
|
</Button>
|
|
{mode === 'inbox' && uncategorizedCount > 0 && (
|
|
<>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={onOpenSwipeView}
|
|
disabled={isLoadingSuggestions}
|
|
>
|
|
<Wand className="mr-2 h-4 w-4" />
|
|
{isLoadingSuggestions ? 'Laddar...' : 'Gå igenom alla'}
|
|
</Button>
|
|
<Button
|
|
variant={isBatchMode ? 'default' : 'outline'}
|
|
size="sm"
|
|
onClick={onToggleBatchMode}
|
|
>
|
|
<CheckSquare className="mr-2 h-4 w-4" />
|
|
{isBatchMode ? 'Avsluta' : 'Välj flera'}
|
|
</Button>
|
|
</>
|
|
)}
|
|
<Button
|
|
size="sm"
|
|
onClick={onOpenCreateDialog}
|
|
disabled={!canWrite}
|
|
title={!canWrite ? 'Du har endast läsbehörighet i detta företag' : undefined}
|
|
>
|
|
{canWrite ? (
|
|
<Plus className="mr-2 h-4 w-4" />
|
|
) : (
|
|
<Lock className="mr-2 h-4 w-4" />
|
|
)}
|
|
Ny transaktion
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mode toggle - segmented control style */}
|
|
<div className="inline-flex rounded-lg border bg-muted p-1">
|
|
<Button
|
|
variant={mode === 'inbox' ? 'default' : 'ghost'}
|
|
size="sm"
|
|
className="h-8 rounded-md"
|
|
onClick={() => onModeChange('inbox')}
|
|
>
|
|
Att bokföra
|
|
{uncategorizedCount > 0 && (
|
|
<Badge
|
|
variant={mode === 'inbox' ? 'secondary' : 'outline'}
|
|
className="ml-2 text-xs"
|
|
>
|
|
{uncategorizedCount}
|
|
</Badge>
|
|
)}
|
|
</Button>
|
|
<Button
|
|
variant={mode === 'history' ? 'default' : 'ghost'}
|
|
size="sm"
|
|
className="h-8 rounded-md"
|
|
onClick={() => onModeChange('history')}
|
|
>
|
|
Alla transaktioner
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|