From 447b7e733e6ec8bfae85eb89617be4154c4d1b60 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 22:48:21 +0000 Subject: [PATCH] Preview: Dev-Modus nur mit dev-Skript waehlbar Co-Authored-By: Claude Sonnet 5.5 --- src/components/PreviewBlock.jsx | 7 ++++--- src/lib/previewView.js | 13 +++++++++++++ tests/unit/previewView.test.mjs | 20 +++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/components/PreviewBlock.jsx b/src/components/PreviewBlock.jsx index f3a7f9f..06498a2 100644 --- a/src/components/PreviewBlock.jsx +++ b/src/components/PreviewBlock.jsx @@ -1,5 +1,5 @@ import { useState, useEffect, useCallback, useRef } from 'react' -import { MODE_OPTIONS, previewStatus, modeHint } from '../lib/previewView' +import { modeOptionsFor, previewStatus, modeHint, isDevUnavailableError, NO_DEV_HINT } from '../lib/previewView' import { isValidEnvName, buildEnvPayload, isDevPreview, dotColor, envValueTooLong, MAX_ENV_VARS } from '../lib/previewEnv' import { fetchPreview, @@ -198,15 +198,16 @@ export default function PreviewBlock({ projectId }) { disabled={busy || inactive} onChange={(e) => run(() => setPreviewMode(projectId, e.target.value))} > - {MODE_OPTIONS.map((o) => )} + {modeOptionsFor(data.devAvailable).map((o) => )} + {data.devAvailable === false &&
{NO_DEV_HINT}
} {hint &&
{hint}
}
{status.text}
- {data.state === 'failed' && data.error && ( + {data.state === 'failed' && data.error && !isDevUnavailableError(data.error) && (
{data.error}
)} diff --git a/src/lib/previewView.js b/src/lib/previewView.js index 13a25ed..8219d28 100644 --- a/src/lib/previewView.js +++ b/src/lib/previewView.js @@ -7,6 +7,18 @@ export const MODE_OPTIONS = [ { value: 'off', label: 'Aus' }, ] +export const NO_DEV_HINT = 'Reines HTML braucht keinen Dev-Server – Änderungen erscheinen nach jedem Push automatisch.' +const DEV_UNAVAILABLE = 'Dev-Modus hier nicht möglich' + +// Modus-Optionen; "Dev-Server" nur wählbar, wenn das Projekt ein dev-Skript hat (devAvailable !== false) +export function modeOptionsFor(devAvailable) { + return MODE_OPTIONS.map((o) => (o.value === 'dev' && devAvailable === false + ? { ...o, label: 'Dev-Server (kein dev-Skript)', disabled: true } + : { ...o, disabled: false })) +} + +export const isDevUnavailableError = (error) => typeof error === 'string' && error.startsWith(DEV_UNAVAILABLE) + const dateFormat = new Intl.DateTimeFormat('de-DE', { timeZone: 'Europe/Berlin', day: '2-digit', @@ -40,6 +52,7 @@ export function previewStatus(p) { case 'sleeping': return { color: 'yellow', text: 'Dev-Server schläft, startet beim nächsten Aufruf' } case 'failed': + if (isDevUnavailableError(p.error)) return { color: 'yellow', text: 'Dev-Modus hier nicht möglich – statische Version ist online' } return { color: 'red', text: 'Build fehlgeschlagen – letzte funktionierende Version ist online' } default: return { color: 'grey', text: 'Keine Preview' } diff --git a/tests/unit/previewView.test.mjs b/tests/unit/previewView.test.mjs index c6ede46..d586709 100644 --- a/tests/unit/previewView.test.mjs +++ b/tests/unit/previewView.test.mjs @@ -2,7 +2,7 @@ import test from 'node:test' import assert from 'node:assert/strict' import { loadModule } from './load.mjs' -const { previewStatus, detectedLabel, modeHint, MODE_OPTIONS } = await loadModule('src/lib/previewView.js') +const { previewStatus, detectedLabel, modeHint, MODE_OPTIONS, modeOptionsFor, isDevUnavailableError } = await loadModule('src/lib/previewView.js') test('previewStatus online: Datum in Europe/Berlin und sha7', () => { const s = previewStatus({ state: 'online', builtAt: '2026-09-30T13:05:00Z', builtSha: 'abcdef1234567890' }) @@ -61,3 +61,21 @@ test('MODE_OPTIONS', () => { { value: 'off', label: 'Aus' }, ]) }) + +test('previewStatus failed + Dev-Modus nicht möglich => gelb, kein roter Build-Fehler', () => { + const s = previewStatus({ state: 'failed', error: 'Dev-Modus hier nicht möglich – statische Version ist online' }) + assert.deepEqual(s, { color: 'yellow', text: 'Dev-Modus hier nicht möglich – statische Version ist online' }) + assert.equal(previewStatus({ state: 'failed', error: 'Build kaputt' }).color, 'red') + assert.equal(isDevUnavailableError('Dev-Modus hier nicht möglich – x'), true) + assert.equal(isDevUnavailableError(''), false) +}) + +test('modeOptionsFor: dev nur bei devAvailable === false gesperrt', () => { + for (const a of [true, null, undefined]) { + assert.ok(modeOptionsFor(a).every((o) => !o.disabled)) + assert.equal(modeOptionsFor(a).find((o) => o.value === 'dev').label, 'Dev-Server') + } + const o = modeOptionsFor(false) + assert.deepEqual(o.find((x) => x.value === 'dev'), { value: 'dev', label: 'Dev-Server (kein dev-Skript)', disabled: true }) + assert.equal(o.filter((x) => x.disabled).length, 1) +})