Skip to content

Commit 21a9caa

Browse files
committed
feat: move Epics under Tasks tabs — /tasks/epics routes
- Add Epics tab to TasksTabs component - Move epic routes from /epics/* to /tasks/epics/* - Remove Epics from sidebar navigation (now accessed via Tasks tab) - Update all epic navigate links across UI
1 parent edd6c74 commit 21a9caa

8 files changed

Lines changed: 24 additions & 22 deletions

File tree

ui/src/app/App.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ export default function App() {
6969
<Route path="tasks/new" element={<TaskNewPage />} />
7070
<Route path="tasks/:taskId/edit" element={<TaskEditPage />} />
7171
<Route path="tasks/:taskId" element={<TaskDetailPage />} />
72-
<Route path="epics" element={<EpicsPage />} />
73-
<Route path="epics/new" element={<EpicNewPage />} />
74-
<Route path="epics/:epicId/edit" element={<EpicEditPage />} />
75-
<Route path="epics/:epicId" element={<EpicDetailPage />} />
72+
<Route path="tasks/epics" element={<EpicsPage />} />
73+
<Route path="tasks/epics/new" element={<EpicNewPage />} />
74+
<Route path="tasks/epics/:epicId/edit" element={<EpicEditPage />} />
75+
<Route path="tasks/epics/:epicId" element={<EpicDetailPage />} />
7676
<Route path="skills" element={<SkillsPage />} />
7777
<Route path="skills/new" element={<SkillNewPage />} />
7878
<Route path="skills/:skillId/edit" element={<SkillEditPage />} />

ui/src/pages/epics/[epicId].tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export default function EpicDetailPage() {
7575
setDeleting(true);
7676
try {
7777
await deleteEpic(projectId, epicId);
78-
navigate(`/${projectId}/epics`);
78+
navigate(`/${projectId}/tasks/epics`);
7979
} finally { setDeleting(false); }
8080
};
8181

@@ -88,13 +88,13 @@ export default function EpicDetailPage() {
8888
<Box>
8989
<PageTopBar
9090
breadcrumbs={[
91-
{ label: 'Epics', to: `/${projectId}/epics` },
91+
{ label: 'Epics', to: `/${projectId}/tasks/epics` },
9292
{ label: epic.title },
9393
]}
9494
actions={
9595
canWrite ? (
9696
<>
97-
<Button startIcon={<EditIcon />} onClick={() => navigate(`/${projectId}/epics/${epicId}/edit`)}>Edit</Button>
97+
<Button startIcon={<EditIcon />} onClick={() => navigate(`/${projectId}/tasks/epics/${epicId}/edit`)}>Edit</Button>
9898
<Button startIcon={<DeleteIcon />} color="error" onClick={() => setDeleteOpen(true)}>Delete</Button>
9999
</>
100100
) : undefined

ui/src/pages/epics/edit.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default function EpicEditPage() {
2525
const handleSubmit = async (data: Parameters<typeof updateEpic>[2]) => {
2626
if (!projectId || !epicId) return;
2727
await updateEpic(projectId, epicId, data);
28-
navigate(`/${projectId}/epics/${epicId}`);
28+
navigate(`/${projectId}/tasks/epics/${epicId}`);
2929
};
3030

3131
if (loading) return <Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}><CircularProgress /></Box>;
@@ -35,8 +35,8 @@ export default function EpicEditPage() {
3535
<Box>
3636
<PageTopBar
3737
breadcrumbs={[
38-
{ label: 'Epics', to: `/${projectId}/epics` },
39-
{ label: epic.title, to: `/${projectId}/epics/${epicId}` },
38+
{ label: 'Epics', to: `/${projectId}/tasks/epics` },
39+
{ label: epic.title, to: `/${projectId}/tasks/epics/${epicId}` },
4040
{ label: 'Edit' },
4141
]}
4242
actions={
@@ -47,7 +47,7 @@ export default function EpicEditPage() {
4747
<EpicForm
4848
epic={epic}
4949
onSubmit={handleSubmit}
50-
onCancel={() => navigate(`/${projectId}/epics/${epicId}`)}
50+
onCancel={() => navigate(`/${projectId}/tasks/epics/${epicId}`)}
5151
/>
5252
</Box>
5353
);

ui/src/pages/epics/index.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useFilters } from '@/shared/lib/useFilters.ts';
1717
import { PageTopBar, StatusBadge, Tags, PaginationBar, DateDisplay, FilterBar, FilterControl } from '@/shared/ui/index.ts';
1818
import { listEpics, type Epic, type EpicStatus } from '@/entities/epic/index.ts';
1919
import { PRIORITY_COLORS, PRIORITY_BADGE_COLOR, priorityLabel, type TaskPriority } from '@/entities/task/index.ts';
20+
import { TasksTabs } from '@/pages/tasks/TasksTabs.tsx';
2021

2122
const EPIC_STATUS_COLOR: Record<EpicStatus, string> = {
2223
open: '#1976d2',
@@ -142,13 +143,14 @@ export default function EpicsPage() {
142143
return (
143144
<Box>
144145
<PageTopBar
145-
breadcrumbs={[{ label: 'Epics' }]}
146+
breadcrumbs={[{ label: 'Tasks', to: `/${projectId}/tasks` }, { label: 'Epics' }]}
146147
actions={canWrite ? (
147-
<Button variant="contained" startIcon={<AddIcon />} onClick={() => navigate(`/${projectId}/epics/new`)}>
148+
<Button variant="contained" startIcon={<AddIcon />} onClick={() => navigate(`/${projectId}/tasks/epics/new`)}>
148149
New Epic
149150
</Button>
150151
) : undefined}
151152
/>
153+
<TasksTabs />
152154

153155
{/* Filter bar */}
154156
<FilterBar activeFilters={activeFilterChips} onClearAll={handleClearAll}>
@@ -194,7 +196,7 @@ export default function EpicsPage() {
194196
{epics.length === 0 && canWrite ? 'Create your first epic to group related tasks' : ''}
195197
</Typography>
196198
{epics.length === 0 && canWrite && (
197-
<Button variant="contained" startIcon={<AddIcon />} onClick={() => navigate(`/${projectId}/epics/new`)}>New Epic</Button>
199+
<Button variant="contained" startIcon={<AddIcon />} onClick={() => navigate(`/${projectId}/tasks/epics/new`)}>New Epic</Button>
198200
)}
199201
</Box>
200202
) : (
@@ -219,7 +221,7 @@ export default function EpicsPage() {
219221
key={epic.id}
220222
hover
221223
sx={{ cursor: 'pointer' }}
222-
onClick={() => navigate(`/${projectId}/epics/${epic.id}`)}
224+
onClick={() => navigate(`/${projectId}/tasks/epics/${epic.id}`)}
223225
>
224226
<TableCell>
225227
<Typography variant="body2" fontWeight={600} noWrap>{epic.title}</Typography>

ui/src/pages/epics/new.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ export default function EpicNewPage() {
1313
const handleSubmit = async (data: Parameters<typeof createEpic>[1]) => {
1414
if (!projectId) return;
1515
const epic = await createEpic(projectId, data);
16-
navigate(`/${projectId}/epics/${epic.id}`);
16+
navigate(`/${projectId}/tasks/epics/${epic.id}`);
1717
};
1818

1919
return (
2020
<Box>
2121
<PageTopBar
2222
breadcrumbs={[
23-
{ label: 'Epics', to: `/${projectId}/epics` },
23+
{ label: 'Epics', to: `/${projectId}/tasks/epics` },
2424
{ label: 'Create' },
2525
]}
2626
actions={
@@ -32,7 +32,7 @@ export default function EpicNewPage() {
3232
{!canWrite && <Alert severity="warning" sx={{ mb: 2 }}>Read-only access.</Alert>}
3333
<EpicForm
3434
onSubmit={handleSubmit}
35-
onCancel={() => navigate(`/${projectId}/epics`)}
35+
onCancel={() => navigate(`/${projectId}/tasks/epics`)}
3636
submitLabel="Create"
3737
/>
3838
</Box>

ui/src/pages/tasks/TasksTabs.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@ import { Tabs, Tab, Box } from '@mui/material';
33
import DashboardIcon from '@mui/icons-material/Dashboard';
44
import ViewListIcon from '@mui/icons-material/ViewList';
55
import ViewKanbanIcon from '@mui/icons-material/ViewKanban';
6+
import FlagIcon from '@mui/icons-material/Flag';
67

78
const TABS = [
89
{ label: 'Summary', value: 'summary', icon: <DashboardIcon fontSize="small" /> },
910
{ label: 'List', value: 'list', icon: <ViewListIcon fontSize="small" /> },
1011
{ label: 'Board', value: 'board', icon: <ViewKanbanIcon fontSize="small" /> },
12+
{ label: 'Epics', value: 'epics', icon: <FlagIcon fontSize="small" /> },
1113
] as const;
1214

1315
export function TasksTabs() {
1416
const { projectId } = useParams<{ projectId: string }>();
1517
const navigate = useNavigate();
1618
const { pathname } = useLocation();
1719

18-
const current = TABS.find(t => pathname.endsWith(`/tasks/${t.value}`))?.value ?? 'summary';
20+
const current = TABS.find(t => pathname.includes(`/tasks/${t.value}`))?.value ?? 'summary';
1921

2022
return (
2123
<Box sx={{ mb: 2, borderBottom: 1, borderColor: 'divider' }}>

ui/src/pages/tasks/[taskId].tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export default function TaskDetailPage() {
203203
{epicLinks.map(r => (
204204
<Box key={r.toId} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
205205
<FlagIcon sx={{ fontSize: 16, color: '#1976d2' }} />
206-
<Link component="button" variant="body2" onClick={() => navigate(`/${projectId}/epics/${r.toId}`)}>
206+
<Link component="button" variant="body2" onClick={() => navigate(`/${projectId}/tasks/epics/${r.toId}`)}>
207207
{r.title || r.toId}
208208
</Link>
209209
{canWrite && (

ui/src/widgets/layout/Layout.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import CableIcon from '@mui/icons-material/Cable';
2424
import ExpandLess from '@mui/icons-material/ExpandLess';
2525
import ExpandMore from '@mui/icons-material/ExpandMore';
2626
import AssignmentIcon from '@mui/icons-material/Assignment';
27-
import FlagIcon from '@mui/icons-material/Flag';
2827
import { useProjects, type WorkspaceInfo } from '@/entities/project/index.ts';
2928
import { useThemeMode } from '@/shared/lib/ThemeModeContext.tsx';
3029
import { WsProvider } from '@/shared/lib/useWebSocket.ts';
@@ -57,7 +56,6 @@ interface NavItem {
5756
const NAV_ITEMS: NavItem[] = [
5857
{ label: 'Dashboard', icon: <DashboardIcon />, path: 'dashboard', color: { dark: '#569cd6', light: '#1976d2' } },
5958
{ label: 'Tasks', icon: <AssignmentIcon />, path: 'tasks', color: { dark: '#ce9178', light: '#d84315' } },
60-
{ label: 'Epics', icon: <FlagIcon />, path: 'epics', color: { dark: '#ce9178', light: '#d84315' } },
6159
{ label: 'Knowledge', icon: <LightbulbIcon />, path: 'knowledge', color: { dark: '#dcdcaa', light: '#b8860b' } },
6260
{ label: 'Skills', icon: <PsychologyIcon />, path: 'skills', color: { dark: '#c586c0', light: '#9c27b0' } },
6361
{ label: 'Docs', icon: <DescriptionIcon />, path: 'docs', color: { dark: '#9cdcfe', light: '#0288d1' } },

0 commit comments

Comments
 (0)