Projektarten: Zuordnen-Seite nachgebessert
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7e79dac6b4
commit
8653756935
1 file changed
+19
-7
@@ -44,6 +44,8 @@ export default function AssignProjectPage() {
|
|||||||
const [showNewCustomer, setShowNewCustomer] = useState(false)
|
const [showNewCustomer, setShowNewCustomer] = useState(false)
|
||||||
const [tickets, setTickets] = useState([])
|
const [tickets, setTickets] = useState([])
|
||||||
const [ticketsLoading, setTicketsLoading] = useState(false)
|
const [ticketsLoading, setTicketsLoading] = useState(false)
|
||||||
|
const [ticketsError, setTicketsError] = useState(false)
|
||||||
|
const [ticketsReload, setTicketsReload] = useState(0)
|
||||||
const [previewOn, setPreviewOn] = useState(true)
|
const [previewOn, setPreviewOn] = useState(true)
|
||||||
const [subdomain, setSubdomain] = useState('')
|
const [subdomain, setSubdomain] = useState('')
|
||||||
const [busy, setBusy] = useState(false)
|
const [busy, setBusy] = useState(false)
|
||||||
@@ -75,17 +77,20 @@ export default function AssignProjectPage() {
|
|||||||
if (state.step !== 'ticket' || !state.customerId) return
|
if (state.step !== 'ticket' || !state.customerId) return
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
setTicketsLoading(true)
|
setTicketsLoading(true)
|
||||||
|
setTicketsError(false)
|
||||||
databases
|
databases
|
||||||
.listDocuments(DATABASE_ID, COLLECTIONS.WORKORDERS, [
|
.listDocuments(DATABASE_ID, COLLECTIONS.WORKORDERS, [
|
||||||
Query.equal('customerId', state.customerId),
|
Query.equal('customerId', state.customerId),
|
||||||
|
Query.notEqual('status', 'Closed'),
|
||||||
|
Query.notEqual('status', 'Cancelled'),
|
||||||
Query.orderDesc('$createdAt'),
|
Query.orderDesc('$createdAt'),
|
||||||
Query.limit(200),
|
Query.limit(200),
|
||||||
])
|
])
|
||||||
.then((r) => { if (!cancelled) setTickets(r.documents || []) })
|
.then((r) => { if (!cancelled) setTickets(r.documents || []) })
|
||||||
.catch(() => { if (!cancelled) setTickets([]) })
|
.catch(() => { if (!cancelled) { setTickets([]); setTicketsError(true) } })
|
||||||
.finally(() => { if (!cancelled) setTicketsLoading(false) })
|
.finally(() => { if (!cancelled) setTicketsLoading(false) })
|
||||||
return () => { cancelled = true }
|
return () => { cancelled = true }
|
||||||
}, [state.step, state.customerId])
|
}, [state.step, state.customerId, ticketsReload])
|
||||||
|
|
||||||
const openTickets = useMemo(() => openTicketsFor(tickets, state.customerId), [tickets, state.customerId])
|
const openTickets = useMemo(() => openTicketsFor(tickets, state.customerId), [tickets, state.customerId])
|
||||||
|
|
||||||
@@ -142,7 +147,7 @@ export default function AssignProjectPage() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const result = await completeAssignment(id, body)
|
const result = await completeAssignment(id, body)
|
||||||
let path = '/projects'
|
let path = state.forCustomer ? '/projects' : '/services'
|
||||||
if (result.ticketId) {
|
if (result.ticketId) {
|
||||||
try {
|
try {
|
||||||
const t = await databases.getDocument(DATABASE_ID, COLLECTIONS.WORKORDERS, result.ticketId)
|
const t = await databases.getDocument(DATABASE_ID, COLLECTIONS.WORKORDERS, result.ticketId)
|
||||||
@@ -183,7 +188,7 @@ export default function AssignProjectPage() {
|
|||||||
<div style={{ fontWeight: 700, fontSize: 18 }}>{repoName || project.projectName}</div>
|
<div style={{ fontWeight: 700, fontSize: 18 }}>{repoName || project.projectName}</div>
|
||||||
<div className="muted" style={{ marginTop: 4 }}>
|
<div className="muted" style={{ marginTop: 4 }}>
|
||||||
{repoUrl && <a href={repoUrl} target="_blank" rel="noreferrer">Repository in Gitea öffnen</a>}
|
{repoUrl && <a href={repoUrl} target="_blank" rel="noreferrer">Repository in Gitea öffnen</a>}
|
||||||
{project.$createdAt && <> · angelegt am {fmtDate(project.$createdAt)}{project.createdByGitea ? ' (über Gitea)' : ''}</>}
|
{project.$createdAt && <> · angelegt am {fmtDate(project.$createdAt)}{project.createdByGitea ? ` von ${project.createdByGitea}` : ''}</>}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
@@ -239,7 +244,7 @@ export default function AssignProjectPage() {
|
|||||||
key={k.key}
|
key={k.key}
|
||||||
variant={state.kind === k.key ? 'primary' : 'default'}
|
variant={state.kind === k.key ? 'primary' : 'default'}
|
||||||
style={{ padding: '18px 12px', fontSize: 16, fontWeight: 700 }}
|
style={{ padding: '18px 12px', fontSize: 16, fontWeight: 700 }}
|
||||||
onClick={() => patch({ kind: k.key })}
|
onClick={() => patch({ kind: k.key, newTicket: null, ticketId: '' })}
|
||||||
>
|
>
|
||||||
{k.label}
|
{k.label}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -298,7 +303,12 @@ export default function AssignProjectPage() {
|
|||||||
|
|
||||||
{state.step === 'ticket' && (
|
{state.step === 'ticket' && (
|
||||||
<>
|
<>
|
||||||
{ticketsLoading ? <FaSpinner className="spinner" /> : (
|
{ticketsLoading ? <FaSpinner className="spinner" /> : ticketsError ? (
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<div className="text-red" style={{ marginBottom: 8 }}>Die Tickets des Kunden konnten nicht geladen werden.</div>
|
||||||
|
<Button onClick={() => setTicketsReload((n) => n + 1)}>Erneut versuchen</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div className="list" style={{ marginBottom: 12 }}>
|
<div className="list" style={{ marginBottom: 12 }}>
|
||||||
{openTickets.map((t) => (
|
{openTickets.map((t) => (
|
||||||
<div key={t.$id} className="list-row" style={{ cursor: 'pointer', background: state.ticketId === t.$id ? 'var(--surface-2)' : undefined }}
|
<div key={t.$id} className="list-row" style={{ cursor: 'pointer', background: state.ticketId === t.$id ? 'var(--surface-2)' : undefined }}
|
||||||
@@ -313,7 +323,9 @@ export default function AssignProjectPage() {
|
|||||||
{openTickets.length === 0 && <div className="muted" style={{ padding: 12 }}>Dieser Kunde hat keine offenen Tickets.</div>}
|
{openTickets.length === 0 && <div className="muted" style={{ padding: 12 }}>Dieser Kunde hat keine offenen Tickets.</div>}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Button variant={state.newTicket ? 'primary' : 'default'} onClick={startNewTicket}><FaPlus /> Neues Ticket anlegen</Button>
|
{!ticketsError && !ticketsLoading && (
|
||||||
|
<Button variant={state.newTicket ? 'primary' : 'default'} onClick={startNewTicket}><FaPlus /> Neues Ticket anlegen</Button>
|
||||||
|
)}
|
||||||
{state.newTicket && (
|
{state.newTicket && (
|
||||||
<div style={{ marginTop: 12 }}>
|
<div style={{ marginTop: 12 }}>
|
||||||
<div className="flex gap-3 wrap">
|
<div className="flex gap-3 wrap">
|
||||||
|
|||||||
Reference in new issue
Block a user