Skip to content

Commit 0ed7e01

Browse files
committed
refactor: diff views for roadmap
1 parent 3827e75 commit 0ed7e01

File tree

4 files changed

+260
-247
lines changed

4 files changed

+260
-247
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import RoadmapCard from './RoadmapCard';
3+
import { getOnlyBgStatusColor, statusTextColor } from '../../utils/roadmapHelpers';
4+
5+
const RoadmapView = ({ issuesData = [] }) => {
6+
const groupedApps = issuesData.reduce((acc, app) => {
7+
const status = app.status || 'Planned'; // Default to Planned if not specified
8+
if (!acc[status]) {
9+
acc[status] = [];
10+
}
11+
acc[status].push(app);
12+
return acc;
13+
}, {});
14+
15+
const statusOrder = ['Planned', 'In Progress', 'Completed', 'On Hold'];
16+
17+
return (
18+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
19+
{statusOrder.map((status) => (
20+
<div key={status} className="bg-gray-900/70 backdrop-blur-sm rounded-xl shadow-lg p-4 border border-gray-800">
21+
<h3
22+
className={`text-lg font-mono tracking-wider mb-4 flex items-center gap-2 text-white`}
23+
>
24+
<span
25+
className={`w-3 h-3 rounded-full ${getOnlyBgStatusColor(status)} ${statusTextColor(status)}`}
26+
></span>
27+
{status} ({groupedApps[status]?.length || 0})
28+
</h3>
29+
<div className="space-y-4">
30+
{groupedApps[status]?.map((app, appIndex) => (
31+
<RoadmapCard key={app.id} app={app} index={appIndex} />
32+
))}
33+
</div>
34+
</div>
35+
))}
36+
</div>
37+
);
38+
};
39+
40+
export default RoadmapView;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import React, { useState } from 'react';
2+
import { Link } from 'react-router-dom';
3+
import CustomDropdown from '../CustomDropdown';
4+
import { FunnelIcon } from '@phosphor-icons/react';
5+
import { getStatusClasses, getPriorityClasses } from '../../utils/roadmapHelpers';
6+
7+
const TableView = ({ issuesData = [] }) => {
8+
const [sortBy, setSortBy] = useState('title');
9+
const [sortOrder, setSortOrder] = useState('asc'); // 'asc' or 'desc'
10+
const [filterStatus, setFilterStatus] = useState('All');
11+
12+
const filteredApps = issuesData.filter((app) =>
13+
filterStatus === 'All' ? true : (app.status || 'Planned') === filterStatus,
14+
);
15+
16+
const sortedApps = [...filteredApps].sort((a, b) => {
17+
let comparison = 0;
18+
if (sortBy === 'title') {
19+
comparison = a.title.localeCompare(b.title);
20+
} else if (sortBy === 'status') {
21+
const statusOrder = ['Planned', 'In Progress', 'On Hold', 'Completed'];
22+
comparison =
23+
statusOrder.indexOf(a.status || 'Planned') -
24+
statusOrder.indexOf(b.status || 'Planned');
25+
} else if (sortBy === 'created_at') {
26+
comparison = new Date(a.created_at) - new Date(b.created_at);
27+
} else if (sortBy === 'priority') {
28+
const priorityOrder = ['High', 'Medium', 'Low'];
29+
comparison =
30+
priorityOrder.indexOf(a.priority || 'Low') -
31+
priorityOrder.indexOf(b.priority || 'Low');
32+
}
33+
34+
return sortOrder === 'asc' ? comparison : -comparison;
35+
});
36+
37+
const handleSort = (column) => {
38+
if (sortBy === column) {
39+
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
40+
} else {
41+
setSortBy(column);
42+
setSortOrder('asc');
43+
}
44+
};
45+
46+
const renderSortArrow = (column) => {
47+
if (sortBy === column) {
48+
return sortOrder === 'asc' ? ' ↑' : ' ↓';
49+
}
50+
return '';
51+
};
52+
53+
return (
54+
<div className="overflow-x-auto rounded-xl shadow-lg bg-gray-900/70 backdrop-blur-sm border border-gray-800">
55+
<div className="mb-4 mt-4 flex justify-center items-center">
56+
<CustomDropdown
57+
options={[
58+
{ label: 'All Statuses', value: 'All' },
59+
{ label: 'Planned', value: 'Planned' },
60+
{ label: 'In Progress', value: 'In Progress' },
61+
{ label: 'Completed', value: 'Completed' },
62+
{ label: 'On Hold', value: 'On Hold' },
63+
]}
64+
value={filterStatus}
65+
onChange={setFilterStatus}
66+
icon={FunnelIcon}
67+
label="Filter by Status"
68+
/>
69+
</div>
70+
<table className="min-w-full divide-y divide-gray-700 text-white">
71+
<thead className="bg-gray-800/60 border-b border-gray-700">
72+
<tr>
73+
<th
74+
scope="col"
75+
className="px-6 py-3 text-left text-sm font-mono font-bold text-gray-400 uppercase tracking-wide cursor-pointer hover:text-white transition-colors"
76+
onClick={() => handleSort('title')}
77+
>
78+
Title {renderSortArrow('title')}
79+
</th>
80+
<th
81+
scope="col"
82+
className="px-6 py-3 text-left text-sm font-mono font-bold text-gray-400 uppercase tracking-wide"
83+
>
84+
Description
85+
</th>
86+
<th
87+
scope="col"
88+
className="px-6 py-3 text-left text-sm font-mono font-bold text-gray-400 uppercase tracking-wide cursor-pointer hover:text-white transition-colors"
89+
onClick={() => handleSort('status')}
90+
>
91+
Status {renderSortArrow('status')}
92+
</th>
93+
<th
94+
scope="col"
95+
className="px-6 py-3 text-left text-sm font-mono font-bold text-gray-400 uppercase tracking-wide cursor-pointer hover:text-white transition-colors"
96+
onClick={() => handleSort('priority')}
97+
>
98+
Priority {renderSortArrow('priority')}
99+
</th>
100+
<th
101+
scope="col"
102+
className="px-6 py-3 text-left text-sm font-mono font-bold text-gray-400 uppercase tracking-wide cursor-pointer hover:text-white transition-colors"
103+
onClick={() => handleSort('created_at')}
104+
>
105+
Created At {renderSortArrow('created_at')}
106+
</th>
107+
<th
108+
scope="col"
109+
className="px-6 py-3 text-left text-sm font-mono font-bold text-gray-400 uppercase tracking-wide"
110+
>
111+
Notes
112+
</th>
113+
</tr>
114+
</thead>
115+
<tbody className="divide-y divide-gray-700">
116+
{sortedApps.map((app, index) => (
117+
<tr key={app.id} className={`group hover:bg-indigo-500/20 transition-colors ${index % 2 === 0 ? 'bg-gray-900/40' : 'bg-gray-800/40'}`}>
118+
<td className="px-6 py-4 whitespace-nowrap text-sm font-mono font-medium text-white">
119+
<Link to={`/roadmap/${app.id}`} className="hover:underline text-purple-400">
120+
{app.title}
121+
</Link>
122+
</td>
123+
<td className="px-6 py-4 text-sm font-mono text-gray-400">
124+
{app.description}
125+
</td>
126+
<td className="px-6 py-4 whitespace-nowrap">
127+
<span
128+
className={`px-2 py-0 inline-flex text-xs font-mono font-semibold rounded-md shadow-sm border ${getStatusClasses(app.status)}`}
129+
>
130+
{app.status || 'Planned'}
131+
</span>
132+
</td>
133+
<td className="px-6 py-4 whitespace-nowrap">
134+
<span
135+
className={`px-2 py-0 inline-flex text-xs font-mono font-semibold rounded-md shadow-sm border ${getPriorityClasses(app.priority)}`}
136+
>
137+
{app.priority || 'Low'}
138+
</span>
139+
</td>
140+
<td className="px-6 py-4 whitespace-nowrap text-sm font-mono text-gray-400">
141+
{new Date(app.created_at).toLocaleDateString()}
142+
</td>
143+
<td className="px-6 py-4 text-sm font-mono text-gray-500">
144+
{app.notes || '-'}
145+
</td>
146+
</tr>
147+
))}
148+
</tbody>
149+
</table>
150+
</div>
151+
);
152+
};
153+
154+
export default TableView;

0 commit comments

Comments
 (0)