feat: Jetzt-hosten-Button + Deploy-Status auf Projektkarten
- Karten zeigen fuer Hosting-Typen (Preview/Website/Vorlage) einen Status: gehostet (ready/deployed) vs nicht deployt (synced etc.) - nicht-deployte Projekte: Button "Jetzt hosten" -> deployt via PATCH-Route (setzt status=deployed/ready), danach funktioniert die Preview - deployte Projekte zeigen weiterhin den Preview-Link Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,16 @@ function typeOf(project) {
|
||||
return 'project'
|
||||
}
|
||||
|
||||
// Typen mit Hosting (Subdomain + Deploy)
|
||||
const HOSTED_TYPES = new Set(['preview', 'website', 'template'])
|
||||
|
||||
// Gleiche Logik wie isPreviewProjectReady im Backend (auth.js):
|
||||
// nur 'ready'/'deployed' gelten als tatsaechlich gehostet.
|
||||
function isProjectReady(project) {
|
||||
const st = String(project.provisioningStatus || project.status || '').toLowerCase()
|
||||
return ['ready', 'deployed'].includes(st)
|
||||
}
|
||||
|
||||
export default function ProjectsPage() {
|
||||
const { projects, loading, error, fetchAllProjects, assignProjects, unassignProject } = useWebsiteProjects()
|
||||
const { customers } = useCustomers()
|
||||
@@ -93,6 +103,7 @@ export default function ProjectsPage() {
|
||||
const [syncLoading, setSyncLoading] = useState(false)
|
||||
const [notice, setNotice] = useState('')
|
||||
const [assignBusyId, setAssignBusyId] = useState('')
|
||||
const [hostBusyId, setHostBusyId] = useState('')
|
||||
|
||||
// Bearbeiten-Modal
|
||||
const [editProject, setEditProject] = useState(null)
|
||||
@@ -194,6 +205,25 @@ export default function ProjectsPage() {
|
||||
setAssignBusyId('')
|
||||
}
|
||||
|
||||
const handleHostNow = async (project) => {
|
||||
setHostBusyId(project.$id)
|
||||
setNotice('')
|
||||
try {
|
||||
// Deploy ueber die vorhandene PATCH-Route anstossen (Typ bleibt gleich)
|
||||
const r = await updateWebsiteProject(project.$id, { projectType: typeOf(project) })
|
||||
setNotice(
|
||||
r.deploy?.deployed
|
||||
? `"${project.projectName}" wird jetzt gehostet${r.previewUrl ? ': ' + r.previewUrl : ''}`
|
||||
: `Deploy angestossen, aber nicht bestaetigt: ${r.deploy?.error || 'siehe Server-Logs'}`
|
||||
)
|
||||
await fetchAllProjects()
|
||||
} catch (err) {
|
||||
setNotice('Hosten fehlgeschlagen: ' + (err.message || 'unbekannt'))
|
||||
} finally {
|
||||
setHostBusyId('')
|
||||
}
|
||||
}
|
||||
|
||||
const openEdit = (project) => {
|
||||
setEditError('')
|
||||
setEditProject(project)
|
||||
@@ -324,7 +354,11 @@ export default function ProjectsPage() {
|
||||
}}
|
||||
>
|
||||
{filteredProjects.map((project) => {
|
||||
const t = TYPES[typeOf(project)]
|
||||
const type = typeOf(project)
|
||||
const t = TYPES[type]
|
||||
const hosted = HOSTED_TYPES.has(type)
|
||||
const ready = isProjectReady(project)
|
||||
const needsHosting = hosted && !ready && !project.archived
|
||||
return (
|
||||
<div
|
||||
key={project.$id}
|
||||
@@ -342,9 +376,16 @@ export default function ProjectsPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span className={`badge ${t.badge}`} style={{ whiteSpace: 'nowrap', flexShrink: 0 }}>
|
||||
{t.icon} {t.short}
|
||||
</span>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px', alignItems: 'flex-end', flexShrink: 0 }}>
|
||||
<span className={`badge ${t.badge}`} style={{ whiteSpace: 'nowrap' }}>
|
||||
{t.icon} {t.short}
|
||||
</span>
|
||||
{hosted && !project.archived && (
|
||||
<span className={`badge ${ready ? 'badge-ok' : 'badge-warn'}`} style={{ whiteSpace: 'nowrap' }}>
|
||||
{ready ? 'gehostet' : 'nicht deployt'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px', fontSize: '13px' }}>
|
||||
@@ -389,7 +430,20 @@ export default function ProjectsPage() {
|
||||
>
|
||||
<FaPen /> Bearbeiten
|
||||
</button>
|
||||
<PreviewLinkButton href={project.previewUrl} />
|
||||
{needsHosting ? (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-teal"
|
||||
style={{ padding: '6px 12px', fontSize: '13px' }}
|
||||
onClick={() => handleHostNow(project)}
|
||||
disabled={hostBusyId === project.$id}
|
||||
title="Projekt jetzt deployen/hosten"
|
||||
>
|
||||
<FaGlobe /> {hostBusyId === project.$id ? 'Hosten…' : 'Jetzt hosten'}
|
||||
</button>
|
||||
) : (
|
||||
<PreviewLinkButton href={project.previewUrl} />
|
||||
)}
|
||||
<GiteaLinkButton href={project.giteaRepoUrl} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user