feat: Lauf von Hand anstossen und Spiegelschalter in der Liste

Direkt nach dem Einrichten wuesste man sonst erst beim naechsten Takt,
ob die Zugangsdaten stimmen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-11 23:59:14 +02:00
co-authored by Claude Opus 5
parent 3befcc19a3
commit f5eadefdbb
4 changed files with 99 additions and 1 deletions
@@ -0,0 +1,46 @@
'use client'
import { useTransition } from 'react'
import { useRouter } from 'next/navigation'
import { Loader2, RefreshCw } from 'lucide-react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { laufAnstossen } from '@/server/aktionen/integrationen'
/**
* Stößt einen Lauf von Hand an.
*
* Vor allem direkt nach dem Einrichten: Sonst wüsste man erst beim nächsten
* Takt, ob die Zugangsdaten stimmen.
*/
export function LaufKnopf({ quelle }: { quelle: string }) {
const router = useRouter()
const [laeuft, starte] = useTransition()
return (
<Button
art="umriss"
disabled={laeuft}
onClick={() => {
starte(async () => {
const daten = new FormData()
daten.set('quelle', quelle)
const antwort = await laufAnstossen(daten)
if (antwort.ok) {
toast.success(antwort.meldung)
router.refresh()
} else {
toast.error(antwort.fehler)
}
})
}}
>
{laeuft ? (
<Loader2 className="animate-spin" aria-hidden />
) : (
<RefreshCw aria-hidden />
)}
Jetzt abrufen
</Button>
)
}
+13
View File
@@ -1,5 +1,6 @@
import type { Metadata } from 'next'
import { Plug } from 'lucide-react'
import { LaufKnopf } from '@/app/(app)/integrationen/lauf-knopf'
import { TelegramChats } from '@/app/(app)/integrationen/telegram-chats'
import {
Card,
@@ -27,6 +28,8 @@ type Zeile = {
/** Und auch eingerichtet? */
aktiv: boolean
hinweis: string
/** Laesst sich der Lauf von Hand anstossen? Der Empfaenger wartet nur. */
abrufbar?: boolean
}
export default async function IntegrationenSeite() {
@@ -59,6 +62,7 @@ export default async function IntegrationenSeite() {
vorgesehen: einst.instanz === 'firma',
aktiv: einst.zammadAktiv,
hinweis: 'ZAMMAD_URL und ZAMMAD_TOKEN fehlen.',
abrufbar: einst.zammadAktiv,
},
{
quelle: 'WARTUNGSPLANER',
@@ -77,6 +81,7 @@ export default async function IntegrationenSeite() {
vorgesehen: einst.instanz === 'daheim',
aktiv: einst.telegramAktiv,
hinweis: 'TELEGRAM_TOKEN fehlt.',
abrufbar: einst.telegramAktiv,
},
{
quelle: 'SPIEGEL',
@@ -88,6 +93,8 @@ export default async function IntegrationenSeite() {
vorgesehen: true,
aktiv: einst.spiegelSendenAktiv || einst.spiegelEmpfangenAktiv,
hinweis: 'SPIEGEL_GEHEIMNIS fehlt.',
// Die empfangende Seite ruft nichts ab – sie wartet.
abrufbar: einst.spiegelSendenAktiv,
},
]
@@ -141,6 +148,12 @@ export default async function IntegrationenSeite() {
</p>
)}
{!zustand && zeile.aktiv && <p>Noch kein Lauf.</p>}
{zeile.abrufbar && (
<div className="pt-2">
<LaufKnopf quelle={zeile.quelle} />
</div>
)}
</CardInhalt>
</Card>
)
@@ -14,6 +14,8 @@ type Eigenschaften = {
leerText: string
mitEinsortieren?: boolean
mitUebernehmen?: boolean
/** Schalter zum Spiegeln zeigen; nur auf der sendenden Instanz sinnvoll. */
mitSpiegeln?: boolean
/** Bearbeiten direkt in der Liste erlauben. */
mitBearbeiten?: boolean
}
@@ -25,6 +27,7 @@ export function AufgabenListe({
leerText,
mitEinsortieren = false,
mitUebernehmen = false,
mitSpiegeln = false,
mitBearbeiten = true,
}: Eigenschaften) {
const [bearbeitet, setBearbeitet] = useState<string | null>(null)
@@ -60,6 +63,7 @@ export function AufgabenListe({
bereiche={bereiche}
mitEinsortieren={mitEinsortieren}
mitUebernehmen={mitUebernehmen}
mitSpiegeln={mitSpiegeln}
{...(mitBearbeiten
? { onBearbeiten: () => setBearbeitet(aufgabe.id) }
: {})}
+36 -1
View File
@@ -3,7 +3,15 @@
import { useState, useTransition } from 'react'
import { useRouter } from 'next/navigation'
import Link from 'next/link'
import { Check, ExternalLink, Loader2, Pencil, RotateCcw, Trash2 } from 'lucide-react'
import {
Check,
ExternalLink,
Loader2,
Pencil,
RotateCcw,
Share2,
Trash2,
} from 'lucide-react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { Kennzeichen } from '@/components/ui/kennzeichen'
@@ -22,6 +30,7 @@ import {
aufgabeLoeschen,
einsortieren,
ideeUebernehmen,
spiegelnUmschalten,
statusSetzen,
type Rueckmeldung,
} from '@/server/aktionen/aufgaben'
@@ -36,6 +45,8 @@ type Eigenschaften = {
mitEinsortieren?: boolean
/** Zeigt den Knopf "Zur Aufgabe machen" (Ideen). */
mitUebernehmen?: boolean
/** Zeigt den Schalter zum Spiegeln; nur auf der sendenden Instanz. */
mitSpiegeln?: boolean
onBearbeiten?: (aufgabe: AufgabeAnzeige) => void
}
@@ -44,6 +55,7 @@ export function AufgabenZeile({
bereiche = [],
mitEinsortieren = false,
mitUebernehmen = false,
mitSpiegeln = false,
onBearbeiten,
}: Eigenschaften) {
const router = useRouter()
@@ -198,6 +210,29 @@ export function AufgabenZeile({
</div>
<div className="flex shrink-0 items-center gap-1">
{mitSpiegeln && (
<Button
art="leise"
groesse="quadrat"
title={
aufgabe.spiegeln
? 'Geht mit nach Hause – hier abschalten'
: 'Mit nach Hause spiegeln'
}
disabled={laeuft}
onClick={() => {
const daten = new FormData()
daten.set('id', aufgabe.id)
mitRueckmeldung(() => spiegelnUmschalten(daten))
}}
>
<Share2
className={aufgabe.spiegeln ? 'text-betont' : undefined}
aria-hidden
/>
<span className="sr-only">Spiegeln</span>
</Button>
)}
{onBearbeiten && (
<Button
art="leise"