This commit is contained in:
End 2026-03-18 20:06:14 -07:00
parent eadf80d523
commit 7e65d9eee8
No known key found for this signature in database
3 changed files with 67 additions and 1 deletions

View file

@ -491,6 +491,30 @@ projects.get("/:id", async ({ params, headers }) => {
hasSubmittedFeedback = feedbackCheck.length > 0;
}
// Check if project can be resubmitted (first submission must be on or before March 17th, 2026)
let canResubmit = true;
if (isOwner) {
const firstSubmission = await db
.select({ createdAt: projectActivityTable.createdAt })
.from(projectActivityTable)
.where(
and(
eq(projectActivityTable.projectId, parseInt(params.id)),
eq(projectActivityTable.action, "project_submitted"),
),
)
.orderBy(projectActivityTable.createdAt)
.limit(1);
if (firstSubmission.length > 0) {
const cutoffDate = new Date("2026-03-18T00:00:00Z");
canResubmit = firstSubmission[0].createdAt < cutoffDate;
} else {
// Never submitted before - cannot submit new projects
canResubmit = false;
}
}
// Calculate effective hours (subtract overlapping shipped project hours)
// Uses activity-derived shipped dates for ordering (consistent with Airtable sync and admin review)
const projectHours = project[0].hoursOverride ?? project[0].hours ?? 0;
@ -533,6 +557,7 @@ projects.get("/:id", async ({ params, headers }) => {
owner: projectOwner[0] || null,
isOwner,
hasSubmittedFeedback: isOwner ? hasSubmittedFeedback : undefined,
canResubmit: isOwner ? canResubmit : undefined,
activity,
};
});
@ -840,6 +865,27 @@ projects.post("/:id/submit", async ({ params, headers, body }) => {
return { error: "Project cannot be submitted in current status" };
}
// Check if this is a resubmission - if so, verify first submission was on or before March 17th
const firstSubmission = await db
.select({ createdAt: projectActivityTable.createdAt })
.from(projectActivityTable)
.where(
and(
eq(projectActivityTable.projectId, parseInt(params.id)),
eq(projectActivityTable.action, "project_submitted"),
),
)
.orderBy(projectActivityTable.createdAt)
.limit(1);
if (firstSubmission.length > 0) {
// This is a resubmission - check if first submission was on or before March 17th, 2026
const cutoffDate = new Date("2026-03-18T00:00:00Z"); // Midnight March 18th = end of March 17th
if (firstSubmission[0].createdAt >= cutoffDate) {
return { error: "Resubmissions are only allowed for projects first submitted on or before March 17th" };
}
}
// Sync hours from Hackatime before submitting
if (project[0].hackatimeProject) {
await syncSingleProject(parseInt(params.id));

View file

@ -74,6 +74,7 @@
let owner = $state<Owner | null>(null);
let isOwner = $state(false);
let isAdmin = $state(false);
let canResubmit = $state(true);
let activity = $state<ActivityEntry[]>([]);
let loading = $state(true);
let error = $state<string | null>(null);
@ -104,6 +105,7 @@
project = result.project;
owner = result.owner;
isOwner = result.isOwner;
canResubmit = result.canResubmit ?? true;
activity = result.activity || [];
} catch (e) {
error = e instanceof Error ? e.message : 'Failed to load project';
@ -454,7 +456,7 @@
<Send size={18} />
{$t.project.awaitingReview}
</span>
{:else if project.status === 'shipped'}
{:else if project.status === 'shipped' && canResubmit}
<a
href="/projects/{project.id}/submit"
class="flex flex-1 cursor-pointer items-center justify-center gap-2 rounded-full border-4 border-black bg-black px-4 py-3 text-sm font-bold text-white transition-all duration-200 hover:bg-gray-800 sm:px-6 sm:text-base"
@ -462,6 +464,13 @@
<RefreshCw size={18} />
ship update
</a>
{:else if project.status === 'shipped' && !canResubmit}
<span
class="flex flex-1 cursor-not-allowed items-center justify-center gap-2 rounded-full border-4 border-black bg-gray-200 px-4 py-3 text-center text-sm font-bold text-gray-600 sm:px-6 sm:text-base"
>
<CheckCircle size={18} />
shipped
</span>
{:else if project.status === 'permanently_rejected'}
<span
class="flex flex-1 cursor-not-allowed items-center justify-center gap-2 rounded-full border-4 border-black bg-red-100 px-4 py-3 text-center text-sm font-bold text-red-600 sm:px-6 sm:text-base"
@ -469,6 +478,13 @@
<XCircle size={18} />
{$t.project.permanentlyRejected}
</span>
{:else if !canResubmit}
<span
class="flex flex-1 cursor-not-allowed items-center justify-center gap-2 rounded-full border-4 border-black bg-gray-200 px-4 py-3 text-center text-sm font-bold text-gray-600 sm:px-6 sm:text-base"
>
<XCircle size={18} />
submissions closed
</span>
{:else if $tutorialActiveStore}
<span
data-tutorial="submit-button"

View file

@ -128,6 +128,10 @@
if (!responseData.isOwner) {
throw new Error('You can only submit your own projects');
}
// Check if resubmission is allowed
if (responseData.canResubmit === false) {
throw new Error('Resubmissions are only allowed for projects first submitted on or before March 17th');
}
project = responseData.project;
imagePreview = project?.image || null;
hasSubmittedFeedbackBefore = responseData.hasSubmittedFeedback ?? false;