From 192140c95dfb07e0a632480f7014b8c8a9cc5680 Mon Sep 17 00:00:00 2001 From: lizerk Date: Mon, 13 Apr 2026 15:53:35 +0300 Subject: [PATCH] refactor(frontend): align request person chips --- .../requests/RequestComposeModal.tsx | 17 ++++++++++++++++- .../components/requests/RequestListItem.tsx | 11 +---------- .../components/requests/RequestUserBadge.tsx | 4 +++- .../components/shared/SearchableSelect.tsx | 19 ++++++++++++++----- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/requests/RequestComposeModal.tsx b/frontend/src/components/requests/RequestComposeModal.tsx index 5942c29b..3456e20f 100644 --- a/frontend/src/components/requests/RequestComposeModal.tsx +++ b/frontend/src/components/requests/RequestComposeModal.tsx @@ -19,6 +19,7 @@ import { type Dispatch, type SetStateAction, } from "react"; +import { RequestUserBadge } from "./RequestUserBadge"; type RequestComposeModalProps = { actionError: string | null; @@ -70,7 +71,7 @@ export function RequestComposeModal({ const selectableEmployees = useMemo( () => employees .filter((employee) => !currentUserId || employee.id !== currentUserId) - .map((employee) => ({ id: employee.id, name: displayUserName(employee) })), + .map((employee) => ({ id: employee.id, name: displayUserName(employee), user: employee })), [currentUserId, employees], ); @@ -312,6 +313,13 @@ export function RequestComposeModal({ items={selectableEmployees} selectedIds={form.recipient_ids} onToggle={(id) => toggleSelection("recipient_ids", id)} + renderSelectedItem={(item) => ( + + )} /> {showCcField && ( toggleSelection("cc_user_ids", id)} + renderSelectedItem={(item) => ( + + )} /> )} diff --git a/frontend/src/components/requests/RequestListItem.tsx b/frontend/src/components/requests/RequestListItem.tsx index e18bffd0..6c489733 100644 --- a/frontend/src/components/requests/RequestListItem.tsx +++ b/frontend/src/components/requests/RequestListItem.tsx @@ -53,16 +53,7 @@ type RequestListItemProps = { function renderDecisionMaker(decisionMaker: User | undefined, currentUserId?: number | null) { if (!decisionMaker) return —; - const decisionMakerLink = userProfileLink(decisionMaker, currentUserId); - const decisionMakerName = displayUserName(decisionMaker); - - return decisionMakerLink ? ( - - {decisionMakerName} - - ) : ( - {decisionMakerName} - ); + return ; } export function RequestListItem({ diff --git a/frontend/src/components/requests/RequestUserBadge.tsx b/frontend/src/components/requests/RequestUserBadge.tsx index 88a29955..ebf6d62a 100644 --- a/frontend/src/components/requests/RequestUserBadge.tsx +++ b/frontend/src/components/requests/RequestUserBadge.tsx @@ -5,12 +5,14 @@ import { RequestAvatar } from "./RequestAvatar"; type RequestUserBadgeProps = { currentUserId?: number | null; + disableLink?: boolean; large?: boolean; person: User; }; export function RequestUserBadge({ currentUserId, + disableLink = false, large = false, person, }: RequestUserBadgeProps) { @@ -31,7 +33,7 @@ export function RequestUserBadge({ const className = `app-badge inline-flex max-w-full items-center gap-2 rounded-full ${large ? "px-3 py-1.5 text-sm" : "px-2.5 py-1 text-xs"} font-medium`; - return personLink + return personLink && !disableLink ? {chip} : {chip}; } diff --git a/frontend/src/components/shared/SearchableSelect.tsx b/frontend/src/components/shared/SearchableSelect.tsx index 090528ec..9e6e3bd9 100644 --- a/frontend/src/components/shared/SearchableSelect.tsx +++ b/frontend/src/components/shared/SearchableSelect.tsx @@ -1,6 +1,7 @@ "use client"; import { ChevronDown } from "lucide-react"; +import type { ReactNode } from "react"; import { useEffect, useRef, useState } from "react"; /* ── Single-select dropdown with search ── */ @@ -91,7 +92,7 @@ export function SearchableSelectSingle({ } /* ── Multi-select dropdown with search ── */ -export function SearchableSelectMulti({ +export function SearchableSelectMulti({ label, items, selectedIds, @@ -99,14 +100,16 @@ export function SearchableSelectMulti({ placeholder, layout = "stacked", className = "", + renderSelectedItem, }: { label: string; - items: { id: number; name: string }[]; + items: T[]; selectedIds: number[]; onToggle: (id: number) => void; placeholder?: string; layout?: "stacked" | "inline"; className?: string; + renderSelectedItem?: (item: T) => ReactNode; }) { const [open, setOpen] = useState(false); const [q, setQ] = useState(""); @@ -140,9 +143,15 @@ export function SearchableSelectMulti({ {label} {selectedItems.length > 0 ? selectedItems.map((item) => ( - - {item.name} - + renderSelectedItem ? ( + + {renderSelectedItem(item)} + + ) : ( + + {item.name} + + ) )) : {placeholder || "Выбрать..."}}