Make verified repair acceptance prominent and simplify confirmation email
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { authFetch, getApiBase, clearToken } from '../../../lib/auth'
|
||||
import ResolutionChoice from '../../../ui/ResolutionChoice'
|
||||
|
||||
type Issue = { id: number; kind: string; title: string; status: string; permissions?: { can_confirm_resolution?: boolean } }
|
||||
|
||||
export default function ConfirmIssuePage() {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const router = useRouter()
|
||||
const [item, setItem] = useState<Issue | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [result, setResult] = useState('')
|
||||
const login = () => {
|
||||
clearToken()
|
||||
router.replace(`/login?next=${encodeURIComponent(`/issues/confirm/${id}`)}`)
|
||||
}
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
setLoading(true); setItem(null); setError(''); setResult('')
|
||||
const load = async () => {
|
||||
try {
|
||||
const response = await authFetch(`${getApiBase()}/portal/items/${id}`, { signal: controller.signal, cache: 'no-store' })
|
||||
if (response.status === 401) { login(); return }
|
||||
if (!response.ok) throw new Error('This issue is unavailable. Please sign in with the account that reported it.')
|
||||
const data = await response.json()
|
||||
if (data.item?.kind !== 'issue') throw new Error('This link does not belong to an issue.')
|
||||
setItem(data.item)
|
||||
} catch (err) { if (!controller.signal.aborted) setError(err instanceof Error ? err.message : 'Could not load this issue. Please try again.') }
|
||||
finally { if (!controller.signal.aborted) setLoading(false) }
|
||||
}
|
||||
void load()
|
||||
return () => controller.abort()
|
||||
// The confirmation link identifies one issue. Never submit an answer on GET.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id])
|
||||
|
||||
const answer = async (resolved: boolean) => {
|
||||
if (busy) return
|
||||
setBusy(true); setError('')
|
||||
try {
|
||||
const response = await authFetch(`${getApiBase()}/portal/issues/${id}/resolution-response`, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ resolved }),
|
||||
})
|
||||
if (response.status === 401) { login(); return }
|
||||
if (!response.ok) throw new Error('Your answer could not be saved. This issue may already have been answered. Refresh to check before trying again.')
|
||||
setResult(resolved ? 'Thanks! Your issue is now closed.' : 'Thanks for letting us know. Your issue stays open for another look.')
|
||||
} catch (err) { setError(err instanceof Error ? err.message : 'Could not save your answer. Please try again.') }
|
||||
finally { setBusy(false) }
|
||||
}
|
||||
return <main className="resolution-response-page">
|
||||
{error && <p role="alert" className="status-banner">{error}</p>}
|
||||
{loading ? <p role="status">Loading your issue…</p> : result ? <section className="resolution-choice" role="status"><h2>{result}</h2><a href="/portal/issues">Back to issues</a></section> : item ? (
|
||||
item.status === 'awaiting_confirmation' && item.permissions?.can_confirm_resolution
|
||||
? <ResolutionChoice title={item.title} busy={busy} onAnswer={(value) => void answer(value)} />
|
||||
: <section className="resolution-choice"><h2>{item.status === 'awaiting_confirmation' ? 'This question is for the person who reported the issue.' : 'No answer is needed right now.'}</h2><p>{item.title}</p><a href={`/portal/issues?item=${item.id}`}>View issue</a></section>
|
||||
) : null}
|
||||
</main>
|
||||
}
|
||||
Reference in New Issue
Block a user