diff --git a/Components/AlertProvider.js b/Components/AlertProvider.js
index 819933e..654aceb 100644
--- a/Components/AlertProvider.js
+++ b/Components/AlertProvider.js
@@ -28,8 +28,6 @@ export function AlertProvider(props) {
const handleContinueClick = () => {
setOpen(false)
- console.debug('[handleContinueClick] message: ' + message)
- console.info('[handleContinueClick] onContinue: ' + onContinue)
onContinue()
}
diff --git a/Components/CompetitionNavbar.tsx b/Components/CompetitionNavbar.tsx
new file mode 100644
index 0000000..6c8c8bc
--- /dev/null
+++ b/Components/CompetitionNavbar.tsx
@@ -0,0 +1,103 @@
+import { Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap'
+import classnames from 'classnames'
+import Link from 'next/link'
+import { useState } from 'react'
+import AuthenticatedCompetition from './authenticated/Competition'
+import { useConvexAuth } from 'convex/react'
+
+export default function CompetitionNavbar(props: any) {
+ const [activeTab, setActiveTab] = useState(props.tabId)
+
+ const { isAuthenticated } = useConvexAuth()
+
+ const toggle = (tab: number) => {
+ if (activeTab !== tab) setActiveTab(tab)
+ }
+ return (
+
+ {isAuthenticated ?
: null}
+
+
+ {props.children}
+
+
+ )
+}
diff --git a/Components/Teams.tsx b/Components/Teams.tsx
index e62c0a1..1bf466a 100644
--- a/Components/Teams.tsx
+++ b/Components/Teams.tsx
@@ -1,8 +1,20 @@
-import { Button, Col, List, ListInlineItem, Row } from 'reactstrap'
+import {
+ Button,
+ Col,
+ Input,
+ Label,
+ List,
+ ListInlineItem,
+ Modal,
+ ModalBody,
+ ModalFooter,
+ ModalHeader,
+ Row,
+} from 'reactstrap'
import { useMutation, useQuery } from 'convex/react'
import { api } from '../convex/_generated/api'
import { useState } from 'react'
-import InvitesAndJoinRequests from './authenticated/InvitesAndJoinRequests'
+import InvitesAndJoinRequests from './authenticated/UserInvites'
import { Id } from '../convex/_generated/dataModel'
export default function Teams(props: any) {
@@ -14,15 +26,32 @@ export default function Teams(props: any) {
const [inviteButtonMessage, setInviteBtnMsg] = useState('Invite to Team')
const handleJoinClick = (id: Id<'teams'>) => {
+ setRequestJoinModal(false)
requestJoin({ id })
setJoinButtonMessage('Join requested!')
}
+ const handleJoinModalOpen = () => {
+ setRequestJoinModal(true)
+ }
+
+ const [modal, setModal] = useState(false)
+ const [requestJoinModal, setRequestJoinModal] = useState(false)
+
const handleInviteClick = (joinerId: Id<'users'>) => {
+ setModal(false)
invite({ joinerId, competitionId: props.competitionId })
setInviteBtnMsg('Invite sent!')
}
+ const handleInviteModalOpen = () => {
+ setModal(true)
+ }
+
+ const toggle = () => setModal(!modal)
+
+ const toggleJoinRequestModal = () => setRequestJoinModal(!requestJoinModal)
+
return (
@@ -48,12 +77,68 @@ export default function Teams(props: any) {
participant &&
participant.userMembership.team == item._id
}
- onClick={() => {
- handleInviteClick(member._id)
- }}
+ onClick={handleInviteModalOpen}
>
{inviteButtonMessage}
+
+
+ Invite {member.name}
+
+
+
+
+
+
+ {' '}
+
+
+
+
+
+ Request to Join
+
+
+
+
+
+
+ {' '}
+
+
+
))}
@@ -63,9 +148,7 @@ export default function Teams(props: any) {
}
disabled={!participant}
color="primary"
- onClick={() => {
- handleJoinClick(item._id)
- }}
+ onClick={handleJoinModalOpen}
>
{joinButtonMessage}
diff --git a/Components/User.tsx b/Components/User.tsx
index 7e28a1e..c64e7cc 100644
--- a/Components/User.tsx
+++ b/Components/User.tsx
@@ -4,7 +4,7 @@ import { Doc } from '../convex/_generated/dataModel'
export function UserBubble(sender: Doc<'users'>) {
return (
{props.name}
{participation ? (
<>
-
- Enter Submission
-
@@ -53,7 +46,6 @@ export default function AuthenticatedCompetition(props) {
>
Delete Competition
- View Team
)
}
diff --git a/Components/authenticated/InvitesAndJoinRequests.tsx b/Components/authenticated/InvitesAndJoinRequests.tsx
deleted file mode 100644
index 340a429..0000000
--- a/Components/authenticated/InvitesAndJoinRequests.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function InvitesAndJoinRequests() {
- return (
- <>
- Invites and Join Requests
- >
- )
-}
diff --git a/Components/authenticated/UserInvites.tsx b/Components/authenticated/UserInvites.tsx
new file mode 100644
index 0000000..a32323e
--- /dev/null
+++ b/Components/authenticated/UserInvites.tsx
@@ -0,0 +1,7 @@
+export default function UserInvites() {
+ return (
+ <>
+ Invites
+ >
+ )
+}
diff --git a/convex/competition.ts b/convex/competition.ts
index f2f7a2c..09be5cc 100644
--- a/convex/competition.ts
+++ b/convex/competition.ts
@@ -56,6 +56,7 @@ export const deleteCompetition = mutation({
args: { id: v.id('competitions') },
handler: async ({ db }, { id }) => {
await db.delete(id)
+ // TODO: New table for submissions attached to a team so these can be separated
},
})
diff --git a/convex/lib/team.ts b/convex/lib/team.ts
index 0e2b0e4..14cde09 100644
--- a/convex/lib/team.ts
+++ b/convex/lib/team.ts
@@ -1,6 +1,6 @@
import { GenericDatabaseReader, GenericDatabaseWriter } from 'convex/server'
import { DataModel, Doc, Id } from '../_generated/dataModel'
-import { RequestValidity } from '../../shared/info'
+import { RequestValidity } from '../../lib/shared'
import { ConvexError } from 'convex/values'
import { convertToUserDocumentArray, fulfillAndFlatten } from './helpers'
diff --git a/convex/participant.ts b/convex/participant.ts
index ad1df00..45d0760 100644
--- a/convex/participant.ts
+++ b/convex/participant.ts
@@ -6,7 +6,7 @@ import {
addUserToTeam,
validateTeamJoinRequest,
} from './lib/team'
-import { RequestValidity } from '../shared/info'
+import { RequestValidity } from '../lib/shared'
/**
* @inheritDoc team.findTeamOfUser
@@ -65,8 +65,11 @@ export const joinCompetition = mutation({
* @param id The id of the team that the user would like to join
*/
export const requestJoin = mutation({
- args: { id: v.id('teams') },
- handler: async ({ db, auth }, { id }) => {
+ args: { id: v.id('teams'), pitch: v.optional(v.string()) },
+ handler: async (
+ { db, auth },
+ { id, pitch = 'Hey there! I would like to join your team.' }
+ ) => {
const user = await verifyUser(db, auth)
const inviterTeam = await db.get(id)
if (!inviterTeam) {
@@ -84,6 +87,7 @@ export const requestJoin = mutation({
user: user._id,
userConsent: true,
teamConsent: false,
+ pitch,
})
return await db.replace(inviterTeam._id, inviterTeam)
case RequestValidity.INVITED:
@@ -105,8 +109,19 @@ export const requestJoin = mutation({
* @param competitionId The id of the competition that the team is in
*/
export const inviteToTeam = mutation({
- args: { joinerId: v.id('users'), competitionId: v.id('competitions') },
- handler: async ({ db, auth }, { joinerId, competitionId }) => {
+ args: {
+ joinerId: v.id('users'),
+ competitionId: v.id('competitions'),
+ pitch: v.optional(v.string()),
+ },
+ handler: async (
+ { db, auth },
+ {
+ joinerId,
+ competitionId,
+ pitch = 'Hey there! We would love for you to join our team. ',
+ }
+ ) => {
const inviter = await verifyUser(db, auth)
const inviterTeam = await findTeamOfUser(db, inviter, competitionId)
if (!inviterTeam) {
@@ -129,6 +144,7 @@ export const inviteToTeam = mutation({
user: joiner._id,
userConsent: false,
teamConsent: true,
+ pitch,
})
return await db.patch(inviterTeam._id, {
joinRequests: inviterTeam.joinRequests,
diff --git a/convex/schema.ts b/convex/schema.ts
index 7751a1b..186307b 100644
--- a/convex/schema.ts
+++ b/convex/schema.ts
@@ -24,6 +24,7 @@ export const joinRequest = {
user: v.id('users'),
teamConsent: v.boolean(),
userConsent: v.boolean(),
+ pitch: v.string(),
}
const team = {
diff --git a/convex/team.ts b/convex/team.ts
index b7094ae..51c8228 100644
--- a/convex/team.ts
+++ b/convex/team.ts
@@ -10,7 +10,9 @@ import { includeUserDocument } from './lib/helpers'
export const list = query({
args: { competitionId: v.id('competitions') },
handler: async ({ db, auth }, { competitionId }) => {
- return await listCompetitionTeams(db, competitionId)
+ const competitionTeams = await listCompetitionTeams(db, competitionId)
+ competitionTeams.sort((a, b) => a.members.length - b.members.length)
+ return competitionTeams
},
})
diff --git a/lib/client.ts b/lib/client.ts
new file mode 100644
index 0000000..51176c0
--- /dev/null
+++ b/lib/client.ts
@@ -0,0 +1,47 @@
+import {api} from '../convex/_generated/api'
+import {ConvexHttpClient} from 'convex/browser'
+import {Id} from '../convex/_generated/dataModel'
+import {GetStaticPaths, GetStaticProps, GetStaticPropsContext} from 'next'
+
+const convex = new ConvexHttpClient(
+ process.env.NEXT_PUBLIC_CONVEX_DEPLOYMENT_URL || ''
+)
+
+export const Competition = {
+ routes: async function () {
+ const competitions = await convex.query(api.competition.listCompetitions)
+
+ return {
+ paths: competitions.map((item) => {
+ return {
+ params: {id: item._id},
+ }
+ }),
+ fallback: false,
+ }
+ } satisfies GetStaticPaths,
+ page: async function (context: GetStaticPropsContext) {
+ if (!context.params) {
+ return {
+ notFound: true,
+ }
+ }
+
+ const competition = await convex.query(api.competition.getCompetition, {
+ id: context.params.id as Id<'competitions'>,
+ })
+
+ if (!competition) {
+ return {
+ notFound: true,
+ }
+ }
+
+ return {
+ props: {
+ id: competition._id,
+ ...competition,
+ },
+ }
+ } satisfies GetStaticProps,
+}
diff --git a/shared/info.ts b/lib/shared.ts
similarity index 100%
rename from shared/info.ts
rename to lib/shared.ts
diff --git a/pages/competitions/[id].tsx b/pages/competitions/[id].tsx
index 4e8cf2f..99d4786 100644
--- a/pages/competitions/[id].tsx
+++ b/pages/competitions/[id].tsx
@@ -1,21 +1,7 @@
-import classnames from 'classnames'
-import {ConvexHttpClient} from 'convex/browser'
import Head from 'next/head'
-import {useState} from 'react'
-import {Nav, NavItem, NavLink, TabContent, TabPane} from 'reactstrap'
-import {useConvexAuth} from 'convex/react'
-import {api} from '../../convex/_generated/api'
-import Rules from '../../Components/rules'
-import SubmissionsList from '../../Components/submissionsList'
import Overview from '../../Components/overview'
-import AuthenticatedCompetition from '../../Components/authenticated/Competition'
-import {GetStaticPaths, GetStaticProps} from "next";
-import {Id} from "../../convex/_generated/dataModel";
-import Teams from "../../Components/Teams";
-
-const convex = new ConvexHttpClient(
- process.env.NEXT_PUBLIC_CONVEX_DEPLOYMENT_URL || ''
-)
+import CompetitionNavbar from '../../Components/CompetitionNavbar'
+import { Competition } from '../../lib/client'
/**
* @param props.id {string} The id of the competition
@@ -23,128 +9,18 @@ const convex = new ConvexHttpClient(
* @param props.thumbnail {string} The thumbnail of the competition
*/
export default function App(props: any) {
- const [activeTab, setActiveTab] = useState(1)
- const {isAuthenticated} = useConvexAuth()
-
- const toggle = (tab: number) => {
- if (activeTab !== tab) setActiveTab(tab)
- }
-
- return (
-
-
-
{props.name} | Musathon
-
-
- {isAuthenticated ?
: null}
-
-
-
-
-
-
-
-
-
- {JSON.stringify(props.prizeList)}
-
-
-
-
-
-
-
-
-
- )
+ return (
+
+
+
{props.name} | Musathon
+
+
+
+
+
+
+ )
}
-export const getStaticProps = (async (context) => {
- if (!context.params) {
- return {
- notFound: true
- }
- }
-
- const competition = await convex.query(
- api.competition.getCompetition,
- {id: context.params.id as Id<'competitions'>}
- )
-
- if (!competition) {
- return {
- notFound: true
- }
- }
-
- return {
- props: {
- id: competition._id, ...competition
- }
- }
-}) satisfies GetStaticProps
-
-export const getStaticPaths = (async () => {
- const competitions = await convex.query(api.competition.listCompetitions)
-
- return {
- paths: competitions.map((item) => {
- return {
- params: {id: item._id},
- }
- }),
- fallback: false,
- }
-}) satisfies GetStaticPaths
+export const getStaticProps = Competition.page
+export const getStaticPaths = Competition.routes
diff --git a/pages/competitions/[id]/gallery.tsx b/pages/competitions/[id]/gallery.tsx
new file mode 100644
index 0000000..334bd31
--- /dev/null
+++ b/pages/competitions/[id]/gallery.tsx
@@ -0,0 +1,18 @@
+import CompetitionNavbar from '../../../Components/CompetitionNavbar'
+import SubmissionsList from '../../../Components/submissionsList'
+import { Competition } from '../../../lib/client'
+import Head from 'next/head'
+
+export default function CompetitionGallery(props: any) {
+ return (
+
+
+ Submission Gallery
+
+
+
+ )
+}
+
+export const getStaticPaths = Competition.routes
+export const getStaticProps = Competition.page
diff --git a/pages/competitions/[id]/prizes.tsx b/pages/competitions/[id]/prizes.tsx
new file mode 100644
index 0000000..5b44873
--- /dev/null
+++ b/pages/competitions/[id]/prizes.tsx
@@ -0,0 +1,17 @@
+import CompetitionNavbar from '../../../Components/CompetitionNavbar'
+import { Competition } from '../../../lib/client'
+import Head from 'next/head'
+
+export default function CompetitionPrizes(props: any) {
+ return (
+
+
+ Prizes
+
+ {JSON.stringify(props.prizeList)}
+
+ )
+}
+
+export const getStaticPaths = Competition.routes
+export const getStaticProps = Competition.page
diff --git a/pages/competitions/[id]/rules.tsx b/pages/competitions/[id]/rules.tsx
new file mode 100644
index 0000000..ff36bee
--- /dev/null
+++ b/pages/competitions/[id]/rules.tsx
@@ -0,0 +1,18 @@
+import Rules from '../../../Components/rules'
+import CompetitionNavbar from '../../../Components/CompetitionNavbar'
+import { Competition } from '../../../lib/client'
+import Head from 'next/head'
+
+export default function CompetitionRules(props: any) {
+ return (
+
+
+ Competition Rules
+
+
+
+ )
+}
+
+export const getStaticPaths = Competition.routes
+export const getStaticProps = Competition.page
diff --git a/pages/competitions/[id]/team.tsx b/pages/competitions/[id]/team.tsx
index af52d13..79fd7e4 100644
--- a/pages/competitions/[id]/team.tsx
+++ b/pages/competitions/[id]/team.tsx
@@ -1,119 +1,84 @@
-import {Button, Col, Container, List, Row} from 'reactstrap'
-import {useConvexAuth, useMutation, useQuery} from 'convex/react'
-import {api} from '../../../convex/_generated/api'
-import {Id} from "../../../convex/_generated/dataModel";
-import {GetStaticPaths, GetStaticProps} from "next";
-import {ConvexHttpClient} from "convex/browser";
-import React from "react";
-import TeamMembers from "../../../Components/team/TeamMembers";
-import Chat from "../../../Components/Chat";
-import {UserBubble} from "../../../Components/User";
-
-const convex = new ConvexHttpClient(
- process.env.NEXT_PUBLIC_CONVEX_DEPLOYMENT_URL || ''
-)
+import { Button, Col, List, Row } from 'reactstrap'
+import { useMutation, useQuery } from 'convex/react'
+import { api } from '../../../convex/_generated/api'
+import { Id } from '../../../convex/_generated/dataModel'
+import React from 'react'
+import TeamMembers from '../../../Components/team/TeamMembers'
+import Chat from '../../../Components/Chat'
+import { UserBubble } from '../../../Components/User'
+import Head from 'next/head'
+import CompetitionNavbar from '../../../Components/CompetitionNavbar'
+import { Competition } from '../../../lib/client'
+import Link from 'next/link'
export default function Team(props: any) {
- const teamInfo = useQuery(api.team.get, {competitionId: props.id})
- const acceptJoin = useMutation(api.participant.inviteToTeam)
- const {isAuthenticated} = useConvexAuth()
-
- if (!teamInfo) {
- return (
-
- You must join the competition to be on a team
-
- )
- }
-
- if (!isAuthenticated) {
- return (
-
- You must be signed in to view your team
-
- )
- }
-
- const handleAcceptJoin = (joinerId: Id<'users'>) => {
- acceptJoin({
- joinerId,
- competitionId: props.id
- })
- }
+ const teamInfo = useQuery(api.team.get, { competitionId: props.id })
+ const acceptJoin = useMutation(api.participant.inviteToTeam)
- return (
-
- Team Dashboard
-
-
-
-
-
- Join Requests
-
- {teamInfo.joinRequests.map(request => (
-
-
-
-
- ))}
-
- Pending Invitations
-
- {teamInfo.invitations.map(invitation => (
-
-
- {invitation.user.name}
-
- ))}
-
-
-
-
-
-
-
-
+ const handleAcceptJoin = (joinerId: Id<'users'>) => {
+ acceptJoin({
+ joinerId,
+ competitionId: props.id,
+ })
+ }
-
-
- )
+ return (
+
+
+ My Team
+
+ Team Dashboard
+
+
+ {teamInfo ? : null}
+
+
+ Join Requests
+
+ {teamInfo?.joinRequests.map((request) => (
+
+
+ {request.pitch}
+
+
+ ))}
+
+ Pending Invitations
+
+ {teamInfo?.invitations.map((invitation) => (
+
+
+ {invitation.user.name}
+
+ {invitation.pitch}
+
+ ))}
+
+
+
+
+
+ Submission
+
+ Enter Submission
+
+
+
+ {teamInfo ? (
+
+ ) : null}
+
+
+
+ )
}
-export const getStaticProps = (async (context) => {
- if (!context.params) {
- return {
- notFound: true
- }
- }
-
- const competition = await convex.query(
- api.competition.getCompetition,
- {id: context.params.id as Id<'competitions'>}
- )
-
- if (!competition) {
- return {
- notFound: true
- }
- }
-
- return {
- props: {
- id: competition._id, ...competition
- }
- }
-}) satisfies GetStaticProps
-
-export const getStaticPaths = (async () => {
- const competitions = await convex.query(api.competition.listCompetitions)
- // console.log(competitions)
- return {
- paths: competitions.map((item) => {
- return {
- params: {id: item._id},
- }
- }),
- fallback: false,
- }
-}) satisfies GetStaticPaths
+export const getStaticProps = Competition.page
+export const getStaticPaths = Competition.routes
diff --git a/pages/competitions/[id]/teams.tsx b/pages/competitions/[id]/teams.tsx
new file mode 100644
index 0000000..bebe32c
--- /dev/null
+++ b/pages/competitions/[id]/teams.tsx
@@ -0,0 +1,18 @@
+import CompetitionNavbar from '../../../Components/CompetitionNavbar'
+import Teams from '../../../Components/Teams'
+import { Competition } from '../../../lib/client'
+import Head from 'next/head'
+
+export default function CompetitionTeams(props: any) {
+ return (
+
+
+ Teams
+
+
+
+ )
+}
+
+export const getStaticPaths = Competition.routes
+export const getStaticProps = Competition.page
diff --git a/pages/new-competition.tsx b/pages/new-competition.tsx
index 00eba5b..2a0183d 100644
--- a/pages/new-competition.tsx
+++ b/pages/new-competition.tsx
@@ -20,9 +20,9 @@ import Head from 'next/head'
import { useRouter } from 'next/router'
import Link from 'next/link'
import { api } from '../convex/_generated/api'
-import { Access } from '../shared/info'
+import { Access } from '../lib/shared'
-export default function App(props: object) {
+export default function App() {
const router = useRouter()
const [user, setUser] = useState('user name')
diff --git a/pages/profile.tsx b/pages/profile.tsx
index 2a3941a..3a16f13 100644
--- a/pages/profile.tsx
+++ b/pages/profile.tsx
@@ -14,7 +14,7 @@ import {
Label,
Row,
} from 'reactstrap'
-import { useQuery, useMutation, useConvexAuth } from 'convex/react'
+import { useQuery, useConvexAuth } from 'convex/react'
import { api } from '../convex/_generated/api'
function SubmissionViewer() {
@@ -57,7 +57,6 @@ export default function Profile() {
const { isAuthenticated } = useConvexAuth()
const user = useQuery(api.user.getUser)
const [bio, setBio] = useState('Loading Bio...')
- const storeUser = useMutation(api.user.storeUser)
const [editMode, setEditMode] = useState(false)
useEffect(() => {
@@ -66,10 +65,6 @@ export default function Profile() {
}
}, [user])
- async function saveBio() {
- await storeUser({ bio })
- }
-
// TODO: Base on NoteFlight profile page: https://www.noteflight.com/profile/e747973656d52d4b41ebe1ff8ebf1cbc8792d7f7
if (!isAuthenticated) {
@@ -81,7 +76,7 @@ export default function Profile() {