Preview: Dev-Modus nur mit dev-Skript waehlbar
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c1818fadcf
commit
447b7e733e
3 files changed
+36
-4
No files matched your search
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
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 { isValidEnvName, buildEnvPayload, isDevPreview, dotColor, envValueTooLong, MAX_ENV_VARS } from '../lib/previewEnv'
|
||||||
import {
|
import {
|
||||||
fetchPreview,
|
fetchPreview,
|
||||||
@@ -198,15 +198,16 @@ export default function PreviewBlock({ projectId }) {
|
|||||||
disabled={busy || inactive}
|
disabled={busy || inactive}
|
||||||
onChange={(e) => run(() => setPreviewMode(projectId, e.target.value))}
|
onChange={(e) => run(() => setPreviewMode(projectId, e.target.value))}
|
||||||
>
|
>
|
||||||
{MODE_OPTIONS.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
|
{modeOptionsFor(data.devAvailable).map((o) => <option key={o.value} value={o.value} disabled={o.disabled}>{o.label}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
{data.devAvailable === false && <div className="text-grey" style={{ ...small, marginTop: 4 }}>{NO_DEV_HINT}</div>}
|
||||||
{hint && <div className="text-grey" style={{ ...small, marginTop: 4 }}>{hint}</div>}
|
{hint && <div className="text-grey" style={{ ...small, marginTop: 4 }}>{hint}</div>}
|
||||||
<div style={{ marginTop: 8, display: 'flex', gap: 6, alignItems: 'flex-start' }}>
|
<div style={{ marginTop: 8, display: 'flex', gap: 6, alignItems: 'flex-start' }}>
|
||||||
<span style={{ width: 10, height: 10, borderRadius: '50%', background: dotColor(STATE_OF_COLOR[status.color]), display: 'inline-block', flexShrink: 0, marginTop: 4 }} />
|
<span style={{ width: 10, height: 10, borderRadius: '50%', background: dotColor(STATE_OF_COLOR[status.color]), display: 'inline-block', flexShrink: 0, marginTop: 4 }} />
|
||||||
<span style={{ overflowWrap: 'anywhere' }}>{status.text}</span>
|
<span style={{ overflowWrap: 'anywhere' }}>{status.text}</span>
|
||||||
</div>
|
</div>
|
||||||
{data.state === 'failed' && data.error && (
|
{data.state === 'failed' && data.error && !isDevUnavailableError(data.error) && (
|
||||||
<div style={{ color: '#f87171', ...small, marginTop: 4, overflowWrap: 'anywhere' }}>{data.error}</div>
|
<div style={{ color: '#f87171', ...small, marginTop: 4, overflowWrap: 'anywhere' }}>{data.error}</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -7,6 +7,18 @@ export const MODE_OPTIONS = [
|
|||||||
{ value: 'off', label: 'Aus' },
|
{ 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', {
|
const dateFormat = new Intl.DateTimeFormat('de-DE', {
|
||||||
timeZone: 'Europe/Berlin',
|
timeZone: 'Europe/Berlin',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
@@ -40,6 +52,7 @@ export function previewStatus(p) {
|
|||||||
case 'sleeping':
|
case 'sleeping':
|
||||||
return { color: 'yellow', text: 'Dev-Server schläft, startet beim nächsten Aufruf' }
|
return { color: 'yellow', text: 'Dev-Server schläft, startet beim nächsten Aufruf' }
|
||||||
case 'failed':
|
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' }
|
return { color: 'red', text: 'Build fehlgeschlagen – letzte funktionierende Version ist online' }
|
||||||
default:
|
default:
|
||||||
return { color: 'grey', text: 'Keine Preview' }
|
return { color: 'grey', text: 'Keine Preview' }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import test from 'node:test'
|
|||||||
import assert from 'node:assert/strict'
|
import assert from 'node:assert/strict'
|
||||||
import { loadModule } from './load.mjs'
|
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', () => {
|
test('previewStatus online: Datum in Europe/Berlin und sha7', () => {
|
||||||
const s = previewStatus({ state: 'online', builtAt: '2026-09-30T13:05:00Z', builtSha: 'abcdef1234567890' })
|
const s = previewStatus({ state: 'online', builtAt: '2026-09-30T13:05:00Z', builtSha: 'abcdef1234567890' })
|
||||||
@@ -61,3 +61,21 @@ test('MODE_OPTIONS', () => {
|
|||||||
{ value: 'off', label: 'Aus' },
|
{ 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)
|
||||||
|
})
|
||||||
Reference in new issue
Block a user