Skip to content

Commit a0a1edb

Browse files
authored
Merge pull request #214 from ITBooster-practice/feat/add-task-badges-81
feature(web): добавлены компоненты Badge-статусов и приоритетов ( #81 )
2 parents 3464975 + 84dc664 commit a0a1edb

File tree

12 files changed

+248
-131
lines changed

12 files changed

+248
-131
lines changed

apps/web/app/(dashboard)/sprints/page.tsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { notFound } from 'next/navigation'
22

33
import { Card, CardContent } from '@repo/ui'
44

5+
import { TaskPriority, TaskPriorityBadge, TaskType, TaskTypeBadge } from '@/entities/task'
56
import { FEATURES } from '@/shared/config'
67

78
type SprintTask = {
89
id: string
910
title: string
10-
type: 'Эпик' | 'Стори' | 'Баг'
11+
type: TaskType
1112
tags: string[]
13+
priority: TaskPriority
1214
}
1315

1416
const sprintColumns: Array<{
@@ -23,12 +25,14 @@ const sprintColumns: Array<{
2325
title: 'Realtime уведомления через WebSocket',
2426
type: 'Стори',
2527
tags: ['backend', 'realtime'],
28+
priority: 'HIGH',
2629
},
2730
{
2831
id: 'TT-8',
2932
title: 'AI генерация эпиков из описания фичи',
3033
type: 'Эпик',
3134
tags: ['ai', 'feature'],
35+
priority: 'MEDIUM',
3236
},
3337
],
3438
},
@@ -38,14 +42,16 @@ const sprintColumns: Array<{
3842
{
3943
id: 'TT-3',
4044
title: 'Фильтры и поиск задач',
41-
type: 'Стори',
45+
type: 'Тех. долг',
4246
tags: ['ui', 'search'],
47+
priority: 'LOW',
4348
},
4449
{
4550
id: 'TT-9',
4651
title: 'Система ролей и прав доступа',
47-
type: 'Стори',
52+
type: 'Баг',
4853
tags: ['auth', 'roles'],
54+
priority: 'CRITICAL',
4955
},
5056
],
5157
},
@@ -55,26 +61,21 @@ const sprintColumns: Array<{
5561
{
5662
id: 'TT-1',
5763
title: 'Реализовать систему авторизации',
58-
type: 'Эпик',
64+
type: 'Задача',
5965
tags: ['auth', 'security'],
66+
priority: 'MEDIUM',
6067
},
6168
{
6269
id: 'TT-2',
6370
title: 'Kanban доска c drag & drop',
6471
type: 'Стори',
6572
tags: ['ui', 'core'],
73+
priority: 'MEDIUM',
6674
},
6775
],
6876
},
6977
]
7078

