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:
1 parent
3c44c42681
commit
1a6e533a9a
6 files changed
+132
No files matched your search
@@ -4,6 +4,7 @@ import PreviewBlock from './PreviewBlock'
|
||||
import AnalyseRow from './AnalyseRow'
|
||||
import GiteaLinkButton from './GiteaLinkButton'
|
||||
import PreviewLinkButton from './PreviewLinkButton'
|
||||
import VorschauLinkRow from './VorschauLinkRow'
|
||||
import { statusValueTone } from './projectTones'
|
||||
import { suggestSubdomain } from '../lib/subdomain'
|
||||
import { needsPreviewButton, assignSummary } from '../lib/projectAssign'
|
||||
@@ -87,6 +88,8 @@ export default function AssignedProjectCard({ project, onUnassign, showActions =
|
||||
|
||||
{project.subdomain && <AnalyseRow key={project.$id} projectId={project.$id} />}
|
||||
|
||||
{showActions && project.subdomain && <VorschauLinkRow key={`vl-${project.$id}`} project={project} onChanged={onChanged} />}
|
||||
|
||||
{showActions && project.subdomain && (
|
||||
<>
|
||||
<button type="button" className="link" aria-expanded={showPreviewBlock} onClick={() => setShowPreviewBlock((v) => !v)}>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user