0.12.0 Begin neu Architecture

This commit is contained in:
2026-03-18 15:35:10 +00:00
parent d00191324b
commit 0a9cf6969f
30 changed files with 4570 additions and 183 deletions
+27 -15
View File
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import { Toast } from 'primereact/toast';
import { Card } from 'primereact/card';
import { Button } from 'primereact/button';
@@ -20,10 +20,12 @@ import blurayIndicatorIcon from '../assets/media-bluray.svg';
import discIndicatorIcon from '../assets/media-disc.svg';
import otherIndicatorIcon from '../assets/media-other.svg';
import audiobookIndicatorIcon from '../assets/media-audiobook.svg';
import { resolveJobPluginExecution } from '../utils/pluginExecution';
import { getStatusLabel, getStatusSeverity, normalizeStatus } from '../utils/statusPresentation';
const processingStates = ['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'ENCODING', 'CD_ANALYZING', 'CD_RIPPING', 'CD_ENCODING'];
const driveActiveStates = ['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'CD_ANALYZING', 'CD_RIPPING'];
const activeCdDriveStates = ['CD_ANALYZING', 'CD_METADATA_SELECTION', 'CD_READY_TO_RIP', 'CD_RIPPING', 'CD_ENCODING'];
const dashboardStatuses = new Set([
'ANALYZING',
'METADATA_SELECTION',
@@ -822,8 +824,10 @@ export default function DashboardPage({
jobsRefreshToken,
pendingExpandedJobId,
onPendingExpandedJobHandled,
downloadSummary = null
downloadSummary = null,
children = null
}) {
const location = useLocation();
const navigate = useNavigate();
const [busy, setBusy] = useState(false);
const [busyJobIds, setBusyJobIds] = useState(() => new Set());
@@ -955,6 +959,7 @@ export default function DashboardPage({
: audiobookUploadPhase === 'error'
? 'Fehler'
: 'Inaktiv';
const isSubpageRoute = location.pathname !== '/';
const loadDashboardJobs = async () => {
setJobsLoading(true);
@@ -1335,19 +1340,12 @@ export default function DashboardPage({
}
};
const refreshKnownDrives = () => {
api.getDetectedDrives()
.then((res) => setKnownDrives(Array.isArray(res?.drives) ? res.drives : []))
.catch(() => {});
};
const handleRescan = async () => {
setBusy(true);
try {
const response = await api.rescanDisc();
refreshKnownDrives();
const allDetected = response?.result?.allDetected || [];
const emitted = response?.result?.emitted || 'none';
const allDetected = Array.isArray(response?.result?.allDetected) ? response.result.allDetected : [];
const count = allDetected.length;
toastRef.current?.show({
severity: count > 0 ? 'success' : 'info',
@@ -1357,7 +1355,6 @@ export default function DashboardPage({
: 'Kein Medium erkannt.',
life: 2800
});
void emitted; // used implicitly via count
await refreshPipeline();
await loadDashboardJobs();
} catch (error) {
@@ -1367,6 +1364,12 @@ export default function DashboardPage({
}
};
const refreshKnownDrives = () => {
api.getDetectedDrives()
.then((res) => setKnownDrives(Array.isArray(res?.drives) ? res.drives : []))
.catch(() => {});
};
const handleRescanDrive = async (devicePath) => {
setDriveRescanBusy((prev) => new Set([...prev, devicePath]));
try {
@@ -2029,7 +2032,6 @@ export default function DashboardPage({
const lastNonCdDiscEvent = lastDiscEvent?.mediaProfile !== 'cd' ? lastDiscEvent : null;
const contextDevice = pipeline?.context?.device;
const device = lastNonCdDiscEvent || (contextDevice?.mediaProfile !== 'cd' ? contextDevice : null);
const cdDriveEntries = Object.entries(pipeline?.cdDrives || {});
// Unified list of all known drives: merge lsblk drives + CD state + non-CD pipeline state
const allDrives = useMemo(() => {
@@ -2360,6 +2362,7 @@ export default function DashboardPage({
const driveJobId = normalizeJobId(cdDrive?.jobId);
const driveCtx = cdDrive?.context && typeof cdDrive.context === 'object' ? cdDrive.context : {};
const cdState = cdDrive ? String(cdDrive.state || '').toUpperCase() : null;
const isCdDriveLocked = cdState ? activeCdDriveStates.includes(cdState) : false;
return (
<div key={drivePath} className="drive-list-item">
@@ -2380,7 +2383,7 @@ export default function DashboardPage({
size="small"
severity="secondary"
loading={isRescanBusy}
disabled={isRescanBusy || isDriveActive}
disabled={isRescanBusy || isDriveActive || isCdDriveLocked}
onClick={() => handleRescanDrive(drivePath)}
title="Laufwerk neu lesen"
aria-label="Laufwerk neu lesen"
@@ -2481,7 +2484,12 @@ export default function DashboardPage({
</div>
<div className="dashboard-col dashboard-col-center">
<Card title="Job Übersicht" subTitle="Kompakte Liste; Klick auf Zeile öffnet die volle Job-Detailansicht mit passenden CTAs">
{isSubpageRoute ? (
<div className="dashboard-subpage-content">
{children}
</div>
) : (
<Card title="Job Übersicht" subTitle="Kompakte Liste; Klick auf Zeile öffnet die volle Job-Detailansicht mit passenden CTAs">
{jobsLoading ? (
<p>Jobs werden geladen ...</p>
) : dashboardJobs.length === 0 ? (
@@ -2530,6 +2538,7 @@ export default function DashboardPage({
? pipelineForJob.context.selectedMetadata
: {};
const audiobookChapterCount = Array.isArray(audiobookMeta?.chapters) ? audiobookMeta.chapters.length : 0;
const pluginExecution = resolveJobPluginExecution(job, pipelineForJob?.context);
if (isExpanded) {
return (
@@ -2552,6 +2561,7 @@ export default function DashboardPage({
</strong>
<div className="dashboard-job-badges">
<Tag value={statusBadgeValue} severity={statusBadgeSeverity} />
{pluginExecution ? <Tag value={pluginExecution.label} severity="warning" title={pluginExecution.title} /> : null}
{isCurrentSession ? <Tag value="Aktive Session" severity="info" /> : null}
{isResumable ? <Tag value="Fortsetzbar" severity="success" /> : null}
{normalizedStatus === 'READY_TO_ENCODE'
@@ -2681,6 +2691,7 @@ export default function DashboardPage({
</div>
<div className="dashboard-job-badges">
<Tag value={statusBadgeValue} severity={statusBadgeSeverity} />
{pluginExecution ? <Tag value={pluginExecution.label} severity="warning" title={pluginExecution.title} /> : null}
{isCurrentSession ? <Tag value="Aktive Session" severity="info" /> : null}
{isResumable ? <Tag value="Fortsetzbar" severity="success" /> : null}
{normalizedStatus === 'READY_TO_ENCODE'
@@ -2700,6 +2711,7 @@ export default function DashboardPage({
</div>
)}
</Card>
)}
</div>
<div className="dashboard-col dashboard-col-right">
@@ -3051,7 +3063,7 @@ export default function DashboardPage({
</div>
</div>
{monitoringState.enabled ? (
{!isSubpageRoute && monitoringState.enabled ? (
<Card title="Hardware Monitoring">
<div className="hardware-monitor-head">
{!monitoringState.enabled ? (