71-
const typeClassName: Record<SprintTask['type'], string> = {
72-
Эпик: 'border border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-500/25 dark:bg-violet-500/20 dark:text-violet-200',
73-
Стори:
74-
'border border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-500/25 dark:bg-sky-500/20 dark:text-sky-200',
75-
Баг: 'border border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-500/25 dark:bg-rose-500/20 dark:text-rose-200',
76-
}
77-
7879
export default function SprintsPage() {
7980
if (!FEATURES.SPRINTS) {
8081
notFound()
@@ -129,18 +130,18 @@ export default function SprintsPage() {
129130
>
130131
<CardContent className='space-y-2.5 px-3 py-3 text-foreground'>
131132
<div className='flex items-center gap-2'>
132-
<span
133-
className={`rounded-full px-2 py-0.5 text-xs font-medium ${typeClassName[task.type]}`}
134-
>
135-
{task.type}
136-
</span>
133+
<TaskTypeBadge type={task.type} />
137134
<span className='text-xs text-foreground'>{task.id}</span>
138135
</div>
139136

140137
<p className='text-lg font-medium leading-snug text-foreground'>
141138
{task.title}
142139
</p>
143140

141+
<div className='flex items-center gap-2'>
142+
<TaskPriorityBadge priority={task.priority} />
143+
</div>
144+
144145
<div className='flex flex-wrap gap-1.5'>
145146
{task.tags.map((tag) => (
146147
<span

apps/web/app/(dashboard)/tasks/loading.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,30 @@ export default function Loading() {
3232
</tr>
3333
</thead>
3434
<tbody>
35-
{Array.from({ length: 4 }).map((_, index) => (
35+
{Array.from({ length: 5 }).map((_, index) => (
3636
<tr key={index} className='border-b border-border last:border-b-0'>
37-
<td className='px-4 py-4'>
38-
<Skeleton className='h-5 w-5 rounded-[calc(var(--radius-control)-2px)]' />
37+
<td className='px-4 py-3'>
38+
<Skeleton className='size-5 rounded-[calc(var(--radius-control)-2px)]' />
3939
</td>
40-
<td className='px-3 py-4'>
41-
<Skeleton className='h-4 w-14' />
40+
<td className='px-3 py-3'>
41+
<Skeleton className='text-body w-14 text-transparent'>noop</Skeleton>
4242
</td>
43-
<td className='px-3 py-4'>
44-
<Skeleton className='h-4 w-64 max-w-full' />
43+
<td className='px-3 py-3'>
44+
<Skeleton className='w-64 max-w-full text-transparent'>noop</Skeleton>
4545
</td>
46-
<td className='px-3 py-4'>
47-
<Skeleton className='h-4 w-24' />
46+
<td className='px-3 py-3'>
47+
<Skeleton className='text-body w-20 text-transparent'>noop</Skeleton>
4848
</td>
49-
<td className='px-3 py-4'>
50-
<Skeleton className='h-4 w-28' />
49+
<td className='px-3 py-3'>
50+
<Skeleton className='px-2 py-0.5 text-xs w-28 text-transparent border border-transparent'>
51+
noop
52+
</Skeleton>
5153
</td>
52-
<td className='px-3 py-4'>
53-
<Skeleton className='h-4 w-20' />
54+
<td className='px-3 py-3'>
55+
<Skeleton className='size-3.5' />
5456
</td>
55-
<td className='px-3 py-4'>
56-
<Skeleton className='h-4 w-24' />
57+
<td className='px-3 py-3'>
58+
<Skeleton className='text-body w-24 text-transparent'>noop</Skeleton>
5759
</td>
5860
</tr>
5961
))}

apps/web/app/(dashboard)/tasks/page.tsx

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,68 @@ import { notFound } from 'next/navigation'
22

33
import { Button } from '@repo/ui'
44

5+
import {
6+
TaskPriority,
7+
TaskPriorityBadge,
8+
TaskStatus,
9+
TaskStatusBadge,
10+
TaskType,
11+
TaskTypeBadge,
12+
} from '@/entities/task'
513
import { FEATURES } from '@/shared/config'
614

7-
const mockTasks = [
15+
interface Task {
16+
id: string
17+
title: string
18+
type: TaskType
19+
status: TaskStatus
20+
priority: TaskPriority
21+
assignee: string
22+
}
23+
24+
const mockTasks: Task[] = [
825
{
926
id: 'TT-1',
1027
title: 'Реализовать систему авторизации',
1128
type: 'Эпик',
12-
status: 'В работе',
13-
priority: 'Высокий',
29+
status: 'IN_PROGRESS',
30+
priority: 'HIGH',
1431
assignee: 'Алексей',
1532
},
1633
{
1734
id: 'TT-2',
1835
title: 'Kanban доска с drag & drop',
1936
type: 'Стори',
20-
status: 'В работе',
21-
priority: 'Высокий',
37+
status: 'IN_PROGRESS',
38+
priority: 'HIGH',
2239
assignee: 'Мария',
2340
},
2441
{
2542
id: 'TT-3',
2643
title: 'Фильтры и поиск задач',
2744
type: 'Стори',
28-
status: 'К выполнению',
29-
priority: 'Средний',
45+
status: 'TODO',
46+
priority: 'MEDIUM',
3047
assignee: 'Дмитрий',
3148
},
3249
{
3350
id: 'TT-4',
3451
title: 'Исправить баг с отображением аватаров',
3552
type: 'Баг',
36-
status: 'Ревью',
37-
priority: 'Высокий',
53+
status: 'IN_REVIEW',
54+
priority: 'HIGH',
3855
assignee: 'Елена',
3956
},
4057
{
4158
id: 'TT-5',
4259
title: 'Настроить CI/CD pipeline',
4360
type: 'Тех. долг',
44-
status: 'Готово',
45-
priority: 'Средний',
61+
status: 'DONE',
62+
priority: 'MEDIUM',
4663
assignee: 'Павел',
4764
},
4865
] as const
4966

50-
const typeBadgeClassName: Record<(typeof mockTasks)[number]['type'], string> = {
51-
Эпик: 'border border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-500/25 dark:bg-violet-500/20 dark:text-violet-200',
52-
Стори:
53-
'border border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-500/25 dark:bg-sky-500/20 dark:text-sky-200',
54-
Баг: 'border border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-500/25 dark:bg-rose-500/20 dark:text-rose-200',
55-
'Тех. долг':
56-
'border border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-500/25 dark:bg-amber-500/20 dark:text-amber-200',
57-
}
58-
5967
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
6068

6169
export default async function TasksPage() {
@@ -111,22 +119,24 @@ export default async function TasksPage() {
111119
{mockTasks?.map((task) => (
112120
<tr key={task.id} className='border-b border-border last:border-b-0'>
113121
<td className='px-4 py-3'>
114-
<span className='block h-5 w-5 rounded-[calc(var(--radius-control)-2px)] border-2 border-primary' />
122+
<div className='block size-5 rounded-[calc(var(--radius-control)-2px)] border-2 border-primary' />
123+
</td>
124+
<td className='px-3 py-3 text-body text-muted-foreground'>
125+
<span>{task.id}</span>
126+
</td>
127+
<td className='px-3 py-3 text-body font-medium text-foreground'>
128+
<span>{task.title}</span>
129+
</td>
130+
<td className='px-3 py-3 leading-none'>
131+
<TaskTypeBadge type={task.type} />
115132
</td>
116-
<td className='px-3 py-3 text-xl text-muted-foreground'>{task.id}</td>
117-
<td className='px-3 py-3 text-xl font-medium text-foreground'>
118-
{task.title}
133+
<td className='px-3 py-3 leading-none'>
134+
<TaskStatusBadge status={task.status} />
119135
</td>
120136
<td className='px-3 py-3'>
121-
<span
122-
className={`inline-flex rounded-full px-2.5 py-1 text-sm font-medium ${typeBadgeClassName[task.type]}`}
123-
>
124-
{task.type}
125-
</span>
137+
<TaskPriorityBadge priority={task.priority} />
126138
</td>
127-
<td className='px-3 py-3 text-xl text-foreground'>{task.status}</td>
128-
<td className='px-3 py-3 text-xl text-foreground'>{task.priority}</td>
129-
<td className='px-3 py-3 text-xl text-foreground'>{task.assignee}</td>
139+
<td className='px-3 py-3 text-body text-foreground'>{task.assignee}</td>
130140
</tr>
131141
))}
132142
</tbody>

apps/web/entities/task/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from './ui/task-priority-badge'
2+
export * from './ui/task-status-badge'
3+
export * from './ui/task-type-badge'
4+
5+
export type { TaskType, TaskPriority, TaskStatus } from './model/types'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type TaskType = 'Эпик' | 'Стори' | 'Баг' | 'Тех. долг' | 'Задача'
2+
export type TaskPriority = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
3+
export type TaskStatus = 'TODO' | 'IN_PROGRESS' | 'IN_REVIEW' | 'DONE'
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@repo/ui'
2+
import { AlertTriangle, ArrowDown, ArrowUp, Minus } from '@repo/ui/icons'
3+
4+
import { TaskPriority } from '../model/types'
5+
6+
const priorityIcons: Record<TaskPriority, React.ReactNode> = {
7+
CRITICAL: <AlertTriangle className='size-3.5 text-destructive' />,
8+
HIGH: <ArrowUp className='size-3.5 text-warning' />,
9+
MEDIUM: <Minus className='size-3.5 text-primary' />,
10+
LOW: <ArrowDown className='size-3.5 text-muted-foreground' />,
11+
}
12+
13+
const priorityLabels: Record<TaskPriority, string> = {
14+
LOW: 'Низкий',
15+
MEDIUM: 'Средний',
16+
HIGH: 'Высокий',
17+
CRITICAL: 'Критичный',
18+
}
19+
20+
interface TaskPriorityBadgeProps {
21+
priority: TaskPriority
22+
className?: string
23+
}
24+
25+
export function TaskPriorityBadge({ priority, className }: TaskPriorityBadgeProps) {
26+
return (
27+
<TooltipProvider>
28+
<Tooltip>
29+
<TooltipTrigger className={className} asChild>
30+
{priorityIcons[priority]}
31+
</TooltipTrigger>
32+
<TooltipContent>
33+
<p>{priorityLabels[priority]}</p>
34+
</TooltipContent>
35+
</Tooltip>
36+
</TooltipProvider>
37+
)
38+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Badge, cn } from '@repo/ui'
2+
3+
import { TaskStatus } from '../model/types'
4+
5+
const statusLabels: Record<TaskStatus, string> = {
6+
TODO: 'К выполнению',
7+
IN_PROGRESS: 'В работе',
8+
IN_REVIEW: 'Ревью',
9+
DONE: 'Готово',
10+
}
11+
12+
interface TaskStatusBadgeProps {
13+
status: TaskStatus
14+
className?: string
15+
}
16+
17+
export function TaskStatusBadge({ status, className }: TaskStatusBadgeProps) {
18+
return (
19+
<Badge variant='ghost' className={cn(className)}>
20+
{statusLabels[status]}
21+
</Badge>
22+
)
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Badge, cn } from '@repo/ui'
2+
3+
import { TaskType } from '../model/types'
4+
5+
const typeVariants: Record<TaskType, string> = {
6+
Эпик: 'border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-500/25 dark:bg-violet-500/20 dark:text-violet-200',
7+
Стори:
8+
'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-500/25 dark:bg-sky-500/20 dark:text-sky-200',
9+
Баг: 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-500/25 dark:bg-rose-500/20 dark:text-rose-200',
10+
'Тех. долг':
11+
'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-500/25 dark:bg-amber-500/20 dark:text-amber-200',
12+
Задача:
13+
'border-slate-200 bg-slate-50 text-slate-600 dark:border-slate-500/25 dark:bg-slate-500/20 dark:text-slate-300',
14+
}
15+
16+
interface TaskTypeBadgeProps {
17+
type: TaskType
18+
className?: string
19+
}
20+
21+
export function TaskTypeBadge({ type, className }: TaskTypeBadgeProps) {
22+
return (
23+
<Badge variant='outline' className={cn(typeVariants[type], className)}>
24+
{type}
25+
</Badge>
26+
)
27+
}

0 commit comments

Comments
 (0)