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} +

+ + + + + + + + + {projects.map((project, idx) => ( + + + + + ))} + +
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 +
+ + {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