feat: Integrationen sichtbar und bedienbar machen
Eine Seite zeigt je Integration, ob sie vorgesehen, eingerichtet oder gestoert ist, wann sie zuletzt geglueckt ist und woran sie klemmt. Der Wartungsplaner steht sichtbar als offene Luecke darin, nicht als Leerstelle. Telegram-Chats werden hier gekoppelt und wieder getrennt, Meldungen je Chat einzeln geschaltet. Aufgaben bekommen einen Schalter zum Spiegeln — einzeln und je Aufgabe, denn wer versehentlich einen ganzen Bereich spiegelt, schickt Kundennamen nach Hause. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bc60e5f864
commit
1281c3ea96
@@ -0,0 +1,139 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useTransition } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Bell, BellOff, Link2, Loader2, Trash2 } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
chatEntfernen,
|
||||
chatMeldungenUmschalten,
|
||||
kopplungAnfordern,
|
||||
} from '@/server/aktionen/integrationen'
|
||||
|
||||
export type ChatAnzeige = {
|
||||
id: string
|
||||
chatId: string
|
||||
bezeichnung: string | null
|
||||
meldungen: boolean
|
||||
erstelltAm: string
|
||||
zuletztBenutzt: string | null
|
||||
}
|
||||
|
||||
export function TelegramChats({ chats }: { chats: ChatAnzeige[] }) {
|
||||
const router = useRouter()
|
||||
const [laeuft, starte] = useTransition()
|
||||
const [code, setCode] = useState<{ code: string; minuten: number } | null>(null)
|
||||
|
||||
function anfordern() {
|
||||
starte(async () => {
|
||||
const antwort = await kopplungAnfordern()
|
||||
if (antwort.ok) {
|
||||
setCode({ code: antwort.code, minuten: antwort.minuten })
|
||||
} else {
|
||||
toast.error(antwort.fehler)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function mitRueckmeldung(
|
||||
tun: () => Promise<{ ok: boolean; meldung?: string; fehler?: string }>,
|
||||
) {
|
||||
starte(async () => {
|
||||
const antwort = await tun()
|
||||
if (antwort.ok) {
|
||||
toast.success(antwort.meldung ?? 'Erledigt.')
|
||||
router.refresh()
|
||||
} else {
|
||||
toast.error(antwort.fehler ?? 'Das hat nicht geklappt.')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{chats.length === 0 ? (
|
||||
<p className="text-gedaempft-vordergrund text-sm">
|
||||
Noch kein Chat verbunden. Ohne Verbindung nimmt der Bot nichts an.
|
||||
</p>
|
||||
) : (
|
||||
<ul className="divide-rand divide-y">
|
||||
{chats.map((chat) => (
|
||||
<li key={chat.id} className="flex items-center gap-3 py-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm">{chat.bezeichnung ?? `Chat ${chat.chatId}`}</p>
|
||||
<p className="text-gedaempft-vordergrund text-xs">
|
||||
Verbunden {chat.erstelltAm}
|
||||
{chat.zuletztBenutzt
|
||||
? ` · zuletzt benutzt ${chat.zuletztBenutzt}`
|
||||
: ' · noch nichts geschickt'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
art="leise"
|
||||
groesse="quadrat"
|
||||
title={
|
||||
chat.meldungen ? 'Keine Meldungen mehr hierher' : 'Meldungen hierher'
|
||||
}
|
||||
disabled={laeuft}
|
||||
onClick={() => {
|
||||
const daten = new FormData()
|
||||
daten.set('id', chat.id)
|
||||
mitRueckmeldung(() => chatMeldungenUmschalten(daten))
|
||||
}}
|
||||
>
|
||||
{chat.meldungen ? (
|
||||
<Bell className="text-erfolg" aria-hidden />
|
||||
) : (
|
||||
<BellOff aria-hidden />
|
||||
)}
|
||||
<span className="sr-only">Meldungen umschalten</span>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
art="leise"
|
||||
groesse="quadrat"
|
||||
title="Chat trennen"
|
||||
disabled={laeuft}
|
||||
onClick={() => {
|
||||
const daten = new FormData()
|
||||
daten.set('id', chat.id)
|
||||
mitRueckmeldung(() => chatEntfernen(daten))
|
||||
}}
|
||||
>
|
||||
<Trash2 aria-hidden />
|
||||
<span className="sr-only">Trennen</span>
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{code ? (
|
||||
<div className="border-rand rounded-md border p-3">
|
||||
<p className="text-sm">
|
||||
Schick dem Bot jetzt diesen Code — als Nachricht oder als{' '}
|
||||
<code className="text-xs">/start {code.code}</code>:
|
||||
</p>
|
||||
<p className="my-2 font-mono text-2xl tracking-widest">{code.code}</p>
|
||||
<p className="text-gedaempft-vordergrund text-xs">
|
||||
Gültig für {code.minuten} Minuten und nur ein einziges Mal. Danach erscheint
|
||||
der Chat hier — spätestens nach dem nächsten Abholen.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Button art="umriss" disabled={laeuft} onClick={anfordern}>
|
||||
{laeuft ? (
|
||||
<Loader2 className="animate-spin" aria-hidden />
|
||||
) : (
|
||||
<Link2 aria-hidden />
|
||||
)}
|
||||
Chat verbinden
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user