Rechtliches: Daten-Hook fuer Rechtstexte und Anbieter-Vertraege

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-30 10:19:04 +00:00
1 parent 1212173cb4
commit 31be2af4c3
1 file changed
+167
+167
View File
@@ -0,0 +1,167 @@
import { useState, useEffect, useCallback } from 'react'
import {
COLLECTIONS,
DATABASE_ID,
LEGAL_BUCKET_ID,
ID,
Query,
databases,
storage,
} from '../lib/appwrite'
import { planNewVersion } from '../lib/legal'
const isMissing = (err) => err?.code === 404 || /not found/i.test(err?.message || '')
async function listAll(collectionId, queries = []) {
const res = await databases.listDocuments(DATABASE_ID, collectionId, [...queries, Query.limit(5000)])
return res.documents || []
}
/** Laedt die Dateinamen des Buckets, damit Listen Namen statt IDs zeigen. */
async function listFileNames() {
try {
const res = await storage.listFiles(LEGAL_BUCKET_ID, [Query.limit(5000)])
return Object.fromEntries((res.files || []).map((f) => [f.$id, f.name]))
} catch {
return {}
}
}
async function uploadFiles(files = []) {
const ids = []
for (const file of files) {
const created = await storage.createFile(LEGAL_BUCKET_ID, ID.unique(), file)
ids.push(created.$id)
}
return ids
}
export function fileUrl(fileId) {
return storage.getFileView(LEGAL_BUCKET_ID, fileId).toString()
}
export function fileDownloadUrl(fileId) {
return storage.getFileDownload(LEGAL_BUCKET_ID, fileId).toString()
}
function useLoader(load) {
const [state, setState] = useState({ loading: true, error: null, missingSetup: false })
const [data, setData] = useState(null)
const refresh = useCallback(async () => {
try {
setData(await load())
setState({ loading: false, error: null, missingSetup: false })
} catch (err) {
console.error('Rechtliches laden:', err)
setData(null)
setState({ loading: false, error: isMissing(err) ? null : err.message, missingSetup: isMissing(err) })
}
}, [load])
useEffect(() => {
refresh()
}, [refresh])
return { data, ...state, setError: (error) => setState((s) => ({ ...s, error })), refresh }
}
const loadTexts = async () => {
const [documents, versions, fileNames] = await Promise.all([
listAll(COLLECTIONS.LEGAL_DOCUMENTS, [Query.orderAsc('sort')]),
listAll(COLLECTIONS.LEGAL_VERSIONS),
listFileNames(),
])
return { documents, versions, fileNames }
}
export function useLegalDocuments() {
const { data, loading, error, missingSetup, setError, refresh } = useLoader(loadTexts)
const versions = data?.versions || []
// Neue Version anlegen; eine juengere gueltige Version loest die bisherige ab.
const addVersion = async (documentKey, input, files) => {
const fileIds = await uploadFiles(files)
const plan = planNewVersion(versions, { ...input, documentKey })
await databases.createDocument(DATABASE_ID, COLLECTIONS.LEGAL_VERSIONS, ID.unique(), {
...plan.create,
fileIds,
})
await applyUpdates(plan.updates, 'Neue Version gespeichert')
}
// Entwurf gueltig machen: dieselbe Abloese-Regel wie beim Anlegen.
const promoteDraft = async (version) => {
const plan = planNewVersion(versions, { ...version, status: 'current' })
const { status, validTo } = plan.create
await databases.updateDocument(DATABASE_ID, COLLECTIONS.LEGAL_VERSIONS, version.$id, { status, validTo })
await applyUpdates(plan.updates, 'Version ist gültig')
}
const applyUpdates = async (updates, done) => {
try {
for (const u of updates) {
await databases.updateDocument(DATABASE_ID, COLLECTIONS.LEGAL_VERSIONS, u.id, u.patch)
}
await refresh()
} catch (err) {
await refresh()
setError(`${done}, alte Version konnte nicht abgelöst werden: ${err.message}`)
}
}
const updateVersion = async (id, patch) => {
await databases.updateDocument(DATABASE_ID, COLLECTIONS.LEGAL_VERSIONS, id, patch)
await refresh()
}
return {
documents: data?.documents || [],
versions,
fileNames: data?.fileNames || {},
loading,
error,
missingSetup,
addVersion,
promoteDraft,
updateVersion,
refresh,
}
}
const loadContracts = async () => {
const [contracts, fileNames] = await Promise.all([
listAll(COLLECTIONS.PROVIDER_CONTRACTS, [Query.orderAsc('provider')]),
listFileNames(),
])
return { contracts, fileNames }
}
export function useProviderContracts() {
const { data, loading, error, missingSetup, refresh } = useLoader(loadContracts)
const saveContract = async (values, files, id) => {
const newIds = await uploadFiles(files)
const existing = id ? data?.contracts.find((c) => c.$id === id)?.fileIds || [] : []
const doc = { ...values, fileIds: [...existing, ...newIds] }
if (id) await databases.updateDocument(DATABASE_ID, COLLECTIONS.PROVIDER_CONTRACTS, id, doc)
else await databases.createDocument(DATABASE_ID, COLLECTIONS.PROVIDER_CONTRACTS, ID.unique(), doc)
await refresh()
}
const deleteContract = async (id) => {
await databases.deleteDocument(DATABASE_ID, COLLECTIONS.PROVIDER_CONTRACTS, id)
await refresh()
}
return {
contracts: data?.contracts || [],
fileNames: data?.fileNames || {},
loading,
error,
missingSetup,
saveContract,
deleteContract,
refresh,
}
}