Projekte: Typ-, Hosting- und Aktionslogik als getestete Lib-Funktionen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bea91d9beb
commit
d2d4ad9cfb
2 files changed
+146
No files matched your search
@@ -0,0 +1,90 @@
|
||||
// Logik der Projektliste (Typ, Hosting-Zustand, Aktionen je Zeile) ohne Markup.
|
||||
// Aus ProjectsPage herausgezogen; die Töne (info/ok/warn/muted) lösen die Badge-Komponenten auf.
|
||||
import { formatDistance } from 'date-fns'
|
||||
import { de } from 'date-fns/locale'
|
||||
import { fmtDateTime } from './format'
|
||||
|
||||
// Projekt-Typen (= "Template"/Hosting-Modus). Die Reihenfolge der Schlüssel bestimmt die Optionen im Dialog.
|
||||
export const PROJECT_TYPES = {
|
||||
preview: {
|
||||
label: 'Preview (mit Login)',
|
||||
short: 'Preview',
|
||||
tone: 'info',
|
||||
hint: 'Login-geschuetzte Vorschau unter <subdomain>.project.webklar.com',
|
||||
},
|
||||
website: {
|
||||
label: 'Oeffentliche Website',
|
||||
short: 'Website',
|
||||
tone: 'ok',
|
||||
hint: 'Oeffentlich erreichbar ohne Login unter <subdomain>.project.webklar.com',
|
||||
},
|
||||
template: {
|
||||
label: 'Vorlage',
|
||||
short: 'Vorlage',
|
||||
tone: 'warn',
|
||||
hint: 'Website-Vorlage (login-geschuetzt) - erscheint nur fuer Admins in der Vorlagen-Ansicht',
|
||||
adminOnly: true,
|
||||
},
|
||||
project: {
|
||||
label: 'Internes Projekt (kein Hosting)',
|
||||
short: 'Projekt',
|
||||
tone: 'muted',
|
||||
hint: 'Nur Gitea-Repository, keine Website/Subdomain',
|
||||
},
|
||||
}
|
||||
|
||||
// Typen mit Hosting (Subdomain + Deploy)
|
||||
export const HOSTED_TYPES = new Set(['preview', 'website', 'template'])
|
||||
|
||||
export function typeOf(project) {
|
||||
if (project?.isTemplate) return 'template'
|
||||
if (project?.isPublic) return 'website'
|
||||
if (project?.subdomain) return 'preview'
|
||||
return 'project'
|
||||
}
|
||||
|
||||
// Gleiche Logik wie isPreviewProjectReady im Backend (auth.js):
|
||||
// nur 'ready'/'deployed' gelten als tatsaechlich gehostet.
|
||||
export function isProjectReady(project) {
|
||||
const st = String(project?.provisioningStatus || project?.status || '').toLowerCase()
|
||||
return ['ready', 'deployed'].includes(st)
|
||||
}
|
||||
|
||||
/**
|
||||
* Welche Aktionen und welches Hosting-Kennzeichen eine Zeile zeigt.
|
||||
* host = "Hosten" (noch nicht deployt), preview = Vorschau-Link, refresh = "Aktualisieren" (erneut deployen).
|
||||
*/
|
||||
export function projectActions(project) {
|
||||
const type = typeOf(project)
|
||||
const hosted = HOSTED_TYPES.has(type)
|
||||
const ready = isProjectReady(project)
|
||||
const archived = Boolean(project?.archived)
|
||||
const host = hosted && !ready && !archived
|
||||
const preview = !host && Boolean(project?.previewUrl)
|
||||
const refresh = hosted && ready && !archived
|
||||
const gitea = Boolean(project?.giteaRepoUrl)
|
||||
const hostedBadge = !hosted || archived
|
||||
? null
|
||||
: ready
|
||||
? { text: 'gehostet', tone: 'ok' }
|
||||
: { text: 'nicht deployt', tone: 'warn' }
|
||||
return { type, hosted, ready, host, preview, refresh, gitea, hostedBadge }
|
||||
}
|
||||
|
||||
/** Ticketnummer (woid) der Zeile: aus der Ticketliste, sonst die $id, ohne Ticket leer. */
|
||||
export function projectTicketRef(project, ticketMap) {
|
||||
const id = project?.ticketId
|
||||
if (!id) return ''
|
||||
return String(ticketMap?.[id] ?? id)
|
||||
}
|
||||
|
||||
/** "vor etwa 3 Stunden" mit Titel in Berliner Zeit; fehlend oder unlesbar -> "-". */
|
||||
export function updatedLabel(iso, now = new Date()) {
|
||||
if (!iso) return { text: '-', title: '' }
|
||||
const date = new Date(iso)
|
||||
if (Number.isNaN(date.getTime())) return { text: '-', title: '' }
|
||||
return {
|
||||
text: formatDistance(date, now, { addSuffix: true, locale: de }),
|
||||
title: fmtDateTime(iso),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user