FEAT-12a: multi-select bulk 'Zur Abgabe stellen' on the Tiere list
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { de } from '../strings/de'
|
||||
import { listGerbils } from '../api/gerbils'
|
||||
import { listGerbils, updateGerbil } from '../api/gerbils'
|
||||
import { listColorVarieties } from '../api/lookups'
|
||||
import { andFilter, condition, type GridifyQuery } from '../api/gridify'
|
||||
import { GENDERS, GERBIL_STATUSES, type Gender, type GerbilStatus } from '../api/types'
|
||||
import { useApi } from '../hooks/useApi'
|
||||
import { useApi, useMutation } from '../hooks/useApi'
|
||||
import { formatDate, genderLabel, statusLabel } from '../format/labels'
|
||||
import './gerbils.css'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
@@ -64,6 +65,26 @@ export default function GerbilsPage() {
|
||||
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE))
|
||||
const items = gerbils.data?.items ?? []
|
||||
|
||||
// Multi-select bulk "Zur Abgabe stellen".
|
||||
const [selected, setSelected] = useState<Set<string>>(new Set())
|
||||
const toggleSelect = (id: string) =>
|
||||
setSelected((prev) => {
|
||||
const next = new Set(prev)
|
||||
if (next.has(id)) next.delete(id)
|
||||
else next.add(id)
|
||||
return next
|
||||
})
|
||||
const bulkForSale = useMutation((ids: string[]) =>
|
||||
Promise.all(ids.map((id) => updateGerbil(id, { status: 'ForSale' }))),
|
||||
)
|
||||
async function markSelectedForSale() {
|
||||
const result = await bulkForSale.run([...selected])
|
||||
if (result.ok) {
|
||||
setSelected(new Set())
|
||||
gerbils.reload()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="page">
|
||||
<header className="page-head">
|
||||
@@ -159,10 +180,34 @@ export default function GerbilsPage() {
|
||||
<p className="muted">{t.empty}</p>
|
||||
)}
|
||||
|
||||
{selected.size > 0 && (
|
||||
<div className="bulk-bar">
|
||||
<span>
|
||||
{selected.size} {t.countLabel}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn--primary"
|
||||
disabled={bulkForSale.pending}
|
||||
onClick={markSelectedForSale}
|
||||
>
|
||||
{de.pages.abgabe.markMultiAction}
|
||||
</button>
|
||||
{bulkForSale.error && <span className="error-text">{bulkForSale.error}</span>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{items.length > 0 && (
|
||||
<ul className="card-list">
|
||||
{items.map((g) => (
|
||||
<li key={g.id}>
|
||||
<li key={g.id} className="gerbil-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="gerbil-row__check"
|
||||
checked={selected.has(g.id)}
|
||||
onChange={() => toggleSelect(g.id)}
|
||||
aria-label={g.name}
|
||||
/>
|
||||
<Link to={`/rennmaeuse/${g.id}`} className="gerbil-card">
|
||||
<span className="gerbil-card__name">{g.name}</span>
|
||||
<span className={`badge badge--${g.status.toLowerCase()}`}>
|
||||
|
||||
Reference in New Issue
Block a user