Skip to content

Commit 2b9defc

Browse files
authored
feat: submission of projects (#71)
* feat: improved submission projects * feat: improved information modals * feat: added select theme on project submission * feat: update database to the new parameter theme
1 parent f313f65 commit 2b9defc

File tree

10 files changed

+129
-11
lines changed

10 files changed

+129
-11
lines changed

src/components/forms/selector.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export default function Selector({ param, title, options }) {
1616
--Please choose an option--
1717
</option>
1818
{options.map((option) => (
19-
<option key={option}>{option}</option>
19+
<option key={option.key} value={option.key}>{option.value}</option>
2020
))}
2121
</select>
2222
</div>

src/components/header.astro

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ const headerClass = sticky
4444
Register
4545
</a>
4646
</li>
47+
<li>
48+
<a
49+
href="/projects"
50+
class="rounded-full px-2.5 py-1 text-xs font-semibold text-white shadow-sm ring-1 ring-inset ring-white hover:ring-secondary"
51+
>
52+
Upload Project
53+
</a>
54+
</li>
4755
</ul>
4856
</nav>
4957
<button

src/components/projectSubmission.jsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ import { useState } from "react";
22
import FormsTemplate from "./forms/formsTemplate.jsx";
33
import TextInput from "~/components/forms/textInput.jsx";
44
import TextBox from "~/components/forms/textBox.jsx";
5+
import Selector from "~/components/forms/selector.jsx";
56
import ConfirmationModal from "~/components/confirmationModal.jsx";
7+
import InformationModal from "~/components/informationModal.jsx";
68
import Button from "./button.jsx";
79
import ErrorBox from "~/components/forms/errorBox.jsx";
10+
import themes from "~/data/themes.json";
811

912
export default function ProjectDelivery() {
1013
const [responseErrors, setResponseErrors] = useState([]);
1114
const [loadingState, setLoadingState] = useState(false);
1215
const [showModal, setShowModal] = useState(false);
16+
const [showInfoModal, setShowInfoModal] = useState(false);
1317

1418
async function submit(e) {
1519
console.log("submit");
@@ -28,7 +32,8 @@ export default function ProjectDelivery() {
2832
setResponseErrors(data.message.errors);
2933
setLoadingState(false);
3034
} else {
31-
window.location.href = "/";
35+
setShowInfoModal(true);
36+
setLoadingState(false);
3237
}
3338
}
3439

@@ -40,6 +45,15 @@ export default function ProjectDelivery() {
4045
setShowModal(false);
4146
}
4247

48+
function goBack() {
49+
closeInfoModal();
50+
window.location.href = "/";
51+
}
52+
53+
function closeInfoModal() {
54+
setShowInfoModal(false);
55+
}
56+
4357
return (
4458
<FormsTemplate
4559
title="Project Submission"
@@ -65,6 +79,16 @@ export default function ProjectDelivery() {
6579
title="Project link"
6680
placeholder="Insert your project repo link"
6781
/>
82+
83+
<Selector
84+
param="theme"
85+
title="Project Theme"
86+
options={themes.map((theme) => ({
87+
key: theme.company,
88+
value: `${theme.company}: ${theme.theme}`
89+
}))}
90+
/>
91+
6892
<TextBox
6993
param="description"
7094
title="Project description"
@@ -83,10 +107,24 @@ export default function ProjectDelivery() {
83107
/>
84108
{showModal && (
85109
<ConfirmationModal
86-
placeHolder="Are you sure you want to submit?"
110+
title="Are you sure you want to submit?"
111+
content="This is a unique submission and cannot be undone."
87112
closeModal={closeModal}
88113
/>
89114
)}
115+
{showInfoModal && (
116+
<InformationModal
117+
title="Project submitted!"
118+
content={
119+
<>
120+
Your project has been successfully submitted!
121+
Thank you for participating in <span class="text-primary">Hackathon Bugsbyte</span>. Good luck!
122+
</>
123+
}
124+
closeModal={goBack}
125+
/>
126+
)}
127+
90128
</form>
91129
</FormsTemplate>
92130
);

src/components/registerForm.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export default function Form() {
127127
<Selector
128128
param="university"
129129
title="University"
130-
options={universities}
130+
options={universities.map((uni) => ({ key: uni, value: uni }))}
131131
/>
132132

133133
<TextInput

src/data/themes.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"company": "McSonae",
4+
"theme": "Price optimization tool"
5+
},
6+
{
7+
"company": "Uphold",
8+
"theme": "Crypto basket price predictor"
9+
},
10+
{
11+
"company": "SingleStore",
12+
"theme": "Real-Time GenAI: Build Smarter, Faster AI Apps"
13+
}
14+
]

src/pages/api/projects/create.ts

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ const supabase = createClient(
99
import.meta.env.SUPABASE_ANON_KEY,
1010
);
1111

12+
const apiGithub = "https://api.github.com/repos/";
13+
// TODO: Change this date to the contest start date
14+
const beginContestDate = new Date("2022-03-28T18:00:00Z");
15+
1216
export const POST: APIRoute = async ({ request }) => {
1317
const formData = await request.formData();
1418

@@ -31,6 +35,7 @@ export const POST: APIRoute = async ({ request }) => {
3135
name: formData.get("name")?.toString() ?? "",
3236
description: formData.get("description")?.toString() ?? "",
3337
link: formData.get("link")?.toString() ?? "",
38+
theme: formData.get("theme")?.toString() ?? "",
3439
};
3540

3641
const insertion_msg = await supabase.from("projects").insert([data]).select();
@@ -57,15 +62,27 @@ export const POST: APIRoute = async ({ request }) => {
5762
};
5863

5964
const validateForms = async (formData: FormData, errors: String[]) => {
65+
const team_code = formData.get("team_code")?.toString().replace("#", "");
66+
const link = formData.get("link")?.toString();
67+
68+
if (!team_code || !link) {
69+
errors.push("All fields are required.");
70+
return false
71+
}
72+
73+
const valid = (await validateTeamCode(team_code, errors) && await validateLink(link, errors))
74+
75+
return valid;
76+
};
77+
78+
79+
const validateTeamCode = async (team_code: string, errors: String[]) => {
6080
let valid = true;
6181

62-
// check if there's already a project with the same team code
63-
// if there is one, return an error
64-
const team_code = formData.get("team_code")?.toString().replace("#", "");
6582
let { data: projects, error } = await supabase
66-
.from("projects")
67-
.select("*")
68-
.eq("team_code", team_code);
83+
.from("projects")
84+
.select("*")
85+
.eq("team_code", team_code);
6986

7087
if (error) {
7188
errors.push(
@@ -78,4 +95,31 @@ const validateForms = async (formData: FormData, errors: String[]) => {
7895
}
7996

8097
return valid;
81-
};
98+
}
99+
100+
const validateLink = async (link: string, errors: String[]) => {
101+
const githubLinkRegex = /^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/;
102+
103+
const match = link.match(githubLinkRegex);
104+
if (!match) {
105+
errors.push("The link must be a valid GitHub repository URL.");
106+
return false;
107+
}
108+
109+
const [, username, repositoryName] = match;
110+
111+
const response = await fetch(apiGithub + `${username}/${repositoryName}`);
112+
if (!response.ok) {
113+
errors.push("The repository doesn't exist or is private.");
114+
return false;
115+
}
116+
117+
const data = await response.json();
118+
const creationDate = new Date(data.created_at);
119+
if (creationDate < beginContestDate) {
120+
errors.push("The repository must be created after the contest start date.");
121+
return false;
122+
}
123+
124+
return true;
125+
}
File renamed without changes.

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export interface SubmitProjectItem {
5353
name: string;
5454
description: string;
5555
link: string;
56+
theme: string;
5657
}
5758

5859
export interface SideBarOption {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Step 1: Drop the current primary key constraint
2+
ALTER TABLE public.projects
3+
DROP CONSTRAINT projects_pkey;
4+
5+
-- Step 2: Drop the id column
6+
ALTER TABLE public.projects
7+
DROP COLUMN id;
8+
9+
-- Step 3: Set the team_code column as the new primary key
10+
ALTER TABLE public.projects
11+
ADD CONSTRAINT projects_pkey PRIMARY KEY (team_code);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE public.projects
2+
ADD COLUMN theme text;

0 commit comments

Comments
 (0)