refactor(frontend): remove redundant department panels
This commit is contained in:
parent
64eafeea46
commit
969554bf3f
@ -232,10 +232,10 @@
|
||||
Использовать как референс для:
|
||||
|
||||
- detail screen, который должен уметь переключаться между `showcase` и `management` без смены route и без ощущения двух разных страниц
|
||||
- page-level controller для domain actions вокруг одного объекта: overview, members, head management, roles
|
||||
- richer detail layout, где есть сильный header, один основной roster-блок и один secondary-блок ролей, без раздувания служебных summary-panels
|
||||
- page-level controller для domain actions вокруг одного объекта: overview, members и inline management actions
|
||||
- richer detail layout, где есть сильный header и один основной roster/workspace-блок, без раздувания служебных summary-panels и вторичных management-panels
|
||||
- member roster с role assignment и person-chip подачей вместо сырых строк участников
|
||||
- role management на уровне названий ролей и назначений участникам, без фронтового permission-editor
|
||||
- role management на уровне названий ролей и назначений участникам, встроенный в roster, без фронтового permission-editor
|
||||
|
||||
С оговоркой:
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
| `users/directory` | `mixed` | `users/page.tsx` и `employees/page.tsx` уже живут в app-surface language, сценарии user/employee navigation выровнены и читаются как один cluster | список людей всё ещё проще и беднее по interaction density, чем основные рабочие модули, поэтому это не полноценный stylistic reference | считать baseline-pass завершённым и трогать дальше только при появлении richer directory requirements |
|
||||
| `home/feed` | `mixed` | сильный app-shell fit, хорошая operational density, уже много app primitives | page очень большой, комментарии и post-compose logic перегружены, встречаются legacy `rounded-md` fragments | использовать как reference по feed density, но разрезать modal/comments/post flows и дочистить geometry |
|
||||
| `notifications` | `mixed` | близок к operational app-style, фильтры и list hierarchy уже читаются системно | page-level logic пока монолитная, нет выделенного feature controller, есть место для уплотнения и симметрии с messages/requests | после people cluster сделать короткий structural pass без крупного редизайна |
|
||||
| `departments` | `mixed` | detail screen уже работает как dual-mode page: showcase-first для обычного просмотра и встроенный management-layer для управленцев, page-level controller вынесен в hook | список отделов всё ещё проще detail screen, а сам модуль ещё не проверен длинным visual-pass и живым backend-сценарием | считать `departments/[id]` baseline reference для object-detail pages с mixed audience и затем при желании подтянуть list screen до той же richness |
|
||||
| `departments` | `mixed` | detail screen уже работает как dual-mode page: showcase-first для обычного просмотра и встроенный inline management-layer для управленцев, page-level controller вынесен в hook | список отделов всё ещё проще detail screen, а сам модуль ещё не проверен длинным visual-pass и живым backend-сценарием | считать `departments/[id]` baseline reference для object-detail pages с mixed audience и затем при желании подтянуть list screen до той же richness |
|
||||
| `auth/legal` | `mixed` | изолированные flows уже отделены от app-shell, общая подача аккуратная | `rounded-[2rem]` и локальная hero-card геометрия образуют мини-систему отдельно от app shell | не смешивать с operational refactor; сделать отдельный compact pass по auth/legal geometry |
|
||||
| `documents/dashboard` | `legacy-heavy` | есть готовые data blocks | заметны старые rounded patterns и мелкие utility-решения | включить в documents refactor pass |
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
|
||||
- у `settings` после controller extraction резать section-level UI и только затем geometry
|
||||
- у `departments` держать detail screen в логике `showcase + management layer`, а не возвращать его в mini-admin panel
|
||||
- detail screen `departments/[id]` уже не считать пустой зоной; дальнейшие работы здесь должны быть про polishing, а не про возврат permission-editor и служебных панелей
|
||||
- detail screen `departments/[id]` уже не считать пустой зоной; дальнейшие работы здесь должны быть про polishing, а не про возврат permission-editor, служебных панелей и отдельных role/head blocks
|
||||
- у `auth/legal` выровнять isolated geometry отдельно от app-shell жанра
|
||||
|
||||
### Волна 7. Calendar special audit
|
||||
|
||||
@ -18,13 +18,12 @@ import {
|
||||
} from "lucide-react";
|
||||
|
||||
import { AppShell } from "@/components/AppShell";
|
||||
import { DepartmentPersonChip } from "@/components/departments/DepartmentPersonChip";
|
||||
import { RequestAvatar } from "@/components/requests/RequestAvatar";
|
||||
import { SearchableSelectSingle } from "@/components/shared/SearchableSelect";
|
||||
import { Modal } from "@/components/ui/Modal";
|
||||
import { useDepartmentPage } from "@/hooks/useDepartmentPage";
|
||||
import { displayUserName, userProfileLink } from "@/lib/shared";
|
||||
import type { DepartmentMemberLink, DepartmentRole } from "@/types/api";
|
||||
import type { DepartmentMemberLink } from "@/types/api";
|
||||
|
||||
function MetaChip({
|
||||
children,
|
||||
@ -297,68 +296,6 @@ function DepartmentMemberRow({
|
||||
);
|
||||
}
|
||||
|
||||
function DepartmentRoleCard({
|
||||
canManageRoles,
|
||||
pendingKey,
|
||||
role,
|
||||
usageCount,
|
||||
onDelete,
|
||||
onEdit,
|
||||
}: {
|
||||
canManageRoles: boolean;
|
||||
pendingKey: string | null;
|
||||
role: DepartmentRole;
|
||||
usageCount: number;
|
||||
onDelete: (role: DepartmentRole) => Promise<void>;
|
||||
onEdit: (role: DepartmentRole) => void;
|
||||
}) {
|
||||
const isDeleting = pendingKey === `role-delete-${role.id}`;
|
||||
|
||||
return (
|
||||
<article className="app-surface-muted rounded-xl p-4">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="min-w-0">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<h3 className="text-sm font-semibold text-[var(--foreground)]">
|
||||
{role.name}
|
||||
</h3>
|
||||
<span className="app-badge inline-flex rounded-full px-2.5 py-1 text-xs font-medium">
|
||||
{usageCount} участников
|
||||
</span>
|
||||
</div>
|
||||
<p className="app-text-muted mt-2 text-sm">
|
||||
{usageCount
|
||||
? "Используется в составе отдела."
|
||||
: "Пока не назначена участникам отдела."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{canManageRoles ? (
|
||||
<div className="flex shrink-0 flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onEdit(role)}
|
||||
className="app-action-secondary inline-flex items-center gap-2 rounded-lg px-3 py-2 text-sm"
|
||||
>
|
||||
<PencilLine size={14} />
|
||||
Переименовать
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void onDelete(role)}
|
||||
disabled={isDeleting}
|
||||
className="app-action-danger inline-flex items-center gap-2 rounded-lg px-3 py-2 text-sm disabled:opacity-50"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
Удалить
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
function DepartmentEditorModal({
|
||||
draft,
|
||||
isOpen,
|
||||
@ -811,108 +748,6 @@ export default function DepartmentDetailPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isManagementMode && h.userPerms.can_change_head ? (
|
||||
<div className="app-surface-muted rounded-xl p-4">
|
||||
<div className="mb-3 flex flex-wrap items-center gap-2">
|
||||
<p className="app-card-caption">Руководитель отдела</p>
|
||||
{h.department.head ? (
|
||||
<DepartmentPersonChip
|
||||
currentUserId={h.currentUserId}
|
||||
person={h.department.head}
|
||||
subtitle={
|
||||
h.department.head.position?.name ||
|
||||
h.department.head.email
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<span className="app-text-muted text-sm">
|
||||
Руководитель пока не назначен
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<SearchableSelectSingle
|
||||
label="Назначить руководителя"
|
||||
items={h.headCandidates}
|
||||
selectedId={h.selectedHeadId}
|
||||
onSelect={h.setSelectedHeadId}
|
||||
placeholder="Выберите сотрудника"
|
||||
disabled={!h.headCandidates.length}
|
||||
/>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void h.submitHeadChange()}
|
||||
disabled={h.pendingKey === "head"}
|
||||
className="app-action-primary rounded-lg px-4 py-2 text-sm disabled:opacity-50"
|
||||
>
|
||||
Сохранить
|
||||
</button>
|
||||
{h.department.head ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void h.submitHeadRemoval()}
|
||||
disabled={h.pendingKey === "head"}
|
||||
className="app-action-secondary rounded-lg px-4 py-2 text-sm disabled:opacity-50"
|
||||
>
|
||||
Снять
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<section className="app-surface rounded-2xl p-5">
|
||||
<div className="mb-4 flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<h2 className="app-card-caption">Роли отдела</h2>
|
||||
<p className="app-text-muted mt-2 text-sm">
|
||||
{isManagementMode
|
||||
? "Именованные роли внутри отдела: их можно создавать, переименовывать и назначать участникам."
|
||||
: "Справочный список ролей, которые используются внутри отдела."}
|
||||
</p>
|
||||
</div>
|
||||
{isManagementMode && h.userPerms.can_assign_roles ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={h.openCreateRole}
|
||||
className="app-action-secondary inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm"
|
||||
>
|
||||
<Plus size={14} />
|
||||
Создать роль
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{h.roles.length ? (
|
||||
h.roles.map((role) => (
|
||||
<DepartmentRoleCard
|
||||
key={role.id}
|
||||
canManageRoles={isManagementMode && h.userPerms.can_assign_roles}
|
||||
pendingKey={h.pendingKey}
|
||||
role={role}
|
||||
usageCount={h.roleUsage[role.id] || 0}
|
||||
onDelete={h.submitRoleDelete}
|
||||
onEdit={h.openEditRole}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="app-surface-muted rounded-xl p-8 text-center">
|
||||
<p className="text-sm font-medium text-[var(--foreground)]">
|
||||
Роли ещё не созданы
|
||||
</p>
|
||||
<p className="app-text-muted mt-2 text-sm">
|
||||
{isManagementMode
|
||||
? "Создай первую роль, если внутри отдела нужно разделить обязанности."
|
||||
: "В этом отделе пока не заведены отдельные внутренние роли."}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user