diff --git a/client/src/index.tsx b/client/src/index.tsx
index 543cb82..b3fe841 100644
--- a/client/src/index.tsx
+++ b/client/src/index.tsx
@@ -15,6 +15,7 @@ import Project from './pages/judge/project';
import './index.css';
import AdminSettings from './pages/admin/settings';
+import Expo from './pages/Expo';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
@@ -51,6 +52,10 @@ const router = createBrowserRouter([
path: '/admin',
element: ,
},
+ {
+ path: '/expo',
+ element: ,
+ },
{
path: '/admin/add-projects',
element: ,
diff --git a/client/src/pages/Expo.tsx b/client/src/pages/Expo.tsx
new file mode 100644
index 0000000..685a596
--- /dev/null
+++ b/client/src/pages/Expo.tsx
@@ -0,0 +1,83 @@
+import { useEffect, useState } from 'react';
+import Container from '../components/Container';
+import { getRequest } from '../api';
+import { errorAlert } from '../util';
+
+const Expo = () => {
+ const [rawProjects, setRawProjects] = useState([]);
+ const [projects, setProjects] = useState([]);
+ const [nameSort, setNameSort] = useState(false);
+
+ // Fetch public project list from DB
+ useEffect(() => {
+ async function fetchProjects() {
+ const res = await getRequest('/project/list/public', '');
+ if (res.status !== 200) {
+ errorAlert(res);
+ return;
+ }
+ setRawProjects(res.data as PublicProject[]);
+ }
+
+ fetchProjects();
+ }, []);
+
+ // On load or when sort changes, sort by name/table #
+ useEffect(() => {
+ if (!rawProjects) return;
+
+ const sortedProjects = [...rawProjects];
+ if (nameSort) {
+ sortedProjects.sort((a, b) => a.name.localeCompare(b.name));
+ } else {
+ sortedProjects.sort((a, b) => a.location - b.location);
+ }
+
+ setProjects(sortedProjects);
+ }, [rawProjects, nameSort]);
+
+ return (
+
+ Project Expo
+
+ {process.env.REACT_APP_JURY_NAME}
+
+
+
+
+ setNameSort(true)}
+ className={
+ 'px-4 py-2 cursor-pointer text-left ' + (nameSort && 'underline')
+ }
+ >
+ Name
+ |
+ setNameSort(false)}
+ className={
+ 'px-4 py-2 cursor-pointer text-left ' + (!nameSort && 'underline')
+ }
+ >
+ Table
+ |
+
+
+
+ {projects.map((project, idx) => (
+
+
+
+ {project.name}
+
+ |
+ {project.location} |
+
+ ))}
+
+
+
+ );
+};
+
+export default Expo;
diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx
index 7d8986f..7e966bf 100644
--- a/client/src/pages/Home.tsx
+++ b/client/src/pages/Home.tsx
@@ -15,6 +15,9 @@ const App = () => {
+
diff --git a/client/src/types.d.ts b/client/src/types.d.ts
index 5680f99..8336761 100644
--- a/client/src/types.d.ts
+++ b/client/src/types.d.ts
@@ -12,6 +12,16 @@ interface Project {
last_activity: number;
}
+interface PublicProject {
+ name: string;
+ location: number;
+ description: string;
+ url: string;
+ try_link: string;
+ video_link: string;
+ challenge_list: string;
+}
+
interface Judge {
id: string;
name: string;
diff --git a/server/router/init.go b/server/router/init.go
index be84c60..2ed8972 100644
--- a/server/router/init.go
+++ b/server/router/init.go
@@ -59,6 +59,7 @@ func NewRouter(db *mongo.Database) *gin.Engine {
adminRouter.POST("/project/devpost", AddDevpostCsv)
adminRouter.POST("/project/new", AddProject)
adminRouter.GET("/project/list", ListProjects)
+ defaultRouter.GET("/project/list/public", ListPublicProjects)
adminRouter.POST("/project/csv", AddProjectsCsv)
judgeRouter.GET("/project/:id", GetProject)
judgeRouter.GET("/judge/vote/info", GetVotingProjectInfo)
diff --git a/server/router/project.go b/server/router/project.go
index f090e85..d7754d7 100644
--- a/server/router/project.go
+++ b/server/router/project.go
@@ -146,6 +146,45 @@ func ListProjects(ctx *gin.Context) {
ctx.JSON(http.StatusOK, projects)
}
+type PublicProject struct {
+ Name string `json:"name"`
+ Location int64 `json:"location"`
+ Description string `json:"description"`
+ Url string `json:"url"`
+ TryLink string `json:"tryLink"`
+ VideoLink string `json:"videoLink"`
+ ChallengeList string `json:"challengeList"`
+}
+
+func ListPublicProjects(ctx *gin.Context) {
+ // Get the database from the context
+ db := ctx.MustGet("db").(*mongo.Database)
+
+ // Get the projects from the database
+ projects, err := database.FindAllProjects(db)
+ if err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{"error": "error getting projects from database: " + err.Error()})
+ return
+ }
+
+ // Convert projects to public projects
+ publicProjects := make([]PublicProject, len(projects))
+ for i, project := range projects {
+ publicProjects[i] = PublicProject{
+ Name: project.Name,
+ Location: project.Location,
+ Description: project.Description,
+ Url: project.Url,
+ TryLink: project.TryLink,
+ VideoLink: project.VideoLink,
+ ChallengeList: strings.Join(project.ChallengeList, ", "),
+ }
+ }
+
+ // Send OK
+ ctx.JSON(http.StatusOK, publicProjects)
+}
+
// POST /project/csv - Endpoint to add projects from a CSV file
func AddProjectsCsv(ctx *gin.Context) {
// Get the database from the context