And now here's the reasoning tokens of DeepSeek-R1
<reasoning_tokens_R1>
Okay, let me try to figure out how to fix these two issues in the Next.js app.
First, the financial section isn't updating. Looking at the `opportunity-update-form.tsx`, the form schema includes the financial fields. But in the `use-update-opportunity` hook, when constructing the payload, maybe the financial data isn't being properly included. Wait, in the `updateOpportunity` function, the `finalPayload` is built from `values`, which includes the financial data. But maybe when sending the PUT request to the API, the backend isn't handling the financial data correctly. Let me check the API route `app/api/admin/opportunities/[id]/route.ts`. The PUT handler updates the main opportunity but doesn't mention the financial data. Oh, right! The financial data is part of the sections in the database. Wait, in the database schema, each section has a custom_content field. For the financial section, the form stores the financial data as a JSON string in custom_content. But in the update form, the financial data is a separate object. So maybe the update form isn't syncing the financial data back into the section's custom_content.
Looking at `opportunity-update-form.tsx`, when the form is submitted, the data includes the financial object. But the PUT API endpoint doesn't process this financial data. The API updates the sections but only processes the sections array from the payload. The financial data in the form isn't being saved into the corresponding section's custom_content. That's probably why the financial fields aren't updating. The form isn't converting the financial data back into the section's custom_content JSON.
In the create form (`opportunity-form.tsx`), there's a useEffect that syncs the financial data into the section's custom_content. But the update form doesn't have this. So, when submitting the update form, the financial data in the form isn't being stringified into the section's custom_content, leading to an empty payload for the financial section. The API then doesn't receive the updated financial data, so the database isn't updated.
To fix this, the update form needs to ensure that the financial data is serialized into the corresponding section's custom_content before submission. So in the onSubmit function of the update form, when preparing the data, we should find the financial section and set its custom_content to the JSON string of the financial data.
Second issue: the image captions and details aren't editable in the update form. Looking at the `opportunity-update-form.tsx`, when rendering existing images, there's no input fields for caption and details. In the create form (`opportunity-form.tsx`), existing images have inputs for caption and details, and there's a `handleExistingImageUpdate` function to update them. But the update form's render doesn't include these inputs. Instead, it just displays the caption if it exists but doesn't allow editing. So the update form is missing the input fields for caption and details on existing images.
To fix this, in the update form's rendering of existing images, we need to add input fields for caption and details, similar to how it's done in the create form. Then, when those inputs change, we should update the form state for those images. Additionally, the `handleExistingImageUpdate` function from the create form isn't present in the update form. So we need to implement that in the update form to update the images array in the form state.
Another thing to check: the update form's image handling. The update form's `onSubmit` calls `updateOpportunity`, which in `use-update-opportunity.ts` processes the sections. It combines existing images with new ones. However, if existing images have their captions or details updated, the current code in `use-update-opportunity` might not handle that. It just appends new images to the existing ones. Wait, in the `use-update-opportunity` hook's `updateOpportunity` function, when processing sections, it takes the existing images and adds the new ones. But if existing images' captions or details are changed, the existing images in the form's state would have those changes, but the code in the hook doesn't update the existing images in the database. The PUT API endpoint, when handling images, deletes images not present in the new list and adds new ones. But if existing images are modified (like caption or details), the current code doesn't update them. So the API isn't handling updates to existing images, only additions and deletions. That's a problem because changing captions wouldn't be persisted.
So the PUT handler in the API route for opportunities/[id] needs to not only delete missing images and add new ones but also update existing images if their data has changed. Currently, the code in the PUT route checks for existing images, deletes those not in the new list, and adds new ones. But it doesn't check if existing images have updated captions or details. Therefore, the API isn't updating existing image records, only adding or deleting.
This means that even if the frontend sends updated captions for existing images, the backend won't process those changes. So both the frontend and backend need adjustments. The frontend needs to send all images (existing and new) with their updated data, and the backend needs to update existing images accordingly.
Wait, looking at the PUT handler in the API route: when updating a section, it fetches the existing section and its images. Then, it compares the existing image URLs with the new ones. Images that are in the existing but not in the new are deleted. New images (those not in existing) are added. But images that are present in both are not updated. So any changes to caption or details on existing images won't be saved because the backend doesn't handle updates, only inserts and deletes.
To fix this, the backend needs to update existing images when their data changes. So in the PUT handler, for each image in the section's images array, if it exists (based on image_url?), it should update the caption and details. Alternatively, perhaps the frontend should send the image IDs, and the backend can update by ID. But currently, the images in the form data don't have IDs; they're just image_url, caption, details, etc.
Looking at the database schema, the images table has an id (UUID), section_id, image_url, caption, details, order_number. So each image has a unique ID. When the frontend fetches an opportunity, the existing images include their IDs. However, in the update form's initialData, the sections' images should have these IDs. But in the current `opportunity-update-form.tsx`, when initialData is set, are the images including their IDs? Let's check the GET route for the opportunity: yes, it includes the images with their IDs. So in the form's initialData, each image in a section should have an id.
In the update form's `formSchema`, the images array in sections is defined with an optional id. So when the form is populated, existing images should have their ids. Then, when submitting the form, the images in the sections include their ids. The PUT handler in the API needs to handle updates to existing images (those with ids) by updating their caption and details, not just adding new ones or deleting others.
But the current PUT handler in the API doesn't do that. It deletes images not present in the new list and adds new ones. So existing images that are kept but have changed captions won't be updated. Therefore, the backend logic is incorrect.
So to handle image updates (caption, details), the backend needs to:
1. For each image in the section's images array:
- If it has an ID, update the existing image record.
- If it doesn't have an ID, insert a new image.
2. Delete any images in the existing section that are not present in the new images array.
This approach would handle updates to existing images. The current backend code only handles insert and delete, not update. Therefore, the PUT route's image handling needs to be adjusted.
So the steps to fix both issues are:
1. Financial Section Update:
- In the update form's onSubmit, ensure that the financial data is serialized into the corresponding section's custom_content before sending the request.
2. Image Caption and Details:
- Update the update form to include input fields for existing images' captions and details.
- Implement handlers to update the form state when these inputs change.
- Adjust the backend PUT route to update existing images if their data has changed.
Let me start with the financial section issue.
In the `opportunity-update-form.tsx`, during submission, the form data includes the financial object. However, the financial data needs to be saved into the corresponding section's custom_content as a JSON string. The create form does this via a useEffect that parses the financial data from the section's custom_content and also updates it when financial fields change. But the update form doesn't have this synchronization.
So, in the update form's onSubmit function, before sending the data, we need to find the financial section in the sections array and set its custom_content to the JSON string of the financial data. Otherwise, the financial data in the form won't be saved into the section's custom_content, and the backend will not receive the updated values.
Wait, looking at the create form's `opportunity-form.tsx`, there's a useEffect that syncs the financial data into the section's custom_content. But in the update form, this is missing. So when the user updates a financial field in the update form, the financial section's custom_content is not updated, leading to the backend not receiving the new data.
Therefore, in the update form's onSubmit handler, before sending the data, we need to:
- Find the financial section in the data's sections array.
- Update its custom_content to be the JSON string of the financial data.
Alternatively, during form submission, we can map through the sections and update the financial section's custom_content.
So, modifying the onSubmit function in `opportunity-update-form.tsx`:
Before calling updateOpportunity, process the data to ensure the financial section's custom_content is set correctly.
But how is the financial section identified? In the form's sections, each section has a section_type. The financial section has section_type "financial". So in the data, we can find the section with section_type "financial" and update its custom_content.
So, in the onSubmit function:
const onSubmit = async (data: FormValues) => {
if (!opportunityId) {
toast.error("Opportunity ID is required");
return;
}
try {
setIsSubmitting(true);
// Find the financial section and update its custom_content
const updatedData = { ...data };
const financialSectionIndex = updatedData.sections.findIndex(
(section) => section.section_type === "financial"
);
if (financialSectionIndex !== -1) {
updatedData.sections[financialSectionIndex].custom_content = JSON.stringify(
updatedData.financial
);
}
await updateOpportunity(opportunityId, updatedData);
} catch (error) {
// handle error
} finally {
setIsSubmitting(false);
}
};
This way, when the form is submitted, the financial data is serialized into the financial section's custom_content, which the backend will then save.
Now, the backend when processing the sections will see the updated custom_content for the financial section and save it. So the PUT route in the API will update the section's custom_content, which includes the financial data.
This should fix the financial section not updating.
Now, the second issue: image captions and details in the update form.
In the `opportunity-update-form.tsx`, when rendering existing images, there's no input fields for caption and details. Let's compare with the create form's rendering.
In the create form's `opportunity-form.tsx`, for existing images (from initial data), each image has input fields for caption and details. When those inputs change, the `handleExistingImageUpdate` function is called, which updates the form state.
In the update form's existing images rendering (in `opportunity-update-form.tsx`), the code for existing images under the financial section is:
existingImages.map((image, imageIndex) => (
<div key={image.image_url} ...>
<img ... />
<div ...>
{image.caption && <p>{image.caption}</p>}
<Button onClick={() => handleImageRemove(...)}>Remove</Button>
</div>
</div>
))
But there are no input fields. So, the update form isn't allowing edits to existing images' captions and details.
We need to add input fields similar to the create form. Also, implement a handler to update the form's state when these inputs change.
In the create form, the code for existing images includes:
<Input
type="text"
placeholder="Add a caption..."
value={image.caption || ''}
onChange={(e) => handleExistingImageUpdate(sectionIndex, imageIndex, { caption:
e.target.value })}
/>
<Textarea
placeholder="Add details..."
value={image.details || ''}
onChange={(e) => handleExistingImageUpdate(sectionIndex, imageIndex, { details:
e.target.value })}
/>
So in the update form, we need to add these inputs and the corresponding handlers.
But the update form currently doesn't have a `handleExistingImageUpdate` function. So we need to implement that.
In the create form, `handleExistingImageUpdate` is defined as:
const handleExistingImageUpdate = (sectionIndex: number, imageIndex: number, updates: Partial<ImageUpdate>) => {
const currentImages = form.getValues(`sections.${sectionIndex}.images`) || [];
const updatedImages =
currentImages.map((img: ExistingImage, idx: number) =>
idx === imageIndex ? { ...img, ...updates } : img
);
form.setValue(`sections.${sectionIndex}.images`, updatedImages);
};
In the update form, we need to add this function. Then, in the existing images rendering, add the input fields and use this handler.
So, modifying the `opportunity-update-form.tsx`:
Add the handleExistingImageUpdate function:
const handleExistingImageUpdate = (
sectionIndex: number,
imageIndex: number,
updates: Partial<LocalImage>
) => {
const currentImages = form.getValues(`sections.${sectionIndex}.images`) || [];
const updatedImages =
currentImages.map((img, idx) =>
idx === imageIndex ? { ...img, ...updates } : img
);
form.setValue(`sections.${sectionIndex}.images`, updatedImages);
};
Wait, but in the update form, the existing images are part of the `images` array in the section, which includes the id. So when the user edits the caption or details, we need to update the corresponding image in the `images` array.
In the existing images loop, add the input fields:
For each existing image, under the image preview:
<div className="absolute bottom-0 left-0 right-0 p-6 space-y-3">
<Input
type="text"
placeholder="Caption"
value={image.caption || ''}
onChange={(e) => handleExistingImageUpdate(sectionIndex, imageIndex, { caption:
e.target.value })}
className="text-white bg-black/50"
/>
<Textarea
placeholder="Details"
value={image.details || ''}
onChange={(e) => handleExistingImageUpdate(sectionIndex, imageIndex, { details:
e.target.value })}
className="text-white bg-black/50"
/>
<Button ...>Remove</Button>
</div>
But wait, in the current code, the existing images are in a grid, and the caption is displayed as a paragraph. So we need to replace that with input fields.
Modify the existing images rendering in the update form's financial section and other sections.
For example, in the financial section's existing images:
{
existingImages.map((image, imageIndex) => (
<div key={image.image_url} ...>
<img ... />
<div ...>
<Input
type="text"
placeholder="Caption"
value={image.caption || ''}
onChange={(e) => handleExistingImageUpdate(sectionIndex, imageIndex, { caption:
e.target.value })}
/>
<Textarea
placeholder="Details"
value={image.details || ''}
onChange={(e) => handleExistingImageUpdate(sectionIndex, imageIndex, { details:
e.target.value })}
/>
<Button ...>Remove</Button>
</div>
</div>
))}
But the current structure uses absolute positioning and hover effects. Adding inputs might affect the layout. Alternatively, adjust the structure to include the inputs below the image.
But regardless, the main point is to add the input fields and connect them to the handleExistingImageUpdate function.
Once the frontend is updated to send the modified images with updated captions and details, the backend needs to process these changes.
In the backend's PUT route for updating opportunities, when processing images for a section, the current code:
1. Fetches existing images for the section.
2. Determines which images to delete (those not present in the new images array).
3. Determines new images to add (those not in existing).
4. Inserts new images.
But this approach doesn't update existing images. So even if the frontend sends existing images with updated captions, the backend won't update them because it only checks image_url.
To handle updates, the backend needs to:
For each image in the new images array:
- If it has an id, update the existing image.
- If it doesn't have an id, insert as new.
But how to check if an image exists? The image's id is the primary key. So in the PUT handler, for each image in the section's images:
Check if the image has an id. If yes, update the corresponding record. If not, insert a new one. Then, delete any images in the existing section that are not present in the new images array (based on id).
Wait, but in the current setup, the images in the form data for existing images should include their ids. So in the PUT handler, when processing the images:
- For each image in the new images array:
- If it has an id, update the image (caption, details, order_number).
- If it doesn't have an id, insert as new.
Then, after processing all images in the new array, delete any images in the existing section that are not in the new array (based on id).
This way, existing images can be updated, new ones added, and unused ones deleted.
Modifying the PUT route's image handling code:
In the section loop, after fetching existingSection:
// Handle images
const typedExistingSection = existingSection as SectionWithImages;
if (typedExistingSection.images) {
const existingImageIds =
typedExistingSection.images.…(img =>
img.id);
const newImages = section.images || [];
// Update or insert images
for (const image of newImages) {
if (
image.id) {
// Update existing image
await supabaseAdmin
.from('images')
.update({
caption: image.caption,
details: image.details,
order_number: image.order_number
})
.eq('id',
image.id);
} else {
// Insert new image
await supabaseAdmin
.from('images')
.insert({
section_id:
existingSection.id,
image_url: image.image_url,
caption: image.caption,
details: image.details,
order_number: image.order_number
});
}
}
// Delete images not present in newImages
const newImageIds =
newImages.map(img =>
img.id).filter(id => id !== undefined);
const imagesToDelete = typedExistingSection.images.filter(img => !newImageIds.includes(
img.id));
if (imagesToDelete.length > 0) {
await supabaseAdmin
.from('images')
.delete()
.in('id',
imagesToDelete.map(img =>
img.id));
}
}
This approach would handle updates, inserts, and deletes correctly.
However, the current code in the PUT route's image handling doesn't do this. It only deletes images not in the new list and adds new ones. So the backend code needs to be modified to handle updates.
So, in the PUT route's image handling section:
Original code:
// Handle images
const typedExistingSection = existingSection as SectionWithImages;
if (typedExistingSection.images) {
// Determine which images to remove
const existingImageUrls =
typedExistingSection.images.…((img) => img.image_url);
const newImageUrls = (section.images || []).map((img) => img.image_url);
const imagesToDelete = typedExistingSection.images.filter(
(img) => !newImageUrls.includes(img.image_url)
);
if (imagesToDelete.length > 0) {
await supabaseAdmin
.from("images")
.delete()
.in(
"id",
imagesToDelete.map((img) =>
img.id)
);
}
// Determine new images to add
const newImages = (section.images || []).filter(
(img) => !existingImageUrls.includes(img.image_url)
);
if (newImages.length > 0) {
await supabaseAdmin.from("images").insert(
newImages.map((image: ImageInput) => ({
section_id:
existingSection.id,
image_url: image.image_url,
caption: image.caption,
details: image.details,
order_number: image.order_number,
}))
);
}
} else if (section.images && section.images.length > 0) {
// If no existing images but new ones provided
await supabaseAdmin.from("images").insert(
section.images.map((image: ImageInput) => ({
section_id:
existingSection.id,
image_url: image.image_url,
caption: image.caption,
details: image.details,
order_number: image.order_number,
}))
);
}
This code compares image_urls to determine deletions and additions. However, this approach doesn't account for existing images that might have their captions/details updated but the same image_url. Also, if the image_url is the same but other fields change, this code won't update the existing image.
To fix this, the code should:
1. For each image in the new images array:
- If the image has an id (meaning it's an existing image), update its caption, details, and order_number.
- If it doesn't have an id, insert a new image.
2. Delete any images in the existing section that are not present in the new images array (based on id).
But this requires the frontend to send the image ids for existing images. Which it should, since when the form is initialized, the existing images are fetched with their ids. So in the update form's sections.images array, each image should have an id.
In the form schema for the update form (`opportunity-update-form.tsx`), the images are defined with an optional id:
images: z.array(
z.object({
id: z.string().optional(),
image_url: z.string(),
caption: z.string().optional(),
details: z.string().optional(),
order_number: z.number(),
})
).optional(),
So when the form is populated, existing images have their ids. When the user submits the form, the images in the sections include their ids. Therefore, the backend can check for the presence of id to determine if it's an existing image.
Modifying the PUT route's image handling code to:
For each image in the section's images array:
if (
image.id exists) {
update the image with that id
} else {
insert new image
}
Then, after processing all images, delete any images in the existing section that are not in the new images array (based on id).
So, updating the code in the PUT route:
// Handle images
const typedExistingSection = existingSection as SectionWithImages;
const existingImages = typedExistingSection.images || [];
const newImages = section.images || [];
// Collect existing image IDs
const existingImageIds =
existingImages.map(img =>
img.id);
// Process each new image
for (const image of newImages) {
if (
image.id) {
// Update existing image
await supabaseAdmin
.from('images')
.update({
caption: image.caption,
details: image.details,
order_number: image.order_number
})
.eq('id',
image.id);
} else {
// Insert new image
await supabaseAdmin
.from('images')
.insert({
section_id:
existingSection.id,
image_url: image.image_url,
caption: image.caption,
details: image.details,
order_number: image.order_number
});
}
}
// Determine which existing images are not in new images and delete them
const newImageIds =
newImages.map(img =>
img.id).filter((id): id is string => !!id);
const imagesToDelete = existingImages.filter(img => !newImageIds.includes(
img.id));
if (imagesToDelete.length > 0) {
await supabaseAdmin
.from('images')
.delete()
.in('id',
imagesToDelete.map(img =>
img.id));
}
This way, existing images are updated, new ones are added, and those not present are deleted.
This should handle updates to existing images' captions and details.