add Ko-fi bean
Some checks failed
CI / frontend-tests (push) Has been cancelled
CI / backend-tests (push) Has been cancelled

This commit is contained in:
Julian Tabel
2026-03-20 16:39:52 +01:00
parent 6c36cbfe12
commit 088cd35002
13 changed files with 733 additions and 4 deletions

View File

@@ -0,0 +1,101 @@
import { useState } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { useRun } from '../hooks/useRuns'
import { useBossResults, useGameBosses } from '../hooks/useBosses'
import {
useJournalEntry,
useUpdateJournalEntry,
useDeleteJournalEntry,
} from '../hooks/useJournal'
import { JournalEntryView } from '../components/journal/JournalEntryView'
import { JournalEditor } from '../components/journal/JournalEditor'
export function JournalEntryPage() {
const { runId, entryId } = useParams<{ runId: string; entryId: string }>()
const navigate = useNavigate()
const runIdNum = Number(runId)
const [isEditing, setIsEditing] = useState(false)
const { data: run, isLoading: runLoading } = useRun(runIdNum)
const { data: entry, isLoading: entryLoading, error } = useJournalEntry(runIdNum, entryId ?? null)
const { data: bossResults } = useBossResults(runIdNum)
const { data: bosses } = useGameBosses(run?.gameId ?? null)
const updateEntry = useUpdateJournalEntry(runIdNum, entryId ?? '')
const deleteEntry = useDeleteJournalEntry(runIdNum)
const isLoading = runLoading || entryLoading
if (isLoading) {
return (
<div className="max-w-3xl mx-auto p-8 flex justify-center">
<div className="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin" />
</div>
)
}
if (error || !entry || !run) {
return (
<div className="max-w-3xl mx-auto p-8">
<div className="rounded-lg bg-status-failed-bg p-4 text-status-failed">
Failed to load journal entry.
</div>
<button
type="button"
onClick={() => navigate(`/runs/${runId}`)}
className="inline-block mt-4 text-blue-600 hover:underline"
>
Back to run
</button>
</div>
)
}
const linkedBossResult = entry.bossResultId
? bossResults?.find((br) => br.id === entry.bossResultId)
: null
const linkedBoss = linkedBossResult
? bosses?.find((b) => b.id === linkedBossResult.bossBattleId)
: null
const handleSave = (data: { title: string; body: string; bossResultId: number | null }) => {
updateEntry.mutate(data, {
onSuccess: () => setIsEditing(false),
})
}
const handleDelete = () => {
deleteEntry.mutate(entry.id, {
onSuccess: () => navigate(`/runs/${runId}?tab=journal`),
})
}
if (isEditing) {
return (
<div className="max-w-3xl mx-auto p-8">
<h1 className="text-2xl font-bold text-text-primary mb-6">Edit Journal Entry</h1>
<JournalEditor
entry={entry}
bossResults={bossResults}
bosses={bosses}
onSave={handleSave}
onDelete={handleDelete}
onCancel={() => setIsEditing(false)}
isSaving={updateEntry.isPending}
isDeleting={deleteEntry.isPending}
/>
</div>
)
}
return (
<div className="max-w-3xl mx-auto p-8">
<JournalEntryView
entry={entry}
bossResult={linkedBossResult}
boss={linkedBoss}
onEdit={() => setIsEditing(true)}
onBack={() => navigate(`/runs/${runId}?tab=journal`)}
/>
</div>
)
}