2026-03-20 16:39:52 +01:00
|
|
|
import { useState } from 'react'
|
|
|
|
|
import { useParams, useNavigate } from 'react-router-dom'
|
|
|
|
|
import { useRun } from '../hooks/useRuns'
|
|
|
|
|
import { useBossResults, useGameBosses } from '../hooks/useBosses'
|
2026-03-20 21:41:38 +01:00
|
|
|
import { useJournalEntry, useUpdateJournalEntry, useDeleteJournalEntry } from '../hooks/useJournal'
|
2026-03-20 16:39:52 +01:00
|
|
|
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>
|
|
|
|
|
)
|
|
|
|
|
}
|