Skip to content

Commit de7b1c7

Browse files
edit to admin page, view all quests page, ability to mark incomplete, modal edits
1 parent 306b200 commit de7b1c7

16 files changed

+303
-98
lines changed

src/App.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import NotfoundPage from './pages/NotfoundPage';
88
import UnauthorizedPage from './pages/UnauthorizedPage';
99
import AuthenticatedAdminPage from './pages/admin/AdminPage';
1010
import AuthenticatedSuggestionsPage from './pages/admin/SuggestionsPage';
11+
import CompletedQuestsPage from './pages/AllQuestsPage';
1112

1213
import './App.css';
1314
import Background from './components/Background';
@@ -28,11 +29,14 @@ function App() {
2829
<Route path="/login" element={<LoginPage />} />
2930
<Route path="/unauthorized" element={<UnauthorizedPage />} />
3031

32+
{/* authorized routes */}
33+
<Route path="/all-quests" element={<CompletedQuestsPage />} />
34+
3135
{/* admin routes */}
3236
<Route path="/admin" element={<AuthenticatedAdminPage />} />
3337
<Route path="/admin/suggestions" element={<AuthenticatedSuggestionsPage />} />
34-
<Route path="/admin/quests" element={<div>Admin Quests</div>} />
35-
<Route path="/admin/users" element={<div>Admin Users</div>} />
38+
{/* <Route path="/admin/quests" element={<div>Admin Quests</div>} /> */}
39+
{/* <Route path="/admin/users" element={<div>Admin Users</div>} /> */}
3640

3741
{/* user routes */}
3842
<Route path="*" element={<NotfoundPage />} />
@@ -41,7 +45,7 @@ function App() {
4145

4246
{
4347
user &&
44-
<div className="fixed bottom-0 left-0 flex flex-col justify-end p-2 m-4 rounded text-black/50 bg-white/50">
48+
<div className="fixed flex flex-col justify-end p-2 rounded bottom-8 left-8 text-black/50 bg-white/50">
4549
<p className="text-right">
4650
<span className="font-bold">{user.username}</span>
4751
{

src/components/Modal/Modal.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ const QuestModal: React.FC<ModalProps> = ({ onClose, isOpen = true, children })
3535

3636
return (
3737
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
38-
<div ref={modal} className="max-w-full m-0 sm:m-2 md:m-0 md:max-w-[42rem] max-h-[50rem] overflow-y-auto p-2 rounded-lg shadow-lg bg-secondary text-text">
39-
{children}
38+
<div ref={modal} className="p-2 bg-secondary max-h-[90vh] shadow-lg">
39+
<div className="max-w-full m-0 h-full max-h-[85vh] overflow-y-auto sm:m-2 md:m-0 md:max-w-[42rem] text-text">
40+
{children}
41+
</div>
4042
</div>
4143
</div>
4244
);

src/components/Table/Table.tsx

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
type TableProps<T> = {
2+
data: T[];
3+
columns: Array<{ header: string; accessor: keyof T }>;
4+
page: number;
5+
maxLength: number;
6+
setPage: (page: number) => void;
7+
rowClick?: (index: number) => void;
8+
};
9+
10+
const Table = <T extends { id: number | string }>({
11+
data,
12+
columns,
13+
page,
14+
rowClick,
15+
maxLength,
16+
setPage
17+
}: TableProps<T>) => {
18+
return (
19+
<div>
20+
{data.length === 0 ? (
21+
<div className="flex justify-center w-full mt-8">
22+
<h1 className="text-2xl font-bold text-white">No data available</h1>
23+
</div>
24+
) : (
25+
<div className="flex flex-col items-center mt-8 text-xl">
26+
<table className="text-black bg-white">
27+
<thead className="font-bold">
28+
<tr>
29+
{columns.map((column, index) => (
30+
<td key={index} className="px-4 py-2">
31+
{column.header.toUpperCase()}
32+
</td>
33+
))}
34+
</tr>
35+
</thead>
36+
<tbody>
37+
{data.map((row, rowIndex) => (
38+
<tr
39+
key={row.id + rowIndex.toString()}
40+
onClick={() => rowClick && rowClick(rowIndex)}
41+
aria-disabled={!rowClick}
42+
className="bg-slate-100 even:bg-slate-200 hover:bg-white hover:cursor-pointer"
43+
>
44+
{columns.map((column, colIndex) => (
45+
<td key={colIndex} className="px-2 py-1">
46+
{String(row[column.accessor])}
47+
</td>
48+
))}
49+
</tr>
50+
))}
51+
</tbody>
52+
</table>
53+
54+
<div className="flex justify-center w-full gap-2 mt-8">
55+
<button
56+
className="px-4 py-2 font-bold bg-white rounded disabled:bg-white/50"
57+
disabled={page === 1}
58+
onClick={() => setPage(page - 1)}
59+
>
60+
Previous
61+
</button>
62+
<span className="px-4 py-2 font-bold bg-white rounded select-none">
63+
Page: {page}
64+
</span>
65+
<button
66+
className="px-4 py-2 font-bold bg-white rounded disabled:bg-white/50"
67+
disabled={data.length < maxLength}
68+
onClick={() => setPage(page + 1)}
69+
>
70+
Next
71+
</button>
72+
</div>
73+
</div>
74+
)}
75+
</div>
76+
);
77+
};
78+
79+
export default Table;

src/components/Table/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Table from "./Table";
2+
3+
export default Table;

src/components/Toaster/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const Toaster: React.FC = () => {
77
useEffect(() => {
88
const unsubscribe = ToastState.subscribe((toast: Toast) => {
99
setToasts((prevToasts) => {
10-
if (prevToasts.length > 2) {
10+
if (prevToasts.length > 5) {
1111
prevToasts.pop();
1212
}
1313
return [toast, ...prevToasts];
@@ -27,7 +27,7 @@ const Toaster: React.FC = () => {
2727
};
2828

2929
return (
30-
<div className="absolute bottom-0 right-0 m-5">
30+
<div className="absolute bottom-24 right-8">
3131
{
3232
toasts.map((toast, index) => {
3333
let toastStyle = null;
@@ -48,7 +48,7 @@ const Toaster: React.FC = () => {
4848
}
4949

5050
return (
51-
<div className={`${toastStyle} font-bold font-roboto px-4 py-2 ${index !== 0 ? "mt-4" : ""}`} key={toast.id}>
51+
<div className={`${toastStyle} font-bold font-roboto px-4 py-2 ${index !== 0 ? "mt-3" : ""}`} key={toast.id}>
5252
<span>{toast.message}</span>
5353
</div>)
5454
})

src/components/Toaster/state.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Observer {
4343
return this.create({ message });
4444
};
4545

46-
error = (message: string, error: unknown): void => {
46+
error = (message: string, error?: unknown): void => {
4747
if (error) console.error(error);
4848
return this.create({ type: 'error', message });
4949
};
@@ -55,6 +55,10 @@ class Observer {
5555
warning = (message: string): void => {
5656
return this.create({ type: 'warning', message });
5757
};
58+
59+
info = (message: string): void => {
60+
return this.create({ type: 'info', message });
61+
};
5862
}
5963

6064
export const ToastState = new Observer();
@@ -75,5 +79,6 @@ const basicToast = toastFunction;
7579
export const toast = Object.assign(basicToast, {
7680
success: ToastState.success,
7781
warning: ToastState.warning,
82+
info: ToastState.info,
7883
error: ToastState.error,
7984
});

src/modals/ViewQuest/ViewQuest.tsx

+24-20
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,29 @@
11
import React from 'react';
22
import Button from '../../components/Button';
3+
import { Quest } from '@/models/QuestModels/questResponse';
34

45
interface ModalProps {
56
onClose: () => void;
6-
onSkip: () => void;
7-
onComplete: () => void;
8-
imageUrl: string;
9-
title: string;
10-
category: string;
11-
description: string;
12-
objectives: string[];
13-
infoUrl?: string;
7+
onSkip?: () => void;
8+
onComplete?: () => void;
9+
onIncomplete?: () => void;
10+
quest: Quest
1411
}
1512

1613
const ViewQuest: React.FC<ModalProps> = (props) => {
1714
const {
1815
onClose,
1916
onSkip,
2017
onComplete,
21-
imageUrl,
22-
title,
23-
category,
24-
description,
25-
objectives,
26-
infoUrl
18+
onIncomplete,
19+
quest
2720
} = props;
21+
const { title, description, objectives, image_url, category, info_url } = quest;
2822

2923
return (
3024
<>
3125
<img
32-
src={imageUrl}
26+
src={image_url}
3327
alt="logo"
3428
className='w-full max-h-[16rem] object-cover'
3529
/>
@@ -43,7 +37,7 @@ const ViewQuest: React.FC<ModalProps> = (props) => {
4337
</div>
4438

4539
<div className="w-full p-4 mt-2 overflow-x-hidden overflow-y-auto text-lg font-bold max-h-48 text-text-secondary-highlight bg-secondary-highlight scrollbar-modern">
46-
<p style={{ lineHeight: '1.1' }} className="text-pretty">{description} {description} {description} {description} {description} {description} {description} {description}</p>
40+
<p style={{ lineHeight: '1.1' }} className="text-pretty">{description}</p>
4741
{
4842
(objectives && objectives.length > 0) &&
4943
<div className="flex flex-col justify-start w-full mt-4">
@@ -68,13 +62,23 @@ const ViewQuest: React.FC<ModalProps> = (props) => {
6862
<div className="flex justify-start w-full sm:w-fit">
6963
<Button text="close" onClick={onClose} />
7064
{
71-
infoUrl &&
72-
<Button text="learn more" type="info" onClick={() => window.open(infoUrl, '_blank', 'noopener,noreferrer')} />
65+
info_url &&
66+
<Button text="learn more" type="info" onClick={() => window.open(info_url, '_blank', 'noopener,noreferrer')} />
7367
}
7468
</div>
7569
<div className="flex flex-col w-full gap-2 mt-2 sm:w-fit sm:mt-0 sm:flex-row sm:justify-end">
76-
<Button text="Skip" type="cancel" onClick={onSkip} />
77-
<Button text="complete" type="confirm" onClick={onComplete} />
70+
{
71+
onSkip &&
72+
<Button text="Skip" type="cancel" onClick={onSkip} />
73+
}
74+
{
75+
onComplete &&
76+
<Button text="complete" type="confirm" onClick={onComplete} />
77+
}
78+
{
79+
onIncomplete &&
80+
<Button text="mark incomplete" type="cancel" onClick={onIncomplete} />
81+
}
7882
</div>
7983
</div>
8084
</div>

src/models/QuestModels/questResponse.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,9 @@ export interface Quest {
55
objectives: string[],
66
image_url: string,
77
category: string,
8-
info_url?: string
8+
info_url?: string,
9+
}
10+
11+
export interface AllQuestsResponse extends Quest {
12+
completed: boolean;
913
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface Suggestion {
2+
id: string;
3+
username: string;
4+
title: string;
5+
description: string;
6+
}

0 commit comments

Comments
 (0)