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'
|
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() {
|
export default function ProjectsPage() {
|
||||||
const { projects, loading, error, fetchAllProjects, assignProjects, unassignProject } = useWebsiteProjects()
|
const { projects, loading, error, fetchAllProjects, assignProjects, unassignProject } = useWebsiteProjects()
|
||||||
const { customers } = useCustomers()
|
const { customers } = useCustomers()
|
||||||
@@ -93,6 +103,7 @@ export default function ProjectsPage() {
|
|||||||
const [syncLoading, setSyncLoading] = useState(false)
|
const [syncLoading, setSyncLoading] = useState(false)
|
||||||
const [notice, setNotice] = useState('')
|
const [notice, setNotice] = useState('')
|
||||||
const [assignBusyId, setAssignBusyId] = useState('')
|
const [assignBusyId, setAssignBusyId] = useState('')
|
||||||
|
const [hostBusyId, setHostBusyId] = useState('')
|
||||||
|
|
||||||
// Bearbeiten-Modal
|
// Bearbeiten-Modal
|
||||||
const [editProject, setEditProject] = useState(null)
|
const [editProject, setEditProject] = useState(null)
|
||||||
@@ -194,6 +205,25 @@ export default function ProjectsPage() {
|
|||||||
setAssignBusyId('')
|
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) => {
|
const openEdit = (project) => {
|
||||||
setEditError('')
|
setEditError('')
|
||||||
setEditProject(project)
|
setEditProject(project)
|
||||||
@@ -324,7 +354,11 @@ export default function ProjectsPage() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{filteredProjects.map((project) => {
|
{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 (
|
return (
|
||||||
<div
|
<div
|
||||||
key={project.$id}
|
key={project.$id}
|
||||||
@@ -342,9 +376,16 @@ export default function ProjectsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className={`badge ${t.badge}`} style={{ whiteSpace: 'nowrap', flexShrink: 0 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px', alignItems: 'flex-end', flexShrink: 0 }}>
|
||||||
{t.icon} {t.short}
|
<span className={`badge ${t.badge}`} style={{ whiteSpace: 'nowrap' }}>
|
||||||
</span>
|
{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>
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px', fontSize: '13px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px', fontSize: '13px' }}>
|
||||||
@@ -389,7 +430,20 @@ export default function ProjectsPage() {
|
|||||||
>
|
>
|
||||||
<FaPen /> Bearbeiten
|
<FaPen /> Bearbeiten
|
||||||
</button>
|
</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} />
|
<GiteaLinkButton href={project.giteaRepoUrl} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user