/
dmitriysturov
/
workspaces_platform
Обзор
Документация
Войти
/
dmitriysturov
/
workspaces_platform
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
frontend/src/components/WorkspacesSection.tsx
124 строки
5 KB
dmitriysturov
vkr
04 июн 2026, 22:24
04 июн 2026, 22:24
971dfdc
Код
Авторство
О чём код?
import type { Workspace } from '../types' import { formatDate } from '../utils/format' import { navigate } from '../router/router' import { workspaceAccessLabel } from '../utils/accessLabels' import { resourceImageSrc } from '../utils/resourceImage' import { appIconNode } from '../utils/serviceUi' import { Button } from './ui/Button' import { Badge } from './ui/Badge' import { EmptyState } from './ui/EmptyState' import { Skeleton } from './ui/Skeleton' import { CreateResourceCard, ResourceCard, ResourceGrid } from './ResourceGrid' type WorkspacesTab = 'mine' | 'invites' type WorkspacesSectionProps = { items: Workspace[] activeTab: WorkspacesTab isLoading: boolean error: string | null onTabChange: (tab: WorkspacesTab) => void onCreate: () => void onEdit: (workspace: Workspace) => void onDelete: (workspace: Workspace) => void onRetry: () => void } export function WorkspacesSection({ items, activeTab, isLoading, error, onTabChange, onCreate, onEdit, onDelete, onRetry, }: WorkspacesSectionProps) { return ( <section className="dashboard-section" aria-labelledby="workspaces-title"> <div id="workspaces" className="section-anchor" /> <header className="section-header"> <div> <h2 id="workspaces-title">Рабочие пространства</h2> <p className="muted">Создавайте пространства для проектов и совместной работы.</p> </div> <Button type="button" onClick={onCreate}> Создать пространство </Button> </header> <div className="tab-list" role="tablist" aria-label="Рабочие пространства"> <button type="button" className={activeTab === 'mine' ? 'is-active' : ''} onClick={() => onTabChange('mine')}> Доступные пространства </button> <button type="button" className={activeTab === 'invites' ? 'is-active' : ''} onClick={() => onTabChange('invites')}> Приглашения </button> </div> {activeTab === 'mine' && ( <> {isLoading && <Skeleton rows={4} />} {!isLoading && error && ( <div className="ui-alert ui-alert-error"> <div> <strong>Не удалось загрузить рабочие пространства</strong> <p>{error}</p> </div> <Button variant="secondary" onClick={onRetry}> Повторить </Button> </div> )} {!isLoading && !error && items.length === 0 && ( <EmptyState title="У вас пока нет доступных рабочих пространств" description="Создайте первое пространство, чтобы добавить проекты и подключаемые сервисы." action={<Button onClick={onCreate}>Создать пространство</Button>} /> )} {!isLoading && !error && items.length > 0 && ( <ResourceGrid> <CreateResourceCard title="Создать пространство" description="Новое пространство для проектов и совместной работы" onCreate={onCreate} /> {items.map((workspace) => ( <ResourceCard key={workspace.id} type="workspace" title={workspace.name} description={workspace.description} subtitle={workspace.description || 'Описание пока не добавлено.'} roleLabel={workspaceAccessLabel(workspace.currentUserRole)} updatedAt={`Обновлено ${formatDate(workspace.updatedAt)}`} thumbnailUrl={resourceImageSrc(workspace.imageUrl)} icon={appIconNode('workspace')} onOpen={() => navigate(`/workspaces/${workspace.id}`)} menuActions={ <div className="row-actions"> <Badge tone="neutral">{workspaceAccessLabel(workspace.currentUserRole)}</Badge> <Button variant="ghost" size="small" onClick={() => onEdit(workspace)}> Изменить </Button> <Button variant="ghost" size="small" onClick={() => onDelete(workspace)}> Удалить </Button> </div> } /> ))} </ResourceGrid> )} </> )} {activeTab === 'invites' && ( <div className="placeholder-panel"> <EmptyState title="Приглашений пока нет" description="Активные приглашения в рабочие пространства отображаются на отдельной странице приглашений." /> </div> )} </section> ) }