M18: implement operational workspaces
This commit is contained in:
@@ -12,13 +12,14 @@ const STATUS_OPTIONS = ["available", "rented", "cleaning", "maintenance", "block
|
||||
|
||||
export function Vehicles() {
|
||||
const { t } = useTranslation("fleet");
|
||||
const { formatNumber } = useLocaleFormat();
|
||||
const { formatDateTime, formatNumber } = useLocaleFormat();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [vehicles, setVehicles] = useState<Page<Vehicle> | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const status = searchParams.get("status") ?? "";
|
||||
const attentionOnly = searchParams.get("attention_only") === "true";
|
||||
const query = searchParams.get("q") ?? "";
|
||||
const location = searchParams.get("location") ?? "";
|
||||
const page = Math.max(1, Number(searchParams.get("page") ?? "1") || 1);
|
||||
|
||||
function updateFilters(updates: Record<string, string | boolean | number | null>) {
|
||||
@@ -37,13 +38,14 @@ export function Vehicles() {
|
||||
if (status) params.set("status", status);
|
||||
if (attentionOnly) params.set("attention_only", "true");
|
||||
if (query) params.set("query", query);
|
||||
if (location) params.set("location", location);
|
||||
params.set("page", String(page));
|
||||
params.set("page_size", "25");
|
||||
api
|
||||
.get<Page<Vehicle>>(`/api/v1/vehicles?${params.toString()}`)
|
||||
.then(setVehicles)
|
||||
.catch(() => setError(t("list.unavailable")));
|
||||
}, [status, attentionOnly, query, page, t]);
|
||||
}, [status, attentionOnly, query, location, page, t]);
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
@@ -73,6 +75,7 @@ export function Vehicles() {
|
||||
/>
|
||||
{t("list.attentionOnly")}
|
||||
</label>
|
||||
<label>{t("list.locationFilter")}<input value={location} onChange={(event) => updateFilters({ location: event.target.value, page: 1 })} placeholder={t("list.locationPlaceholder")} /></label>
|
||||
</form>
|
||||
|
||||
{error && <ErrorState message={error} />}
|
||||
@@ -88,6 +91,8 @@ export function Vehicles() {
|
||||
<th scope="col">{t("list.columns.location")}</th>
|
||||
<th scope="col">{t("list.columns.status")}</th>
|
||||
<th scope="col">{t("list.columns.odometer")}</th>
|
||||
<th scope="col">{t("list.columns.service")}</th>
|
||||
<th scope="col">{t("list.columns.nextBooking")}</th>
|
||||
<th scope="col">{t("list.columns.attention")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -106,7 +111,9 @@ export function Vehicles() {
|
||||
<StatusBadge status={v.operational_status} label={t(`statuses.${v.operational_status}`, { defaultValue: v.operational_status })} />
|
||||
</td>
|
||||
<td data-label={t("list.columns.odometer")}>{formatNumber(v.odometer_km)}</td>
|
||||
<td data-label={t("list.columns.attention")}>{v.attention ? <span className="attention-flag">{t("list.needsAttention")}</span> : "—"}</td>
|
||||
<td data-label={t("list.columns.service")} className={v.service_remaining_km <= 0 ? "is-overdue" : ""}>{v.service_remaining_km <= 0 ? t("list.serviceDue") : t("list.serviceRemaining", { count: formatNumber(v.service_remaining_km) })}</td>
|
||||
<td data-label={t("list.columns.nextBooking")}>{v.next_booking_ref && v.next_booking_at ? <Link className="cell-link" to={`/bookings/${v.next_booking_ref}`}>{v.next_booking_ref}<span className="table-secondary">{formatDateTime(v.next_booking_at)}</span></Link> : "—"}</td>
|
||||
<td data-label={t("list.columns.attention")}>{v.attention && v.attention_reason ? <span className="attention-flag">{t(`list.attentionReasons.${v.attention_reason}`)}</span> : "—"}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user