86fbe8f396
- Liste als echte Dateitabelle und Raster mit Vorschaubildern; Typkacheln je Dateifamilie (OKLCH-Tokens, Kontrast hell und dunkel per Test geprüft), Ordner im gedämpften Mosaik-Gelb - Ordnerpfad mit Ablagezielen, Auswahlleiste (klebt beim Scrollen), Tastatur (Enter, Rücktaste/Alt+Pfeil hoch, Entf, F2, Strg+A, Escape), Zeilen- und Rechtsklickmenü - Dialoge für neuen Ordner, Umbenennen, Verschieben (eigene Ordnerwahl) und Löschen in den Papierkorb - Ziehen und Ablegen mit Streifen-Markierung und Ordnerzeilen als Ziel, auch ganze Ordner; Übertragungsleiste mit Fortschritt, Abbrechen, Klartextfehlern und Rückfrage bei vergebenem Namen - Speicheranzeige zählt im Unterordner den Speicher des ganzen Kontos; Ansicht je Benutzer gemerkt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
110 lines
4.2 KiB
TypeScript
110 lines
4.2 KiB
TypeScript
'use client';
|
|
|
|
import { useTranslations } from 'next-intl';
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
import { TabBar } from '@/components/accounting/tab-bar';
|
|
import { SettingsSection } from '@/components/control-center/settings-section';
|
|
import { PageHeader } from '@/components/layout/page-header';
|
|
import { getNextcloudFilesStatus, type NextcloudFilesStatus } from '@/lib/nextcloud-files-api';
|
|
import { useCanManageModule } from '@/lib/use-module-capability';
|
|
import { AccountBar } from './components/AccountBar';
|
|
import { ConnectPanel } from './components/ConnectPanel';
|
|
import { FileBrowser } from './components/FileBrowser';
|
|
import { SettingsTab } from './components/SettingsTab';
|
|
|
|
type TabId = 'files' | 'settings';
|
|
|
|
/**
|
|
* Dateien (quick-261008-mzu): die Nextcloud-Dateien jedes Benutzers in Tessera.
|
|
* Die Adresse der Nextcloud stellen Administratoren und Benutzer mit der
|
|
* Freigabestufe Verwalten im Reiter "Einstellungen" ein; alle anderen sehen nur
|
|
* den Reiter "Dateien" ohne Reiterleiste — bindend ist allein die API. Im
|
|
* Abschnitt `nextcloud-files-main` verbindet jeder Benutzer sein eigenes Konto
|
|
* (ConnectPanel); ist es verbunden, steht dort die Dateiansicht
|
|
* (`nextcloud-files-browser`, FileBrowser). Laeuft die Verbindung ab, laedt die
|
|
* Seite den Stand neu und zeigt wieder den Anmeldebildschirm mit Hinweis.
|
|
*/
|
|
export default function NextcloudFilesPage() {
|
|
const t = useTranslations('nextcloudFiles');
|
|
const canManage = useCanManageModule('nextcloud-files') === true;
|
|
|
|
const [status, setStatus] = useState<NextcloudFilesStatus | null>(null);
|
|
const [statusError, setStatusError] = useState(false);
|
|
const [tab, setTab] = useState<TabId>('files');
|
|
|
|
const reloadStatus = useCallback(async () => {
|
|
try {
|
|
setStatus(await getNextcloudFilesStatus());
|
|
setStatusError(false);
|
|
} catch {
|
|
setStatusError(true);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
void reloadStatus();
|
|
}, [reloadStatus]);
|
|
|
|
const account = status?.account ?? null;
|
|
const connected = account?.connected === true;
|
|
|
|
const tabs: { id: TabId; label: string }[] = [{ id: 'files', label: t('tabs.files') }];
|
|
if (canManage) tabs.push({ id: 'settings', label: t('tabs.settings') });
|
|
const activeTab: TabId = tabs.some((x) => x.id === tab) ? tab : 'files';
|
|
|
|
return (
|
|
<div className="mx-auto max-w-6xl space-y-6 p-3 sm:p-6">
|
|
<PageHeader
|
|
moduleSlug="nextcloud-files"
|
|
title={t('title')}
|
|
description={t('description')}
|
|
actions={
|
|
activeTab === 'files' && status?.configured && account && connected ? (
|
|
<AccountBar account={account} host={status.host} onDisconnected={reloadStatus} />
|
|
) : undefined
|
|
}
|
|
/>
|
|
{statusError && (
|
|
<p role="alert" className="text-sm text-destructive">
|
|
{t('errors.loadStatus')}
|
|
</p>
|
|
)}
|
|
{canManage && <TabBar tabs={tabs} active={activeTab} onChange={setTab} />}
|
|
{activeTab === 'files' && status && !status.configured && (
|
|
<SettingsSection title={t('notConfigured.title')}>
|
|
<p className="text-sm text-muted-foreground">
|
|
{canManage ? t('notConfigured.managerHint') : t('notConfigured.userHint')}
|
|
</p>
|
|
{canManage && (
|
|
<button
|
|
type="button"
|
|
onClick={() => setTab('settings')}
|
|
className="mt-3 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:opacity-90"
|
|
>
|
|
{t('notConfigured.goToSettings')}
|
|
</button>
|
|
)}
|
|
</SettingsSection>
|
|
)}
|
|
{activeTab === 'files' && status?.configured && (
|
|
<section data-testid="nextcloud-files-main" className="space-y-4">
|
|
{connected ? (
|
|
<div data-testid="nextcloud-files-browser">
|
|
<FileBrowser serverUrl={status.serverUrl} onExpired={() => void reloadStatus()} />
|
|
</div>
|
|
) : (
|
|
<ConnectPanel
|
|
host={status.host}
|
|
expired={account?.expired === true}
|
|
onConnected={reloadStatus}
|
|
/>
|
|
)}
|
|
</section>
|
|
)}
|
|
{activeTab === 'settings' && canManage && (
|
|
<SettingsTab onChanged={() => void reloadStatus()} />
|
|
)}
|
|
</div>
|
|
);
|
|
}
|