Conversation
Add optional logo upload field to partner create/update forms so ecosystem partners can have distinct branding in OTP emails. - Add imageUrl to Partner type - Add imageUrl to add/update partner mutation hooks - Add ImageUpload component to partner form with existing logo preview - Upload logo via useDashboardStorageUpload before sending URL to API - Add logo File field to partner form Zod schema Co-Authored-By: Claude Opus 4.6 <[email protected]>
…nd validation - Add file type validation (PNG, JPG, WEBP only) to logo upload schema - Add removeLogo boolean to form schema for explicit logo removal - Add X button on existing logo preview to allow clearing the logo - Fix update form to handle 3 states: new upload, explicit removal, preserve existing - Reset removeLogo when a new file is uploaded Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
WalkthroughAdds optional partner imageUrl and UI for uploading/removing a partner logo in the dashboard. Wires storage upload into add/update partner flows, validates image file (image/png, image/jpeg, image/webp ≤500KB), and includes imageUrl in create/update API payloads. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Form as Partner Form
participant Storage as Storage Upload
participant API as API Mutation
participant DB as Database
User->>Form: Select/logo file and submit
Form->>Storage: Upload image file
activate Storage
Storage-->>Form: imageUrl or error
deactivate Storage
alt Upload success
Form->>API: Create/Update partner with imageUrl
activate API
API->>DB: Persist partner (imageUrl optional)
DB-->>API: OK
API-->>Form: Success
deactivate API
Form->>User: Redirect / success UI
else Upload failure
Form->>User: Show upload error toast
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8668 +/- ##
=======================================
Coverage 52.72% 52.72%
=======================================
Files 934 934
Lines 62975 62975
Branches 4136 4136
=======================================
Hits 33205 33205
Misses 29670 29670
Partials 100 100
🚀 New features to boost your workflow:
|
size-limit report 📦
|
…hange - Show drag-and-drop only when no logo is set - When logo exists, show image with X (remove) and pencil (change) overlays - X button uses ghost variant with bg-background instead of destructive red - New file upload shows preview immediately via URL.createObjectURL Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx:
- Around line 160-165: The preview URL is being created on every render
(newFilePreview = URL.createObjectURL(logoFile)) and never revoked; update the
partner-form.client.tsx component to import useEffect and useState alongside
useId/useRef, create a state like [newFilePreview, setNewFilePreview] in the
component, and move the blob creation into a useEffect that depends on the
watched logo file (logoFile from form.watch) and removeLogo as needed—inside the
effect call URL.createObjectURL(logoFile) and setNewFilePreview, and in the
effect cleanup revoke the previous URL with URL.revokeObjectURL and clear state
so URLs are revoked when the file changes or the component unmounts.
| render={() => { | ||
| const removeLogo = form.watch("removeLogo"); | ||
| const logoFile = form.watch("logo"); | ||
| const newFilePreview = logoFile | ||
| ? URL.createObjectURL(logoFile) | ||
| : undefined; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd "partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 480
🏁 Script executed:
wc -l "apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 199
🏁 Script executed:
sed -n '140,180p' "apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 1523
🏁 Script executed:
head -50 "apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 1612
🏁 Script executed:
sed -n '50,120p' "apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 2693
🏁 Script executed:
sed -n '155,210p' "apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 2437
🏁 Script executed:
sed -n '200,250p' "apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 2428
🏁 Script executed:
rg "URL.createObjectURL" -A 3 -B 3Repository: thirdweb-dev/js
Length of output: 9859
🏁 Script executed:
sed -n '1,10p' "apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx"Repository: thirdweb-dev/js
Length of output: 476
Move blob URL creation to an effect with proper cleanup to prevent memory leaks.
URL.createObjectURL is called during every render and never revoked, causing blob URLs to leak and potentially reload the preview. Move preview creation to a useEffect with a cleanup function that revokes the URL when the file changes or the component unmounts. This also requires adding useEffect and useState to the React import (currently only useId and useRef are imported).
♻️ Suggested fix (creates/revokes preview URLs safely)
-import { useId, useRef } from "react";
+import { useEffect, useId, useRef, useState } from "react";
@@
const accessControlEnabled = form.watch("accessControlEnabled");
const serverVerifierEnabled = form.watch("serverVerifierEnabled");
const allowedOperationsEnabled = form.watch("allowedOperationsEnabled");
+ const logoFile = form.watch("logo");
+ const removeLogo = form.watch("removeLogo");
+ const [newFilePreview, setNewFilePreview] = useState<string | undefined>(
+ undefined,
+ );
+
+ useEffect(() => {
+ if (!logoFile) {
+ setNewFilePreview(undefined);
+ return;
+ }
+ const objectUrl = URL.createObjectURL(logoFile);
+ setNewFilePreview(objectUrl);
+ return () => URL.revokeObjectURL(objectUrl);
+ }, [logoFile]);
@@
- const removeLogo = form.watch("removeLogo");
- const logoFile = form.watch("logo");
- const newFilePreview = logoFile
- ? URL.createObjectURL(logoFile)
- : undefined;📝 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.
| render={() => { | |
| const removeLogo = form.watch("removeLogo"); | |
| const logoFile = form.watch("logo"); | |
| const newFilePreview = logoFile | |
| ? URL.createObjectURL(logoFile) | |
| : undefined; | |
| render={() => { |
🤖 Prompt for AI Agents
In
`@apps/dashboard/src/app/`(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/client/partner-form.client.tsx
around lines 160 - 165, The preview URL is being created on every render
(newFilePreview = URL.createObjectURL(logoFile)) and never revoked; update the
partner-form.client.tsx component to import useEffect and useState alongside
useId/useRef, create a state like [newFilePreview, setNewFilePreview] in the
component, and move the blob creation into a useEffect that depends on the
watched logo file (logoFile from form.watch) and removeLogo as needed—inside the
effect call URL.createObjectURL(logoFile) and setNewFilePreview, and in the
effect cleanup revoke the previous URL with URL.revokeObjectURL and clear state
so URLs are revoked when the file changes or the component unmounts.
https://linear.app/thirdweb/issue/BLD-547/partner-branding-for-ecosystem-wallet-email-otp
PR-Codex overview
This PR introduces support for uploading and managing partner logos in the dashboard application. It adds optional
imageUrlfields in relevant data structures and modifies forms to handle logo uploads, including validation for file types and sizes.Detailed summary
imageUrlproperty toecosystems.tsand related types.useUpdatePartneranduseAddPartnerhooks to includeimageUrl.partnerFormSchemato validate logo file uploads.AddPartnerFormandUpdatePartnerForm.partner-form.client.tsx.Summary by CodeRabbit