Skip to content

Commit 0692187

Browse files
savathoonamyjchensomethingnew2-0chippydip
authored
2024 hackweek (#184)
Co-authored-by: Amy Chen <[email protected]> Co-authored-by: Peter Collins <[email protected]> Co-authored-by: Chip Bradford <[email protected]>
1 parent 1c1ff99 commit 0692187

40 files changed

+2053
-2291
lines changed

src/App.tsx

Lines changed: 154 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import {Link as RouterLink, Route, Routes} from 'react-router-dom';
33

4-
import {styled} from '@mui/material/styles';
4+
import {createTheme, styled, ThemeProvider} from '@mui/material/styles';
55
import Link from '@mui/material/Link';
66
import MuiDrawer from '@mui/material/Drawer';
77
import Box from '@mui/material/Box';
@@ -37,6 +37,19 @@ import ReadTag from './pages/tags/Read';
3737
import ReadUser from './pages/users/Read';
3838
import {useCurrentUser} from './authentication';
3939
import ReadRequest from './pages/requests/Read';
40+
import {
41+
alpha,
42+
CssBaseline,
43+
PaletteMode,
44+
Stack,
45+
ToggleButton,
46+
ToggleButtonGroup,
47+
Tooltip,
48+
useMediaQuery,
49+
useTheme,
50+
} from '@mui/material';
51+
import {DarkMode, LightMode, Monitor} from '@mui/icons-material';
52+
import {lightGreen, red, yellow} from '@mui/material/colors';
4053

4154
const drawerWidth: number = 240;
4255

@@ -88,7 +101,65 @@ const Drawer = styled(MuiDrawer, {
88101
},
89102
}));
90103

