import React, { useEffect, useState } from "react"; import { Autocomplete, Button, Chip, Stack, TextField } from "@mui/material"; import { DateTime } from "luxon"; import { useSnackbar } from "notistack"; import ActionBar from "../../../../components/ActionBar"; import Card from "../../../../components/Card"; import CardActions from "../../../../components/Card/CardActions"; import CardCancelButton from "../../../../components/Card/CardCancelButton"; import CardContent from "../../../../components/Card/CardContent"; import CardFieldSwitch from "../../../../components/Card/CardFieldSwitch"; import CardFieldText from "../../../../components/Card/CardFieldText"; import CardHeader from "../../../../components/Card/CardHeader"; import CardRow from "../../../../components/Card/CardRow"; import CardSaveButton from "../../../../components/Card/CardSaveButton"; import { ContribInlines } from "../../../../components/ContribInlines"; import { Header } from "../../../../components/Header"; import { Page } from "../../../../components/Page"; import { TitleBar } from "../../../../components/TitleBar"; import Content, { ContentWrapperWithActionBar, LeftColumn, RightColumn, } from "../../../../containers/Content"; import { useApi } from "../../../../contexts/ApiContext"; import { useCardContext } from "../../../../contexts/CardContext"; import { useI18n } from "../../../../contexts/I18nContext"; import { useUser } from "../../../../contexts/UserContext"; import { PageComponent } from "../../../../types"; import { hasPermission } from "../../../../util/has_permission"; import { GroupOption, StaffDetail, StaffOptions } from "../../types/staff"; interface StaffUpdateValues { is_active: string; groups: string; } const StaffDetailPage: PageComponent = ({ data }) => { const { t } = useI18n(); const api = useApi(); const { user: currentUser } = useUser(); const { enqueueSnackbar } = useSnackbar(); const [user, setUser] = useState(data); const [availableGroups, setAvailableGroups] = useState([]); const [isResettingPassword, setIsResettingPassword] = useState(false); const formatDateTime = (dateString: string | null) => { if (!dateString) return t("Never"); return DateTime.fromISO(dateString as string).toISODate() ?? undefined; }; useEffect(() => { const fetchOptions = async () => { const response = await api.operations["user.staff:options"].call({}); if (response.ok) { const options: StaffOptions = await response.json(); setAvailableGroups(options.groups); } }; fetchOptions(); }, [api]); const handleSubmit = async (values: StaffUpdateValues) => { const response = await api.operations["user.staff:update"].call({ params: { id: user.id }, body: { is_active: values.is_active === "on", groups: values.groups ? values.groups .split(",") .map((id) => parseInt(id.trim(), 10)) .filter((id) => !isNaN(id)) : [], }, }); if (response.ok) { const updatedUser = await response.json(); setUser(updatedUser); return t("User updated successfully"); } throw new Error("Failed to update user"); }; const handlePasswordReset = async () => { setIsResettingPassword(true); try { const response = await api.operations["user.staff:password-reset-request"].call({ body: { email: user.email, }, }); if (response.ok) { const data = await response.json(); enqueueSnackbar(data.message || t("Password reset link sent successfully"), { variant: "success", }); } else { const errorData = await response.json(); if (Array.isArray(errorData.detail)) { enqueueSnackbar(errorData.detail[0]?.msg || t("Failed to send password reset link"), { variant: "error", }); } else if (typeof errorData.detail === "string") { enqueueSnackbar(errorData.detail, { variant: "error" }); } else { enqueueSnackbar(t("Failed to send password reset link"), { variant: "error" }); } } } catch (error) { console.error("[PASSWORD_RESET]", error); enqueueSnackbar(t("Failed to send password reset link"), { variant: "error" }); } finally { setIsResettingPassword(false); } }; const GroupsField = () => { const { isEditing } = useCardContext(); const [selectedGroups, setSelectedGroups] = useState( availableGroups.filter((g) => user.groups.includes(g.name)), ); if (isEditing) { return ( <> option.name} isOptionEqualToValue={(option, value) => option.id === value.id} options={availableGroups} renderInput={(params) => ( )} renderTags={(value, getTagProps) => value.map((option, index) => ( )) } size="small" value={selectedGroups} onChange={(_, newValue) => setSelectedGroups(newValue)} /> g.id).join(", ")} /> ); } return ( {t("Groups")} {user.groups.length > 0 ? ( user.groups.map((group) => ) ) : ( {t("No groups")} )} ); }; return (
isCompact={true} isEditable={hasPermission(currentUser, "user.change_user")} onSubmit={handleSubmit} > {hasPermission(currentUser, "user.change_user") && ( )}
); }; export default StaffDetailPage;