Skip to content
Draft
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1fd51c2
Add typography
Feb 6, 2024
a6a58a9
Add TODO
Feb 6, 2024
06238a8
Finish the page
Feb 6, 2024
a0b266e
Upgrade the table header
Feb 6, 2024
f363234
Use radix icon instead of svg
Feb 6, 2024
546e64b
Eslint
Feb 6, 2024
883e837
Add useclient and correct the url
Feb 6, 2024
e25c86f
Items
Feb 12, 2024
2c57b56
More
Feb 12, 2024
fd6c07a
Add row actions
Feb 12, 2024
e0b5348
Merge generic table
Feb 12, 2024
62569f9
Fix
Feb 12, 2024
86af834
Fix
Feb 12, 2024
3fc605f
Bulk action
Feb 12, 2024
87d788b
Merge generic table
Feb 12, 2024
81704cb
Init
Feb 19, 2024
ae2a42a
Merge main
Feb 19, 2024
46f8563
Clean up
Feb 19, 2024
639eace
Merge main
Feb 19, 2024
3a56891
Fix hydration error and modify to the pattern
Feb 19, 2024
cece221
Clean up
Feb 20, 2024
4a6fc72
Clean up
Feb 20, 2024
d0f7c9a
Clean up
Feb 20, 2024
54b24b5
Clean up
Feb 20, 2024
54534a2
Clean up
Feb 20, 2024
1a7e386
Old code
Mar 18, 2024
482b7a1
Merge main
Mar 18, 2024
4d41ddd
Old code
Mar 18, 2024
560256c
Merge main
Mar 18, 2024
d50ec7c
Hook backend to frontend
Mar 18, 2024
6f25339
Hook backend to frontend
Mar 18, 2024
518cc9b
Resolve comments
Mar 18, 2024
658335c
Make page server-side
Mar 18, 2024
db63e6e
Resolve comments
Mar 18, 2024
1b26227
Resolve comments
Mar 18, 2024
8aab055
Resolve comments
Mar 25, 2024
045f607
Merge #16
Mar 25, 2024
715ba1f
Move to correct dir
Mar 25, 2024
56959ae
Merge main
Mar 25, 2024
bb4ae36
Refactor to not use 'use client'
Mar 25, 2024
7faf73b
Clean up
Apr 1, 2024
51e0345
Merge branch 'main' into 21-project-page
Apr 1, 2024
7d2f128
Remove mock data
Apr 1, 2024
cf7e2e5
Merge branch 'main' into 21-project-page
Apr 16, 2024
ac1bc6a
Fix
May 10, 2024
c02ecca
Clean up
May 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const nextConfig = {
return config
},
env: {
BACKEND_BASE_URI: process.env.BACKEND_BASE_URI,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought Abhinav already put this env variable into the repo?

BACKEND_URL: process.env.BACKEND_URL,
},
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
"@svgr/webpack": "^8.1.0",
"@tanstack/react-table": "^8.11.8",
"class-variance-authority": "^0.7.0",
Expand Down
1 change: 1 addition & 0 deletions src/_temp_types/api/teams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type ApiTeamTemplate = {
updatedAt: string
slug: string
requirements: ApiProjectRequirement[]
number_of_teams: number
max_people: number
min_people: number
}
Expand Down
5 changes: 0 additions & 5 deletions src/_temp_types/projectSet.ts

This file was deleted.

20 changes: 16 additions & 4 deletions src/_temp_types/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,37 @@
* A requirement for a student with a specific attribute to work on a project
*/
export type ProjectRequirement = {
id: number
attribute: number
operator: RequirementOperator
// The number of students with this attribute required as described by the RequirementOperator
value: number
}

export enum RequirementOperator {
EXACTLY = "exactly",
LESS_THAN = "less than",
MORE_THAN = "more than",
GT = "Greater Than",
GTE = "Greater Than or Equal",
LT = "Less Than",
LTE = "Less Than or Equal",
IN = "In",
NOT_IN = "Not In",
CONTAINS = "Contains",
EQ = "Equal",
}

/**
* A model for a project with requirements that a Team may work on
*/
export type Project = {
id: number
name?: string
name: string
// Specifies the number of teams that can work on this project
numberOfTeams: number
requirements?: ProjectRequirement[]
}

export type ProjectSet = {
id: number
name: string
numProjects: number
}
2 changes: 2 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import './globals.css'
import Navbar from "@/components/Navbar"
import Footer from "@/components/Footer"
import {Separator} from "@/components/ui/separator"
import {Toaster} from "@/components/ui/toaster"

const manrope = Manrope({ subsets: ['latin'] })

