added requesting option
This commit is contained in:
146
nextjs/app/%5Frequest/[id]/page.js
Normal file
146
nextjs/app/%5Frequest/[id]/page.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import { get, runCleanupIfNeeded } from '@/src/lib/db.js';
|
||||
import { formatTimestamp, readSearchParam } from '@/src/lib/format.js';
|
||||
|
||||
import { StatusMessage } from '@/app/manage/_components/status-message.js';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
function normalizeRequestId(value) {
|
||||
return String(value || '').trim().toUpperCase();
|
||||
}
|
||||
|
||||
function isValidRequestId(value) {
|
||||
return /^[A-Z2-7]{6,24}$/.test(value);
|
||||
}
|
||||
|
||||
function requestState(requestEntry, now) {
|
||||
if (!requestEntry) {
|
||||
return 'missing';
|
||||
}
|
||||
if (Number(requestEntry.completed_at || 0) > 0) {
|
||||
return 'completed';
|
||||
}
|
||||
if (Number(requestEntry.expires_at || 0) <= now) {
|
||||
return 'expired';
|
||||
}
|
||||
return 'open';
|
||||
}
|
||||
|
||||
export default async function UploadRequestPage({ params, searchParams }) {
|
||||
await runCleanupIfNeeded();
|
||||
|
||||
const resolvedParams = await params;
|
||||
const requestId = normalizeRequestId(resolvedParams.id);
|
||||
|
||||
if (!isValidRequestId(requestId)) {
|
||||
return (
|
||||
<main className="page-shell narrow">
|
||||
<section className="panel centered">
|
||||
<h1>Ungültige Anfrage</h1>
|
||||
<p className="muted">Die Upload-Anfrage konnte nicht verarbeitet werden.</p>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
const requestEntry = await get(
|
||||
`SELECT id, note, created_at, expires_at, completed_at, uploaded_original_name
|
||||
FROM upload_requests
|
||||
WHERE id = ?`,
|
||||
[requestId]
|
||||
);
|
||||
|
||||
const now = Date.now();
|
||||
const state = requestState(requestEntry, now);
|
||||
|
||||
const resolvedSearchParams = await searchParams;
|
||||
const error = readSearchParam(resolvedSearchParams, 'error');
|
||||
const success = readSearchParam(resolvedSearchParams, 'success');
|
||||
|
||||
if (state === 'missing') {
|
||||
return (
|
||||
<main className="page-shell narrow">
|
||||
<StatusMessage error={error} success={success} />
|
||||
<section className="panel centered">
|
||||
<h1>Anfrage nicht gefunden</h1>
|
||||
<p className="muted">Diese Upload-Anfrage existiert nicht oder wurde entfernt.</p>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="page-shell narrow">
|
||||
<header className="page-header">
|
||||
<div className="header-main">
|
||||
<h1>Datei-Anfrage</h1>
|
||||
<p className="muted">Anfrage-ID: {requestEntry.id}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<StatusMessage error={error} success={success} />
|
||||
|
||||
<section className="panel">
|
||||
<div className="info-stack">
|
||||
<div className="info-card">
|
||||
<strong>Status</strong>
|
||||
<span className="muted">
|
||||
{state === 'open' ? 'Offen' : state === 'completed' ? 'Bereits abgeschlossen' : 'Abgelaufen'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="info-card">
|
||||
<strong>Erstellt</strong>
|
||||
<span className="muted">{formatTimestamp(requestEntry.created_at)}</span>
|
||||
</div>
|
||||
<div className="info-card">
|
||||
<strong>Gültig bis</strong>
|
||||
<span className="muted">{formatTimestamp(requestEntry.expires_at)}</span>
|
||||
</div>
|
||||
{requestEntry.note ? (
|
||||
<div className="info-card">
|
||||
<strong>Notiz</strong>
|
||||
<span className="muted">{requestEntry.note}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{state === 'open' ? (
|
||||
<section className="panel panel-spotlight">
|
||||
<h2>Datei hochladen</h2>
|
||||
<form
|
||||
className="form-grid"
|
||||
method="post"
|
||||
action={`/_request/${encodeURIComponent(requestEntry.id)}/upload`}
|
||||
encType="multipart/form-data"
|
||||
>
|
||||
<label className="field">
|
||||
Datei
|
||||
<input className="input" type="file" name="file" required />
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
Dein Name (optional)
|
||||
<input className="input" name="fulfilledBy" placeholder="z. B. Max Mustermann" />
|
||||
</label>
|
||||
|
||||
<button className="btn" type="submit">
|
||||
Datei senden
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{state === 'completed' ? (
|
||||
<section className="panel centered">
|
||||
<h2>Vielen Dank</h2>
|
||||
<p className="muted">
|
||||
{requestEntry.uploaded_original_name
|
||||
? `Diese Anfrage wurde bereits mit „${requestEntry.uploaded_original_name}“ abgeschlossen.`
|
||||
: 'Diese Anfrage wurde bereits abgeschlossen.'}
|
||||
</p>
|
||||
</section>
|
||||
) : null}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user