Files
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

82 lines
2.8 KiB
TypeScript

import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
import { getCurrentUser } from '@/lib/auth/current'
import { visibleCaseWhere } from '@/lib/access/caseFilter'
import { visiblePersonnelWhere } from '@/lib/access/personnelFilter'
import { listRecent } from '@/lib/recent/service'
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
/** So viele Treffer je Art zeigt die Palette. Mehr passt nicht auf den Schirm. */
const LIMIT = 5
/** So viele Sprünge zeigt die Palette, solange nichts im Feld steht. */
const RECENT_IN_PALETTE = 6
/**
* Schnellsuche für die Kommandopalette.
*
* Bewusst schmal: Aktenzeichen, Titel und Namen, kein Volltext über Inhalte.
* Wer die Palette öffnet, will springen, nicht recherchieren - dafür gibt es
* `/suche`.
*
* Der Trigram-Index auf `caseNumber` und `displayName` fängt Tippfehler ab,
* siehe docs/08-suche-und-export.md.
*/
export async function GET(request: Request): Promise<Response> {
const user = await getCurrentUser()
if (!user) return NextResponse.json({ cases: [], personnel: [] }, { status: 404 })
const params = new URL(request.url).searchParams
// Leeres Feld: die eigene Sprungliste. Wer die Palette öffnet, will meistens
// dorthin zurück, wo er eben war - und nicht erst tippen.
if (params.get('recent') === '1') {
const zuletzt = await listRecent(user.permissions, RECENT_IN_PALETTE)
return NextResponse.json({
cases: [],
personnel: [],
recent: zuletzt.map((eintrag) => ({
href: eintrag.href,
primary: eintrag.label,
secondary: eintrag.secondary,
classification: eintrag.classification,
})),
})
}
const begriff = (params.get('q') ?? '').trim().slice(0, 100)
if (begriff.length < 2) return NextResponse.json({ cases: [], personnel: [] })
const enthaelt = { contains: begriff, mode: 'insensitive' as const }
const darfPersonal =
user.permissions.isAdmin || user.permissions.permissions.has('personnel.read')
const [faelle, personen] = await Promise.all([
db.case.findMany({
where: {
AND: [
visibleCaseWhere(user.permissions),
{ OR: [{ caseNumber: enthaelt }, { title: enthaelt }] },
],
},
orderBy: { updatedAt: 'desc' },
take: LIMIT,
select: { id: true, caseNumber: true, title: true, classification: true },
}),
darfPersonal
? db.personnelFile.findMany({
where: {
AND: [visiblePersonnelWhere(user.permissions), { displayName: enthaelt }],
},
orderBy: { displayName: 'asc' },
take: LIMIT,
select: { id: true, displayName: true, rank: true, classification: true },
})
: Promise.resolve([]),
])
return NextResponse.json({ cases: faelle, personnel: personen })
}