Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Registration from '../views/Registration.vue';
import LoginPage from '../views/LoginPage.vue';
import ActivitiesPage from '../views/ActivitiesPage.vue';
import HardwarePage from '../views/HardwarePage.vue';
import TeamsPage from '@/views/TeamsPage.vue';
import CategoriesPage from '../views/CategoriesPage.vue';

const routes = [
Expand Down Expand Up @@ -34,6 +35,14 @@ const routes = [
name: 'Hardware',
component: HardwarePage,
},
{
path: '/teams',
name: 'Team',
component: TeamsPage,
meta: {
requiresAuth: true
}
},
{
path: '/categories',
name: 'Categories',
Expand Down
22 changes: 12 additions & 10 deletions src/store/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,17 @@ export default createStore({
// 2. Check for the cookie row first.
if (!tokenCookie) return { success: false, message: "No token found" };

const tokenString = tokenCookie.split('=')[1]; // ✅ This is the raw string now.
const tokenString = tokenCookie.split('=')[1];

if (!token) return { success: false, message: "No token found"};

const response = await axios.post(`${state.apiBaseUrl}/user/register/user/auth`, {token}, {
headers: {
"Content-Type": "application/json",
},
});
// 3. Check if the string is empty/malformed.
if (!tokenString) return { success: false, message: "No token string found" };

// 4. Send the token string directly in the request body object.
const response = await axios.post(`http://localhost:3000/user/auth`, { token: tokenString }, {
headers: {
"Content-Type": "application/json",
},
});

const data = await response.data;
const user = new UserAdapter(data.data);
Expand Down Expand Up @@ -147,7 +149,7 @@ export default createStore({

async getActiveEvent({ commit, state }) {
try {
const response = await axios.get(`${state.apiBaseUrl}/event/active`);
const response = await axios.get(`http://localhost:3000/user/register/event/active`);

// Convert dates from UTC to local time (i.e., EST) and to a user-friendly format
const event = response.data.event
Expand All @@ -171,7 +173,7 @@ export default createStore({
const eventId = 1; //getters.getEvent.id ||

try{
const response = await axios.get(`${state.apiBaseUrl}/teams/${userId}/team`, {
const response = await axios.get(`http://localhost:3000/teams/${userId}/team`, {
params: { eventId }
});

Expand Down
247 changes: 247 additions & 0 deletions src/views/TeamsPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
<template>
<div class="container-top">
<header class="main-header">
<div class="text-center py-4">
<h1 class="mb-2">
{{ teamData.teamName || 'Team Project Submission' }}
</h1>
<hr class="header-line" />
</div>
</header>
</div>
<div class="container-fluid">
<div class="row align-items-start mt-5">
<main class="col-md-12 col-lg-8 offset-lg-2">
<div class="p-4 border rounded bg-white shadow-sm main-box">

<div v-if="!teamId" class="alert alert-danger text-center py-4">
You must be part of a team to access project submission.
</div>

<div v-else-if="isLoading" class="text-center py-5">
<p>Loading project data...</p>
</div>

<div v-else-if="!isSubmissionTime" class="alert alert-warning text-center py-4">
<span v-if="getEvent && new Date() < new Date(getEvent.startDate)">
Submissions open when the event officially begins on {{ new Date(getEvent.startDate).toLocaleDateString() }}.
</span>
<span v-else>
Submissions are **closed**. You cannot make further changes.
</span>
</div>

<form v-else @submit.prevent="handleSubmit">
<div v-if="successMessage" class="alert alert-success">{{ successMessage }}</div>
<div v-if="errorMessage" class="alert alert-danger">{{ errorMessage }}</div>

<div class="mb-3">
<label for="projectName" class="form-label fw-bold">Project Name</label>
<input
id="projectName"
v-model="teamData.projectName"
type="text"
class="form-control"
required
:disabled="isSubmitting"
/>
</div>

<div class="mb-3">
<label for="projectDescription" class="form-label fw-bold">Project Description</label>
<textarea
id="projectDescription"
v-model="teamData.projectDescription"
class="form-control"
rows="4"
required
:disabled="isSubmitting"
></textarea>
</div>

<div class="mb-3">
<label for="githubLink" class="form-label fw-bold">GitHub Link</label>
<input
id="githubLink"
v-model="teamData.githubLink"
type="url"
class="form-control"
placeholder="https://github.com/my-team/project"
:disabled="isSubmitting"
/>
<div class="form-text">Must be a publicly accessible repository.</div>
</div>

<div class="mb-4">
<label for="presentationLink" class="form-label fw-bold">Presentation Link</label>
<input
id="presentationLink"
v-model="teamData.presentationLink"
type="url"
class="form-control"
placeholder="https://slides.google.com/d/project-pitch"
:disabled="isSubmitting"
/>
<div class="form-text">Link to your presentation slides (e.g., Google Slides, PDF).</div>
</div>

<button type="submit" class="btn btn-success w-100" :disabled="isSubmitting">
{{ isSubmitting ? 'Submitting...' : 'Update Project Details' }}
</button>
<div class="text-center mt-2 small text-muted">Submissions are open until the event ends.</div>

</form>
</div>
</main>
</div>
</div>
</template>

<script>
import { mapGetters } from "vuex";
import axios from 'axios';
import store from "@/store/store.js";

export default {
name: "TeamRegistrationPage",
data() {
return {
isLoading: true,
isSubmitting: false,
// isSubmissionsOpen: true,
errorMessage: null,
successMessage: null,
teamData: {
teamName: "",
projectName: "",
projectDescription: "",
presentationLink: "",
githubLink: "",
}
};
},
computed: {
...mapGetters(["getUserTeamId", "getEvent"]),

teamId() {
return this.getUserTeamId;
},
isSubmissionTime(){
const event = this.getEvent;
if (!event || !event.startDate || !event.endDate) {
// Assume open if data isn't loaded yet, matching your old console warning logic
return true;
}

const now = new Date();
const startDate = new Date(event.startDate);
const endDate = new Date(event.endDate);

// Submissions are active if (now >= startDate) AND (now < endDate)
return now >= startDate && now < endDate;
},
},
mounted() {
if (this.teamId) {
this.fetchProjectDetails();
} else {
this.isLoading = false;
}
},
methods: {
async fetchProjectDetails() {
this.isLoading = true;
this.errorMessage = null;

try {
const url = `http://localhost:3000/teams/${this.teamId}/project-details`;
const response = await axios.get(url);

this.teamData = {
...this.teamData,
...response.data.data,
};

} catch (error) {
console.error("Error loading team data:", error);
if (error.response && error.response.status !== 404) {
this.errorMessage = error.response?.data?.message || "Failed to load project details.";
}
} finally {
this.isLoading = false;
}
},

/**
* Submits the updated project details using direct Axios call.
*/
async handleSubmit() {
if (!this.isSubmissionTime || this.isSubmitting) return;

this.isSubmitting = true;
this.errorMessage = null;
this.successMessage = null;

try {
// API Endpoint Assumption: PUT /teams/{teamId}/project-details
const url = `http://localhost:3000/teams/${this.teamId}/project-details`;
const payload = {
projectName: this.teamData.projectName,
projectDescription: this.teamData.projectDescription,
presentationLink: this.teamData.presentationLink,
githubLink: this.teamData.githubLink,
};

const response = await axios.put(url, payload);

this.successMessage = response.data.message || "Project details updated successfully!";

} catch (error) {
console.error("Submission failed:", error);
this.errorMessage = error.response?.data?.message || "Failed to save project details. Check your links and try again.";
} finally {
this.isSubmitting = false;
}
}
},
};
</script>

<style scoped>
body {
font-family: Lato, sans-serif;
color: #fff;
font-weight: 300;
font-size: 18px;
overflow-y: scroll;
overflow-x: hidden;
}
.container-top{
background-color: #64965d;
position: relative;
overflow: hidden;
}
.container-fluid {
background-color: #93dda3;
position: relative;
overflow: hidden;
min-height: 100vh;
}
.main-box {
margin-top: 0;
}
.main-header{
margin-top: 6rem;
margin-bottom: 0;
color: #6c757d;
width: 100%;
}
.main-header h1 {
color: #f8f9fa;
}
.header-line{
width: 60%;
margin: 0.5rem auto 0;
border-top: 3px solid #f8f9fa;
}
</style>