UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: дизайн вкладки хранилища воркспейса
This commit is contained in:
parent
3231ee9b55
commit
b4f9c58eb5
|
|
@ -10,6 +10,8 @@ import useSWR from "swr";
|
|||
// plane imports
|
||||
import type { IWorkspaceStorageProjectSummary } from "@plane/types";
|
||||
import { cn } from "@plane/utils";
|
||||
// components
|
||||
import { SettingsHeading } from "@/components/settings/heading";
|
||||
// services
|
||||
import { WorkspaceService } from "@/services/workspace.service";
|
||||
|
||||
|
|
@ -38,12 +40,12 @@ const StatCard = (props: {
|
|||
const { title, value, caption, icon: Icon, tone = "default" } = props;
|
||||
|
||||
return (
|
||||
<div className="rounded-[28px] border border-white/5 bg-custom-background-80/85 p-5 shadow-[0_22px_80px_rgba(0,0,0,0.28)]">
|
||||
<div className="nodedc-settings-card p-5">
|
||||
<div className="mb-5 flex items-start justify-between gap-4">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-tertiary">{title}</div>
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-10 shrink-0 items-center justify-center rounded-full bg-custom-background-90 text-secondary",
|
||||
"flex size-10 shrink-0 items-center justify-center rounded-full bg-white/5 text-secondary",
|
||||
tone === "accent" && "bg-accent-primary/20 text-accent-primary",
|
||||
tone === "warning" && "bg-red-500/15 text-red-300"
|
||||
)}
|
||||
|
|
@ -62,31 +64,52 @@ const ProjectStorageRow = (props: { project: IWorkspaceStorageProjectSummary; ma
|
|||
const ratio = maxSize > 0 ? Math.max((project.logical_size / maxSize) * 100, project.logical_size > 0 ? 3 : 0) : 0;
|
||||
|
||||
return (
|
||||
<tr className="border-b border-white/6 last:border-0">
|
||||
<td className="py-4 pr-4 align-middle">
|
||||
<div className="flex min-w-0 flex-col">
|
||||
<span className="truncate text-14 font-semibold text-primary">{project.name}</span>
|
||||
<span className="mt-1 text-11 uppercase tracking-[0.16em] text-tertiary">{project.identifier}</span>
|
||||
<div className="nodedc-settings-field grid min-w-[62rem] grid-cols-[minmax(14rem,1.35fr)_0.55fr_0.55fr_minmax(15rem,1.45fr)_0.75fr_0.75fr_0.65fr_0.65fr] items-center gap-4 px-4 py-3.5">
|
||||
<div className="flex min-w-0 flex-col">
|
||||
<span className="truncate text-14 font-semibold text-primary">{project.name}</span>
|
||||
<span className="mt-1 text-11 uppercase tracking-[0.16em] text-tertiary">{project.identifier}</span>
|
||||
</div>
|
||||
<StorageValue>{formatCount(project.file_count)}</StorageValue>
|
||||
<StorageValue>{formatCount(project.blob_count)}</StorageValue>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<div className="h-2 flex-1 overflow-hidden rounded-full bg-white/6">
|
||||
<div className="h-full rounded-full bg-accent-primary" style={{ width: `${ratio}%` }} />
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-4 align-middle text-13 text-secondary">{formatCount(project.file_count)}</td>
|
||||
<td className="px-4 py-4 align-middle text-13 text-secondary">{formatCount(project.blob_count)}</td>
|
||||
<td className="px-4 py-4 align-middle">
|
||||
<div className="flex min-w-[12rem] items-center gap-3">
|
||||
<div className="h-2 flex-1 overflow-hidden rounded-full bg-custom-background-90">
|
||||
<div className="h-full rounded-full bg-accent-primary" style={{ width: `${ratio}%` }} />
|
||||
</div>
|
||||
<span className="w-20 text-right text-13 font-medium text-primary">{formatBytes(project.logical_size)}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-4 align-middle text-13 text-secondary">{formatBytes(project.physical_size)}</td>
|
||||
<td className="px-4 py-4 align-middle text-13 text-accent-primary">{formatBytes(project.dedup_savings)}</td>
|
||||
<td className="px-4 py-4 align-middle text-13 text-secondary">{formatCount(project.failed_upload_count)}</td>
|
||||
<td className="pl-4 py-4 align-middle text-13 text-secondary">{formatCount(project.soft_deleted_count)}</td>
|
||||
</tr>
|
||||
<span className="w-20 text-right text-13 font-medium text-primary">{formatBytes(project.logical_size)}</span>
|
||||
</div>
|
||||
<StorageValue>{formatBytes(project.physical_size)}</StorageValue>
|
||||
<StorageValue accent>{formatBytes(project.dedup_savings)}</StorageValue>
|
||||
<StorageValue warning={project.failed_upload_count > 0}>{formatCount(project.failed_upload_count)}</StorageValue>
|
||||
<StorageValue>{formatCount(project.soft_deleted_count)}</StorageValue>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const StorageValue = (props: { accent?: boolean; children: string; warning?: boolean }) => (
|
||||
<span
|
||||
className={cn(
|
||||
"text-13 font-medium text-secondary",
|
||||
props.accent && "text-accent-primary",
|
||||
props.warning && "text-red-300"
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
|
||||
const ProjectStorageHeader = () => (
|
||||
<div className="grid min-w-[62rem] grid-cols-[minmax(14rem,1.35fr)_0.55fr_0.55fr_minmax(15rem,1.45fr)_0.75fr_0.75fr_0.65fr_0.65fr] gap-4 px-4 text-[11px] font-semibold uppercase tracking-[0.16em] text-tertiary">
|
||||
<span>Проект</span>
|
||||
<span>Файлы</span>
|
||||
<span>Blob</span>
|
||||
<span>Логический объем</span>
|
||||
<span>Физический</span>
|
||||
<span>Дедуп</span>
|
||||
<span>Ошибки</span>
|
||||
<span>Удалено</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
type TStorageSettingsContentProps = {
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
|
@ -101,24 +124,22 @@ export function StorageSettingsContent({ workspaceSlug }: TStorageSettingsConten
|
|||
const maxProjectSize = Math.max(...projects.map((project) => project.logical_size), 0);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-normal text-primary">Хранилище</h1>
|
||||
<p className="mt-2 max-w-3xl text-14 leading-6 text-secondary">
|
||||
Контроль объема файлов, дедупликации и кандидатов на очистку по workspace и проектам.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex w-full flex-col gap-7">
|
||||
<SettingsHeading
|
||||
title="Хранилище"
|
||||
description="Контроль объема файлов, дедупликации и кандидатов на очистку по workspace и проектам."
|
||||
/>
|
||||
|
||||
{isLoading && (
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<div key={index} className="h-36 animate-pulse rounded-[28px] bg-custom-background-80/80" />
|
||||
<div key={index} className="nodedc-settings-card h-36 animate-pulse" />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="rounded-[28px] border border-red-500/20 bg-red-500/10 px-5 py-4 text-14 text-red-200">
|
||||
<div className="nodedc-settings-card bg-red-500/10 px-5 py-4 text-14 text-red-200">
|
||||
Не удалось загрузить данные хранилища.
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -176,37 +197,26 @@ export function StorageSettingsContent({ workspaceSlug }: TStorageSettingsConten
|
|||
/>
|
||||
</div>
|
||||
|
||||
<section className="rounded-[28px] border border-white/5 bg-custom-background-80/85 p-5 shadow-[0_22px_80px_rgba(0,0,0,0.28)]">
|
||||
<section className="nodedc-settings-card p-5">
|
||||
<div className="mb-5 flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold tracking-normal text-primary">Проекты</h2>
|
||||
<p className="mt-1 text-13 text-secondary">Сортировка по логическому объему файлов.</p>
|
||||
</div>
|
||||
<div className="rounded-full bg-custom-background-90 px-4 py-2 text-12 font-medium text-secondary">
|
||||
<div className="nodedc-settings-chip flex min-h-0 items-center px-4 py-2 text-12 font-medium text-secondary">
|
||||
{formatCount(projects.length)} проектов
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full min-w-[58rem] border-collapse">
|
||||
<thead>
|
||||
<tr className="border-b border-white/8 text-left text-[11px] font-semibold uppercase tracking-[0.16em] text-tertiary">
|
||||
<th className="pb-3 pr-4">Проект</th>
|
||||
<th className="px-4 pb-3">Файлы</th>
|
||||
<th className="px-4 pb-3">Blob</th>
|
||||
<th className="px-4 pb-3">Логический объем</th>
|
||||
<th className="px-4 pb-3">Физический</th>
|
||||
<th className="px-4 pb-3">Дедуп</th>
|
||||
<th className="px-4 pb-3">Ошибки</th>
|
||||
<th className="pb-3 pl-4">Удалено</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div className="flex min-w-[62rem] flex-col gap-2">
|
||||
<ProjectStorageHeader />
|
||||
<div className="flex flex-col gap-2">
|
||||
{projects.map((project) => (
|
||||
<ProjectStorageRow key={project.id} project={project} maxSize={maxProjectSize} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
|
|
|||
Loading…
Reference in New Issue