Projekte im Ticketsystem: Anlegen, Archiv und Anzeige repariert
- Neues Projekt (Projekte-Seite): Repo-Name blieb nach dem ersten
Buchstaben der Subdomain stehen ("m" statt "musterfirma")
- Ticket "Projekt waehlen": archivierte Repos nach Flag ausblenden, nicht
nach status (reaktivierte fehlten, archivierte wurden angeboten)
- Projektliste: "Aktive Projekte" + neue Ansicht "Alle inkl. Archiv";
Liste wird auch nach einem Anlege-Fehler neu geladen
- Kundenauswahl: "Kein Kunde" entfernt Kunde und Ticket wirklich (mit
Rueckfrage); beim Kundenwechsel bleibt kein fremdes Ticket haengen
- Zuordnen-Assistent ueberschreibt eine vorhandene Website nicht mehr
- Neues Projekt am Ticket bekommt die Art Website
- Umlaute in Subdomains wie im Portal (mueller statt m-ller)
- Preview-Block nur mit gespeicherter Subdomain; archivierte Projekte
erst reaktivieren, bevor sie bearbeitet werden
- Repo-Import beim Aufklappen hoechstens einmal pro Minute
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
78bc99a73b
commit
f4c8b235b9
13 files changed
+269
-92
No files matched your search
@@ -6,15 +6,13 @@ import { useAuth } from '../context/AuthContext'
|
||||
import { databases, DATABASE_ID, COLLECTIONS, Query } from '../lib/appwrite'
|
||||
import { fetchServices, completeAssignment, fetchProject } from '../lib/projectKindsApi'
|
||||
import { nextStep, prevStep, newTicketDefaults, canFinish, openTicketsFor } from '../lib/assignFlow'
|
||||
import { suggestSubdomain } from '../lib/subdomain'
|
||||
import { suggestSubdomain, slugify } from '../lib/subdomain'
|
||||
import { isArchived } from '../lib/leads'
|
||||
import { PageHeader, Card, Button, Field } from '../components/ui'
|
||||
import { CreateCustomerForm } from './CustomersPage'
|
||||
|
||||
const todayIso = () => new Date().toLocaleDateString('sv-SE', { timeZone: 'Europe/Berlin' })
|
||||
const fmtDate = (iso) => (iso ? new Date(iso).toLocaleDateString('de-DE') : '')
|
||||
const slugify = (v) =>
|
||||
String(v || '').toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 63)
|
||||
|
||||
const STEP_TITLES = {
|
||||
kind: 'Was ist das?',
|
||||
@@ -95,11 +93,15 @@ export default function AssignProjectPage() {
|
||||
const openTickets = useMemo(() => openTicketsFor(tickets, state.customerId), [tickets, state.customerId])
|
||||
|
||||
const isWebsiteWithCustomer = state.kind === 'website' && state.forCustomer
|
||||
// Hat das Projekt schon eine Website (z. B. ueber "Neues Projekt" mit Subdomain angelegt), bleibt
|
||||
// sie wie sie ist: kein Vorschlag, kein erneutes Einschalten (das benannte sie sonst um und
|
||||
// stellte eine oeffentliche Website auf Login-Preview).
|
||||
const hasPreviewAlready = Boolean(project?.subdomain)
|
||||
useEffect(() => {
|
||||
if (state.step === 'summary' && isWebsiteWithCustomer && !subdomain) {
|
||||
if (state.step === 'summary' && isWebsiteWithCustomer && !hasPreviewAlready && !subdomain) {
|
||||
setSubdomain(suggestSubdomain('', repoName))
|
||||
}
|
||||
}, [state.step, isWebsiteWithCustomer, subdomain, repoName])
|
||||
}, [state.step, isWebsiteWithCustomer, hasPreviewAlready, subdomain, repoName])
|
||||
|
||||
const patch = (p) => setState((s) => ({ ...s, ...p }))
|
||||
const go = (step) => { setError(null); patch({ step }) }
|
||||
@@ -141,7 +143,7 @@ export default function AssignProjectPage() {
|
||||
const { topic, requestedBy, startDate, details } = state.newTicket
|
||||
body.newTicket = { topic, requestedBy, startDate, details }
|
||||
}
|
||||
if (isWebsiteWithCustomer && previewOn && subdomain.trim()) {
|
||||
if (isWebsiteWithCustomer && !hasPreviewAlready && previewOn && subdomain.trim()) {
|
||||
body.preview = { subdomain: slugify(subdomain) }
|
||||
}
|
||||
}
|
||||
@@ -353,7 +355,12 @@ export default function AssignProjectPage() {
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
{isWebsiteWithCustomer && (
|
||||
{isWebsiteWithCustomer && hasPreviewAlready && (
|
||||
<p className="muted" style={{ marginTop: 0 }}>
|
||||
Preview läuft bereits unter https://{project.subdomain}.project.webklar.com und bleibt unverändert.
|
||||
</p>
|
||||
)}
|
||||
{isWebsiteWithCustomer && !hasPreviewAlready && (
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<label className="flex gap-2" style={{ alignItems: 'center' }}>
|
||||
<input type="checkbox" checked={previewOn} onChange={(e) => setPreviewOn(e.target.checked)} />
|
||||
@@ -366,7 +373,7 @@ export default function AssignProjectPage() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Button variant="primary" disabled={busy || !canFinish(state) || (isWebsiteWithCustomer && previewOn && !slugify(subdomain))} onClick={finish}>
|
||||
<Button variant="primary" disabled={busy || !canFinish(state) || (isWebsiteWithCustomer && !hasPreviewAlready && previewOn && !slugify(subdomain))} onClick={finish}>
|
||||
{busy ? <FaSpinner className="spinner" /> : <><FaCheck /> Abschließen</>}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
Reference in new issue
Block a user