|
1 | 1 | import React from 'react'; |
| 2 | +import { Typography, Card, Row, Col, List, Tag, Avatar, Statistic, Empty } from 'antd'; |
| 3 | +import { |
| 4 | + FileTextOutlined, |
| 5 | + SyncOutlined, |
| 6 | + CheckCircleOutlined, |
| 7 | + StopOutlined, |
| 8 | + ClockCircleOutlined, |
| 9 | + ExclamationCircleOutlined, |
| 10 | + WarningOutlined, |
| 11 | + MinusCircleOutlined |
| 12 | +} from '@ant-design/icons'; |
| 13 | + |
| 14 | +const { Title, Paragraph, Text } = Typography; |
2 | 15 |
|
3 | 16 | interface DevlogEntry { |
4 | 17 | id: string; |
@@ -27,123 +40,165 @@ interface DashboardProps { |
27 | 40 | export function Dashboard({ stats, recentDevlogs, onViewDevlog }: DashboardProps) { |
28 | 41 | const getStatusColor = (status: string) => { |
29 | 42 | switch (status) { |
30 | | - case 'done': return 'bg-green-100 text-green-800'; |
31 | | - case 'in-progress': return 'bg-blue-100 text-blue-800'; |
32 | | - case 'blocked': return 'bg-red-100 text-red-800'; |
33 | | - case 'todo': return 'bg-gray-100 text-gray-800'; |
34 | | - default: return 'bg-gray-100 text-gray-800'; |
| 43 | + case 'done': return 'success'; |
| 44 | + case 'in-progress': return 'processing'; |
| 45 | + case 'blocked': return 'error'; |
| 46 | + case 'todo': return 'default'; |
| 47 | + default: return 'default'; |
| 48 | + } |
| 49 | + }; |
| 50 | + |
| 51 | + const getStatusIcon = (status: string) => { |
| 52 | + switch (status) { |
| 53 | + case 'done': return <CheckCircleOutlined />; |
| 54 | + case 'in-progress': return <SyncOutlined spin />; |
| 55 | + case 'blocked': return <StopOutlined />; |
| 56 | + case 'todo': return <ClockCircleOutlined />; |
| 57 | + default: return <MinusCircleOutlined />; |
35 | 58 | } |
36 | 59 | }; |
37 | 60 |
|
38 | 61 | const getPriorityColor = (priority: string) => { |
39 | 62 | switch (priority) { |
40 | | - case 'critical': return 'bg-red-100 text-red-800'; |
41 | | - case 'high': return 'bg-orange-100 text-orange-800'; |
42 | | - case 'medium': return 'bg-yellow-100 text-yellow-800'; |
43 | | - case 'low': return 'bg-green-100 text-green-800'; |
44 | | - default: return 'bg-gray-100 text-gray-800'; |
| 63 | + case 'critical': return 'red'; |
| 64 | + case 'high': return 'orange'; |
| 65 | + case 'medium': return 'gold'; |
| 66 | + case 'low': return 'green'; |
| 67 | + default: return 'default'; |
| 68 | + } |
| 69 | + }; |
| 70 | + |
| 71 | + const getPriorityIcon = (priority: string) => { |
| 72 | + switch (priority) { |
| 73 | + case 'critical': return <ExclamationCircleOutlined />; |
| 74 | + case 'high': return <WarningOutlined />; |
| 75 | + case 'medium': return <MinusCircleOutlined />; |
| 76 | + case 'low': return <CheckCircleOutlined />; |
| 77 | + default: return <MinusCircleOutlined />; |
45 | 78 | } |
46 | 79 | }; |
47 | 80 |
|
48 | 81 | return ( |
49 | | - <div className="space-y-6"> |
50 | | - <div> |
51 | | - <h1 className="text-2xl font-bold text-gray-900">Dashboard</h1> |
52 | | - <p className="text-gray-600">Overview of your development progress</p> |
| 82 | + <div> |
| 83 | + <div style={{ marginBottom: '24px' }}> |
| 84 | + <Title level={2}>Dashboard</Title> |
| 85 | + <Paragraph type="secondary"> |
| 86 | + Overview of your development progress |
| 87 | + </Paragraph> |
53 | 88 | </div> |
54 | 89 |
|
55 | 90 | {/* Stats Cards */} |
56 | 91 | {stats && ( |
57 | | - <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> |
58 | | - <div className="bg-white rounded-lg shadow p-6"> |
59 | | - <div className="flex items-center"> |
60 | | - <div className="p-2 bg-blue-100 rounded-lg"> |
61 | | - <span className="text-blue-600 text-xl">📝</span> |
62 | | - </div> |
63 | | - <div className="ml-4"> |
64 | | - <p className="text-sm font-medium text-gray-600">Total Devlogs</p> |
65 | | - <p className="text-2xl font-bold text-gray-900">{stats.total}</p> |
66 | | - </div> |
67 | | - </div> |
68 | | - </div> |
69 | | - |
70 | | - <div className="bg-white rounded-lg shadow p-6"> |
71 | | - <div className="flex items-center"> |
72 | | - <div className="p-2 bg-yellow-100 rounded-lg"> |
73 | | - <span className="text-yellow-600 text-xl">🏃</span> |
74 | | - </div> |
75 | | - <div className="ml-4"> |
76 | | - <p className="text-sm font-medium text-gray-600">In Progress</p> |
77 | | - <p className="text-2xl font-bold text-gray-900">{stats.byStatus['in-progress'] || 0}</p> |
78 | | - </div> |
79 | | - </div> |
80 | | - </div> |
81 | | - |
82 | | - <div className="bg-white rounded-lg shadow p-6"> |
83 | | - <div className="flex items-center"> |
84 | | - <div className="p-2 bg-green-100 rounded-lg"> |
85 | | - <span className="text-green-600 text-xl">✅</span> |
86 | | - </div> |
87 | | - <div className="ml-4"> |
88 | | - <p className="text-sm font-medium text-gray-600">Completed</p> |
89 | | - <p className="text-2xl font-bold text-gray-900">{stats.byStatus['done'] || 0}</p> |
90 | | - </div> |
91 | | - </div> |
92 | | - </div> |
93 | | - |
94 | | - <div className="bg-white rounded-lg shadow p-6"> |
95 | | - <div className="flex items-center"> |
96 | | - <div className="p-2 bg-red-100 rounded-lg"> |
97 | | - <span className="text-red-600 text-xl">🚫</span> |
98 | | - </div> |
99 | | - <div className="ml-4"> |
100 | | - <p className="text-sm font-medium text-gray-600">Blocked</p> |
101 | | - <p className="text-2xl font-bold text-gray-900">{stats.byStatus['blocked'] || 0}</p> |
102 | | - </div> |
103 | | - </div> |
104 | | - </div> |
105 | | - </div> |
| 92 | + <Row gutter={[16, 16]} style={{ marginBottom: '32px' }}> |
| 93 | + <Col xs={24} sm={12} lg={6}> |
| 94 | + <Card> |
| 95 | + <Statistic |
| 96 | + title="Total Devlogs" |
| 97 | + value={stats.total} |
| 98 | + prefix={<FileTextOutlined style={{ color: '#1890ff' }} />} |
| 99 | + valueStyle={{ color: '#1890ff' }} |
| 100 | + /> |
| 101 | + </Card> |
| 102 | + </Col> |
| 103 | + <Col xs={24} sm={12} lg={6}> |
| 104 | + <Card> |
| 105 | + <Statistic |
| 106 | + title="In Progress" |
| 107 | + value={stats.byStatus['in-progress'] || 0} |
| 108 | + prefix={<SyncOutlined spin style={{ color: '#faad14' }} />} |
| 109 | + valueStyle={{ color: '#faad14' }} |
| 110 | + /> |
| 111 | + </Card> |
| 112 | + </Col> |
| 113 | + <Col xs={24} sm={12} lg={6}> |
| 114 | + <Card> |
| 115 | + <Statistic |
| 116 | + title="Completed" |
| 117 | + value={stats.byStatus['done'] || 0} |
| 118 | + prefix={<CheckCircleOutlined style={{ color: '#52c41a' }} />} |
| 119 | + valueStyle={{ color: '#52c41a' }} |
| 120 | + /> |
| 121 | + </Card> |
| 122 | + </Col> |
| 123 | + <Col xs={24} sm={12} lg={6}> |
| 124 | + <Card> |
| 125 | + <Statistic |
| 126 | + title="Blocked" |
| 127 | + value={stats.byStatus['blocked'] || 0} |
| 128 | + prefix={<StopOutlined style={{ color: '#ff4d4f' }} />} |
| 129 | + valueStyle={{ color: '#ff4d4f' }} |
| 130 | + /> |
| 131 | + </Card> |
| 132 | + </Col> |
| 133 | + </Row> |
106 | 134 | )} |
107 | 135 |
|
108 | 136 | {/* Recent Devlogs */} |
109 | | - <div className="bg-white rounded-lg shadow"> |
110 | | - <div className="px-6 py-4 border-b border-gray-200"> |
111 | | - <h2 className="text-lg font-medium text-gray-900">Recent Devlogs</h2> |
112 | | - </div> |
113 | | - <div className="divide-y divide-gray-200"> |
114 | | - {recentDevlogs.length === 0 ? ( |
115 | | - <div className="px-6 py-8 text-center"> |
116 | | - <p className="text-gray-500">No devlogs found</p> |
117 | | - </div> |
118 | | - ) : ( |
119 | | - recentDevlogs.map((devlog) => ( |
120 | | - <div |
121 | | - key={devlog.id} |
122 | | - className="px-6 py-4 hover:bg-gray-50 cursor-pointer" |
| 137 | + <Card |
| 138 | + title="Recent Devlogs" |
| 139 | + bodyStyle={{ padding: 0 }} |
| 140 | + > |
| 141 | + {recentDevlogs.length === 0 ? ( |
| 142 | + <Empty |
| 143 | + image={Empty.PRESENTED_IMAGE_SIMPLE} |
| 144 | + description="No devlogs found" |
| 145 | + style={{ padding: '40px' }} |
| 146 | + /> |
| 147 | + ) : ( |
| 148 | + <List |
| 149 | + itemLayout="horizontal" |
| 150 | + dataSource={recentDevlogs} |
| 151 | + renderItem={(devlog) => ( |
| 152 | + <List.Item |
| 153 | + style={{ cursor: 'pointer', padding: '16px 24px' }} |
123 | 154 | onClick={() => onViewDevlog(devlog)} |
| 155 | + actions={[ |
| 156 | + <Text type="secondary" key="date"> |
| 157 | + {new Date(devlog.updatedAt).toLocaleDateString()} |
| 158 | + </Text> |
| 159 | + ]} |
124 | 160 | > |
125 | | - <div className="flex items-center justify-between"> |
126 | | - <div className="flex-1"> |
127 | | - <h3 className="text-sm font-medium text-gray-900">{devlog.title}</h3> |
128 | | - <p className="text-sm text-gray-500 mt-1">{devlog.description}</p> |
129 | | - <div className="flex items-center space-x-4 mt-2"> |
130 | | - <span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getStatusColor(devlog.status)}`}> |
131 | | - {devlog.status} |
132 | | - </span> |
133 | | - <span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${getPriorityColor(devlog.priority)}`}> |
134 | | - {devlog.priority} |
135 | | - </span> |
136 | | - <span className="text-xs text-gray-500"> |
137 | | - {new Date(devlog.updatedAt).toLocaleDateString()} |
138 | | - </span> |
| 161 | + <List.Item.Meta |
| 162 | + avatar={ |
| 163 | + <Avatar |
| 164 | + icon={getStatusIcon(devlog.status)} |
| 165 | + style={{ |
| 166 | + backgroundColor: getStatusColor(devlog.status) === 'success' ? '#52c41a' : |
| 167 | + getStatusColor(devlog.status) === 'processing' ? '#1890ff' : |
| 168 | + getStatusColor(devlog.status) === 'error' ? '#ff4d4f' : '#d9d9d9' |
| 169 | + }} |
| 170 | + /> |
| 171 | + } |
| 172 | + title={ |
| 173 | + <div> |
| 174 | + <Text strong>{devlog.title}</Text> |
| 175 | + <div style={{ marginTop: '4px' }}> |
| 176 | + <Tag |
| 177 | + color={getStatusColor(devlog.status)} |
| 178 | + icon={getStatusIcon(devlog.status)} |
| 179 | + > |
| 180 | + {devlog.status} |
| 181 | + </Tag> |
| 182 | + <Tag |
| 183 | + color={getPriorityColor(devlog.priority)} |
| 184 | + icon={getPriorityIcon(devlog.priority)} |
| 185 | + > |
| 186 | + {devlog.priority} |
| 187 | + </Tag> |
| 188 | + </div> |
139 | 189 | </div> |
140 | | - </div> |
141 | | - </div> |
142 | | - </div> |
143 | | - )) |
144 | | - )} |
145 | | - </div> |
146 | | - </div> |
| 190 | + } |
| 191 | + description={ |
| 192 | + <Text type="secondary" ellipsis> |
| 193 | + {devlog.description} |
| 194 | + </Text> |
| 195 | + } |
| 196 | + /> |
| 197 | + </List.Item> |
| 198 | + )} |
| 199 | + /> |
| 200 | + )} |
| 201 | + </Card> |
147 | 202 | </div> |
148 | 203 | ); |
149 | 204 | } |
0 commit comments