This commit is contained in:
Timo Knuth
2026-02-27 15:19:24 +01:00
parent b7f8221095
commit 253c3c1c6d
134 changed files with 11188 additions and 1871 deletions

View File

@@ -0,0 +1,142 @@
'use client'
import { trpc } from '@/lib/trpc-client'
import { useState } from 'react'
export default function EinstellungenPage() {
const { data: org, isLoading } = trpc.organizations.me.useQuery()
const updateMutation = trpc.organizations.update.useMutation()
const avvMutation = trpc.organizations.acceptAvv.useMutation()
const [name, setName] = useState('')
const [contactEmail, setContactEmail] = useState('')
if (isLoading) return <div className="text-gray-500">Wird geladen...</div>
if (!org) return null
return (
<div className="max-w-2xl space-y-8">
<h1 className="text-2xl font-bold text-gray-900">Einstellungen</h1>
{/* Org Settings */}
<div className="bg-white rounded-lg border p-6 space-y-4">
<h2 className="font-semibold text-gray-900">Innung</h2>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Name der Innung</label>
<input
defaultValue={org.name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Kontakt-E-Mail</label>
<input
type="email"
defaultValue={org.contactEmail ?? ''}
onChange={(e) => setContactEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:border-transparent"
/>
</div>
<button
onClick={() => updateMutation.mutate({ name: name || undefined, contactEmail: contactEmail || undefined })}
disabled={updateMutation.isPending}
className="bg-brand-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-brand-600 disabled:opacity-60 transition-colors"
>
{updateMutation.isPending ? 'Wird gespeichert...' : 'Speichern'}
</button>
{updateMutation.isSuccess && (
<p className="text-sm text-green-600">Einstellungen gespeichert </p>
)}
</div>
{/* AVV */}
<div className="bg-white rounded-lg border p-6 space-y-4">
<h2 className="font-semibold text-gray-900">Auftragsverarbeitungsvertrag (AVV)</h2>
<p className="text-sm text-gray-600">
Der AVV regelt die Verarbeitung personenbezogener Daten im Auftrag Ihrer Innung
durch InnungsApp GmbH gemäß Art. 28 DSGVO.
</p>
<a
href="/avv.pdf"
download
className="inline-flex items-center gap-2 text-sm text-brand-600 hover:underline"
>
📄 AVV als PDF herunterladen
</a>
{org.avvAccepted ? (
<div className="bg-green-50 border border-green-200 rounded-lg p-4">
<p className="text-sm text-green-700 font-medium">
AVV akzeptiert am {org.avvAcceptedAt?.toLocaleDateString('de-DE')}
</p>
</div>
) : (
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 space-y-3">
<p className="text-sm text-yellow-800 font-medium">
Der AVV muss vor dem Go-Live akzeptiert werden.
</p>
<label className="flex items-start gap-2 cursor-pointer">
<input
type="checkbox"
id="avv-check"
className="mt-0.5 rounded border-gray-300"
/>
<span className="text-sm text-gray-700">
Ich bestätige, dass ich den AVV gelesen habe und im Namen der Innung akzeptiere.
</span>
</label>
<button
onClick={() => {
const cb = document.getElementById('avv-check') as HTMLInputElement
if (!cb.checked) { alert('Bitte bestätigen Sie den AVV.'); return }
avvMutation.mutate()
}}
disabled={avvMutation.isPending}
className="bg-brand-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-brand-600 disabled:opacity-60 transition-colors"
>
AVV verbindlich akzeptieren
</button>
</div>
)}
</div>
{/* Registrierungslink */}
<div className="bg-white rounded-lg border p-6 space-y-4">
<h2 className="font-semibold text-gray-900">Registrierungslink</h2>
<p className="text-sm text-gray-600">
Teilen Sie diesen Link mit neuen Mitgliedern. Sie können sich damit selbst registrieren
und erhalten einen Aktivierungslink per E-Mail.
</p>
<div className="flex gap-2">
<input
readOnly
value={`${typeof window !== 'undefined' ? window.location.origin : ''}/registrierung/${org.slug}`}
className="flex-1 px-3 py-2 border border-gray-200 rounded-lg text-sm bg-gray-50 text-gray-700 focus:outline-none"
/>
<button
type="button"
onClick={() =>
navigator.clipboard.writeText(
`${window.location.origin}/registrierung/${org.slug}`
)
}
className="px-4 py-2 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors whitespace-nowrap"
>
Kopieren
</button>
</div>
</div>
{/* Plan Info */}
<div className="bg-white rounded-lg border p-6">
<h2 className="font-semibold text-gray-900 mb-2">Plan</h2>
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-brand-100 text-brand-700 capitalize">
{org.plan}
</span>
<p className="text-sm text-gray-500 mt-2">
Für Upgrades oder Fragen zum Plan: kontakt@innungsapp.de
</p>
</div>
</div>
)
}