Expand All @@ -25,6 +26,7 @@ export default function RootLayout({
{children}
<Separator/>
<Footer />
<Toaster />
</body>
</html>
)
Expand Down

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for consistency, let's keep naming component files with TitleCase.tsx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this applies to every component file

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client'

import {Button} from "@/components/ui/button"
import {FileIcon, Pencil1Icon} from "@radix-ui/react-icons"
import * as React from "react"
import {useRouter} from "next/navigation"

export type EditModeButtonProps = {
currentSearchTerm: string
currentProjectId: number
currentEditMode: boolean
}

export function EditModeButton({currentSearchTerm, currentProjectId, currentEditMode}: EditModeButtonProps) {
const router = useRouter()

const updateIsEditMode = (isEditMode: boolean) => {
router.push(`?isEdit=${isEditMode}&projectId=${currentProjectId}&search=${currentSearchTerm}`)
}

return (
<>
{currentEditMode ? (
<Button variant="outline" size="sm" onClick={() => updateIsEditMode(false)}>
<FileIcon className="mr-2"/>
Save changes
</Button>
) : (
<Button variant="outline" size="sm" onClick={() => updateIsEditMode(true)}>
<Pencil1Icon className="mr-2"/>
Edit mode
</Button>
)}
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client'

import {Text} from "@/components/ui/text"
import {Input} from "@/components/ui/input"
import * as React from "react"
import {type Project} from "@/_temp_types/projects"
import {useRouter} from "next/navigation"

export type NumProjectsSubtitleProps = {
project: Project
isEditMode: boolean

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the concept of something being in edit mode is begging to be a Context, not. just a prop we pass down

projectSetId: number
}

export function NumProjectsSubtitle({project, isEditMode, projectSetId}: NumProjectsSubtitleProps) {
const router = useRouter()

async function handleUpdateNumTeamsPerProject(numOfTeams: number) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
async function handleUpdateNumTeamsPerProject(numOfTeams: number) {
async function updateNumTeams(numOfTeams: number) {

return fetch(process.env.BACKEND_URL + '/api/v1/teamset-templates/' + projectSetId + '/team-templates/' + project.id + '/', {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit;

why no string literals

method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
number_of_teams: numOfTeams,
}),
})
}

return (
<>
<Text element="p" as="smallText">
This project can be completed by&nbsp;
</Text>
{isEditMode ? (
<Input
className="w-8 text-center h-fit text-foreground text-sm font-medium leading-none border-0 border-b p-0 focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:ring-offset-transparent focus-visible:ring-transparent focus-visible:outline-none"
defaultValue={project.numberOfTeams}
onBlur={(e) => {
if (!isNaN(parseInt(e.target.value))) {
handleUpdateNumTeamsPerProject(parseInt(e.target.value))
.then(() => router.refresh())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why router.refresh here?

.catch((err) => console.error(err))
}
}}
/>
) : (
<Text element="p" as="smallText">
{project.numberOfTeams}
</Text>
)}
<Text element="p" as="smallText" className="">
&nbsp;team{project.numberOfTeams > 1 && 's'}.
</Text>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client'

import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select"
import * as React from "react"
import {type ProjectSet} from "@/_temp_types/projects"
import {useRouter} from "next/navigation"

export type ProjectSetSelectProps = {
currentProjectSetId: number
allProjectSets: ProjectSet[]
}

export function ProjectSetSelect({ currentProjectSetId, allProjectSets }: ProjectSetSelectProps) {
const router = useRouter()
const handleProjectSetChanged = (newProjectSetId: string) => {
router.push(`/project-sets/${newProjectSetId}`)
}

return (
<Select
value={currentProjectSetId.toString()}
onValueChange={(newProjectSetId) => handleProjectSetChanged(newProjectSetId)}
>
<SelectTrigger>
<SelectValue/>
</SelectTrigger>
<SelectContent>
{allProjectSets.map((projectSet) => (
<SelectItem
value={projectSet.id.toString()}
key={projectSet.id}>{projectSet.name}
</SelectItem>
))}
</SelectContent>
</Select>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client'

import * as React from "react"

import {SearchBar} from "@/components/SearchBar"
import {Button} from "@/components/ui/button"
import {type Project} from "@/_temp_types/projects"
import {useRouter} from "next/navigation"

export type SidebarProjectsDisplayProps = {
projects: Project[]
currentSearchTerm: string
currentProjectId: number
currentEditMode: boolean
}

export function SidebarProjectsDisplay({projects, currentSearchTerm, currentProjectId, currentEditMode}: SidebarProjectsDisplayProps) {
const router = useRouter()

const updateSearchTerm = (newSearchTerm: string) => {
router.push(`?isEdit=${currentEditMode}&projectId=${currentProjectId}&search=${newSearchTerm}`)
}

const updateProjectIdx = (newProjectId: number) => {
router.push(`?isEdit=${currentEditMode}&projectId=${newProjectId}&search=${currentSearchTerm}`)
}

const handleProjectChanged = (project: Project) => {
updateProjectIdx(project.id)
// TODO: shoot update api

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what? why?

}

return (
<>
<SearchBar

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UX-wise, it'd be nice if the search would dynamically filter as you type, but I know the whole search param thing makes that weird - even more reason to rethink how we're doing this

className="ml-0"
placeholder="Search Projects"
defaultValue={currentSearchTerm}
onBlur={(e) => updateSearchTerm(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
updateSearchTerm(e.currentTarget.value)
}
}}
/>
<div className="flex flex-col w-full mt-2 gap-1 pr-4">
{projects.map((project) => (
<Button
className="justify-start"
variant={project.id === currentProjectId ? "secondary" : "ghost"}
key={project.id}
onClick={() => handleProjectChanged(project)}
>
{project.name}
</Button>
))}
</div>
</>
)
}
Loading