fix(frontend): localize department member validation
This commit is contained in:
parent
8d22156b36
commit
4e0f1c7889
@ -406,6 +406,7 @@ function AddMemberModal({
|
||||
isOpen,
|
||||
items,
|
||||
loading,
|
||||
memberError,
|
||||
mode,
|
||||
optionsLoading,
|
||||
onClose,
|
||||
@ -423,6 +424,7 @@ function AddMemberModal({
|
||||
isOpen: boolean;
|
||||
items: User[];
|
||||
loading: boolean;
|
||||
memberError: string | null;
|
||||
mode: DepartmentMemberModalMode;
|
||||
optionsLoading: boolean;
|
||||
onClose: () => void;
|
||||
@ -592,6 +594,12 @@ function AddMemberModal({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!directoryError && memberError ? (
|
||||
<div className="app-feedback-danger rounded-xl p-3 text-sm">
|
||||
<p>{memberError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{canAssignRoles && !directoryError ? (
|
||||
<div className="space-y-3">
|
||||
<SearchableSelectSingle
|
||||
@ -978,6 +986,7 @@ export default function DepartmentDetailPage() {
|
||||
h.assignableEmployees,
|
||||
)}
|
||||
loading={h.pendingKey === "member"}
|
||||
memberError={h.memberModalError}
|
||||
mode={h.memberModalMode}
|
||||
optionsLoading={h.employeesDirectoryLoading}
|
||||
onClose={h.closeAddMember}
|
||||
|
||||
@ -55,3 +55,31 @@ export function isDepartmentMemberModalSubmitDisabled({
|
||||
if (mode === "assignRole" && !selectedRoleId) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getDepartmentMemberModalError(message, mode) {
|
||||
const normalized = String(message || "").trim();
|
||||
if (!normalized) return null;
|
||||
|
||||
const anotherDepartmentMatch = normalized.match(
|
||||
/Employee already belongs to another active department:\s*(.+?)\.?$/i,
|
||||
);
|
||||
if (anotherDepartmentMatch) {
|
||||
const departmentName = anotherDepartmentMatch[1]?.trim();
|
||||
if (mode === "assignRole") {
|
||||
return null;
|
||||
}
|
||||
return departmentName
|
||||
? `Сотрудник уже состоит в другом активном отделе: ${departmentName}.`
|
||||
: "Сотрудник уже состоит в другом активном отделе.";
|
||||
}
|
||||
|
||||
if (/Employee is inactive\.?$/i.test(normalized)) {
|
||||
return "Можно выбрать только активного сотрудника.";
|
||||
}
|
||||
|
||||
if (/Employee not found\.?$/i.test(normalized)) {
|
||||
return "Сотрудник не найден.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
getDepartmentMemberModalError,
|
||||
getDepartmentMemberModalEmployeeLabel,
|
||||
getDepartmentMemberModalHelperText,
|
||||
getDepartmentMemberModalItems,
|
||||
@ -92,3 +93,24 @@ test("member modal helper texts reflect mode and loading state", () => {
|
||||
/любому активному сотруднику/i,
|
||||
);
|
||||
});
|
||||
|
||||
test("member modal maps backend department validation to localized inline error", () => {
|
||||
assert.equal(
|
||||
getDepartmentMemberModalError(
|
||||
"Employee already belongs to another active department: Бухгалтерия.",
|
||||
"add",
|
||||
),
|
||||
"Сотрудник уже состоит в другом активном отделе: Бухгалтерия.",
|
||||
);
|
||||
assert.equal(
|
||||
getDepartmentMemberModalError("Employee is inactive.", "add"),
|
||||
"Можно выбрать только активного сотрудника.",
|
||||
);
|
||||
assert.equal(
|
||||
getDepartmentMemberModalError(
|
||||
"Employee already belongs to another active department: Бухгалтерия.",
|
||||
"assignRole",
|
||||
),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { getDepartmentMemberModalError } from "@/components/departments/member-modal-state";
|
||||
import { useUser } from "@/contexts/UserContext";
|
||||
import { apiClient } from "@/lib/api";
|
||||
import { loadAllPages } from "@/lib/shared";
|
||||
@ -48,6 +49,7 @@ export type DepartmentPageController = {
|
||||
employeesDirectoryLoading: boolean;
|
||||
loading: boolean;
|
||||
memberModalMode: DepartmentMemberModalMode;
|
||||
memberModalError: string | null;
|
||||
members: DepartmentMemberLink[];
|
||||
pendingKey: SavingKey | null;
|
||||
roleDraft: DepartmentRoleDraft;
|
||||
@ -148,6 +150,7 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
|
||||
const [addMemberOpen, setAddMemberOpen] = useState(false);
|
||||
const [memberModalMode, setMemberModalMode] = useState<DepartmentMemberModalMode>("add");
|
||||
const [memberModalError, setMemberModalError] = useState<string | null>(null);
|
||||
const [selectedMemberId, setSelectedMemberId] = useState<number | null>(null);
|
||||
const [selectedRoleId, setSelectedRoleId] = useState<number | null>(null);
|
||||
|
||||
@ -345,6 +348,7 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
|
||||
const openAddMember = useCallback(() => {
|
||||
setMemberModalMode("add");
|
||||
setMemberModalError(null);
|
||||
setSelectedMemberId(null);
|
||||
setSelectedRoleId(null);
|
||||
setAddMemberOpen(true);
|
||||
@ -357,6 +361,7 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
|
||||
const openAssignRoleModal = useCallback(() => {
|
||||
setMemberModalMode("assignRole");
|
||||
setMemberModalError(null);
|
||||
setSelectedMemberId(null);
|
||||
setSelectedRoleId(null);
|
||||
setAddMemberOpen(true);
|
||||
@ -368,12 +373,23 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
}, [allEmployees.length, loadEmployeesDirectory]);
|
||||
|
||||
const closeAddMember = useCallback(() => {
|
||||
setMemberModalError(null);
|
||||
setSelectedMemberId(null);
|
||||
setSelectedRoleId(null);
|
||||
setMemberModalMode("add");
|
||||
setAddMemberOpen(false);
|
||||
}, []);
|
||||
|
||||
const selectMember = useCallback((id: number | null) => {
|
||||
setMemberModalError(null);
|
||||
setSelectedMemberId(id);
|
||||
}, []);
|
||||
|
||||
const selectRole = useCallback((id: number | null) => {
|
||||
setMemberModalError(null);
|
||||
setSelectedRoleId(id);
|
||||
}, []);
|
||||
|
||||
const submitAddMember = useCallback(async () => {
|
||||
if (!department || !selectedMemberId) {
|
||||
toast.error("Выберите сотрудника");
|
||||
@ -403,6 +419,7 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
}
|
||||
await loadCore();
|
||||
setAddMemberOpen(false);
|
||||
setMemberModalError(null);
|
||||
setSelectedMemberId(null);
|
||||
setSelectedRoleId(null);
|
||||
setMemberModalMode("add");
|
||||
@ -412,14 +429,21 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
: "Роль назначена сотруднику",
|
||||
);
|
||||
} catch (submitError) {
|
||||
toast.error(
|
||||
getErrorMessage(
|
||||
submitError,
|
||||
memberModalMode === "add"
|
||||
? "Не удалось добавить сотрудника"
|
||||
: "Не удалось назначить роль сотруднику",
|
||||
),
|
||||
const message = getErrorMessage(
|
||||
submitError,
|
||||
memberModalMode === "add"
|
||||
? "Не удалось добавить сотрудника"
|
||||
: "Не удалось назначить роль сотруднику",
|
||||
);
|
||||
const inlineMessage = getDepartmentMemberModalError(
|
||||
message,
|
||||
memberModalMode,
|
||||
);
|
||||
if (inlineMessage) {
|
||||
setMemberModalError(inlineMessage);
|
||||
} else {
|
||||
toast.error(message);
|
||||
}
|
||||
} finally {
|
||||
setPendingKey(null);
|
||||
}
|
||||
@ -624,6 +648,7 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
headCandidates,
|
||||
loading,
|
||||
memberModalMode,
|
||||
memberModalError,
|
||||
members,
|
||||
pendingKey,
|
||||
roleDraft,
|
||||
@ -650,8 +675,8 @@ export function useDepartmentPage(departmentId: number): DepartmentPageControlle
|
||||
setRoleDraft,
|
||||
setMemberModalMode,
|
||||
setSelectedHeadId,
|
||||
setSelectedMemberId,
|
||||
setSelectedRoleId,
|
||||
setSelectedMemberId: selectMember,
|
||||
setSelectedRoleId: selectRole,
|
||||
submitAddMember,
|
||||
submitDeleteDepartment,
|
||||
submitHeadChange,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user