feat: add INK2 declaration, full archive export, AI consent gate, fix VAT declaration rutor
- Fix VAT declaration ruta mappings to match SKV 4700 form correctly (ruta 05 = total taxable sales, ruta 10/11/12 = output VAT per rate) - Add INK2 declaration report for aktiebolag with SRU export - Add full archive ZIP export for 7-year retention compliance - Add AI consent gate requiring user approval before AI extension API calls - Add DPA and privacy policy public pages - Add audit trail API routes - Update VAT registration threshold from 80k to 120k kr in onboarding - Update CLAUDE.md documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
50bf7b3b0f
commit
29240738fa
@@ -0,0 +1,102 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { AI_DATA_DISCLOSURES, type AiExtensionId } from '@/lib/extensions/ai-consent'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface AiConsentDialogProps {
|
||||
extensionId: AiExtensionId
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
onConsented: () => void
|
||||
}
|
||||
|
||||
export function AiConsentDialog({
|
||||
extensionId,
|
||||
open,
|
||||
onOpenChange,
|
||||
onConsented,
|
||||
}: AiConsentDialogProps) {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
const disclosure = AI_DATA_DISCLOSURES[extensionId]
|
||||
|
||||
async function handleAccept() {
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
const res = await fetch('/api/ai-consent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ extension_id: extensionId }),
|
||||
})
|
||||
|
||||
if (res.ok) {
|
||||
onOpenChange(false)
|
||||
onConsented()
|
||||
}
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>AI-samtycke kravs</DialogTitle>
|
||||
<DialogDescription>
|
||||
Denna funktion anvander AI-tjanster fran externa leverantorer.
|
||||
Granska informationen nedan innan du fortsatter.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-1">Leverantor</p>
|
||||
<p className="text-sm text-muted-foreground">{disclosure.provider}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-1">Data som skickas</p>
|
||||
<ul className="text-sm text-muted-foreground list-disc pl-5 space-y-1">
|
||||
{disclosure.dataTypes.map((dt) => (
|
||||
<li key={dt}>{dt}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-sm font-medium mb-1">Syfte</p>
|
||||
<p className="text-sm text-muted-foreground">{disclosure.purpose}</p>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Las mer i var{' '}
|
||||
<Link href="/privacy" className="underline underline-offset-4" target="_blank">
|
||||
integritetspolicy
|
||||
</Link>
|
||||
. Du kan nar som helst aterkalla ditt samtycke i installningarna.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isSubmitting}>
|
||||
Avbryt
|
||||
</Button>
|
||||
<Button onClick={handleAccept} disabled={isSubmitting}>
|
||||
{isSubmitting ? 'Sparar...' : 'Jag samtycker'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user