Ticket > Details > Projekte: geheimer Vorschau-Link (erstellen, kopieren, oeffnen, neu, abschalten)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-26 17:59:23 +00:00
1 parent 3c44c42681
commit 1a6e533a9a
6 files changed
+132

No files matched your search

+85
View File
@@ -0,0 +1,85 @@
import { useState } from 'react'
import { copyTextChecked } from './CopyableCredential'
import { erzeugeVorschauLink, schalteVorschauLinkAb } from '../lib/projectAdminApi'
import { vorschauLinkUrl } from '../lib/vorschauLink'
// Geheimer Vorschau-Link im Ticket (Details > Projekte): wer den Link hat, sieht die Seite ohne Login.
export default function VorschauLinkRow({ project, onChanged = () => {} }) {
const [token, setToken] = useState(project.shareToken || '')
const [busy, setBusy] = useState(false)
const [message, setMessage] = useState('')
const url = vorschauLinkUrl({ subdomain: project.subdomain, shareToken: token })
const run = async (action) => {
setBusy(true)
setMessage('')
try {
await action()
onChanged()
} catch (err) {
setMessage(err.message || 'Aktion fehlgeschlagen')
} finally {
setBusy(false)
}
}
const erzeugen = (neu) => {
if (neu && !window.confirm('Neuen Link erzeugen? Der bisherige Link funktioniert dann nicht mehr.')) return
run(async () => {
const r = await erzeugeVorschauLink(project.$id)
setToken(r.shareToken || '')
setMessage(neu ? 'Neuer Link erzeugt, der alte gilt nicht mehr.' : 'Link erstellt.')
})
}
const abschalten = () => {
if (!window.confirm('Link abschalten? Wer ihn hat, kommt dann nicht mehr auf die Seite.')) return
run(async () => {
await schalteVorschauLinkAb(project.$id)
setToken('')
setMessage('Link abgeschaltet.')
})
}
const kopieren = async () => {
try {
await copyTextChecked(url)
setMessage('Link kopiert.')
} catch {
setMessage('Kopieren ging nicht – bitte den Link oben markieren und kopieren.')
}
}
return (
<div className="vorschau-link" data-testid="vorschau-link">
<div className="vorschau-link-head">
<span className="vorschau-link-label">Vorschau-Link</span>
<span className="muted xs">Ohne Login, nur für wen du ihn teilst</span>
</div>
{url ? (
<>
<input
className="input vorschau-link-url"
readOnly
aria-label="Geheimer Vorschau-Link"
value={url}
onFocus={(e) => e.target.select()}
/>
<div className="row-actions">
<button type="button" className="link" onClick={kopieren}>Kopieren</button>
<a className="link" href={url} target="_blank" rel="noopener noreferrer">Öffnen</a>
<button type="button" className="link" disabled={busy} onClick={() => erzeugen(true)}>Neuer Link</button>
<button type="button" className="link link-danger" disabled={busy} onClick={abschalten}>Abschalten</button>
</div>
</>
) : (
<div className="row-actions">
<button type="button" className="link" disabled={busy} onClick={() => erzeugen(false)}>
{busy ? 'Wird erstellt …' : 'Link erstellen'}
</button>
</div>
)}
{message && <p className="msg muted" role="status">{message}</p>}
</div>
)
}