91-
function Dashboard() {
104+
function ThemeToggle({setThemeMode, condensed}: {setThemeMode: (theme: PaletteMode) => void; condensed: boolean}) {
105+
const [storedTheme, setStoredTheme] = React.useState(
106+
localStorage.getItem('user-set-color-scheme') as 'light' | 'dark' | null,
107+
);
108+
const currentTheme = useTheme();
109+
const systemTheme = useMediaQuery('(prefers-color-scheme: dark)') ? 'dark' : 'light';
110+
111+
const handleThemeOverride = (theme: PaletteMode) => {
112+
setThemeMode(theme);
113+
localStorage.setItem('user-set-color-scheme', theme);
114+
setStoredTheme(theme);
115+
};
116+
117+
const handleSystemDefault = () => {
118+
setThemeMode(systemTheme);
119+
localStorage.removeItem('user-set-color-scheme');
120+
setStoredTheme(null);
121+
};
122+
123+
return (
124+
<ToggleButtonGroup size="small">
125+
{(currentTheme.palette.mode != 'light' || !condensed) && (
126+
<Tooltip title="Light Mode">
127+
<ToggleButton
128+
value="left"
129+
selected={storedTheme === 'light'}
130+
onClick={() => handleThemeOverride('light')}
131+
aria-label="Light mode">
132+
<LightMode />
133+
</ToggleButton>
134+
</Tooltip>
135+
)}
136+
{!condensed && (
137+
<Tooltip title="System Default">
138+
<ToggleButton
139+
value="center"
140+
selected={storedTheme == null}
141+
onClick={handleSystemDefault}
142+
aria-label="System Default">
143+
<Monitor />
144+
</ToggleButton>
145+
</Tooltip>
146+
)}
147+
{(currentTheme.palette.mode != 'dark' || !condensed) && (
148+
<Tooltip title="Dark Mode">
149+
<ToggleButton
150+
value="right"
151+
selected={storedTheme === 'dark'}
152+
onClick={() => handleThemeOverride('dark')}
153+
aria-label="Dark mode">
154+
<DarkMode />
155+
</ToggleButton>
156+
</Tooltip>
157+
)}
158+
</ToggleButtonGroup>
159+
);
160+
}
161+
162+
function Dashboard({setThemeMode}: {setThemeMode: (theme: PaletteMode) => void}) {
92163
const [open, setOpen] = React.useState(true);
93164
const toggleDrawer = () => {
94165
setOpen(!open);
@@ -137,7 +208,7 @@ function Dashboard() {
137208
textDecoration: 'none',
138209
}}>
139210
<Avatar src="/logo-square.png" variant="square" />
140-
<Typography component="h1" variant="h5" sx={{px: 2}}>
211+
<Typography component="h1" variant="h5" sx={{px: 2}} color="text.accent">
141212
ACCESS
142213
</Typography>
143214
</Link>
@@ -149,12 +220,15 @@ function Dashboard() {
149220
<List component="nav">
150221
<NavItems open={open} />
151222
</List>
223+
<Stack marginTop="auto" p={2}>
224+
<ThemeToggle setThemeMode={setThemeMode} condensed={!open} />
225+
</Stack>
152226
</Drawer>
153227
<Box
154228
component="main"
155229
sx={{
156230
backgroundColor: (theme) =>
157-
theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900],
231+
theme.palette.mode === 'light' ? theme.palette.grey[200] : theme.palette.grey[800],
158232
flexGrow: 1,
159233
height: '100vh',
160234
overflow: 'auto',
@@ -189,6 +263,81 @@ function Dashboard() {
189263
}
190264

191265
export default function App() {
266+
const storedTheme = localStorage.getItem('user-set-color-scheme') as 'light' | 'dark' | null;
267+
const systemTheme = useMediaQuery('(prefers-color-scheme: dark)') ? 'dark' : 'light';
268+
const initialMode = storedTheme ?? systemTheme;
269+
const [mode, setMode] = React.useState<PaletteMode>(initialMode);
270+
271+
// See https://discord.com/branding
272+
let theme = React.useMemo(() => {
273+
const base = createTheme({
274+
palette: {
275+
mode,
276+
primary: {
277+
main: '#5865F2',
278+
light: '#A5B2FF',
279+
},
280+
secondary: {
281+
main: '#EB459E',
282+
},
283+
error: {
284+
main: '#ED4245',
285+
},
286+
warning: {
287+
main: '#FEE75C',
288+
},
289+
success: {
290+
main: '#57F287',
291+
},
292+
text: {
293+
accent: mode === 'light' ? '#5865F2' : '#A5B2FF',
294+
},
295+
},
296+
components: {
297+
MuiChip: {
298+
styleOverrides: {
299+
colorPrimary: ({ownerState, theme}) => ({
300+
...(ownerState.variant === 'outlined' &&
301+
ownerState.color === 'primary' && {
302+
color: theme.palette.text.accent,
303+
borderColor: theme.palette.text.accent,
304+
}),
305+
}),
306+
deleteIcon: ({ownerState, theme}) => ({
307+
...(ownerState.variant === 'outlined' &&
308+
ownerState.color === 'primary' && {
309+
color: theme.palette.text.accent,
310+
}),
311+
}),
312+
},
313+
},
314+
},
315+
});
316+
return createTheme(base, {
317+
palette: {
318+
highlight: {
319+
success: base.palette.augmentColor({
320+
color: {main: mode === 'light' ? lightGreen[100] : alpha(lightGreen[500], 0.3)},
321+
name: 'success',
322+
}),
323+
warning: base.palette.augmentColor({
324+
color: {main: mode === 'light' ? yellow[100] : alpha(yellow[500], 0.3)},
325+
name: 'warning',
326+
}),
327+
danger: base.palette.augmentColor({
328+
color: {main: mode === 'light' ? red[100] : alpha(red[500], 0.3)},
329+
name: 'danger',
330+
}),
331+
},
332+
},
333+
});
334+
}, [mode]);
335+
192336
useCurrentUser();
193-
return <Dashboard />;
337+
return (
338+
<ThemeProvider theme={theme}>
339+
<CssBaseline />
340+
<Dashboard setThemeMode={setMode} />
341+
</ThemeProvider>
342+
);
194343
}

src/components/AvatarButton.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {Avatar, ButtonBase, Typography} from '@mui/material';
2+
import {ReactNode} from 'react';
3+
4+
interface AvatarButtonProps {
5+
icon: ReactNode;
6+
text?: string;
7+
strikethrough?: boolean;
8+
onClick?: () => void;
9+
}
10+
11+
export default function AvatarButton({icon, text, strikethrough, onClick}: AvatarButtonProps) {
12+
return (
13+
<ButtonBase
14+
disabled={onClick == null}
15+
sx={{
16+
display: 'flex',
17+
flexDirection: 'column',
18+
gap: 0.5,
19+
p: 0.5,
20+
borderRadius: 2,
21+
width: 100,
22+
wordBreak: 'break-word',
23+
}}
24+
onClick={onClick}>
25+
<Avatar sx={{bgcolor: 'primary.main'}}>{icon}</Avatar>
26+
{text && (
27+
<Typography variant="body2" sx={{...(strikethrough && {textDecoration: 'line-through'})}}>
28+
{text}
29+
</Typography>
30+
)}
31+
</ButtonBase>
32+
);
33+
}

src/components/Breadcrumbs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Breadcrumbs from '@mui/material/Breadcrumbs';
22
import Typography from '@mui/material/Typography';
33

4-
import Link, {LinkProps} from '@mui/material/Link';
4+
import Link from '@mui/material/Link';
55
import {Link as RouterLink, useLocation} from 'react-router-dom';
66

77
export default function Crumbs() {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {darken, lighten, PaletteColor, styled} from '@mui/material';
2+
import {DataGrid} from '@mui/x-data-grid';
3+
4+
const getHoverBackgroundColor = (color: PaletteColor, mode: string) => (mode === 'dark' ? color.dark : color.light);
5+
6+
const getSelectedBackgroundColor = (color: PaletteColor, mode: string) =>
7+
mode === 'dark' ? darken(color.dark, 0.5) : lighten(color.light, 0.5);
8+
9+
const getSelectedHoverBackgroundColor = (color: PaletteColor, mode: string) =>
10+
mode === 'dark' ? darken(color.dark, 0.4) : lighten(color.light, 0.4);
11+
12+
const BulkRenewalDataGrid = styled(DataGrid)(
13+
({
14+
theme: {
15+
palette: {highlight, mode},
16+
},
17+
}) => ({
18+
'& .super-app-theme--Expired': {
19+
backgroundColor: highlight.danger.main,
20+
'&:hover': {
21+
backgroundColor: getHoverBackgroundColor(highlight.danger, mode),
22+
},
23+
'&.Mui-selected': {
24+
backgroundColor: getSelectedBackgroundColor(highlight.danger, mode),
25+
'&:hover': {
26+
backgroundColor: getSelectedHoverBackgroundColor(highlight.danger, mode),
27+
},
28+
},
29+
},
30+
'& .super-app-theme--Soon': {
31+
backgroundColor: highlight.warning.main,
32+
'&:hover': {
33+
backgroundColor: getHoverBackgroundColor(highlight.warning, mode),
34+
},
35+
'&.Mui-selected': {
36+
backgroundColor: getSelectedBackgroundColor(highlight.warning, mode),
37+
'&:hover': {
38+
backgroundColor: getSelectedHoverBackgroundColor(highlight.warning, mode),
39+
},
40+
},
41+
},
42+
}),
43+
);
44+
export default BulkRenewalDataGrid;

0 commit comments

Comments
 (0)