// 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 .project.webklar.com', }, website: { label: 'Oeffentliche Website', short: 'Website', tone: 'ok', hint: 'Oeffentlich erreichbar ohne Login unter .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), } }