"use client"; import { Modal } from "@/components/ui"; import { SearchableSelectMulti, SearchableSelectSingle } from "@/components/shared/SearchableSelect"; import { getRequestDateMode, requestTypeLabels, type RequestAttachmentPreview, type RequestFormState, } from "@/hooks/useRequestsPage"; import { displayUserName } from "@/lib/shared"; import type { Request, User } from "@/types/api"; import { Paperclip, X } from "lucide-react"; import { useMemo, useRef, useState, type ChangeEvent, type Dispatch, type SetStateAction, } from "react"; import { RequestUserBadge } from "./RequestUserBadge"; type RequestComposeModalProps = { actionError: string | null; busyKey: string | null; currentUserId?: number | null; editingRequest: Request | null; employees: User[]; recipientEmployees: User[]; form: RequestFormState; mode: "create" | "edit"; onClose: () => void; onPreviewAttachment: (preview: RequestAttachmentPreview) => void; onSubmit: (mode: "create" | "edit", saveAs: "draft" | "submit") => void | Promise; setForm: Dispatch>; }; const allowedAttachmentExtensions = new Set(["pdf", "jpg", "jpeg", "png"]); function FieldLabel({ children, required = false, }: { children: string; required?: boolean; }) { return ( ); } export function RequestComposeModal({ actionError, busyKey, currentUserId, editingRequest, employees, recipientEmployees, form, mode, onClose, onPreviewAttachment, onSubmit, setForm, }: RequestComposeModalProps) { const fileInputRef = useRef(null); const [showCcField, setShowCcField] = useState(() => form.cc_user_ids.length > 0); const selectableEmployees = useMemo( () => employees .filter((employee) => !currentUserId || employee.id !== currentUserId) .map((employee) => ({ id: employee.id, name: displayUserName(employee), user: employee })), [currentUserId, employees], ); const selectableRecipients = useMemo( () => recipientEmployees .filter((employee) => !currentUserId || employee.id !== currentUserId) .map((employee) => ({ id: employee.id, name: displayUserName(employee), user: employee })), [currentUserId, recipientEmployees], ); const dateMode = getRequestDateMode(form.type); const showOptionalDates = dateMode === "optional" && Boolean(form.date_from || form.date_to); const existingAttachmentUrl = editingRequest?.attachment_url || editingRequest?.attachment || ""; const existingAttachmentName = existingAttachmentUrl ? decodeURIComponent(existingAttachmentUrl.split("/").pop() || "Вложение") : ""; const toggleSelection = (field: "recipient_ids" | "cc_user_ids", id: number) => { setForm((prev) => ({ ...prev, [field]: prev[field].includes(id) ? prev[field].filter((item) => item !== id) : [...prev[field], id], })); }; const handleAttachmentChange = (event: ChangeEvent) => { const file = event.target.files?.[0] || null; if (file) { const extension = file.name.split(".").pop()?.toLowerCase() || ""; if (!allowedAttachmentExtensions.has(extension)) { event.target.value = ""; return; } } setForm((prev) => ({ ...prev, attachment: file })); }; return (
)} > {actionError && (

{actionError}

)}
({ id: value, name: label }))} selectedId={form.type || null} onSelect={(value) => setForm((prev) => ({ ...prev, type: typeof value === "string" ? value : "", date_from: "", date_to: "", time_from: "", time_to: "", }))} />
{dateMode === "range" ? "Период" : dateMode === "timed" ? "Дата и время" : "Дата"} {dateMode !== "optional" ? * : null} {dateMode === "optional" && !showOptionalDates && ( Даты не обязательны для этого типа )}
{dateMode === "timed" ? (
) : dateMode === "range" ? (
setForm((prev) => ({ ...prev, date_from: event.target.value }))} className="app-input rounded-lg px-4 py-3 text-sm" /> setForm((prev) => ({ ...prev, date_to: event.target.value }))} className="app-input rounded-lg px-4 py-3 text-sm" />
) : dateMode === "single" ? ( setForm((prev) => ({ ...prev, date_from: event.target.value }))} className="app-input w-full rounded-lg px-4 py-3 text-sm" /> ) : showOptionalDates ? (
setForm((prev) => ({ ...prev, date_from: event.target.value }))} className="app-input rounded-lg px-4 py-3 text-sm" /> setForm((prev) => ({ ...prev, date_to: event.target.value }))} className="app-input rounded-lg px-4 py-3 text-sm" />
) : ( )}

Вложение

Прикрепите подтверждающий файл, если он нужен для заявления.

{(form.attachment || existingAttachmentUrl) && (
{existingAttachmentUrl && !form.attachment && ( )} {form.attachment && ( {form.attachment.name} )}
)}

Адресация

Решение принимает только пользователь из поля «Кому».

{!showCcField && ( )}
toggleSelection("recipient_ids", id)} renderSelectedItem={(item) => ( )} /> {showCcField && ( toggleSelection("cc_user_ids", id)} renderSelectedItem={(item) => ( )} /> )}
Тема setForm((prev) => ({ ...prev, title: event.target.value }))} placeholder="Например: Отпуск с 12 по 16 августа" className="app-input w-full rounded-lg px-4 py-3 text-sm" />
Сообщение