Skip to content

Commit 7577905

Browse files
authored
Merge pull request #9 from manavgup/collection_ui
View collection page
2 parents 69aaa42 + d048ed9 commit 7577905

File tree

8 files changed

+405
-6
lines changed

8 files changed

+405
-6
lines changed

webui/src/App.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import SignIn from "./components/SignIn";
1111
import Dashboard from "./components/Dashboard";
1212
import CollectionForm from "./components/CollectionForm";
1313
import { AuthProvider, useAuth } from "./contexts/AuthContext";
14-
import "./css/common.css";
14+
import Collections from "./components/Collections";
15+
16+
17+
import "./styles/common.css";
18+
import "./styles/global.scss";
1519

1620
function AppContent() {
1721
const { user, loading } = useAuth();
@@ -26,9 +30,9 @@ function AppContent() {
2630
<Suspense fallback={<div>Loading...</div>}>
2731
<div className="App">
2832
{user && <UIHeader />}
29-
<div className="content-wrapper ">
33+
{/* <div className="content-wrapper "> */}
3034
<Content
31-
className={"main-content"}
35+
// className={"main-content"}
3236
>
3337
<Routes>
3438
<Route
@@ -45,14 +49,20 @@ function AppContent() {
4549
user ? <CollectionForm /> : <Navigate to="/signin" />
4650
}
4751
/>
52+
<Route
53+
path="/collections"
54+
element={
55+
user ? <Collections /> : <Navigate to="/signin" />
56+
}
57+
/>
4858
<Route
4959
path="/"
5060
element={<Navigate to={user ? "/dashboard" : "/signin"} />}
5161
/>
5262
</Routes>
5363
</Content>
5464
</div>
55-
</div>
65+
{/* </div> */}
5666
</Suspense>
5767
</Router>
5868
</GlobalTheme>
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import React, { useEffect, useState } from "react";
2+
import { useNavigate } from "react-router-dom";
3+
4+
import {
5+
Button,
6+
Content,
7+
Row,
8+
DataTable,
9+
Loading,
10+
Table,
11+
TableBody,
12+
TableCell,
13+
TableHead,
14+
TableHeader,
15+
TableRow,
16+
TableToolbar,
17+
TableToolbarAction,
18+
TableToolbarContent,
19+
TableToolbarMenu,
20+
TableToolbarSearch,
21+
OverflowMenu,
22+
OverflowMenuItem,
23+
Column,
24+
FlexGrid,
25+
UnorderedList,
26+
ListItem,
27+
Pagination
28+
} from "@carbon/react";
29+
import { Add } from "@carbon/icons-react";
30+
31+
import { getUserCollections } from "../api/api";
32+
import "../styles/view-ui.css";
33+
34+
const Collections = () => {
35+
let navigate = useNavigate();
36+
/* TODO: Add a messageQueue for the application */
37+
const [errorMessage, setErrorMessage] = useState("");
38+
const [isLoadingCollections, setIsLoadingCollections] = useState(false);
39+
const [userCollections, setUserCollections] = useState([]);
40+
const [tableDataRows, setTableDataRows] = useState([]);
41+
const [selectedCollection, setSelectedCollection] = useState([]);
42+
43+
const headers = [
44+
{
45+
key: "name",
46+
header: "Name",
47+
},
48+
{
49+
key: "is_private",
50+
header: "Private",
51+
},
52+
{
53+
key: "status",
54+
header: "Status",
55+
},
56+
];
57+
58+
useEffect(() => {
59+
fetchUserCollections();
60+
}, []);
61+
62+
const fetchUserCollections = async () => {
63+
setIsLoadingCollections(true);
64+
try {
65+
const collections = (await getUserCollections())?.collections;
66+
// setUserCollections(Array.isArray(collections) ? collections : []);
67+
68+
// datatable requires a field called id
69+
const updatedKeys = collections.map(({ collection_id: id, ...rest }) => ({
70+
id,
71+
...rest,
72+
}));
73+
74+
// change other types fields to string
75+
const updatedValues = updatedKeys.map((obj) => {
76+
return obj.is_private
77+
? { ...obj, is_private: "true" }
78+
: { ...obj, is_private: "false" };
79+
});
80+
setUserCollections(Array.isArray(updatedValues) ? updatedValues : []);
81+
setTableDataRows(updatedValues.slice(0, 5));
82+
} catch (error) {
83+
console.error("Error fetching user collections:", error);
84+
setErrorMessage(
85+
"Failed to fetch user collections. Please try again later."
86+
);
87+
} finally {
88+
// console.log(rendRows)
89+
setIsLoadingCollections(false);
90+
}
91+
};
92+
93+
const createNewCollection = () => {
94+
navigate("/create-collection");
95+
};
96+
97+
const onClickCollectionTable = (collectionId) => {
98+
setSelectedCollection(
99+
userCollections.find((collection) => collection.id === collectionId)
100+
);
101+
};
102+
103+
/* TODO: fix bug with next page pagination component */
104+
const pageOnChange = (e) => {
105+
setTableDataRows(
106+
userCollections.slice((e.page - 1) * e.pageSize, e.page * e.pageSize)
107+
);
108+
};
109+
110+
return (
111+
<Content className="view-content">
112+
<>
113+
<div className="view-title">
114+
<h2>Collections</h2>
115+
<Button
116+
renderIcon={Add}
117+
aria-label="Add new collection"
118+
onClick={() => createNewCollection()}
119+
>
120+
New Collection
121+
</Button>
122+
</div>
123+
124+
{isLoadingCollections ? (
125+
<div className="data-loading">
126+
<Loading description="Loading collections" withOverlay={false} />
127+
</div>
128+
) : (
129+
<>
130+
<DataTable rows={tableDataRows} headers={headers}>
131+
{({
132+
rows,
133+
headers,
134+
getTableProps,
135+
getHeaderProps,
136+
getRowProps,
137+
onInputChange,
138+
}) => (
139+
<>
140+
<TableToolbar>
141+
<div className="view-datatable-header">
142+
Any header text about the data below...
143+
</div>
144+
<TableToolbarContent>
145+
{/* pass in `onInputChange` change here to make filtering work */}
146+
<TableToolbarSearch onChange={onInputChange} />
147+
<TableToolbarMenu>
148+
<TableToolbarAction onClick={() => {}}>
149+
Action 1
150+
</TableToolbarAction>
151+
<TableToolbarAction onClick={() => {}}>
152+
Action 2
153+
</TableToolbarAction>
154+
<TableToolbarAction onClick={() => {}}>
155+
Action 3
156+
</TableToolbarAction>
157+
</TableToolbarMenu>
158+
</TableToolbarContent>
159+
</TableToolbar>
160+
161+
<Table {...getTableProps()}>
162+
<TableHead key="table-head">
163+
<TableRow>
164+
{headers.map((header, idx) => (
165+
<TableHeader
166+
{...getHeaderProps({ header, isSortable: true })}
167+
key={idx}
168+
>
169+
{header.header}
170+
</TableHeader>
171+
))}
172+
<TableHeader
173+
key="column-menu-header"
174+
className="view-tabledata-action"
175+
></TableHeader>
176+
</TableRow>
177+
</TableHead>
178+
<TableBody key="table-body">
179+
{rows.map((row, idx) => (
180+
<TableRow
181+
{...getRowProps({ row })}
182+
key={idx}
183+
onClick={() => onClickCollectionTable(row.id)}
184+
>
185+
{row.cells.map((cell) => (
186+
<>
187+
<TableCell key={cell.id}>{cell.value}</TableCell>
188+
</>
189+
))}
190+
<TableCell
191+
key="column-menu"
192+
className="cds--table-column-menu"
193+
>
194+
<OverflowMenu
195+
size="sm"
196+
flipped
197+
aria-label="options 1"
198+
>
199+
<OverflowMenuItem
200+
key="item-1"
201+
itemText="View Collection"
202+
/>
203+
<OverflowMenuItem
204+
key="item-2"
205+
itemText="Edit Collection"
206+
/>
207+
<OverflowMenuItem
208+
key="item-3"
209+
itemText="Delete Collection"
210+
/>
211+
</OverflowMenu>
212+
</TableCell>
213+
</TableRow>
214+
))}
215+
</TableBody>
216+
</Table>
217+
</>
218+
)}
219+
</DataTable>
220+
<Pagination
221+
disabled={false}
222+
page="1"
223+
totalItems={userCollections.length}
224+
pageSize="5"
225+
onChange={(e) => pageOnChange(e)}
226+
pageSizes={[5, 10, 20, 50]}
227+
size="md"
228+
/>
229+
</>
230+
)}
231+
</>
232+
<FlexGrid className="view-detail-grid">
233+
<Row>
234+
<Column>
235+
<div className="view-detail-label">Name:</div>
236+
<div className="view-detail-text">{selectedCollection?.name}</div>
237+
</Column>
238+
<Column>
239+
<div className="view-detail-label">ID:</div>
240+
<div className="view-detail-text">
241+
{selectedCollection?.collection_id}
242+
</div>
243+
</Column>
244+
</Row>
245+
<Row>
246+
<Column>
247+
<div className="view-detail-label">Private:</div>
248+
<div className="view-detail-text">
249+
{selectedCollection?.is_private ? "True" : "False"}
250+
</div>
251+
</Column>
252+
<Column>
253+
<div className="view-detail-label">Status</div>
254+
<div className="view-detail-text">{selectedCollection?.status}</div>
255+
</Column>
256+
</Row>
257+
258+
<Row>
259+
<Column>
260+
<div className="view-detail-label">Created At:</div>
261+
<div className="view-detail-text">
262+
{selectedCollection?.created_at}
263+
</div>
264+
</Column>
265+
<Column>
266+
<div className="view-detail-label">Updated At:</div>
267+
<div className="view-detail-text">
268+
{selectedCollection?.updated_at}
269+
</div>
270+
</Column>
271+
</Row>
272+
<Row>
273+
<Column>
274+
<div className="view-detail-label">Files:</div>
275+
<div className="view-detail-text">
276+
<UnorderedList>
277+
{selectedCollection?.files?.map((file, idx) => {
278+
return <ListItem key={idx}>{file.filename}</ListItem>;
279+
})}
280+
</UnorderedList>
281+
</div>
282+
</Column>
283+
</Row>
284+
</FlexGrid>
285+
</Content>
286+
);
287+
};
288+
289+
export default Collections;

webui/src/components/SideNav.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ const UISideNav = ({ isSideNavExpanded, handleSideNavExpand }) => {
3131
>
3232
Dashboard
3333
</SideNavLink>
34+
<SideNavLink
35+
renderIcon={Fade}
36+
href="#"
37+
onClick={() => onNavigate("/collections")}
38+
isActive={isActive("/collections")}
39+
>
40+
Collections
41+
</SideNavLink>
3442
<SideNavLink
3543
renderIcon={Fade}
3644
href="#"

webui/src/css/__init__.py

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@use '@carbon/react' with (
2+
$css--default-type: true,
3+
$css--reset: true,
4+
$use-flexbox-grid: true
5+
);

webui/src/css/common.css renamed to webui/src/styles/common.css

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
monospace;
1414
}*/
1515

16-
1716
/* App layout */
1817
/* .App {
1918
display: flex;
@@ -100,7 +99,8 @@
10099
}
101100

102101
/* Typography */
103-
h2, p {
102+
h2,
103+
p {
104104
margin-bottom: 15px;
105105
}
106106

@@ -299,4 +299,18 @@ h2, p {
299299

300300
.collection-tile .cds--tile-content__below-the-fold {
301301
padding-top: 16px;
302+
}
303+
304+
/* Data loading */
305+
.data-loading {
306+
width: 100%;
307+
display: flex;
308+
justify-content: center;
309+
padding: 3rem;
310+
opacity: 70%;
311+
}
312+
313+
/* Fix margin for Pagination */
314+
.cds--pagination .cds--form-item {
315+
margin: 0;
302316
}

0 commit comments

Comments
 (0)