Skip to content

Conversation

@hanaeatsplanes
Copy link
Collaborator

  • fix TS71007
  • uh
  • grrr
  • ship ship ship

Copilot AI review requested due to automatic review settings January 25, 2026 01:18
@vercel
Copy link

vercel bot commented Jan 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
aces Ready Ready Preview, Comment Jan 25, 2026 1:18am

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a "ship project" feature that allows users to mark projects as shipped when they have at least one devlog. It also includes several bug fixes and improvements including favicon path corrections, prop renaming for consistency, and a new shop redirect page.

Changes:

  • Added ship project functionality with UI button and API integration
  • Fixed favicon paths to include leading slashes
  • Renamed onCancel to onCancelAction in EditProject component
  • Created new shop redirect page with loading and error states
  • Modified devlogs API to convert empty descriptions to null
  • Updated hackatime projects date filter
  • Added Content-Type header to ship API route

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/components/dashboard/ProjectDetailsClient.tsx Added ship project button with state management, error handling, and devlogCount prop
src/components/dashboard/EditProject.tsx Renamed onCancel prop to onCancelAction and added React import for type annotations
src/components/Meta.tsx Fixed favicon paths by adding leading slashes
src/app/dashboard/shop/page.tsx New shop redirect page with Suspense boundary and error handling
src/app/dashboard/projects/[projectId]/page.tsx Passed devlogCount to ProjectDetailsClient component
src/app/api/shop/form-url/route.ts New API route for fetching shop form URL from environment variables
src/app/api/projects/[projectId]/ship/route.ts Reordered imports and added Content-Type header; renamed unused parameter
src/app/api/hackatime/projects/route.ts Updated start_date filter from 2025-12-01 to 2025-12-21
src/app/api/dashboard/devlogs/route.ts Added type annotation and empty string to null conversion for description field

try {
const res = await fetch(`/api/projects/${project.project_id}/ship`, {
method: "POST",
credentials: "include",
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The credentials: "include" option on line 41 is unnecessary for same-origin requests in Next.js. The cookies are automatically sent with same-origin requests. This option is typically only needed for cross-origin requests with credentials. Consider removing it for clarity.

Suggested change
credentials: "include",

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +33
const { formUrl } = await res.json();
const redirectUrl = `${formUrl}?item=${encodeURIComponent(item!)}`;
window.location.href = redirectUrl;
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The redirectUrl constructed on line 32 should be validated to ensure the formUrl from the API is a trusted URL before redirecting. While the backend should control this, client-side validation would add defense in depth. Consider validating that formUrl is a known trusted domain before performing the redirect.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +18
if (data.description === "") {
data.description = null;
}
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The mutation of the parsed data on lines 16-18 occurs after type assertion but without proper type checking. Since the type annotation on line 10 doesn't provide runtime safety, the check data.description === "" could fail if description is undefined or missing. Consider restructuring this to avoid mutation and ensure type safety, for example: body: JSON.stringify({ ...data, description: data.description === "" ? null : data.description })

Copilot uses AI. Check for mistakes.

if (!res.ok) {
const data = await res.json();
setError(data.error || "Failed to load shop configuration");
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The error response on line 26 uses a generic "Failed to load shop configuration" message without including the backend's error detail. Consider extracting and including the error from the backend response to provide more context: setError(data.error || data.detail || "Failed to load shop configuration")

Suggested change
setError(data.error || "Failed to load shop configuration");
setError((data && (data.error || data.detail)) || "Failed to load shop configuration");

Copilot uses AI. Check for mistakes.
</button>
</div>
{shipError && (
<p className="text-red-600 text-sm mt-2">{shipError}</p>
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The error message displayed on lines 105-107 lacks accessibility attributes. Consider adding role="alert" and aria-live="polite" to the error container so screen readers announce the error when it appears.

Suggested change
<p className="text-red-600 text-sm mt-2">{shipError}</p>
<p
className="text-red-600 text-sm mt-2"
role="alert"
aria-live="polite"
>
{shipError}
</p>

Copilot uses AI. Check for mistakes.
}

let data: unknown;
let data: { project_id: string; content: string; media_url?: string; description?: string | null };
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The type annotation on line 10 is unsafe because req.json() returns any, which doesn't guarantee the data will match this type at runtime. Consider adding runtime validation using a library like Zod, or keeping the type as unknown and performing explicit type checking before using the data properties.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +107
{shipError && (
<p className="text-red-600 text-sm mt-2">{shipError}</p>
)}
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The error message (lines 105-107) is placed as a third child in the flex container with justify-between (line 69). This will position the error horizontally alongside the project info and buttons, which is likely not the intended layout. Consider moving the error message outside this flex container (after line 108) or restructuring the layout to display the error below the buttons on a new line.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +27
onCancelAction,
}: {
projectId: number;
initialData: ProjectData;
onCancel: () => void;
onCancelAction: () => void;
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The prop name "onCancelAction" is unconventional. Standard React naming convention for callback props is "on[EventName]" without the "Action" suffix (e.g., "onCancel", "onClick"). While custom component props can have any name, following the conventional pattern makes the codebase more intuitive. Consider using just "onCancel" instead.

Copilot uses AI. Check for mistakes.
}

const { formUrl } = await res.json();
const redirectUrl = `${formUrl}?item=${encodeURIComponent(item!)}`;
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The non-null assertion operator (!) on line 32 is unnecessary because the item parameter is already checked for null/undefined on line 14, and the function returns early if it's falsy. You can safely use item without the assertion: ${formUrl}?item=${encodeURIComponent(item)}

Suggested change
const redirectUrl = `${formUrl}?item=${encodeURIComponent(item!)}`;
const redirectUrl = `${formUrl}?item=${encodeURIComponent(item)}`;

Copilot uses AI. Check for mistakes.
@hackclub hackclub deleted a comment from Copilot AI Jan 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants