Conversation
…rations - Create setup sharing UI with image upload - Configure Supabase storage for setup images - Add setup listing and card components - Update Next.js config for Supabase image hosting - Implement setup creation and image upload flow
WalkthroughThis update introduces a new "Setup" feature, including database schema changes, server actions, and a UI for users to create, edit, delete, and like setups with images. It adds Supabase SSR support, updates image optimization settings, and enhances the sidebar navigation. New validation schemas and React components support the full setup workflow. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as SetupsList/SetupCard/UploadSetupModal
participant Actions as Server Actions
participant DB as Database
User->>UI: Open Setups Page
UI->>Actions: fetchSetups(page)
Actions->>DB: Query setups with author and likes
DB-->>Actions: Setups data
Actions-->>UI: Setups data
UI-->>User: Display setups
User->>UI: Create/Edit/Delete/Like Setup
UI->>Actions: createSetup/editSetup/deleteSetup/toggleLikeSetup
Actions->>DB: Perform DB operation (create, update, delete, like)
DB-->>Actions: Result
Actions->>UI: Confirmation / Updated data
UI-->>User: Update UI (optimistically if like)
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
agustin-sanc
left a comment
There was a problem hiding this comment.
Muy buen trabajo Nico! Visualmente esta espectacular! En cuanto al código creo que podemos mejorar la calidad bastante, te dejé varios comentarios para que los trabajemos, creo que son una linda oportunidad de aprendizaje varios de ellos
There was a problem hiding this comment.
Veo que esta PR incluye 3 migrations. Lo ideal es que una PR tenga solo una migration. Lo que podes hacer es borrar estas 3 y crear una nueva. Esa nueva que crees, va a tener todos los cambios necesarios para hacer en la base. Avisame y lo vemos por Discord.
There was a problem hiding this comment.
La elimine y la modifique en una sola PR.
Gracias por ofrecerme a ayudar en Discord.
- Move initial setups fetch to server component - Remove client-side initial fetch - Remove unnecessary loading state - Reuse fetchSetups server action
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 10
♻️ Duplicate comments (3)
src/actions/setup/update-setup-image.ts (1)
1-1: Code appears properly formatted.The past review comment mentioned missing Prettier formatting, but the current code appears to be properly formatted.
src/app/(platform)/setups/page.tsx (1)
22-22: Consider fetching setups directly from Prisma for better performance.The past review comment about performance improvement is still valid. Currently, the page uses two different data fetching patterns which could be optimized.
Consider this approach for consistency and performance:
- const setups = await fetchSetups(1); + const setups = await prisma.setup.findMany({ + include: { + author: true, + likes: true, + }, + orderBy: { + createdAt: 'desc', + }, + take: 10, // or whatever page size you need + });src/actions/setup/create-setup.ts (1)
1-1: Formato pendiente con Prettier
🧹 Nitpick comments (8)
src/app/(platform)/setups/page.tsx (1)
8-20: Consider consolidating data fetching patterns.The page uses direct Prisma queries for session data but a server action for setups. This creates inconsistency in data fetching patterns.
For consistency, consider either:
- Using direct Prisma queries for both session and setups data
- Creating a server action for session data as well
src/components/setup/upload-setup-modal.tsx (2)
128-130: Use optional chaining for safer property accessApply this diff to use optional chaining:
- if (e.dataTransfer.files && e.dataTransfer.files[0]) { + if (e.dataTransfer.files?.[0]) { handleFile(e.dataTransfer.files[0]); }
134-136: Use optional chaining for safer property accessApply this diff to use optional chaining:
- if (e.target.files && e.target.files[0]) { + if (e.target.files?.[0]) { handleFile(e.target.files[0]); }src/components/setup/setups-list.tsx (5)
33-33: Consider making the page parameter configurable instead of hardcoding it.The hardcoded page number
1limits the function's reusability and prevents proper pagination implementation.- const fetchSetupsData = async () => { + const fetchSetupsData = async (page: number = 1) => { try { - const data = await fetchSetups(1); + const data = await fetchSetups(page); setSetups(data); } catch (error) { toast.error('Error al cargar los setups'); } };
84-85: Remove duplicatesetShowAuthModal(false)call.The
setShowAuthModal(false)is called twice in thefinallyblock, which is redundant.} finally { - setShowAuthModal(false); setSelectedSetup(null); setModalAction(null); }
178-182: Consider extracting the onSubmit handler to a separate function for better readability.The inline handler function makes the JSX less readable and harder to test.
+ const handleUploadSubmit = () => { + setShowUploadModal(false); + setSetupToEdit(null); + fetchSetupsData(); + }; - onSubmit={() => { - setShowUploadModal(false); - setSetupToEdit(null); - fetchSetupsData(); - }} + onSubmit={handleUploadSubmit}
193-200: Add proper JSX element type for the icon prop.The
iconprop should have a more specific type for better type safety.interface ModalSetupProps { showAuthModal: boolean; setShowAuthModal: (show: boolean) => void; title?: string; description?: string; - icon?: React.ReactNode; + icon?: React.ReactElement; onConfirm?: () => void; }
1-256: Overall component structure looks good, but consider adding error boundaries and loading states.The component successfully implements the setup management functionality without react-query as requested in the past review comments. The integration with server actions is well done.
Consider adding:
- Error boundaries to gracefully handle component-level errors
- Loading states for better user experience during async operations
- Proper pagination controls for setup navigation
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (16)
next.config.mjs(1 hunks)package.json(1 hunks)prisma/migrations/20250609200043_add_setup_model/migration.sql(1 hunks)prisma/schema.prisma(3 hunks)src/actions/setup/create-setup.ts(1 hunks)src/actions/setup/delete-setup.ts(1 hunks)src/actions/setup/edit-setup.ts(1 hunks)src/actions/setup/fetch-setups.ts(1 hunks)src/actions/setup/like-setup.ts(1 hunks)src/actions/setup/update-setup-image.ts(1 hunks)src/app/(platform)/setups/page.tsx(1 hunks)src/components/setup/setup-card.tsx(1 hunks)src/components/setup/setups-list.tsx(1 hunks)src/components/setup/upload-setup-modal.tsx(1 hunks)src/components/ui/app-sidebar.tsx(3 hunks)src/schemas/setup-schema.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
src/actions/setup/create-setup.ts (1)
Learnt from: MauriJC
PR: programaconnosotros/pcn-website#17
File: src/app/(platform)/events/[id]/page.tsx:24-28
Timestamp: 2025-02-04T21:17:26.725Z
Learning: Event ID validation is handled through Prisma's findUnique method which safely returns null for invalid or non-existent IDs, making additional ID format validation unnecessary.
src/schemas/setup-schema.ts (1)
Learnt from: MauriJC
PR: programaconnosotros/pcn-website#17
File: src/components/events/event-card.tsx:19-20
Timestamp: 2025-02-04T22:05:03.472Z
Learning: The Event model in the PCN website uses Zod schema validation (@prisma/generated/zod) which ensures date fields contain valid dates, making additional date validation in the UI components unnecessary.
🧬 Code Graph Analysis (6)
src/actions/setup/fetch-setups.ts (1)
src/lib/constants.ts (1)
ADVISES_PER_PAGE(1-1)
src/actions/setup/edit-setup.ts (1)
src/schemas/setup-schema.ts (1)
setupSchema(3-15)
src/actions/setup/create-setup.ts (1)
src/schemas/setup-schema.ts (1)
setupSchema(3-15)
src/components/setup/upload-setup-modal.tsx (6)
src/actions/setup/edit-setup.ts (1)
editSetup(8-43)src/actions/setup/create-setup.ts (1)
createSetup(8-42)src/actions/setup/update-setup-image.ts (1)
updateSetupImage(6-14)src/components/ui/dialog.tsx (4)
Dialog(94-94)DialogContent(99-99)DialogHeader(100-100)DialogTitle(102-102)src/components/ui/input.tsx (1)
Input(24-24)src/components/ui/textarea.tsx (1)
Textarea(23-23)
src/components/setup/setup-card.tsx (2)
src/actions/setup/like-setup.ts (1)
toggleLikeSetup(7-50)src/components/ui/avatar.tsx (3)
Avatar(47-47)AvatarImage(47-47)AvatarFallback(47-47)
src/components/setup/setups-list.tsx (5)
src/actions/setup/fetch-setups.ts (1)
fetchSetups(7-32)src/actions/setup/delete-setup.ts (1)
deleteSetup(7-24)src/components/setup/setup-card.tsx (1)
SetupCard(28-174)src/components/setup/upload-setup-modal.tsx (1)
UploadSetupModal(42-358)src/components/ui/dialog.tsx (4)
Dialog(94-94)DialogContent(99-99)DialogHeader(100-100)DialogTitle(102-102)
🪛 Biome (1.9.4)
src/components/setup/upload-setup-modal.tsx
[error] 128-128: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 134-134: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (14)
package.json (1)
44-44: LGTM! Clean dependency addition.The
@supabase/ssrpackage addition aligns with the PR objectives to support server-side rendering with Supabase. The caret version range is appropriate.src/components/ui/app-sidebar.tsx (3)
4-4: LGTM! Appropriate icon import.The
Bookicon import supports the icon change for the "Cursos" menu item.
73-73: LGTM! Better semantic icon choice.Changing the "Cursos" icon from
LaptoptoBookis more semantically appropriate for educational content.
93-97: LGTM! Clean navigation addition.The new "Setups" menu item is properly configured with appropriate URL and icon. The
Laptopicon is semantically appropriate for hardware/setup configurations.next.config.mjs (1)
17-24: LGTM! Necessary image configuration for Setup feature.The new remote image patterns support the Setup feature's image handling capabilities. Both use HTTPS which is good for security.
The Supabase hostname
sltoipimgzzeegsdygra.supabase.coappears to be a specific deployment URL. Verify this is the correct long-term hostname and won't change with different environments.src/app/(platform)/setups/page.tsx (1)
24-28: LGTM! Clean component structure.The component structure and rendering logic are well-organized. The session and setups data are properly passed to the child component.
src/actions/setup/delete-setup.ts (1)
7-24: LGTM! Secure deletion with proper authentication.The implementation correctly validates user authentication and uses both
idandauthorIdin the deletion criteria, preventing users from deleting other users' setups. The cache revalidation ensures UI consistency after deletion.src/actions/setup/fetch-setups.ts (1)
30-31: Verify pagination logic with the constant mismatch.Ensure the pagination logic using
ADVISES_PER_PAGEprovides the expected page size for setups functionality.src/actions/setup/like-setup.ts (1)
7-50: LGTM! Well-implemented toggle like functionality.The implementation correctly handles authentication, uses proper upsert logic for toggling likes, and includes comprehensive error handling. The function appropriately doesn't return unnecessary success objects, following good practices.
src/schemas/setup-schema.ts (1)
3-17: LGTM! Well-structured validation schema.The schema provides appropriate validation constraints for setup data:
- Title is required with reasonable length limits
- Description is optional with sensible max length
- ImageUrl validation ensures valid URLs
- Proper TypeScript type inference
src/actions/setup/edit-setup.ts (1)
17-21: Approve the validation logic with proper schema usage.The validation correctly uses the setupSchema to parse form data and imageUrl, ensuring data integrity before database operations.
src/components/setup/setup-card.tsx (1)
28-73: Well-implemented optimistic updates for likes!The optimistic update pattern with local state fallback provides excellent user experience by showing immediate feedback while handling potential errors gracefully.
prisma/schema.prisma (1)
55-68: Clean implementation of polymorphic likes patternThe schema changes properly implement polymorphic relationships by making
adviseIdoptional and addingsetupIdwith appropriate unique constraints. This allows theLikemodel to reference eitherAdviseorSetupentities cleanly.Also applies to: 130-141
prisma/migrations/20250609200043_add_setup_model/migration.sql (1)
1-31: Migration properly implements the schema changesThe migration correctly creates the Setup table and modifies the Like table to support polymorphic relationships. The warning about potential duplicate values is expected when adding unique constraints.
| import prisma from '@/lib/prisma'; | ||
| import { revalidatePath } from 'next/cache'; | ||
|
|
||
| export const updateSetupImage = async (setupId: string, imageUrl: string) => { |
There was a problem hiding this comment.
Add authentication and authorization checks.
The function doesn't verify user authentication or authorization to update the setup. This could allow unauthorized users to modify setup images.
Consider adding authentication checks similar to other server actions in the codebase.
🤖 Prompt for AI Agents
In src/actions/setup/update-setup-image.ts at line 6, the updateSetupImage
function lacks authentication and authorization checks, risking unauthorized
modifications. Add code to verify the user's authentication status and confirm
they have permission to update the specified setup, following the pattern used
in other server actions in the codebase. This typically involves checking the
user's session or token and validating their access rights before proceeding
with the update.
| export const updateSetupImage = async (setupId: string, imageUrl: string) => { | ||
| await prisma.setup.update({ | ||
| where: { id: setupId }, | ||
| data: { imageUrl }, | ||
| }); | ||
|
|
||
| revalidatePath('/setups'); | ||
| revalidatePath('/'); | ||
| }; |
There was a problem hiding this comment.
Add error handling and input validation.
The function lacks critical error handling and input validation, which could lead to runtime errors and security issues.
Apply this diff to improve robustness:
-export const updateSetupImage = async (setupId: string, imageUrl: string) => {
- await prisma.setup.update({
- where: { id: setupId },
- data: { imageUrl },
- });
-
- revalidatePath('/setups');
- revalidatePath('/');
-};
+export const updateSetupImage = async (setupId: string, imageUrl: string) => {
+ if (!setupId || !imageUrl) {
+ throw new Error('Setup ID and image URL are required');
+ }
+
+ try {
+ await prisma.setup.update({
+ where: { id: setupId },
+ data: { imageUrl },
+ });
+
+ revalidatePath('/setups');
+ revalidatePath('/');
+ } catch (error) {
+ console.error('Failed to update setup image:', error);
+ throw new Error('Failed to update setup image');
+ }
+};🤖 Prompt for AI Agents
In src/actions/setup/update-setup-image.ts around lines 6 to 14, the
updateSetupImage function lacks error handling and input validation. Add checks
to ensure setupId and imageUrl are valid (e.g., non-empty strings) before
proceeding. Wrap the prisma.update call and revalidatePath calls in a try-catch
block to handle and log any errors gracefully, preventing unhandled exceptions
and improving robustness.
| @@ -0,0 +1,32 @@ | |||
| 'use server'; | |||
|
|
|||
| import { ADVISES_PER_PAGE } from '@/lib/constants'; | |||
There was a problem hiding this comment.
🛠️ Refactor suggestion
Inconsistent constant name for setups pagination.
The function imports ADVISES_PER_PAGE but uses it for setups pagination, which is semantically inconsistent.
Consider adding a dedicated constant for setups or renaming the existing constant to be more generic:
-import { ADVISES_PER_PAGE } from '@/lib/constants';
+import { ITEMS_PER_PAGE } from '@/lib/constants';Or add a specific constant in src/lib/constants.ts:
+export const SETUPS_PER_PAGE = 1000;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/actions/setup/fetch-setups.ts at line 3, the imported constant
ADVISES_PER_PAGE is used for setups pagination, causing semantic inconsistency.
To fix this, define a new constant specifically for setups pagination in
src/lib/constants.ts, such as SETUPS_PER_PAGE, and import that instead. This
will clarify the purpose of the constant and maintain consistent naming
conventions.
There was a problem hiding this comment.
Aquí si es mejor crear una variable nueva
There was a problem hiding this comment.
🧩 Analysis chain
@MauriJC Perfect! I agree that creating a new variable is the better approach for semantic clarity. Let me plan the implementation:
Task: Create SETUPS_PER_PAGE constant for semantic consistency
- Add new SETUPS_PER_PAGE constant to constants file
- Update import in fetch-setups.ts to use the new constant
- Update the function to use the new constant
🏁 Script executed:
# First, let's find the constants file to see its current structure
fd "constants" --type f --extension ts --extension js
Length of output: 74
Perfect! I found the constants file. Now I can complete the implementation plan:
Task: Create SETUPS_PER_PAGE constant for semantic consistency
- Add new SETUPS_PER_PAGE constant to constants file
- Update import in fetch-setups.ts to use the new constant
- Update the function to use the new constant
The implementation will create semantic consistency by using a dedicated SETUPS_PER_PAGE constant specifically for setups pagination, maintaining the same value (1000) for consistent behavior across the application.
🧬 Code Graph Analysis Results
Relevant snippet from src/lib/constants.ts (line 1)
export const ADVISES_PER_PAGE = 1000;This constant is used in fetchSetups to determine the number of setups to fetch per page (take: ADVISES_PER_PAGE) and to calculate the offset for pagination (skip: (page - 1) * ADVISES_PER_PAGE).
⏳ Preparing changes...
There was a problem hiding this comment.
A summary of the changes CodeRabbit can apply:
Add a new constant SETUPS_PER_PAGE to the constants file and update the fetch-setups.ts file to import and use this new constant for pagination parameters instead of ADVISES_PER_PAGE.
Add a new constant SETUPS_PER_PAGE with value 1000 to the constants file, update the import in fetch-setups.ts to use this new constant, and modify the pagination parameters to reference SETUPS_PER_PAGE instead of ADVISES_PER_PAGE.
- ✅ Create PR with these edits
- 📌 Create commit in current branch
- 📋 Get copyable edits
| where: { id, authorId: session.userId }, | ||
| data: { | ||
| title: validatedData.title, | ||
| content: validatedData.description!, |
There was a problem hiding this comment.
Critical issue: Non-null assertion on optional field.
The code uses validatedData.description! but description is optional in the schema and could be undefined, leading to potential runtime errors.
Fix the optional field handling:
- content: validatedData.description!,
+ content: validatedData.description || '',Or update the schema to make description required if it's always expected to have a value.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| content: validatedData.description!, | |
| content: validatedData.description || '', |
🤖 Prompt for AI Agents
In src/actions/setup/edit-setup.ts at line 37, the code uses a non-null
assertion on the optional field validatedData.description!, which can cause
runtime errors if description is undefined. To fix this, either add a
conditional check to safely handle the case when description is undefined before
using it, or update the schema to make description a required field if it should
always have a value.
| const setup = await prisma.setup.create({ | ||
| data: { | ||
| title: validatedData.title, | ||
| content: validatedData.description!, | ||
| imageUrl: validatedData.imageUrl, | ||
| author: { connect: { id: user.id } }, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Fix potential runtime error with optional description field
The description field is optional in the schema, but you're using a non-null assertion operator (!) which could cause a runtime error if the description is undefined.
Apply this diff to safely handle the optional description:
const setup = await prisma.setup.create({
data: {
title: validatedData.title,
- content: validatedData.description!,
+ content: validatedData.description || '',
imageUrl: validatedData.imageUrl,
author: { connect: { id: user.id } },
},
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const setup = await prisma.setup.create({ | |
| data: { | |
| title: validatedData.title, | |
| content: validatedData.description!, | |
| imageUrl: validatedData.imageUrl, | |
| author: { connect: { id: user.id } }, | |
| }, | |
| }); | |
| const setup = await prisma.setup.create({ | |
| data: { | |
| title: validatedData.title, | |
| content: validatedData.description || '', | |
| imageUrl: validatedData.imageUrl, | |
| author: { connect: { id: user.id } }, | |
| }, | |
| }); |
🤖 Prompt for AI Agents
In src/actions/setup/create-setup.ts around lines 31 to 38, the code uses a
non-null assertion operator on the optional description field, which can cause a
runtime error if description is undefined. To fix this, remove the non-null
assertion operator and safely assign the description field by either providing a
default value or conditionally including it only if it exists, ensuring no
undefined value is passed to content.
There was a problem hiding this comment.
Por que en un lado se llama content y en el otro description?
Sugeriría cambiarle el nombre a description aquí y en el modelo para mejor cohesion
There was a problem hiding this comment.
🧩 Analysis chain
@MauriJC You're absolutely right about the naming inconsistency! This is indeed confusing and reduces code cohesion. Let me investigate how this field is used across the codebase to understand the full scope.
🏁 Script executed:
#!/bin/bash
# Search for usage of 'content' field in relation to Setup
echo "=== Searching for 'content' field usage in Setup-related code ==="
rg -A 3 -B 3 "content.*setup|setup.*content" --ignore-case
echo -e "\n=== Searching for 'description' field usage in Setup-related code ==="
rg -A 3 -B 3 "description.*setup|setup.*description" --ignore-case
echo -e "\n=== Checking Prisma schema Setup model ==="
ast-grep --pattern 'model Setup {
$$$
}'
echo -e "\n=== Checking setupSchema definition ==="
ast-grep --pattern 'export const setupSchema = $$$'
Length of output: 4202
🏁 Script executed:
#!/bin/bash
# Get the actual Prisma schema Setup model definition
echo "=== Full Prisma schema Setup model ==="
cat prisma/schema.prisma | sed -n '/^model Setup/,/^}/p'
echo -e "\n=== Search for all actions that use content field ==="
rg -A 2 -B 2 "content.*validatedData|validatedData.*content"
echo -e "\n=== Check edit-setup action ==="
cat src/actions/setup/edit-setup.ts
Length of output: 4483
Perfect! Now I can see the full picture of the naming inconsistency. You're absolutely right - this creates unnecessary confusion and mapping complexity.
Current State Analysis
The inconsistency exists across multiple layers:
- Validation/Schema layer: Uses
description(insetupSchema) - Database layer: Uses
content(in Prisma Setup model) - Actions: Map
validatedData.description→contentfield - UI Components: Convert back and forth between the two names
This creates problematic mappings like:
// In setups-list.tsx - converting content back to description for editing
description: setup.content
// In actions - converting description to content for database
content: validatedData.description!Two Solutions for Consistency
Looking at your codebase, I see that Advise and Comment models consistently use content throughout. You have two options:
Option 1: Rename to description (as you suggested)
- More semantic and user-friendly
- Matches the frontend terminology
- Requires database migration + updates
Option 2: Rename to content (match existing pattern)
- Consistent with Advise/Comment models in your codebase
- Less database changes, more schema changes
- Follows existing naming convention
Recommendation
I'd lean toward Option 1 (your suggestion) because:
- "description" is more semantically correct for user-facing content
- It matches what users see in forms
- The Setup entity seems more content-descriptive than the shorter Advise model
Would you like me to help plan the changes needed for either approach? The scope would include:
- Prisma schema update
- Database migration
- Action files updates
- Component updates
- Type definitions
🧬 Code Graph Analysis Results
Possibly relevant snippets from src/schemas/setup-schema.ts (lines 3-15)
export const setupSchema = z.object({
title: z
.string()
.min(1, 'El título es requerido')
.max(45, 'El título no puede tener más de 45 caracteres')
.trim(),
description: z
.string()
.max(250, 'La descripción no puede tener más de 250 caracteres')
.trim()
.optional(),
imageUrl: z.string().url('URL de imagen inválida').min(1, 'La imagen es requerida'),
});This schema defines the validation rules for the setup data, including a description field.
File: src/actions/setup/create-setup.ts (lines 1-44)
'use server';
import prisma from '@/lib/prisma';
import { setupSchema } from '@/schemas/setup-schema';
import { revalidatePath } from 'next/cache';
import { cookies } from 'next/headers';
export const createSetup = async (formData: FormData, imageUrl: string) => {
const validatedData = setupSchema.parse({
title: formData.get('title'),
description: formData.get('description'),
imageUrl: imageUrl,
});
const sessionId = cookies().get('sessionId');
if (!sessionId) throw new Error('Not authenticated');
const session = await prisma.session.findUnique({
where: { id: sessionId.value },
});
if (!session) throw new Error('Session not found');
const user = await prisma.user.findUnique({
where: { id: session.userId },
});
if (!user) throw new Error('User not found');
const setup = await prisma.setup.create({
data: {
title: validatedData.title,
content: validatedData.description!, // Note: here 'description' is assigned to 'content'
imageUrl: validatedData.imageUrl,
author: { connect: { id: user.id } },
},
});
revalidatePath('/setups');
revalidatePath('/');
return setup.id;
};This function validates input data using setupSchema (which uses description), but when creating the database record, it assigns validatedData.description to the content field in the Prisma setup model. This shows the naming difference between the input/validation layer (description) and the database model (content).
✏️ Learnings added
Learnt from: MauriJC
PR: programaconnosotros/pcn-website#186
File: src/actions/setup/create-setup.ts:31-38
Timestamp: 2025-07-14T23:37:04.917Z
Learning: In the PCN website codebase, there's a naming inconsistency in the Setup model where the validation schema uses `description` but the database model uses `content`, requiring mapping between layers. The user MauriJC prefers renaming to `description` for better cohesion and semantic clarity.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: MauriJC
PR: programaconnosotros/pcn-website#17
File: prisma/seed.ts:39-83
Timestamp: 2025-02-04T22:32:10.330Z
Learning: The seed.ts script has error handling at the main() function level that catches errors and ensures proper database cleanup through the finally block.
| if (error) { | ||
| console.log({ error }); | ||
| return; | ||
| } | ||
|
|
||
| // Obtenemos la URL pública de la imagen | ||
| const { data: publicUrlData } = supabase.storage.from('setups').getPublicUrl(data.path); | ||
|
|
||
| // Actualizamos el setup con la URL real de la imagen | ||
| await updateSetupImage(setupId, publicUrlData.publicUrl); | ||
| } | ||
|
|
||
| resetForm(); | ||
| onOpenChange(false); | ||
| refetch(); | ||
| toast.success( | ||
| setupToEdit ? 'Setup actualizado exitosamente! 🎉' : 'Setup subido exitosamente! 🎉', | ||
| ); | ||
| }); | ||
| } catch (error) { | ||
| setError('Error al subir la imagen'); | ||
| } finally { | ||
| setIsUploading(false); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Improve error handling to provide better user feedback
The error handling could be more user-friendly by showing specific error messages instead of just logging to console.
Apply this diff to improve error handling:
if (error) {
- console.log({ error });
- return;
+ throw new Error(error.message || 'Error al subir la imagen');
}
// Obtenemos la URL pública de la imagen
const { data: publicUrlData } = supabase.storage.from('setups').getPublicUrl(data.path);
// Actualizamos el setup con la URL real de la imagen
await updateSetupImage(setupId, publicUrlData.publicUrl);
}
resetForm();
onOpenChange(false);
refetch();
toast.success(
setupToEdit ? 'Setup actualizado exitosamente! 🎉' : 'Setup subido exitosamente! 🎉',
);
});
} catch (error) {
- setError('Error al subir la imagen');
+ const errorMessage = error instanceof Error ? error.message : 'Error al procesar la solicitud';
+ setError(errorMessage);
+ toast.error(errorMessage);
} finally {
setIsUploading(false);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (error) { | |
| console.log({ error }); | |
| return; | |
| } | |
| // Obtenemos la URL pública de la imagen | |
| const { data: publicUrlData } = supabase.storage.from('setups').getPublicUrl(data.path); | |
| // Actualizamos el setup con la URL real de la imagen | |
| await updateSetupImage(setupId, publicUrlData.publicUrl); | |
| } | |
| resetForm(); | |
| onOpenChange(false); | |
| refetch(); | |
| toast.success( | |
| setupToEdit ? 'Setup actualizado exitosamente! 🎉' : 'Setup subido exitosamente! 🎉', | |
| ); | |
| }); | |
| } catch (error) { | |
| setError('Error al subir la imagen'); | |
| } finally { | |
| setIsUploading(false); | |
| } | |
| if (error) { | |
| throw new Error(error.message || 'Error al subir la imagen'); | |
| } | |
| // Obtenemos la URL pública de la imagen | |
| const { data: publicUrlData } = supabase.storage | |
| .from('setups') | |
| .getPublicUrl(data.path); | |
| // Actualizamos el setup con la URL real de la imagen | |
| await updateSetupImage(setupId, publicUrlData.publicUrl); | |
| } | |
| resetForm(); | |
| onOpenChange(false); | |
| refetch(); | |
| toast.success( | |
| setupToEdit ? 'Setup actualizado exitosamente! 🎉' : 'Setup subido exitosamente! 🎉', | |
| ); | |
| }); | |
| } catch (error) { | |
| const errorMessage = | |
| error instanceof Error | |
| ? error.message | |
| : 'Error al procesar la solicitud'; | |
| setError(errorMessage); | |
| toast.error(errorMessage); | |
| } finally { | |
| setIsUploading(false); | |
| } |
🤖 Prompt for AI Agents
In src/components/setup/upload-setup-modal.tsx between lines 172 and 195,
replace the console.log error handling with user-friendly feedback by setting an
error state or showing a toast notification with the specific error message.
This will improve user experience by providing clear and actionable error
information instead of silently logging errors to the console.
| const handleDeleteSetup = (setup: any) => { | ||
| if (!session) { | ||
| setModalAction('delete'); | ||
| setShowAuthModal(true); | ||
| return; | ||
| } | ||
| setSelectedSetup(setup); | ||
| setModalAction('delete'); | ||
| setShowAuthModal(true); | ||
| }; |
There was a problem hiding this comment.
Fix the logic error in delete handler - showing auth modal for authenticated users.
The current logic always shows the auth modal for delete actions, even when the user is authenticated. This creates confusing UX.
const handleDeleteSetup = (setup: any) => {
if (!session) {
- setModalAction('delete');
setShowAuthModal(true);
return;
}
setSelectedSetup(setup);
setModalAction('delete');
setShowAuthModal(true);
};The correct logic should show a confirmation dialog for authenticated users, not an auth modal:
const handleDeleteSetup = (setup: SetupWithAuthorAndLikes) => {
if (!session) {
setShowAuthModal(true);
return;
}
setSelectedSetup(setup);
setModalAction('delete');
- setShowAuthModal(true);
+ // Show confirmation dialog instead of auth modal
+ setShowAuthModal(true);
};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/components/setup/setups-list.tsx around lines 49 to 58, the
handleDeleteSetup function incorrectly shows the authentication modal even when
the user is authenticated. To fix this, modify the logic so that if the user is
not authenticated, it shows the auth modal, but if the user is authenticated, it
should show a confirmation dialog instead. Adjust the state updates accordingly
to differentiate between showing the auth modal and the confirmation dialog
based on the user's authentication status.
|
|
||
| {/* Upload Setup Modal */} | ||
| <UploadSetupModal | ||
| userId={session?.user.id!} |
There was a problem hiding this comment.
Avoid using non-null assertion operator on potentially undefined values.
The non-null assertion ! on session?.user.id could cause runtime errors if the session becomes null after the initial check.
<UploadSetupModal
- userId={session?.user.id!}
+ userId={session?.user.id || ''}
open={showUploadModal}Or better yet, add a proper guard:
<UploadSetupModal
- userId={session?.user.id!}
+ userId={session?.user.id ?? ''}
open={showUploadModal}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| userId={session?.user.id!} | |
| <UploadSetupModal | |
| userId={session?.user.id ?? ''} | |
| open={showUploadModal} | |
| /> |
🤖 Prompt for AI Agents
In src/components/setup/setups-list.tsx at line 172, avoid using the non-null
assertion operator on session?.user.id as it may cause runtime errors if session
is null. Instead, add a proper guard to check if session and session.user.id
exist before using userId, such as conditional rendering or early return to
ensure userId is only accessed when defined.
| const [selectedSetup, setSelectedSetup] = useState<any>(null); | ||
| const [setupToEdit, setSetupToEdit] = useState<any>(null); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Replace any types with proper TypeScript interfaces for better type safety.
Using any types defeats the purpose of TypeScript and can lead to runtime errors. Define proper interfaces for these state variables.
+interface SetupToEdit {
+ id: string;
+ title: string;
+ description: string;
+ imageUrl: string;
+}
+
+type SetupWithAuthorAndLikes = Setup & {
+ author: Pick<User, 'id' | 'name' | 'email' | 'image'>;
+ likes: { userId: string }[];
+};
- const [selectedSetup, setSelectedSetup] = useState<any>(null);
- const [setupToEdit, setSetupToEdit] = useState<any>(null);
+ const [selectedSetup, setSelectedSetup] = useState<SetupWithAuthorAndLikes | null>(null);
+ const [setupToEdit, setSetupToEdit] = useState<SetupToEdit | null>(null);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const [selectedSetup, setSelectedSetup] = useState<any>(null); | |
| const [setupToEdit, setSetupToEdit] = useState<any>(null); | |
| // Add these at the top of the file (after your imports) | |
| interface SetupToEdit { | |
| id: string; | |
| title: string; | |
| description: string; | |
| imageUrl: string; | |
| } | |
| type SetupWithAuthorAndLikes = Setup & { | |
| author: Pick<User, 'id' | 'name' | 'email' | 'image'>; | |
| likes: { userId: string }[]; | |
| }; | |
| // … | |
| // Replace the `any` hooks with the new types: | |
| const [selectedSetup, setSelectedSetup] = useState<SetupWithAuthorAndLikes | null>(null); | |
| const [setupToEdit, setSetupToEdit] = useState<SetupToEdit | null>(null); |
🤖 Prompt for AI Agents
In src/components/setup/setups-list.tsx at lines 27 to 28, the useState hooks
use the type 'any' which reduces type safety. Define appropriate TypeScript
interfaces representing the structure of the setup objects and replace 'any'
with these interfaces for selectedSetup and setupToEdit state variables to
ensure better type checking and prevent runtime errors.
| export const ModalSetup = ({ | ||
| showAuthModal, | ||
| setShowAuthModal, | ||
| title, | ||
| description, | ||
| onConfirm, | ||
| }: ModalSetupProps) => { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
The icon prop is defined but never used in the component.
The icon prop is destructured but not rendered in the component, which suggests incomplete implementation.
export const ModalSetup = ({
showAuthModal,
setShowAuthModal,
title,
description,
+ icon,
onConfirm,
}: ModalSetupProps) => {And add it to the render:
<DialogHeader className="pb-4 text-center">
+ {icon && <div className="mb-4 flex justify-center">{icon}</div>}
<DialogTitle className="text-xl font-bold text-gray-900 dark:text-white">
{title}
</DialogTitle>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const ModalSetup = ({ | |
| showAuthModal, | |
| setShowAuthModal, | |
| title, | |
| description, | |
| onConfirm, | |
| }: ModalSetupProps) => { | |
| export const ModalSetup = ({ | |
| showAuthModal, | |
| setShowAuthModal, | |
| title, | |
| description, | |
| icon, | |
| onConfirm, | |
| }: ModalSetupProps) => { | |
| return ( | |
| <Dialog> | |
| <DialogHeader className="pb-4 text-center"> | |
| {icon && <div className="mb-4 flex justify-center">{icon}</div>} | |
| <DialogTitle className="text-xl font-bold text-gray-900 dark:text-white"> | |
| {title} | |
| </DialogTitle> | |
| </DialogHeader> | |
| {/* ... rest of the modal content */} | |
| </Dialog> | |
| ); | |
| }; |
🤖 Prompt for AI Agents
In src/components/setup/setups-list.tsx around lines 202 to 208, the `icon` prop
is destructured in the ModalSetup component but never used in the JSX output. To
fix this, include the `icon` prop in the component's parameter list and add it
appropriately within the rendered JSX so that the icon is displayed as intended.
| imageUrl String | ||
|
|
||
| author User @relation(fields: [authorId], references: [id]) | ||
| authorId String |
There was a problem hiding this comment.
Que opinas de que el nombre del campo sea owner en lugar de author?
| const setup = await prisma.setup.create({ | ||
| data: { | ||
| title: validatedData.title, | ||
| content: validatedData.description!, | ||
| imageUrl: validatedData.imageUrl, | ||
| author: { connect: { id: user.id } }, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Por que en un lado se llama content y en el otro description?
Sugeriría cambiarle el nombre a description aquí y en el modelo para mejor cohesion
|
|
||
| export const deleteSetup = async (id: string) => { | ||
| const sessionId = await cookies().get('sessionId'); | ||
|
|
There was a problem hiding this comment.
Esto lo repetimos en muchos lados, quizás se podría extraer en una función de getSession o checkAuth para evitar repetición de código
| @@ -0,0 +1,32 @@ | |||
| 'use server'; | |||
|
|
|||
| import { ADVISES_PER_PAGE } from '@/lib/constants'; | |||
There was a problem hiding this comment.
Aquí si es mejor crear una variable nueva
This pull request introduces several changes, including updates to dependencies, configuration adjustments, and new features for managing "Setup" entities in the database. The most significant changes involve database schema modifications, new API actions, and dependency updates.
Database and Schema Changes:
Setupmodel inprisma/schema.prismato replace thePostmodel, with fields fortitle,content,imageUrl, and relationships toUserandLike. TheLikemodel was updated to support associations with bothAdviseandSetupentities. ([[1]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-5b443964f4f3a611682db8f7e02177b0a8c632b2039e2bd5e4dd7347815c565cR55-R69),[[2]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-5b443964f4f3a611682db8f7e02177b0a8c632b2039e2bd5e4dd7347815c565cL114-R143))Posttable, create theSetuptable, and update theLikemodel with a unique constraint foruserIdandsetupId. ([[1]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-934dc67b17d5bcf8828d881759a8d1a73698533cd43e7abbc423c09f761a1feaR1-R27),[[2]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-9911a920d0c58de00ee4ef34c1cfcfae5cc01024258bf77c2caaa5b8c1b32bffR1-R15))New API Actions:
createSetupanddeleteSetupserver-side actions to handle the creation and deletion ofSetupentities. These actions include user authentication checks and revalidation of related paths. ([[1]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-6c80b88562f99837165fde6f15d67c01388afa0390df3a6be8bdc92cadacd559R1-R42),[[2]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-bd32d9dc698659ab79df246251260ceb94b8dcc7cb0b3a8af4bac77c98400edbR1-R24))Dependency Updates:
@supabase/ssrtopackage.jsonand updatedpnpm-lock.yamlwith several Supabase-related dependencies (@supabase/auth-js,@supabase/functions-js, etc.) to support server-side rendering and other functionalities. ([[1]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519R44),[[2]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-32824c984905bb02bc7ffcef96a77addd1f1602cff71a11fbbfdd7f53ee026bbR1380-R1406),[[3]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-32824c984905bb02bc7ffcef96a77addd1f1602cff71a11fbbfdd7f53ee026bbR4431-R4477))pnpm-lock.yamlwith additional dependencies such ascookie,ws, andwhatwg-urlto support new features and improve compatibility. ([[1]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-32824c984905bb02bc7ffcef96a77addd1f1602cff71a11fbbfdd7f53ee026bbR1815-R1818),[[2]](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-32824c984905bb02bc7ffcef96a77addd1f1602cff71a11fbbfdd7f53ee026bbR3370-R3381))Configuration Changes:
next.config.mjsto allow images from additional external domains (storage.aceternity.comandsltoipimgzzeegsdygra.supabase.co). ([next.config.mjsR17-R24](https://github.com/programaconnosotros/pcn-website/pull/186/files#diff-18c049b08c4a0f5ab451c598aeb2c4848bb9d7877b51ca3e5effb94a225814d2R17-R24))Video Presentation
screen-recording-2025-06-05-at-24438-am_R6FyhhFz.mp4
Summary by CodeRabbit
New Features
Improvements
Bug Fixes