Skip to content

Commit 0ca32c5

Browse files
committed
feat: scaffolding for package detail
1 parent ff88967 commit 0ca32c5

38 files changed

+2578
-1108
lines changed

app/.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Ignore artifacts:
2+
build
3+
coverage

app/.prettierrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

app/package-lock.json

Lines changed: 775 additions & 525 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/App.css

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,73 @@
1-
.App {
1+
/* Dark Mode Theme for Main App Container */
2+
.app-container {
3+
width: 100%;
4+
display: flex;
5+
flex-direction: column;
26
text-align: center;
7+
background-color: #1e1e1e;
8+
height: 100vh;
9+
color: #e0e0e0;
10+
overflow: hidden;
11+
}
12+
13+
.app-toolbar {
14+
background-color: #181818 !important;
15+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
316
}
417

5-
.App-logo {
6-
height: 40vmin;
7-
pointer-events: none;
18+
.app-logo {
19+
flex-grow: 1;
20+
display: block;
21+
color: #00f58c;
22+
font-size: 24px;
23+
font-family: monospace;
24+
cursor: pointer;
25+
font-weight: bold;
26+
transition: color 0.2s ease;
827
}
928

10-
@media (prefers-reduced-motion: no-preference) {
11-
.App-logo {
12-
animation: App-logo-spin infinite 20s linear;
13-
}
29+
.app-logo:hover {
30+
color: #2dffa0;
1431
}
1532

16-
.App-header {
17-
background-color: #282c34;
18-
min-height: 100vh;
33+
.app-content {
34+
flex: 1;
1935
display: flex;
2036
flex-direction: column;
21-
align-items: center;
22-
justify-content: center;
23-
font-size: calc(10px + 2vmin);
24-
color: white;
2537
}
2638

27-
.App-link {
28-
color: #61dafb;
39+
/* Global Dark Mode styles */
40+
body {
41+
background-color: #1e1e1e;
42+
color: #e0e0e0;
43+
margin: 0;
44+
padding: 0;
45+
}
46+
47+
/* Button overrides */
48+
.MuiButton-containedPrimary {
49+
background-color: #0d47a1 !important;
50+
color: #ffffff !important;
51+
}
52+
53+
.MuiButton-containedPrimary:hover {
54+
background-color: #1565c0 !important;
55+
}
56+
57+
.MuiButton-containedSecondary {
58+
background-color: #00f58c !important;
59+
color: #111111 !important;
60+
}
61+
62+
.MuiButton-containedSecondary:hover {
63+
background-color: #2dffa0 !important;
64+
}
65+
66+
.MuiButton-containedWarning {
67+
background-color: #bf360c !important;
68+
color: #ffffff !important;
2969
}
3070

31-
@keyframes App-logo-spin {
32-
from {
33-
transform: rotate(0deg);
34-
}
35-
to {
36-
transform: rotate(360deg);
37-
}
71+
.MuiButton-containedWarning:hover {
72+
background-color: #e64a19 !important;
3873
}

app/src/App.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import React from 'react';
2-
import { render, screen } from '@testing-library/react';
3-
import App from './App';
1+
import React from "react";
2+
import { render, screen } from "@testing-library/react";
3+
import App from "./App";
44

5-
test('renders learn react link', () => {
5+
test("renders learn react link", () => {
66
render(<App />);
77
const linkElement = screen.getByText(/learn react/i);
88
expect(linkElement).toBeInTheDocument();

app/src/App.tsx

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,27 @@
1-
import React, { ReactNode } from 'react';
2-
import AppBar from '@mui/material/AppBar/AppBar';
3-
import Toolbar from '@mui/material/Toolbar/Toolbar';
4-
import { useNavigate } from 'react-router-dom';
5-
import UserButton from './features/toolbar/components/UserButton';
6-
import { useIsMobile } from './features/toolbar/hooks/useIsMobile';
7-
import SearchBar from './features/toolbar/components/SearchBar';
1+
import React, { ReactNode } from "react";
2+
import AppBar from "@mui/material/AppBar/AppBar";
3+
import Toolbar from "@mui/material/Toolbar/Toolbar";
4+
import { useNavigate } from "react-router-dom";
5+
import UserButton from "./features/toolbar/components/UserButton";
6+
import { useIsMobile } from "./features/toolbar/hooks/useIsMobile";
7+
import SearchBar from "./features/toolbar/components/SearchBar";
8+
import "./App.css";
89

9-
export const FUEL_GREEN = '#00f58c';
10+
export const FUEL_GREEN = "#00f58c";
1011

1112
interface AppProps {
1213
children?: ReactNode;
1314
}
1415

15-
function App({children}: AppProps) {
16+
function App({ children }: AppProps) {
1617
const navigate = useNavigate();
1718
const isMobile = useIsMobile();
1819

1920
return (
20-
<div
21-
style={{
22-
width: '100%',
23-
display: 'flex',
24-
flexDirection: 'column',
25-
textAlign: 'center',
26-
backgroundColor: 'lightGrey',
27-
height: '100vh',
28-
}}>
29-
<AppBar position='static'>
30-
<Toolbar style={{ backgroundColor: '#181818' }}>
31-
<div
32-
style={{
33-
flexGrow: 1,
34-
display: 'block',
35-
color: FUEL_GREEN,
36-
fontSize: '24px',
37-
fontFamily: 'monospace',
38-
cursor: 'pointer'
39-
}} onClick={()=>navigate('/')}>
21+
<div className="app-container">
22+
<AppBar position="static">
23+
<Toolbar className="app-toolbar">
24+
<div className="app-logo" onClick={() => navigate("/")}>
4025
forc.pub
4126
</div>
4227

@@ -45,7 +30,7 @@ function App({children}: AppProps) {
4530
</Toolbar>
4631
{isMobile && <SearchBar />}
4732
</AppBar>
48-
{children}
33+
<div className="app-content">{children}</div>
4934
</div>
5035
);
5136
}

app/src/constants.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
export const SERVER_URI = process.env.REACT_APP_SERVER_URI ?? "http://localhost:8080";
2-
export const REDIRECT_URI = process.env.REACT_APP_REDIRECT_URI ?? "http://localhost:3000";
1+
export const SERVER_URI =
2+
process.env.REACT_APP_SERVER_URI ?? "http://localhost:8080";
3+
export const REDIRECT_URI =
4+
process.env.REACT_APP_REDIRECT_URI ?? "http://localhost:3000";
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/* Dark mode styling for Package Dashboard */
2+
.dashboard-container {
3+
margin-top: 24px;
4+
}
5+
6+
.section-title {
7+
border-bottom: 2px solid #90caf9 !important;
8+
padding-bottom: 8px !important;
9+
color: #ffffff !important;
10+
font-weight: 500 !important;
11+
}
12+
13+
.package-card {
14+
margin-bottom: 16px;
15+
border-radius: 8px;
16+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important;
17+
cursor: pointer;
18+
transition:
19+
transform 0.2s,
20+
background-color 0.2s;
21+
background-color: #2a2a2a !important;
22+
border: 1px solid #333 !important;
23+
}
24+
25+
.package-card-hover {
26+
background-color: #383838 !important;
27+
transform: translateY(-2px);
28+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4) !important;
29+
}
30+
31+
.card-content {
32+
display: flex;
33+
align-items: center;
34+
justify-content: space-between;
35+
background-color: transparent !important;
36+
}
37+
38+
.package-name {
39+
color: #90caf9 !important;
40+
margin-bottom: 8px !important;
41+
}
42+
43+
.package-timestamp {
44+
color: #b0b0b0 !important;
45+
font-size: 0.85rem !important;
46+
}
47+
48+
.package-description {
49+
color: #e0e0e0 !important;
50+
margin-top: 8px !important;
51+
}
52+
53+
.arrow-icon {
54+
color: #90caf9 !important;
55+
margin-left: 16px;
56+
align-self: center;
57+
font-size: 1.2rem !important;
58+
}

app/src/features/dahboard/components/PackageDashboard.tsx

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React from "react";
22
import {
33
Box,
44
Card,
@@ -7,12 +7,12 @@ import {
77
Grid,
88
CircularProgress,
99
Container,
10-
} from '@mui/material';
11-
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
12-
import { useNavigate } from 'react-router-dom';
10+
} from "@mui/material";
11+
import { useNavigate } from "react-router-dom";
1312
import useFetchRecentPackages, {
1413
RecentPackage,
15-
} from '../hooks/useFetchRecentPackages';
14+
} from "../hooks/useFetchRecentPackages";
15+
import "./PackageDashboard.css";
1616

1717
const PackageDashboard: React.FC = () => {
1818
const navigate = useNavigate();
@@ -21,69 +21,68 @@ const PackageDashboard: React.FC = () => {
2121
if (loading) {
2222
return (
2323
<Box
24-
display='flex'
25-
justifyContent='center'
26-
alignItems='center'
27-
minHeight='100vh'>
24+
display="flex"
25+
justifyContent="center"
26+
alignItems="center"
27+
minHeight="50vh"
28+
>
2829
<CircularProgress />
2930
</Box>
3031
);
3132
}
3233

3334
const renderPackages = (packages: RecentPackage[], type: string) => (
3435
<>
35-
<Typography
36-
variant='h5'
37-
gutterBottom
38-
style={{ borderBottom: '2px solid #1976d2', paddingBottom: '8px' }}>
36+
<Typography variant="h5" gutterBottom className="section-title">
3937
{type}
4038
</Typography>
4139
{packages.map((pkg, i) => (
4240
<Card
4341
key={i}
44-
style={{
45-
marginBottom: '16px',
46-
borderRadius: '8px',
47-
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
48-
cursor: 'pointer',
49-
transition: 'transform 0.2s, background-color 0.2s',
50-
}}
51-
onClick={() => navigate('/package/' + pkg.name)}
42+
className="package-card"
43+
onClick={() => navigate("/package/" + pkg.name)}
5244
onMouseEnter={(e) =>
53-
(e.currentTarget.style.backgroundColor = '#f0f8ff')
45+
(e.currentTarget.className = "package-card package-card-hover")
5446
}
55-
onMouseLeave={(e) =>
56-
(e.currentTarget.style.backgroundColor = 'white')
57-
}>
58-
<CardContent style={{ display: 'flex', alignItems: 'center' }}>
47+
onMouseLeave={(e) => (e.currentTarget.className = "package-card")}
48+
>
49+
<CardContent className="card-content">
5950
<Box flex={1}>
60-
<Typography variant='h6' gutterBottom>
51+
<Typography variant="h6" gutterBottom className="package-name">
6152
{pkg.name} (v{pkg.version})
6253
</Typography>
63-
<Typography variant='body2' color='textSecondary' gutterBottom>
64-
{type === 'Just Updated'
54+
<Typography
55+
variant="body2"
56+
color="textSecondary"
57+
gutterBottom
58+
className="package-timestamp"
59+
>
60+
{type === "Just Updated"
6561
? `Updated: ${new Date(pkg.updatedAt!).toLocaleString()}`
6662
: `Added: ${new Date(pkg.createdAt!).toLocaleString()}`}
6763
</Typography>
68-
<Typography variant='body1' paragraph>
69-
{pkg.description || 'No description available.'}
64+
<Typography
65+
variant="body1"
66+
paragraph
67+
className="package-description"
68+
>
69+
{pkg.description || "No description available."}
7070
</Typography>
7171
</Box>
72-
<ArrowForwardIosIcon color='action' />
7372
</CardContent>
7473
</Card>
7574
))}
7675
</>
7776
);
7877

7978
return (
80-
<Container maxWidth='md' style={{ marginTop: '24px' }}>
79+
<Container maxWidth="md" className="dashboard-container">
8180
<Grid container spacing={4}>
8281
<Grid item xs={12} md={6}>
83-
{renderPackages(data.recentlyUpdated, 'Just Updated')}
82+
{renderPackages(data.recentlyUpdated, "Just Updated")}
8483
</Grid>
8584
<Grid item xs={12} md={6}>
86-
{renderPackages(data.recentlyCreated, 'New Packages')}
85+
{renderPackages(data.recentlyCreated, "New Packages")}
8786
</Grid>
8887
</Grid>
8988
</Container>

app/src/features/dahboard/hooks/useFetchRecentPackages.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useState, useEffect } from 'react';
2-
import axios from 'axios';
3-
import HTTP from '../../../utils/http';
1+
import { useState, useEffect } from "react";
2+
import axios from "axios";
3+
import HTTP from "../../../utils/http";
44

55
export interface RecentPackage {
66
name: string;
@@ -31,9 +31,9 @@ const useFetchRecentPackages = () => {
3131
.catch((err: any) =>
3232
setError(
3333
axios.isAxiosError(err)
34-
? err.response?.data?.message || 'An error occurred'
35-
: 'An unknown error occurred'
36-
)
34+
? err.response?.data?.message || "An error occurred"
35+
: "An unknown error occurred",
36+
),
3737
)
3838
.finally(() => setLoading(false));
3939
}, []);

0 commit comments

Comments
 (0)