UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: быстрый выбор проектов на home
This commit is contained in:
@@ -43,11 +43,16 @@ export function HomeProjectStack(props: HomeProjectStackProps) {
|
||||
} = props;
|
||||
|
||||
const activeProject = projects.find((project: THomeProjectData) => project.id === selectedProjectId);
|
||||
const orderedProjects = activeProject
|
||||
const selectedProject = activeProject ?? projects[0];
|
||||
const selectedProjectIndex = Math.max(
|
||||
projects.findIndex((project: THomeProjectData) => project.id === selectedProject?.id),
|
||||
0
|
||||
);
|
||||
const stackProjects = activeProject
|
||||
? [activeProject, ...projects.filter((project: THomeProjectData) => project.id !== activeProject.id)]
|
||||
: projects;
|
||||
|
||||
const visibleProjects = orderedProjects.slice(0, STACK_VISIBLE_LIMIT);
|
||||
const visibleProjects = stackProjects.slice(0, STACK_VISIBLE_LIMIT);
|
||||
const activityCountByProject = (recents ?? []).reduce<Record<string, number>>((acc, activity) => {
|
||||
const projectId = getActivityProjectId(activity);
|
||||
if (!projectId) return acc;
|
||||
@@ -56,8 +61,6 @@ export function HomeProjectStack(props: HomeProjectStackProps) {
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const selectedProject =
|
||||
orderedProjects.find((project: THomeProjectData) => project.id === selectedProjectId) ?? orderedProjects[0];
|
||||
const stackHeight =
|
||||
visibleProjects.length > 0 ? ACTIVE_CARD_HEIGHT + (visibleProjects.length - 1) * STACK_OFFSET : 228;
|
||||
const isHorizontal = orientation === "horizontal";
|
||||
@@ -191,19 +194,7 @@ export function HomeProjectStack(props: HomeProjectStackProps) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isHorizontal ? (
|
||||
<div className="nodedc-home-project-deck-scroller">
|
||||
<div className="nodedc-home-project-deck-row flex items-start px-1 py-2">
|
||||
{visibleProjects.map((project: THomeProjectData, index: number) => renderProjectCard(project, index, true))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative" style={{ height: `${stackHeight}px` }}>
|
||||
{visibleProjects.map((project: THomeProjectData, index: number) => renderProjectCard(project, index, false))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="nodedc-home-project-quick-section mt-4 rounded-[24px] bg-black/10 p-4 xl:mt-auto">
|
||||
<div className="nodedc-home-project-quick-section mb-4 rounded-[24px] bg-black/10 p-4">
|
||||
<div className="mb-3 flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<div className="text-13 font-semibold text-primary">Быстрый выбор</div>
|
||||
@@ -216,9 +207,11 @@ export function HomeProjectStack(props: HomeProjectStackProps) {
|
||||
</div>
|
||||
|
||||
<div className="nodedc-home-project-quick-list">
|
||||
{orderedProjects.map((project: THomeProjectData) => {
|
||||
{projects.map((project: THomeProjectData, index: number) => {
|
||||
const analytics = analyticsMap[project.id];
|
||||
const isActive = project.id === selectedProject?.id;
|
||||
const completionRate = getCompletionRate(analytics);
|
||||
const distanceFromActive = Math.min(Math.abs(index - selectedProjectIndex), 2);
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -226,6 +219,7 @@ export function HomeProjectStack(props: HomeProjectStackProps) {
|
||||
type="button"
|
||||
className="nodedc-home-project-quick-button"
|
||||
data-active={isActive}
|
||||
data-distance={distanceFromActive}
|
||||
aria-label={
|
||||
isActive ? `Открыть рабочую область проекта ${project.name}` : `Выбрать проект ${project.name}`
|
||||
}
|
||||
@@ -240,45 +234,54 @@ export function HomeProjectStack(props: HomeProjectStackProps) {
|
||||
>
|
||||
<span className="nodedc-home-project-quick-main">
|
||||
<span className="nodedc-home-project-quick-logo">
|
||||
<Logo logo={project.logo_props} size={14} />
|
||||
<FolderOpenDot className="size-4" />
|
||||
</span>
|
||||
<span className="truncate">{project.identifier}</span>
|
||||
</span>
|
||||
<span className="nodedc-home-project-quick-metric">
|
||||
<span className="nodedc-home-project-quick-dot" aria-hidden="true" />
|
||||
<span>{getCompletionRate(analytics)}%</span>
|
||||
<span className="nodedc-home-project-quick-name truncate">{project.name}</span>
|
||||
</span>
|
||||
<span className="nodedc-home-project-quick-rate">{completionRate}%</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedProject && (
|
||||
<div className="nodedc-home-project-focus-grid mt-4 grid grid-cols-2 gap-3 md:grid-cols-3">
|
||||
<div className="nodedc-home-project-focus-item px-3 py-2">
|
||||
<div className="text-[11px] tracking-[0.18em] text-placeholder uppercase">Фокус</div>
|
||||
<div className="nodedc-home-project-focus-value mt-1 text-13 font-semibold text-primary">
|
||||
{selectedProject.identifier}
|
||||
</div>
|
||||
</div>
|
||||
<div className="nodedc-home-project-focus-item px-3 py-2">
|
||||
<div className="flex items-center gap-1 text-[11px] tracking-[0.18em] text-placeholder uppercase">
|
||||
<UsersRound className="size-3.5" />
|
||||
<span>Команда</span>
|
||||
</div>
|
||||
<div className="nodedc-home-project-focus-value mt-1 text-13 font-semibold text-primary">
|
||||
{analyticsMap[selectedProject.id]?.total_members ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
<div className="nodedc-home-project-focus-item px-3 py-2">
|
||||
<div className="text-[11px] tracking-[0.18em] text-placeholder uppercase">Контур</div>
|
||||
<div className="nodedc-home-project-focus-value mt-1 text-13 font-semibold text-primary">
|
||||
{activityCountByProject[selectedProject.id] ?? 0} касаний
|
||||
</div>
|
||||
{isHorizontal ? (
|
||||
<div className="nodedc-home-project-deck-scroller">
|
||||
<div className="nodedc-home-project-deck-row flex items-start px-1 py-2">
|
||||
{visibleProjects.map((project: THomeProjectData, index: number) => renderProjectCard(project, index, true))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative" style={{ height: `${stackHeight}px` }}>
|
||||
{visibleProjects.map((project: THomeProjectData, index: number) => renderProjectCard(project, index, false))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedProject && (
|
||||
<div className="nodedc-home-project-focus-grid mt-4 grid grid-cols-2 gap-3 md:grid-cols-3">
|
||||
<div className="nodedc-home-project-focus-item px-3 py-2">
|
||||
<div className="text-[11px] tracking-[0.18em] text-placeholder uppercase">Фокус</div>
|
||||
<div className="nodedc-home-project-focus-value mt-1 text-13 font-semibold text-primary">
|
||||
{selectedProject.identifier}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="nodedc-home-project-focus-item px-3 py-2">
|
||||
<div className="flex items-center gap-1 text-[11px] tracking-[0.18em] text-placeholder uppercase">
|
||||
<UsersRound className="size-3.5" />
|
||||
<span>Команда</span>
|
||||
</div>
|
||||
<div className="nodedc-home-project-focus-value mt-1 text-13 font-semibold text-primary">
|
||||
{analyticsMap[selectedProject.id]?.total_members ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
<div className="nodedc-home-project-focus-item px-3 py-2">
|
||||
<div className="text-[11px] tracking-[0.18em] text-placeholder uppercase">Контур</div>
|
||||
<div className="nodedc-home-project-focus-value mt-1 text-13 font-semibold text-primary">
|
||||
{activityCountByProject[selectedProject.id] ?? 0} касаний
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user