import { DropdownField } from "./DropdownField.jsx"; import { resolveAvatarSrc, roleLabel } from "./utils.js"; import { AvatarCropEditor } from "./AvatarCropEditor.jsx"; const { useEffect, useRef, useState } = React; export function RecordModal({ open, title, tableKey, mode, fields, form, status, accessToken, onClose, onChange, onSubmit, onUploadField, onUploadFieldWithCrop, onRecropAvatar, onApplyRecrop, OverlayComponent, IconButtonComponent, UserAvatarComponent, StatusLineComponent, }) { const Overlay = OverlayComponent; const IconButton = IconButtonComponent; const UserAvatar = UserAvatarComponent; const StatusLine = StatusLineComponent; const [avatarPreviewOpen, setAvatarPreviewOpen] = useState(false); const [userEditing, setUserEditing] = useState(false); const [cropFile, setCropFile] = useState(null); // File waiting for crop selection const [cropInitial, setCropInitial] = useState(null); // {x,y,zoom} restored from avatar_crop_json const isRecropRef = useRef(false); // true when cropFile came from existing original (re-crop flow) const avatarUploadRef = useRef(null); const visibleFields = (fields || []).filter((field) => { if (field.hidden) return false; if (typeof field.visibleWhen !== "function") return true; try { return Boolean(field.visibleWhen(form || {})); } catch (_) { return true; } }); const isUserModal = tableKey === "users"; const avatarField = isUserModal ? visibleFields.find((field) => field.key === "avatar_url") : null; const topicField = isUserModal ? visibleFields.find((field) => field.key === "primary_topic_code") : null; const formFields = isUserModal ? visibleFields.filter((field) => field.key !== "avatar_url") : visibleFields; const fieldMap = new Map(visibleFields.map((field) => [field.key, field])); const avatarValue = String(form?.avatar_url || "").trim(); const userName = String(form?.name || "").trim(); const userEmail = String(form?.email || "").trim(); const userPhone = String(form?.phone || "").trim(); const userRole = roleLabel(form?.role); const topicOptions = topicField && typeof topicField.options === "function" ? topicField.options(form || {}) : []; const currentTopicValue = String(form?.primary_topic_code || "").trim(); const userTopic = (topicOptions.find((option) => String(option?.value || "") === currentTopicValue)?.label || currentTopicValue || "").trim() || "Профиль не указан"; const defaultRate = String(form?.default_rate || "").trim(); const salaryPercent = String(form?.salary_percent || "").trim(); const userActiveRaw = String(form?.is_active ?? ""); const activeLabel = userActiveRaw === "false" ? "Неактивен" : userActiveRaw === "true" || !userActiveRaw ? "Активен" : "Статус не задан"; const avatarPreviewSrc = avatarValue ? resolveAvatarSrc(avatarValue, accessToken, 512) : ""; const statusTone = userActiveRaw === "false" ? "danger" : userActiveRaw === "true" || !userActiveRaw ? "success" : "warn"; const isCreateMode = isUserModal && mode === "create"; useEffect(() => { if (!isUserModal) { setUserEditing(false); setAvatarPreviewOpen(false); return; } setUserEditing(isCreateMode); setAvatarPreviewOpen(false); }, [isCreateMode, isUserModal, open, tableKey]); if (!open) return null; const renderField = (field) => { const value = form[field.key] ?? ""; const options = typeof field.options === "function" ? field.options(form || {}) : []; const id = "record-field-" + field.key; const disabled = Boolean(field.readOnly) || (typeof field.readOnlyWhen === "function" ? Boolean(field.readOnlyWhen(form || {})) : false); if (field.type === "textarea" || field.type === "json") { return (