progress bar + better ui
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import {
|
||||
deleteOwnUploadAction,
|
||||
extendOwnUploadAction,
|
||||
uploadFileAction,
|
||||
userLogoutAction,
|
||||
} from '@/src/lib/actions.js';
|
||||
import { all, runCleanupIfNeeded } from '@/src/lib/db.js';
|
||||
import { sharedLinkName } from '@/src/lib/files.js';
|
||||
import { formatBytes, formatCountdown, formatTimestamp, readSearchParam } from '@/src/lib/format.js';
|
||||
import { ensureCsrfToken, requireAuthenticatedUser } from '@/src/lib/security.js';
|
||||
|
||||
import { CopyLinkButton } from '../_components/copy-link-button.js';
|
||||
import { StatusMessage } from '../_components/status-message.js';
|
||||
import { UploadProgressForm } from '../_components/upload-progress-form.js';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
@@ -26,6 +27,7 @@ export default async function DashboardPage({ searchParams }) {
|
||||
const resolvedSearchParams = await searchParams;
|
||||
const error = readSearchParam(resolvedSearchParams, 'error');
|
||||
const success = readSearchParam(resolvedSearchParams, 'success');
|
||||
const totalBytes = uploads.reduce((total, item) => total + (Number(item.size_bytes) || 0), 0);
|
||||
|
||||
return (
|
||||
<main className="page-shell">
|
||||
@@ -51,28 +53,33 @@ export default async function DashboardPage({ searchParams }) {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Neue Datei hochladen</h2>
|
||||
<StatusMessage error={error} success={success} />
|
||||
<StatusMessage error={error} success={success} />
|
||||
|
||||
<form className="form-grid" action={uploadFileAction} encType="multipart/form-data">
|
||||
<input type="hidden" name="csrfToken" value={csrfToken} />
|
||||
<div className="dashboard-top-grid">
|
||||
<section className="panel panel-spotlight">
|
||||
<h2>Neue Datei hochladen</h2>
|
||||
<p className="muted">Der Fortschritt wird während des Uploads live angezeigt.</p>
|
||||
<UploadProgressForm csrfToken={csrfToken} />
|
||||
</section>
|
||||
|
||||
<label className="field">
|
||||
Datei
|
||||
<input className="input" name="file" type="file" required />
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
Aufbewahrung in Stunden
|
||||
<input className="input" name="retentionHours" placeholder="168" />
|
||||
</label>
|
||||
|
||||
<button className="btn" type="submit">
|
||||
Hochladen
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
<aside className="panel panel-soft">
|
||||
<h2>Schnellüberblick</h2>
|
||||
<div className="info-stack">
|
||||
<div className="info-card">
|
||||
<strong>{uploads.length}</strong>
|
||||
<span className="muted">aktive Uploads</span>
|
||||
</div>
|
||||
<div className="info-card">
|
||||
<strong>{totalBytes > 0 ? formatBytes(totalBytes) : '0 B'}</strong>
|
||||
<span className="muted">genutzter Speicher</span>
|
||||
</div>
|
||||
<div className="info-card">
|
||||
<strong>/_share/<id></strong>
|
||||
<span className="muted">Kurzlinks ohne Dateiendung</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Aktuelle Uploads</h2>
|
||||
@@ -92,7 +99,8 @@ export default async function DashboardPage({ searchParams }) {
|
||||
</thead>
|
||||
<tbody>
|
||||
{uploads.map((item) => {
|
||||
const sharePath = `/_share/${encodeURIComponent(item.stored_name)}`;
|
||||
const shareName = sharedLinkName(item.stored_name);
|
||||
const sharePath = `/_share/${encodeURIComponent(shareName)}`;
|
||||
return (
|
||||
<tr key={item.id}>
|
||||
<td>
|
||||
|
||||
Reference in New Issue
Block a user