Skip to content

Commit dcf7a33

Browse files
committed
refactor: simplify StickyHeadingsWrapper component and remove unused state
feat: add Progress component using Radix UI's progress primitive chore: add @radix-ui/react-progress dependency chore: update pnpm lockfile with new dependencies
1 parent 123afd0 commit dcf7a33

File tree

12 files changed

+3474
-1272
lines changed

12 files changed

+3474
-1272
lines changed
Lines changed: 153 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
'use client';
22

33
import React from 'react';
4-
import { Col, Empty, FloatButton, List, Row, Skeleton, Typography } from 'antd';
5-
import { PlusOutlined } from '@ant-design/icons';
4+
import { Plus } from 'lucide-react';
5+
import { Button } from '@/components/ui/button';
6+
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
7+
import { Skeleton } from '@/components/ui/skeleton';
68
import {
79
Area,
810
Bar,
@@ -29,11 +31,8 @@ import {
2931
formatTooltipLabel,
3032
formatTooltipValue,
3133
} from './chart-utils';
32-
import styles from './Dashboard.module.css';
33-
import { Gutter } from 'antd/es/grid/row';
3434
import { useStickyHeaders } from '@/hooks/useStickyHeaders';
35-
36-
const { Title, Text } = Typography;
35+
import { cn } from '@/lib/utils';
3736

3837
interface DashboardProps {
3938
stats: DevlogStats | null;
@@ -96,73 +95,78 @@ export function Dashboard({
9695
].filter((item) => item.value > 0);
9796
}, [stats]);
9897

99-
// Define gutter for chart rows
100-
const chartRowGutter = [48, 24] as [Gutter, Gutter];
101-
10298
// Setup sticky header detection
10399
useStickyHeaders({
104-
selectorClass: styles.sectionHeader,
105-
stickyClass: styles.isSticky,
100+
selectorClass: 'section-header',
101+
stickyClass: 'is-sticky',
106102
topOffset: 0,
107103
dependencies: [],
108104
});
109105

110106
return (
111107
<div className="flex flex-col h-full w-full overflow-hidden">
112-
<div className={`${styles.dashboardContent} scrollable-content`}>
108+
<div className="flex-1 overflow-y-auto p-6 space-y-8">
113109
{/* Charts Section */}
114-
<div className={styles.dashboardChartsSection}>
110+
<div className="space-y-6">
115111
{isLoadingTimeSeries ? (
116-
<Row gutter={chartRowGutter} className={styles.chartRow}>
117-
<Col xs={24} lg={12}>
118-
<div className={styles.chartCard}>
119-
<Title level={4} className="mb-4">
120-
Development Activity (Last 30 Days)
121-
</Title>
122-
<Skeleton active paragraph={{ rows: 8 }} />
123-
</div>
124-
</Col>
125-
<Col xs={24} lg={12}>
126-
<div className={styles.chartCard}>
127-
<Title level={4} className="mb-4">
128-
Current Status Distribution
129-
</Title>
130-
<Skeleton active paragraph={{ rows: 8 }} />
131-
</div>
132-
</Col>
133-
</Row>
112+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
113+
<Card>
114+
<CardHeader>
115+
<CardTitle>Development Activity (Last 30 Days)</CardTitle>
116+
</CardHeader>
117+
<CardContent>
118+
<div className="space-y-4">
119+
{Array.from({ length: 8 }).map((_, i) => (
120+
<Skeleton key={i} className="h-4 w-full" />
121+
))}
122+
</div>
123+
</CardContent>
124+
</Card>
125+
<Card>
126+
<CardHeader>
127+
<CardTitle>Current Status Distribution</CardTitle>
128+
</CardHeader>
129+
<CardContent>
130+
<div className="space-y-4">
131+
{Array.from({ length: 8 }).map((_, i) => (
132+
<Skeleton key={i} className="h-4 w-full" />
133+
))}
134+
</div>
135+
</CardContent>
136+
</Card>
137+
</div>
134138
) : chartData.length === 0 ? (
135-
<Row gutter={chartRowGutter}>
136-
<Col xs={24} lg={12}>
137-
<div className={styles.chartCard}>
138-
<Title level={4} className="mb-4">
139-
Project Progress & Current Workload
140-
</Title>
141-
<Empty
142-
image={Empty.PRESENTED_IMAGE_SIMPLE}
143-
description="No development activity data available yet"
144-
/>
145-
</div>
146-
</Col>
147-
<Col xs={24} lg={12}>
148-
<div className={styles.chartCard}>
149-
<Title level={4} className="mb-4">
150-
Current Status Distribution
151-
</Title>
152-
<Empty
153-
image={Empty.PRESENTED_IMAGE_SIMPLE}
154-
description="No status distribution data available yet"
155-
/>
156-
</div>
157-
</Col>
158-
</Row>
139+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
140+
<Card>
141+
<CardHeader>
142+
<CardTitle>Project Progress & Current Workload</CardTitle>
143+
</CardHeader>
144+
<CardContent>
145+
<div className="flex flex-col items-center justify-center py-12 text-center">
146+
<div className="text-muted-foreground mb-2">📊</div>
147+
<p className="text-sm text-muted-foreground">No development activity data available yet</p>
148+
</div>
149+
</CardContent>
150+
</Card>
151+
<Card>
152+
<CardHeader>
153+
<CardTitle>Current Status Distribution</CardTitle>
154+
</CardHeader>
155+
<CardContent>
156+
<div className="flex flex-col items-center justify-center py-12 text-center">
157+
<div className="text-muted-foreground mb-2">📈</div>
158+
<p className="text-sm text-muted-foreground">No status distribution data available yet</p>
159+
</div>
160+
</CardContent>
161+
</Card>
162+
</div>
159163
) : (
160-
<Row gutter={chartRowGutter}>
161-
<Col xs={24} lg={12}>
162-
<div className={styles.chartCard}>
163-
<Title level={4} className="mb-4">
164-
Project Progress & Current Workload
165-
</Title>
164+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
165+
<Card>
166+
<CardHeader>
167+
<CardTitle>Project Progress & Current Workload</CardTitle>
168+
</CardHeader>
169+
<CardContent>
166170
<ResponsiveContainer width="100%" height={300}>
167171
<ComposedChart data={chartData}>
168172
<CartesianGrid strokeDasharray="3 3" />
@@ -222,13 +226,13 @@ export function Dashboard({
222226
/>
223227
</ComposedChart>
224228
</ResponsiveContainer>
225-
</div>
226-
</Col>
227-
<Col xs={24} lg={12}>
228-
<div className={styles.chartCard}>
229-
<Title level={4} className="mb-4">
230-
Current Status Distribution
231-
</Title>
229+
</CardContent>
230+
</Card>
231+
<Card>
232+
<CardHeader>
233+
<CardTitle>Current Status Distribution</CardTitle>
234+
</CardHeader>
235+
<CardContent>
232236
<ResponsiveContainer width="100%" height={300}>
233237
<PieChart>
234238
<Pie
@@ -256,108 +260,99 @@ export function Dashboard({
256260
verticalAlign="bottom"
257261
height={36}
258262
formatter={(value: string) => (
259-
<span className={styles.chartLegendText}>{value}</span>
263+
<span className="text-sm">{value}</span>
260264
)}
261265
/>
262266
</PieChart>
263267
</ResponsiveContainer>
264-
</div>
265-
</Col>
266-
</Row>
268+
</CardContent>
269+
</Card>
270+
</div>
267271
)}
268272
</div>
269273

270-
{/* Scrollable Content */}
271-
<div className={`${styles.recentDevlogs} flex-1 flex flex-col`}>
272-
<div className={styles.sectionHeader}>
273-
<Title level={3} className={styles.recentDevlogsTitle}>
274-
Recent Devlogs
275-
</Title>
276-
</div>
277-
<div className="flex-1 overflow-hidden thin-scrollbar-vertical">
278-
{isLoadingDevlogs ? (
279-
<List
280-
itemLayout="horizontal"
281-
dataSource={Array.from({ length: 10 }, (_, index) => ({
282-
key: `skeleton-${index}`,
283-
}))}
284-
renderItem={() => (
285-
<List.Item className={styles.devlogListItem}>
286-
<List.Item.Meta
287-
className={styles.devlogListItemMeta}
288-
avatar={<Skeleton.Avatar size={40} active />}
289-
title={<Skeleton paragraph={{ rows: 2 }} active />}
290-
/>
291-
</List.Item>
292-
)}
293-
/>
294-
) : recentDevlogs.length === 0 ? (
295-
<Empty
296-
image={Empty.PRESENTED_IMAGE_SIMPLE}
297-
description="No devlogs found"
298-
className={styles.emptyDevlogs}
299-
/>
300-
) : (
301-
<List
302-
itemLayout="horizontal"
303-
dataSource={recentDevlogs}
304-
renderItem={(devlog) => (
305-
<List.Item
306-
className={styles.devlogListItem}
307-
onClick={() => onViewDevlog(devlog)}
308-
actions={[
309-
<Text
310-
type="secondary"
311-
key="date"
312-
className={styles.devlogDate}
313-
title={formatTimeAgoWithTooltip(devlog.updatedAt).fullDate}
314-
>
315-
{formatTimeAgoWithTooltip(devlog.updatedAt).timeAgo}
316-
</Text>,
317-
]}
318-
>
319-
<List.Item.Meta
320-
className={styles.devlogListItemMeta}
321-
avatar={
322-
<Text strong className={styles.devlogId}>
323-
{devlog.id}
324-
</Text>
325-
}
326-
title={
327-
<div className={styles.devlogTitleSection}>
328-
<Text strong className={styles.devlogTitleText}>
329-
{devlog.title}
330-
</Text>
331-
<div className={styles.recentDevlogsMeta}>
332-
<DevlogStatusTag status={devlog.status} className={styles.devlogTag} />
333-
<DevlogPriorityTag
334-
priority={devlog.priority}
335-
className={styles.devlogTag}
336-
/>
337-
<DevlogTypeTag type={devlog.type} className={styles.devlogTag} />
274+
{/* Recent Devlogs Section */}
275+
<Card className="flex-1 flex flex-col">
276+
<CardHeader className="section-header">
277+
<CardTitle>Recent Devlogs</CardTitle>
278+
</CardHeader>
279+
<CardContent className="flex-1 overflow-hidden">
280+
<div className="h-full overflow-y-auto">
281+
{isLoadingDevlogs ? (
282+
<div className="space-y-4">
283+
{Array.from({ length: 10 }).map((_, index) => (
284+
<div key={`skeleton-${index}`} className="flex items-start space-x-4 p-4 border-b border-border">
285+
<div className="w-12 h-12 bg-muted rounded flex items-center justify-center">
286+
<Skeleton className="w-6 h-6" />
287+
</div>
288+
<div className="flex-1 space-y-2">
289+
<Skeleton className="h-4 w-3/4" />
290+
<Skeleton className="h-3 w-1/2" />
291+
<div className="flex space-x-2">
292+
<Skeleton className="h-5 w-16" />
293+
<Skeleton className="h-5 w-16" />
294+
<Skeleton className="h-5 w-16" />
295+
</div>
296+
</div>
297+
<Skeleton className="h-3 w-16" />
298+
</div>
299+
))}
300+
</div>
301+
) : recentDevlogs.length === 0 ? (
302+
<div className="flex flex-col items-center justify-center py-12 text-center">
303+
<div className="text-muted-foreground mb-2 text-2xl">📝</div>
304+
<p className="text-sm text-muted-foreground">No devlogs found</p>
305+
</div>
306+
) : (
307+
<div className="space-y-0">
308+
{recentDevlogs.map((devlog) => (
309+
<div
310+
key={devlog.id}
311+
className="flex items-start space-x-4 p-4 border-b border-border hover:bg-muted/50 cursor-pointer transition-colors"
312+
onClick={() => onViewDevlog(devlog)}
313+
>
314+
<div className="w-12 h-12 bg-primary/10 rounded flex items-center justify-center text-primary font-bold text-sm">
315+
{devlog.id}
316+
</div>
317+
<div className="flex-1 min-w-0">
318+
<div className="flex items-start justify-between">
319+
<div className="flex-1 min-w-0">
320+
<h4 className="font-semibold text-sm mb-1 truncate">{devlog.title}</h4>
321+
<p className="text-xs text-muted-foreground mb-2 line-clamp-2">
322+
{devlog.description}
323+
</p>
324+
<div className="flex flex-wrap gap-1">
325+
<DevlogStatusTag status={devlog.status} />
326+
<DevlogPriorityTag priority={devlog.priority} />
327+
<DevlogTypeTag type={devlog.type} />
328+
</div>
338329
</div>
330+
<span
331+
className="text-xs text-muted-foreground ml-4 flex-shrink-0"
332+
title={formatTimeAgoWithTooltip(devlog.updatedAt).fullDate}
333+
>
334+
{formatTimeAgoWithTooltip(devlog.updatedAt).timeAgo}
335+
</span>
339336
</div>
340-
}
341-
description={
342-
<Text type="secondary" ellipsis className={styles.devlogDescription}>
343-
{devlog.description}
344-
</Text>
345-
}
346-
/>
347-
</List.Item>
348-
)}
349-
/>
350-
)}
351-
</div>
352-
</div>
337+
</div>
338+
</div>
339+
))}
340+
</div>
341+
)}
342+
</div>
343+
</CardContent>
344+
</Card>
353345
</div>
354346

355-
<FloatButton
356-
icon={<PlusOutlined />}
357-
tooltip="Create new devlog"
347+
{/* Floating Action Button */}
348+
<Button
349+
size="lg"
350+
className="fixed bottom-6 right-6 h-14 w-14 rounded-full shadow-lg"
358351
onClick={() => router.push('/devlogs/create')}
359-
style={{ right: 24, bottom: 24 }}
360-
/>
352+
title="Create new devlog"
353+
>
354+
<Plus className="h-6 w-6" />
355+
</Button>
361356
</div>
362357
);
363358
}

0 commit comments

Comments
 (0)