Skip to content

Commit

Permalink
Merge pull request #25 from codersforcauses/issue-24-Refactor_Leaderb…
Browse files Browse the repository at this point in the history
…oard_Module_and_Implement_Mock_API_for_Client-Side_Testing

refactor: rename manager to leaderboard module, add mock API responses
  • Loading branch information
yunho7687 authored Dec 11, 2024
2 parents 00d9802 + 5dfde15 commit 38727d1
Show file tree
Hide file tree
Showing 16 changed files with 141 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const LeaderboardList = () => {
isError: isLeaderboardError,
error: leaderboardError,
} = useFetchData<Leaderboard[]>({
queryKey: ["manager.leaderboards"],
endpoint: "manager/leaderboard/",
queryKey: ["leaderboards.list"],
endpoint: "leaderboard/list",
});

if (isLeaderboardLoading) return <div>Loading...</div>;
Expand All @@ -54,8 +54,7 @@ const LeaderboardList = () => {
filters.status === "All" || leaderboard.status === filters.status;

const matchesSearch =
!filters.search ||
leaderboard.name.toLowerCase().includes(filters.search.toLowerCase());
!filters.search || leaderboard.name.includes(filters.search);

return matchesStatus && matchesSearch;
});
Expand Down
34 changes: 18 additions & 16 deletions client/src/components/ui/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import {

interface SearchProps {
children: React.ReactNode;
title: string;
title?: string;
}

/**
* The `Search` component is a container for various search-related input elements.
* It provides a styled wrapper for the children components, typically input or select elements.
* The `Search` component serves as a container for various search-related input elements.
* It provides a styled wrapper for child components, such as input or select elements, with an optional title.
*
* @component
* @example
Expand All @@ -25,20 +25,22 @@ interface SearchProps {
* </Search>
*
* @param {React.ReactNode} children - The content to be rendered inside the search container.
* @param {string} title - The title for the search section.
* @param {string} [title] - An optional title for the search section. If provided, it will be displayed above the content.
*
* @returns {JSX.Element} A styled search container with the title and children.
* @returns {JSX.Element} A styled search container with an optional title and child elements.
*/
const Search = ({ children, title }: SearchProps) => {
function Search({ children, title }: SearchProps): JSX.Element {
return (
<div className="mx-auto mb-6 rounded-lg border bg-white p-4 shadow-md">
<h2 className="mb-4 text-lg font-semibold text-gray-700">
Search {title}
</h2>
{title && (
<h2 className="mb-4 text-lg font-semibold text-gray-700">
Search {title}
</h2>
)}
<div className="flex flex-wrap gap-x-4 gap-y-2">{children}</div>
</div>
);
};
}

interface SearchInputProps {
label: string;
Expand All @@ -62,12 +64,12 @@ interface SearchInputProps {
*
* @returns {JSX.Element} A text input element with search functionality.
*/
const SearchInput = ({
function SearchInput({
label,
value,
placeholder,
onSearch,
}: SearchInputProps) => {
}: SearchInputProps): JSX.Element {
const [tempValue, setTempValue] = useState(value);

const handleOnBlur = () => {
Expand All @@ -94,7 +96,7 @@ const SearchInput = ({
/>
</div>
);
};
}

interface SearchSelectProps {
label: string;
Expand All @@ -118,12 +120,12 @@ interface SearchSelectProps {
*
* @returns {JSX.Element} A styled select dropdown element.
*/
const SearchSelect = ({
function SearchSelect({
label,
options,
placeholder = "Select an option",
onChange,
}: SearchSelectProps) => {
}: SearchSelectProps): JSX.Element {
return (
<div className="flex flex-col">
<label className="text-sm font-medium text-gray-600">{label}</label>
Expand All @@ -144,6 +146,6 @@ const SearchSelect = ({
</Select>
</div>
);
};
}

export { Search, SearchInput, SearchSelect };
7 changes: 6 additions & 1 deletion client/src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import axios from "axios";

const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_BACKEND_URL });
const baseURL =
process.env.NODE_ENV === "development"
? "http://localhost:3000/api" // temporarily use port 3000 in dev for mock data
: process.env.NEXT_PUBLIC_BACKEND_URL;

const api = axios.create({ baseURL });

export default api;
33 changes: 33 additions & 0 deletions client/src/pages/api/healthcheck/ping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* API Route Handler for a simple ping response.
*
* This handler responds to API requests by returning a simple message.
* It serves as a demonstration of a minimal API route implementation in Next.js.
*
* @fileoverview API endpoint located at `/api/ping` for returning a static response.
*
* @module /api/healthcheck/ping
* @see {@link https://nextjs.org/docs/pages/building-your-application/routing/api-routes | Next.js API Routes Documentation}
*/

import { NextApiRequest, NextApiResponse } from "next";

/**
* API Route Handler for responding with a simple message.
*
* This endpoint demonstrates a basic API route in Next.js that sends a plain text response.
* It serves as an example of setting up a minimal API route.
*
* @param {NextApiRequest} _req - The incoming HTTP request object. It is unused in this handler.
* @param {NextApiResponse} res - The HTTP response object used to send the text response.
*
* @returns {void} Sends a plain text response indicating the source of the response.
*/
export default function handler(
_req: NextApiRequest,
res: NextApiResponse,
): void {
res
.status(200)
.json("This response is from client.src.pages.api.healthcheck.ping");
}
73 changes: 73 additions & 0 deletions client/src/pages/api/leaderboard/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* API Route Handler for fetching leaderboard data.
*
* This handler responds to API requests by returning a mock list of leaderboards.
* It is used to simulate the backend behavior and serve static leaderboard data.
*
* @fileoverview API endpoint located at `/api/list` for fetching mock leaderboard data.
*
* @module /api/leaderboard/list
* @see {@link https://nextjs.org/docs/pages/building-your-application/routing/api-routes | Next.js API Routes Documentation}
*/

import { NextApiRequest, NextApiResponse } from "next";

import { Leaderboard } from "@/types/leaderboard";

/**
* Mock data representing leaderboard entries.
*
* @constant {Leaderboard[]}
*/
const mockLeaderboards: Leaderboard[] = [
{
name: "John Doe",
school: "Springfield High",
school_email: "[email protected]",
user_name: "johndoe",
password: "securepassword",
individual_score: 95,
team_name: "Team A",
team_score: 320,
status: "Active",
},
{
name: "Jane Smith",
school: "Shelbyville Academy",
school_email: "[email protected]",
user_name: "janesmith",
password: "anothersecurepassword",
individual_score: 88,
team_name: "Team B",
team_score: 290,
status: "Active",
},
{
name: "Alice Johnson",
school: "Ridgeview College",
school_email: "[email protected]",
user_name: "alicejohnson",
password: "password123",
individual_score: 78,
team_name: "Team C",
team_score: 250,
status: "Inactive",
},
];

/**
* Handles API requests to fetch leaderboard data.
*
* @function
* @name handler
* @param {NextApiRequest} _req - The API request object (not used in this handler).
* @param {NextApiResponse<Leaderboard[]>} res - The API response object to return the leaderboard data.
*
* @returns {void} Responds with a status code of 200 and the mock leaderboard data in JSON format.
*/
export default function handler(
_req: NextApiRequest,
res: NextApiResponse<Leaderboard[]>,
): void {
res.status(200).json(mockLeaderboards);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import LeaderboardList from "@/components/ui/manager/leaderboard-list";
import LeaderboardList from "@/components/ui/leaderboard/leaderboard-list";
import type { NextPageWithLayout } from "@/pages/_app";

/**
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

class AppConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "api.manager"
name = "api.leaderboard"
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class LeaderboardSerializer(serializers.ModelSerializer):
The leaderboard:
- Tracks student details like name, school, email, scores, and status.
- Enables managers to add, delete, and update entries.
- Enables admin to add, delete, and update entries.
- Supports data export to a CSV format for record-keeping.
Fields:
Expand Down
6 changes: 3 additions & 3 deletions server/api/manager/urls.py → server/api/leaderboard/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
URL Configuration for the manager module.
URL Configuration for the leaderboard module.
This file defines routes for the leaderboard feature, including:
- A route for the `LeaderboardView`, allowing interaction with the leaderboard data.
Expand All @@ -17,9 +17,9 @@

router = DefaultRouter()

app_name = "manager"
app_name = "leaderboard"

urlpatterns = [
path("", include(router.urls)),
path("leaderboard/", LeaderboardView.as_view(), name="leaderboard"),
path("list/", LeaderboardView.as_view(), name="list"),
]
File renamed without changes.
2 changes: 1 addition & 1 deletion server/api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"rest_framework",
"corsheaders",
"api.healthcheck",
"api.manager",
"api.leaderboard",
]

MIDDLEWARE = [
Expand Down
2 changes: 1 addition & 1 deletion server/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
urlpatterns = [
path("admin/", admin.site.urls),
path("api/healthcheck/", include(("api.healthcheck.urls"))),
path("api/manager/", include("api.manager.urls")),
path("api/leaderboard/", include("api.leaderboard.urls")),
]

0 comments on commit 38727d1

Please sign in to comment.