Files
vkv/resources/js/pages/AdminUsersPage.tsx
Zdeněk Burda 41e3ce6f25 Initial commit
2026-01-09 21:26:40 +01:00

185 lines
5.0 KiB
TypeScript

import React, { useEffect, useMemo, useState } from "react";
import axios from "axios";
import { Button } from "@heroui/react";
import { useTranslation } from "react-i18next";
import AdminUsersTable from "@/components/admin/users/AdminUsersTable";
import AdminUserForm from "@/components/admin/users/AdminUserForm";
type UserItem = {
id: number;
name: string;
email: string;
is_admin: boolean;
is_active: boolean;
};
type FormMode = "none" | "create" | "edit";
type PaginatedResponse<T> = {
data: T[];
};
export default function AdminUsersPage() {
const { t } = useTranslation("common");
const [items, setItems] = useState<UserItem[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [refreshKey, setRefreshKey] = useState(0);
const [query, setQuery] = useState("");
const [formMode, setFormMode] = useState<FormMode>("none");
const [editing, setEditing] = useState<UserItem | null>(null);
const [submitting, setSubmitting] = useState(false);
const [formError, setFormError] = useState<string | null>(null);
useEffect(() => {
let active = true;
(async () => {
try {
setLoading(true);
setError(null);
const res = await axios.get<PaginatedResponse<UserItem>>("/api/users", {
headers: { Accept: "application/json" },
params: { per_page: 200, query },
withCredentials: true,
});
if (!active) return;
setItems(res.data.data ?? []);
} catch (e: any) {
if (!active) return;
setError(
e?.response?.data?.message ??
(t("admin_users_load_failed") ?? "Nepodařilo se načíst uživatele.")
);
} finally {
if (active) setLoading(false);
}
})();
return () => {
active = false;
};
}, [refreshKey, query, t]);
const visibleItems = useMemo(() => items, [items]);
const resetForm = () => {
setEditing(null);
setFormError(null);
};
const openCreate = () => {
resetForm();
setFormMode("create");
};
const openEdit = (item: UserItem) => {
resetForm();
setEditing(item);
setFormMode("edit");
};
const deactivateUser = async (item: UserItem) => {
try {
await axios.delete(`/api/users/${item.id}`, {
headers: { Accept: "application/json" },
withCredentials: true,
withXSRFToken: true,
});
setRefreshKey((k) => k + 1);
} catch (e: any) {
setError(
e?.response?.data?.message ??
(t("admin_users_deactivate_failed") ?? "Nepodařilo se deaktivovat uživatele.")
);
}
};
const handleSubmit = async (payload: {
name: string;
email: string;
password?: string;
is_admin: boolean;
is_active: boolean;
}) => {
setFormError(null);
try {
setSubmitting(true);
await axios.get("/sanctum/csrf-cookie", { withCredentials: true });
if (formMode === "edit" && editing) {
await axios.put(`/api/users/${editing.id}`, payload, {
headers: { Accept: "application/json" },
withCredentials: true,
withXSRFToken: true,
});
} else {
await axios.post("/api/users", payload, {
headers: { Accept: "application/json" },
withCredentials: true,
withXSRFToken: true,
});
}
setFormMode("none");
resetForm();
setRefreshKey((k) => k + 1);
} catch (e: any) {
setFormError(
e?.response?.data?.message ??
(t("admin_users_save_failed") ?? "Chyba při ukládání uživatele.")
);
} finally {
setSubmitting(false);
}
};
return (
<div className="space-y-6">
<div className="flex flex-wrap gap-3 items-center">
<h1 className="text-xl font-semibold">{t("admin_users_title") ?? "Uživatelé"}</h1>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={t("admin_users_search") ?? "Hledat jméno nebo email"}
className="border rounded px-2 py-1 text-sm"
/>
</div>
{error && <div className="text-red-600 text-sm">{error}</div>}
<AdminUsersTable
items={visibleItems}
loading={loading}
onEdit={openEdit}
onDeactivate={deactivateUser}
/>
<Button onPress={openCreate}>{t("admin_users_create") ?? "Nový uživatel"}</Button>
{formMode !== "none" && (
<Button
variant="light"
onPress={() => {
setFormMode("none");
resetForm();
}}
>
{t("admin_form_close") ?? "Zavřít formulář"}
</Button>
)}
{formMode !== "none" && (
<AdminUserForm
mode={formMode}
editing={editing}
submitting={submitting}
serverError={formError}
onSubmit={handleSubmit}
onCancel={() => {
setFormMode("none");
resetForm();
}}
/>
)}
</div>
);
}