Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughProfile and KYC surfaces were replaced with a Didit integration: new Didit service and client, client hook and components, server API routes (create-session, callback, check-status, webhook, status), DB RLS policies/migrations, docs for Didit integration, and removal of the legacy multi-step KYC modal, related hooks, processors, and service. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User (Browser)
participant KYCCard as KYCCard (Client)
participant API_Create as /api/kyc/didit/create-session
participant Didit as Didit API
participant DB as Supabase (kyc_reviews)
participant API_Check as /api/kyc/didit/check-status
User->>KYCCard: Click "Start KYC"
KYCCard->>API_Create: POST { redirectUrl, metadata }
API_Create->>Didit: create session (email, callbackUrl, metadata)
Didit-->>API_Create: { sessionId, verificationUrl }
API_Create->>DB: Upsert pending kyc_reviews (notes include sessionId)
DB-->>API_Create: OK
API_Create-->>KYCCard: { sessionId, verificationUrl }
KYCCard->>User: Show redirect modal (countdown)
User->>Didit: Complete verification (redirect)
Didit->>API_Check: Callback/webhook (sessionId, status)
API_Check->>DB: Find by sessionId / update status & notes
DB-->>API_Check: OK
sequenceDiagram
participant Browser as Browser
participant ProfilePage as Server ProfilePage
participant SR as Supabase Service Role
participant Dashboard as ProfileDashboard
participant Toast as Toast
Browser->>ProfilePage: GET /profile?kyc=completed&verificationSessionId=X&status=approved
ProfilePage->>SR: Query kyc_reviews where notes LIKE sessionId
SR->>SR: Map Didit status -> internal kyc status
SR->>SR: Update or insert kyc_reviews (status, notes with callback metadata)
SR-->>ProfilePage: Confirmed
ProfilePage->>Dashboard: Render with kycCompleted=true
Dashboard->>Toast: Show status notification
Dashboard->>Browser: Remove query params / reload
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 42
🤖 Fix all issues with AI agents
In `@apps/web/app/`(routes)/profile/page.tsx:
- Around line 28-45: Extract the duplicated Didit-to-KYC mapping into a shared
utility function (e.g., export function mapDiditStatusToKYC(diditStatus:
string): 'pending' | 'approved' | 'rejected' | 'verified') in
~/lib/services/didit.ts and replace the inline switch in page.tsx (the block
assigning kycStatus from params.status) and the other copies in
webhook/route.ts, check-status/route.ts, and callback/route.ts with calls to
this new mapDiditStatusToKYC function; ensure you import the function where used
and preserve the same mapping behavior and return types.
- Around line 17-18: The console.log in ProfilePage that prints the full session
from getServerSession(nextAuthOption) exposes sensitive data; remove the
console.log or replace it with a sanitized log that redacts PII (e.g., remove
tokens, emails, user IDs) and only emits non-sensitive flags (like
isAuthenticated), or conditionally log only in non-production environments by
checking NODE_ENV before logging. Locate the call to getServerSession and the
log statement "📝 ProfilePage session:" and update it to either delete the log
or emit a redacted/ENV-guarded message.
- Around line 82-85: The JSON.parse on kycRecord.notes can throw on malformed
input; update the code that sets the notes constant to defensively parse
kycRecord.notes by wrapping JSON.parse in a try/catch (or use a safeParse
helper) so that on parse error you fall back to an empty object (or
kycRecord.notes if already an object) and optionally log the parsing error for
debugging; ensure you still handle the case where kycRecord.notes is already an
object and keep the resulting variable name notes.
- Around line 108-109: The console.log message "📝 Creating new KYC record for
declined status" is misleading because this branch runs for any missing record
regardless of status; update the log in the KYC record creation branch (the
console.log call) to a generic, accurate message that includes the actual status
(e.g., "Creating new KYC record for status: ${status}") so it reflects any
status handled when no existing record is found; ensure you reference the same
status variable used when creating the record.
In `@apps/web/app/api/kyc/didit/callback/route.ts`:
- Around line 13-19: Add rate limiting to the POST handler to prevent abuse of
the KYC callback: wrap or guard the exported async function POST (which calls
getServerSession(nextAuthOption) and returns NextResponse) with a rate-limiter
check (e.g., per-IP or per-user token-bucket using an in-memory/Redis store or
an existing middleware helper) that returns a 429 NextResponse when the limit is
exceeded; ensure the limiter runs before session validation so unauthorized or
anonymous requests are also throttled, and include a clear retry-after header in
the 429 response.
- Around line 31-48: Extract the duplicated Didit-to-KYC mapping into a single
exported utility: create mapDiditStatusToKycStatus(diditStatus: string):
KycStatus and KycStatus type ('pending'|'approved'|'rejected'|'verified'), move
the switch logic there, then replace the inline switch in callback/route.ts (the
kycStatus assignment), check-status/route.ts and webhook/route.ts to call
mapDiditStatusToKycStatus(status) instead; ensure all call sites import the new
function and update any local kycStatus type annotations to use the shared
KycStatus.
- Around line 99-102: The JSON.parse of kycRecord.notes can throw if notes is
malformed; wrap the parse in a small try/catch (or use a safeParse helper) when
computing the notes variable so a bad payload doesn’t bubble to the outer
handler: attempt JSON.parse(kycRecord.notes) and on error set notes to the
original kycRecord.notes (or {} as a safe fallback) and log the parse error with
context (e.g., kycRecord.id or user id) using the existing logger; update the
assignment site where notes is computed so malformed JSON is handled gracefully.
- Around line 51-57: The LIKE pattern can misbehave if verificationSessionId
contains SQL wildcard characters; in the supabase query using
supabaseServiceRole.from('kyc_reviews').select(...).like('notes', ...), escape
'%' and '_' in verificationSessionId (e.g., replace(/[\\%_]/g, '\\$&')) and
build the pattern with surrounding % before calling .like(..., pattern, {
escape: '\\' }) so the search matches the literal session id; alternatively, if
notes is JSON or you can change schema, use Supabase's contains operator for
exact JSON field matching or add a dedicated indexed column for
verificationSessionId and query that for exact matches (kyc_reviews, notes,
verificationSessionId, supabaseServiceRole).
In `@apps/web/app/api/kyc/didit/check-status/route.ts`:
- Around line 50-53: The JSON.parse call for kycRecord.notes is unguarded and
may throw on malformed data; update the assignment of notes in route.ts (the
block that sets the notes variable from kycRecord.notes) to wrap JSON.parse in a
try-catch, returning the original kycRecord.notes or a safe default (e.g.,
null/empty array/object) on parse failure and optionally logging the parse
error; ensure the code still handles both string and non-string notes branches
and preserves the notes variable shape used later.
- Around line 89-102: The update targets kyc_reviews by .eq('user_id',
session.user.id) which can race when a user has multiple records; modify the
earlier select that reads the most recent KYC record to also return the record
id (e.g., include id in the select) and then change the update call (the
supabaseServiceRole .from('kyc_reviews').update({...}) block) to filter by the
fetched record's id (use .eq('id', fetchedRecord.id)) instead of user_id so the
exact record is updated.
In `@apps/web/app/api/kyc/didit/create-session/route.ts`:
- Around line 40-56: If the upsert to kyc_reviews via
supabaseServiceRole.from('kyc_reviews').upsert returns dbError, abort the
handler instead of returning success: log the full dbError plus contextual
identifiers (session.user.id and diditSession.session_id/session_token), then
return a 500 response or throw an error so the request fails; update the error
branch that currently checks dbError to perform this fail-fast behavior rather
than silently continuing.
- Around line 24-25: Wrap the call to req.json() in its own try-catch inside the
create-session route handler so malformed or missing JSON yields a clear,
specific error (e.g., "Invalid JSON in request body") instead of bubbling to the
outer catch; after successful parsing validate that body contains redirectUrl
(and optional metadata) and return a 400 response with a helpful message when
validation fails, and include the caught parsing error details in the
processLogger/error response for easier debugging.
- Around line 15-72: Add per-user/IP rate limiting to the POST handler by
instantiating and using the existing RateLimiter before calling
createDiditSession: import or construct the RateLimiter used in other auth
flows, choose a key like session.user.id (fallback to client IP from req) and
enforce the same threshold/intervals as the OTP flow, call limiter.consume/check
at the top of POST and return NextResponse.json({ error: 'Too Many Requests' },
{ status: 429 }) when limits are exceeded; only proceed to call
createDiditSession and the supabaseServiceRole.upsert when the limiter permits,
and ensure you increment the limiter on success and handle limiter errors
gracefully so legitimate requests still receive a 500 on unexpected failures.
- Around line 40-51: The upsert against kyc_reviews using
supabaseServiceRole.upsert lacks a defined conflict target and the DB migration
(20250509000000_create_kyc_reviews_table.sql) does not have a UNIQUE on user_id,
so upsert may fail; either add a UNIQUE constraint for user_id in the
migration/schema to match your Drizzle model or modify the call in route.ts to
specify the conflict target (e.g., use .upsert(..., { onConflict: ['user_id']
})) so the upsert knows which column to deduplicate; update whichever you choose
(migration/schema or the upsert call in the file containing supabaseServiceRole
and .upsert) and reconcile the Drizzle schema (unique constraints on
kyc_status_id/reviewer_id) to ensure consistency.
In `@apps/web/app/api/kyc/didit/webhook/route.ts`:
- Around line 106-110: The code blindly JSON.parse's kycRecord.notes into notes,
which will throw on malformed JSON; update the parsing logic in route.ts (around
the kycRecord and notes handling) to wrap JSON.parse in a try-catch, fall back
to a safe default (e.g., the original string or an empty object) when parsing
fails, and log or record a warning/error so the webhook doesn't crash; ensure
the branch that currently does typeof kycRecord.notes === 'string' uses this
defensive parse for the string case.
- Around line 46-48: Wrap the JSON.parse(rawBody) call in a try-catch inside the
webhook handler so malformed payloads don't throw; attempt to parse rawBody into
the DiditWebhookEvent (jsonBody) and on SyntaxError return a clear 400 response
(and log the rawBody and error) instead of letting an exception bubble up to a
500. Ensure the catch only handles parsing errors and retains existing flow for
valid jsonBody handling.
- Around line 95-104: The lookup using .like('notes',
`%${jsonBody.session_id}%`) on the notes JSON/text field is inefficient; add a
dedicated indexed column (e.g., didit_session_id) on the kyc_reviews table,
backfill existing rows by extracting session IDs from notes, and create an
index/unique constraint as appropriate. Update the webhook handler query in
route.ts to use .eq('didit_session_id', jsonBody.session_id) instead of the LIKE
search, and modify any KYC create/update code paths to populate didit_session_id
when records are created or updated so future queries are fast and reliable.
In `@apps/web/app/api/kyc/status/route.ts`:
- Around line 20-26: The query currently selects the unused "notes" field which
is unnecessary; update the Supabase query in the route where supabaseServiceRole
is used (the .from('kyc_reviews') ... .maybeSingle() call that assigns { data:
kycRecord, error }) to remove "notes" from the .select(...) list (or
alternatively include notes in the returned payload if you actually need it) so
only fields consumed (status and updated_at) are fetched, reducing payload and
improving efficiency.
- Around line 11-27: The GET handler in route.ts lacks rate limiting and can be
easily polled; add a rate limit check at the start of the exported async GET
function (before the supabaseServiceRole DB call) that keys limits by
authenticated user id (session.user.id) or request IP and returns
NextResponse.json({ error: 'Too Many Requests' }, { status: 429 }) when
exceeded; implement the limiter using a shared backend store (Redis) or an
existing app util (e.g., a rateLimit or limiter helper) to perform token-bucket
or fixed-window checks, decrement/record usage on each request, and ensure the
limiter is applied prior to calling getServerSession or right after session
validation so excessive requests never hit
supabaseServiceRole.from('kyc_reviews').
In `@apps/web/components/sections/profile/cards/account-info-card.tsx`:
- Around line 52-54: Replace the full page reload with Next's router refresh to
avoid a hard reload: in account-info-card.tsx, instead of calling
window.location.reload() after toast.success(...) and setIsEditing(false),
import and use useRouter from next/navigation (e.g., const router = useRouter())
and call router.refresh() to revalidate server data; ensure you remove the
window.location.reload() call and keep setIsEditing(false) as part of the
success flow.
- Around line 35-39: The slug check only ensures non-empty; update the
validation in account-info-card (around the nextSlug = (formData.get('slug') as
string) code) to enforce allowed characters, length, and reserved-word rules:
normalize with trim().toLowerCase(), then validate against a slug regex (e.g.,
lowercase letters, numbers, hyphens, no leading/trailing hyphen) and enforce
min/max length, and check against a small reservedWords list (e.g.,
"admin","settings"); on validation failure replace the simple toast.error('Slug
cannot be empty') with a descriptive toast error indicating the specific reason
(invalid characters/length/reserved word) and return.
In `@apps/web/components/sections/profile/cards/kyc-card.tsx`:
- Around line 23-55: KYCCard currently calls refreshStatus() in multiple
useEffect hooks even when userId is falsy, causing unnecessary API calls; update
each effect that calls refreshStatus (the mount effect, the kyc-status-updated
listener effect, and the shouldRefresh effect) to first check that userId is
truthy (or userId?.length > 0) before invoking refreshStatus(), and ensure
useDiditKYC(userId || '') remains unchanged so hooks rules are honored.
In `@apps/web/components/sections/profile/cards/personal-info-card.tsx`:
- Line 25: The personal-info-card component declares an unused prop named
`_email`; either remove `_email` from the component props and any callers to
avoid passing a dead prop, or keep it but add a short comment above the `_email`
prop declaration explaining its intended future use (e.g., reserved for future
email display/logic) so its presence is documented; locate the prop in the
PersonalInfoCard (or similarly named component) props/interface and update the
definition and all usages accordingly.
- Around line 54-56: Replace the full page reload with Next.js App Router's
router.refresh(): in the PersonalInfoCard component (where you call
toast.success(...) and setIsEditing(false)), remove window.location.reload() and
instead obtain the router from next/navigation (useRouter or useRouter() hook)
and call router.refresh() to revalidate server data without a full reload,
preserving client state and scroll position.
- Around line 131-139: The image URL input (Input with id/name "image_url")
currently allows any scheme; add client-side validation using the project's
validate-url utility (e.g., import and call the validate function from
validate-url.ts) to reject unsafe schemes like data: and javascript:, require
http/https, and optionally enforce an allowlist of domains before allowing form
submission; wire validation into the form submit handler to prevent saving when
invalid and display a field-level error state/message next to the "image_url"
Input so users must correct the URL before it is stored.
In `@apps/web/components/sections/profile/cards/role-card.tsx`:
- Around line 33-56: In the handleRoleChange function, remove the redundant
setSelectedRole(newRole) call on line 44 since the page reload immediately
following it will make this state update invisible to the user. Additionally,
replace the window.location.reload() call with Next.js router's refresh() method
by importing useRouter from 'next/navigation' and calling useRouter().refresh()
instead to provide a smoother user experience that preserves client state
without a full page reload.
In `@apps/web/components/sections/profile/cards/wallet-card.tsx`:
- Around line 57-64: The rotating Wallet icon inside CardTitle is using an
infinite 20s rotation which can be distracting and costly; update the motion.div
around the Wallet (in wallet-card.tsx) to use a subtler or hover-triggered
animation instead of continuous rotation—either remove the repeat and reduce
rotation to a small angle/shorter duration or swap to a hover animation (use
whileHover/whileTap on the motion.div or parent CardTitle to animate
scale/rotate slightly) so the Wallet component only animates on user interaction
rather than continuously.
In `@apps/web/components/sections/profile/modals/kyc-redirect-modal.tsx`:
- Around line 65-67: handleRedirectNow currently sets window.location.href to
verificationUrl without checking it; if verificationUrl is empty this will
reload the current page. Update handleRedirectNow to guard against empty or
falsy verificationUrl (check verificationUrl.trim() or
Boolean(verificationUrl)), and if it's falsy return early (or show an
error/disable flow). Ensure you reference the existing handleRedirectNow
function and verificationUrl variable and keep the existing redirect behavior
when verificationUrl is valid.
In `@apps/web/components/sections/profile/profile-dashboard.tsx`:
- Around line 86-160: The code currently clears URL params via
window.history.replaceState immediately after firing the fetch in the useEffect
that handles KYC callback, which loses the retry path on failure; change the
flow so you only call window.history.replaceState({}, '', '/profile') and
window.location.reload() after a successful API response (res.ok and
result.status handled), and on non-ok or fetch.catch paths show a toast.error
with the failure message (and do not clear params) so users can retry or see
feedback; modify the block around fetch('/api/kyc/didit/callback', ...) and move
the replaceState/reload into the success branch while adding toast.error in the
else and catch branches (references: useEffect,
fetch('/api/kyc/didit/callback'), window.history.replaceState,
window.location.reload, toast.success/toast.error).
In `@apps/web/components/sections/profile/profile-header.tsx`:
- Around line 27-34: Replace the local getAvatarFallback implementation in the
ProfileHeader component with the shared utility: remove the duplicated function
and add an import for getAvatarFallback from the shared utils, then use that
imported getAvatarFallback wherever the local one was used (no API changes
required). Ensure you delete the local const getAvatarFallback = (...)
declaration and keep existing call sites intact so the more robust utility
(which handles empty strings, extra spaces, and single-word names) is used
instead.
In `@apps/web/components/shared/index.ts`:
- Line 1: The barrel file index.ts currently contains only a comment and should
either be removed or updated to export the existing shared components; pick one:
(A) delete index.ts if you don't want a shared namespace, or (B) convert it into
a proper barrel by adding named exports for each component in the folder (e.g.,
export { default as BenefitsItems } from './benefits-items'; export { default as
CtaButtons } from './cta-buttons'; export { default as FeatureCard } from
'./feature-card'; export { default as NotificationBell } from
'./notification-bell'; export { default as Pagination } from './pagination';
export { default as ProjectCard } from './project-card'; etc.), following the
project’s named export pattern and exporting every component in the directory to
keep the barrel consistent.
In `@apps/web/hooks/use-didit-kyc.ts`:
- Around line 21-27: The hook parameter userId is never used beyond an effect
guard in useDiditKYC; either remove the unused parameter from the function
signature and update all callers to call useDiditKYC() (and adjust any types),
or make the parameter meaningful by including it in the API request (e.g., add
userId as a query param or body when calling '/api/kyc/status') and update the
fetch logic in useDiditKYC to send that value; also update any tests or
consumers accordingly so the hook's behavior and signature remain consistent.
- Around line 116-145: createSession is not memoized which can cause unnecessary
re-renders when passed to children; wrap the createSession function in React's
useCallback to provide referential stability, e.g. change its declaration to
useCallback(async (redirectUrl?: string) => { ... }, [loadKYCStatus, /* any
other external deps like checkStatusFromDidit or auth state used inside */])
while preserving the Promise<CreateSessionResponse> return type and keeping the
same error handling and call to loadKYCStatus.
- Around line 56-72: The effect currently polls every 5s unconditionally; update
the useEffect that references loadKYCStatus and setKycStatus so it only starts
the setInterval when the current KYC status is null or 'pending' (i.e., check
kycStatus.status before creating pollInterval), and ensure the interval is
cleared whenever status becomes a terminal state ('approved' or 'rejected') or
when userId changes/unmounts; keep loadKYCStatus usage but add the conditional
guard and clearInterval in the cleanup and on status transitions to stop
unnecessary requests.
In `@docs/kyc-integration/architecture.md`:
- Around line 53-57: Update the component map to clarify callback ownership:
change the Profile Page entry (/app/(routes)/profile/page.tsx) to state that the
client-side dashboard reads callback URL params and invokes the server API
rather than the server page performing the DB update directly, and explicitly
note that the server-side endpoints (/api/kyc/didit/create-session,
/api/kyc/didit/callback, /api/kyc/didit/webhook) are responsible for creating
sessions and processing incoming callback/webhook requests; ensure the wording
makes it clear that /api/kyc/didit/callback is called by the client dashboard to
update status while webhooks to /api/kyc/didit/webhook come from Didit.
- Around line 68-73: Clarify which user ID claim is authoritative for the
kyc_reviews table RLS by explicitly stating whether auth.uid(), next_auth.uid(),
or current_auth_user_id() is used (and why), and update the Database Layer
section and any RLS policy examples to reference that specific claim; mention
how to map or migrate existing policies if switching claims and add a short
example RLS policy line that uses the chosen function (e.g., using
current_auth_user_id() in row-level checks for SELECT/UPDATE on kyc_reviews).
In `@docs/kyc-integration/components.md`:
- Around line 152-163: The fenced-code block for the component hierarchy lacks a
language tag, causing a lint warning; update the block that lists ProfilePage,
ProfileDashboard, ProfileHeader, Quick Stats Cards (WalletCard, KYCCard,
RoleCard) and Tabs to include a language identifier (e.g., use ```text)
immediately after the opening backticks so the hierarchy block is fenced as
```text ... ``` to satisfy the linter and improve readability.
In `@docs/kyc-integration/database-schema.md`:
- Line 50: The header "Row Level Security (RLS) Policies" should be changed to
use standard hyphenation; update the markdown heading to "Row-Level Security
(RLS) Policies" (and search for other occurrences of "Row Level Security" in
this document like section titles or inline mentions to apply the same
hyphenation) so the terminology is consistent throughout.
- Around line 64-74: Add blank lines before and after each fenced code block so
the SQL policy blocks render correctly; specifically, ensure the ```sql``` fence
enclosing the CREATE POLICY "Whitelisted admin can view all KYC reviews" block
(and the other SQL policy/code blocks referenced later) has an empty line
preceding the opening ``` and an empty line following the closing ```, updating
the code blocks around the policy names and other fenced SQL snippets (e.g., the
blocks at the other noted sections) to follow this spacing convention.
In `@docs/kyc-integration/README.md`:
- Around line 9-15: Update the README table of contents to use relative file
paths instead of in-page anchors: replace each anchor link like
[Architecture](`#architecture`), [Configuration](`#configuration`), [API
Endpoints](`#api-endpoints`), [Database Schema](`#database-schema`),
[Components](`#components`), [User Flow](`#user-flow`),
[Troubleshooting](`#troubleshooting`) with links pointing to the actual docs files
(e.g., architecture.md, configuration.md, api-endpoints.md, database-schema.md,
components.md, user-flow.md, troubleshooting.md) so clicks navigate to the
corresponding files rather than anchors.
In `@docs/kyc-integration/troubleshooting.md`:
- Around line 279-281: Replace the bare URL after the "Didit Documentation"
label with a proper markdown link; locate the text "Didit Documentation:
https://docs.didit.me" and change it to "Didit Documentation: [Didit
Documentation](https://docs.didit.me)" (or simply "[Didit
Documentation](https://docs.didit.me)") so the documentation URL renders as a
clickable markdown link while leaving the surrounding bullet items unchanged.
In `@docs/kyc-integration/user-flow.md`:
- Around line 59-61: The markdown code block containing the example URL lacks a
language specifier; update the fenced block around
"https://kindfi.org/profile?kyc=completed&status=Approved&verificationSessionId=xxx"
to include a language tag such as "text" or "plaintext" (e.g., change ``` to
```text) so the block renders consistently and satisfies linting.
| const notes = | ||
| typeof kycRecord.notes === 'string' | ||
| ? JSON.parse(kycRecord.notes) | ||
| : kycRecord.notes || {} |
There was a problem hiding this comment.
Consider defensive JSON parsing for the notes field.
If kycRecord.notes contains malformed JSON, JSON.parse will throw an unhandled exception, potentially breaking the profile page load.
🛡️ Suggested fix
- const notes =
- typeof kycRecord.notes === 'string'
- ? JSON.parse(kycRecord.notes)
- : kycRecord.notes || {}
+ let notes = {}
+ try {
+ notes =
+ typeof kycRecord.notes === 'string'
+ ? JSON.parse(kycRecord.notes)
+ : kycRecord.notes || {}
+ } catch {
+ console.warn('Failed to parse KYC notes, using empty object')
+ }📝 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 notes = | |
| typeof kycRecord.notes === 'string' | |
| ? JSON.parse(kycRecord.notes) | |
| : kycRecord.notes || {} | |
| let notes = {} | |
| try { | |
| notes = | |
| typeof kycRecord.notes === 'string' | |
| ? JSON.parse(kycRecord.notes) | |
| : kycRecord.notes || {} | |
| } catch { | |
| console.warn('Failed to parse KYC notes, using empty object') | |
| } |
🤖 Prompt for AI Agents
In `@apps/web/app/`(routes)/profile/page.tsx around lines 82 - 85, The JSON.parse
on kycRecord.notes can throw on malformed input; update the code that sets the
notes constant to defensively parse kycRecord.notes by wrapping JSON.parse in a
try/catch (or use a safeParse helper) so that on parse error you fall back to an
empty object (or kycRecord.notes if already an object) and optionally log the
parsing error for debugging; ensure you still handle the case where
kycRecord.notes is already an object and keep the resulting variable name notes.
| } | ||
| ``` | ||
|
|
||
| ## Row Level Security (RLS) Policies |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Minor grammar improvement.
Consider using "Row-Level Security" (hyphenated) for consistency with standard terminology.
-## Row Level Security (RLS) Policies
+## Row-Level Security (RLS) Policies📝 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.
| ## Row Level Security (RLS) Policies | |
| ## Row-Level Security (RLS) Policies |
🧰 Tools
🪛 LanguageTool
[grammar] ~50-~50: Use a hyphen to join words.
Context: ...string decision?: object } ``` ## Row Level Security (RLS) Policies ### SELEC...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents
In `@docs/kyc-integration/database-schema.md` at line 50, The header "Row Level
Security (RLS) Policies" should be changed to use standard hyphenation; update
the markdown heading to "Row-Level Security (RLS) Policies" (and search for
other occurrences of "Row Level Security" in this document like section titles
or inline mentions to apply the same hyphenation) so the terminology is
consistent throughout.
| 2. **"Whitelisted admin can view all KYC reviews"** | ||
| ```sql | ||
| CREATE POLICY "Whitelisted admin can view all KYC reviews" | ||
| ON public.kyc_reviews | ||
| FOR SELECT | ||
| TO authenticated | ||
| USING ( | ||
| EXISTS (SELECT 1 FROM public.kyc_admin_whitelist w | ||
| WHERE w.user_id = public.current_auth_user_id()) | ||
| ); | ||
| ``` |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding blank lines around fenced code blocks.
Per markdown lint rules, fenced code blocks should be surrounded by blank lines for better rendering consistency across markdown parsers.
📝 Example fix for lines 64-74
2. **"Whitelisted admin can view all KYC reviews"**
+
```sql
CREATE POLICY "Whitelisted admin can view all KYC reviews"
...
```
+Also applies to: 90-101, 120-134
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
65-65: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
In `@docs/kyc-integration/database-schema.md` around lines 64 - 74, Add blank
lines before and after each fenced code block so the SQL policy blocks render
correctly; specifically, ensure the ```sql``` fence enclosing the CREATE POLICY
"Whitelisted admin can view all KYC reviews" block (and the other SQL
policy/code blocks referenced later) has an empty line preceding the opening ```
and an empty line following the closing ```, updating the code blocks around the
policy names and other fenced SQL snippets (e.g., the blocks at the other noted
sections) to follow this spacing convention.
| - [Architecture](#architecture) | ||
| - [Configuration](#configuration) | ||
| - [API Endpoints](#api-endpoints) | ||
| - [Database Schema](#database-schema) | ||
| - [Components](#components) | ||
| - [User Flow](#user-flow) | ||
| - [Troubleshooting](#troubleshooting) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider updating anchor links to file paths for cross-file navigation.
The table of contents uses anchor links (e.g., #architecture), but based on the PR context, these sections exist as separate documentation files (architecture.md, configuration.md, etc.). We could enhance this by updating the links to reference the actual file paths for proper navigation.
📝 Suggested link format
## Table of Contents
-- [Architecture](`#architecture`)
-- [Configuration](`#configuration`)
-- [API Endpoints](`#api-endpoints`)
-- [Database Schema](`#database-schema`)
-- [Components](`#components`)
-- [User Flow](`#user-flow`)
-- [Troubleshooting](`#troubleshooting`)
+- [Architecture](./architecture.md)
+- [Configuration](./configuration.md)
+- [API Endpoints](./api-endpoints.md)
+- [Database Schema](./database-schema.md)
+- [Components](./components.md)
+- [User Flow](./user-flow.md)
+- [Troubleshooting](./troubleshooting.md)📝 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.
| - [Architecture](#architecture) | |
| - [Configuration](#configuration) | |
| - [API Endpoints](#api-endpoints) | |
| - [Database Schema](#database-schema) | |
| - [Components](#components) | |
| - [User Flow](#user-flow) | |
| - [Troubleshooting](#troubleshooting) | |
| - [Architecture](./architecture.md) | |
| - [Configuration](./configuration.md) | |
| - [API Endpoints](./api-endpoints.md) | |
| - [Database Schema](./database-schema.md) | |
| - [Components](./components.md) | |
| - [User Flow](./user-flow.md) | |
| - [Troubleshooting](./troubleshooting.md) |
🤖 Prompt for AI Agents
In `@docs/kyc-integration/README.md` around lines 9 - 15, Update the README table
of contents to use relative file paths instead of in-page anchors: replace each
anchor link like [Architecture](`#architecture`), [Configuration](`#configuration`),
[API Endpoints](`#api-endpoints`), [Database Schema](`#database-schema`),
[Components](`#components`), [User Flow](`#user-flow`),
[Troubleshooting](`#troubleshooting`) with links pointing to the actual docs files
(e.g., architecture.md, configuration.md, api-endpoints.md, database-schema.md,
components.md, user-flow.md, troubleshooting.md) so clicks navigate to the
corresponding files rather than anchors.
| - **Didit Support**: [support@didit.me](mailto:support@didit.me) | ||
| - **Didit Documentation**: https://docs.didit.me | ||
| - **Internal Team**: Contact project leads for integration issues |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider formatting the bare URL as a markdown link.
The Didit documentation URL should be formatted as a proper markdown link for consistency and better rendering.
📝 Suggested fix
- **Didit Support**: [support@didit.me](mailto:support@didit.me)
-- **Didit Documentation**: https://docs.didit.me
+- **Didit Documentation**: [https://docs.didit.me](https://docs.didit.me)
- **Internal Team**: Contact project leads for integration issues📝 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.
| - **Didit Support**: [support@didit.me](mailto:support@didit.me) | |
| - **Didit Documentation**: https://docs.didit.me | |
| - **Internal Team**: Contact project leads for integration issues | |
| - **Didit Support**: [support@didit.me](mailto:support@didit.me) | |
| - **Didit Documentation**: [https://docs.didit.me](https://docs.didit.me) | |
| - **Internal Team**: Contact project leads for integration issues |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
280-280: Bare URL used
(MD034, no-bare-urls)
🤖 Prompt for AI Agents
In `@docs/kyc-integration/troubleshooting.md` around lines 279 - 281, Replace the
bare URL after the "Didit Documentation" label with a proper markdown link;
locate the text "Didit Documentation: https://docs.didit.me" and change it to
"Didit Documentation: [Didit Documentation](https://docs.didit.me)" (or simply
"[Didit Documentation](https://docs.didit.me)") so the documentation URL renders
as a clickable markdown link while leaving the surrounding bullet items
unchanged.
| ``` | ||
| https://kindfi.org/profile?kyc=completed&status=Approved&verificationSessionId=xxx | ||
| ``` |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding a language specifier to the code block.
Adding a language specifier (e.g., text or plaintext) improves rendering consistency and satisfies linting rules.
📝 Suggested fix
-```
+```text
https://kindfi.org/profile?kyc=completed&status=Approved&verificationSessionId=xxx</details>
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>
59-59: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
In @docs/kyc-integration/user-flow.md around lines 59 - 61, The markdown code
block containing the example URL lacks a language specifier; update the fenced
block around
"https://kindfi.org/profile?kyc=completed&status=Approved&verificationSessionId=xxx"
to include a language tag such as "text" or "plaintext" (e.g., change ``` to
| // Show success message if user just completed KYC and trigger status refresh | ||
| useEffect(() => { | ||
| if (kycCompleted) { | ||
| // Check URL params for status | ||
| const urlParams = new URLSearchParams(window.location.search) | ||
| const status = urlParams.get('status') | ||
| const sessionId = urlParams.get('verificationSessionId') | ||
|
|
||
| if (status && sessionId) { | ||
| // Show appropriate message based on raw status from URL first | ||
| // This gives immediate feedback before API call completes | ||
| const normalizedStatus = status.replace(/\+/g, ' ') // Handle URL encoding | ||
| if (normalizedStatus === 'Approved') { | ||
| toast.success( | ||
| 'KYC verification approved! Your status is being updated...', | ||
| ) | ||
| } else if (normalizedStatus === 'Declined') { | ||
| toast.error( | ||
| 'KYC verification was declined. Please review the requirements and try again.', | ||
| ) | ||
| } else if ( | ||
| normalizedStatus === 'In Review' || | ||
| normalizedStatus === 'In Progress' | ||
| ) { | ||
| toast.info( | ||
| "KYC verification is under review. We will notify you once it's complete.", | ||
| ) | ||
| } else { | ||
| toast.info('KYC verification completed! Checking your status...') | ||
| } | ||
|
|
||
| // Update status via API | ||
| fetch('/api/kyc/didit/callback', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({ | ||
| verificationSessionId: sessionId, | ||
| status: normalizedStatus, | ||
| }), | ||
| }) | ||
| .then(async (res) => { | ||
| if (res.ok) { | ||
| const result = await res.json() | ||
| // Update toast with final status if different | ||
| if ( | ||
| result.status === 'approved' || | ||
| result.status === 'verified' | ||
| ) { | ||
| toast.success( | ||
| 'KYC verification approved! Your status has been updated.', | ||
| ) | ||
| } else if (result.status === 'rejected') { | ||
| toast.error( | ||
| 'KYC verification was declined. Please review the requirements and try again.', | ||
| ) | ||
| } else if (result.status === 'pending') { | ||
| toast.info( | ||
| "KYC verification is under review. We will notify you once it's complete.", | ||
| ) | ||
| } | ||
| // Reload page to show updated status (server already updated database) | ||
| window.location.reload() | ||
| } else { | ||
| console.error('Failed to update KYC status via API') | ||
| } | ||
| }) | ||
| .catch((error) => { | ||
| console.error('Failed to update KYC status:', error) | ||
| }) | ||
|
|
||
| // Remove query params from URL immediately | ||
| window.history.replaceState({}, '', '/profile') | ||
| } else { |
There was a problem hiding this comment.
Helpful callback UX; avoid clearing params before the update succeeds.
Right now the URL is sanitized immediately, so a failed callback leaves users without a retry path or feedback. Consider only clearing params and reloading after a successful update, and surface a toast on failures.
🔧 Suggested fix
fetch('/api/kyc/didit/callback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
verificationSessionId: sessionId,
status: normalizedStatus,
}),
})
.then(async (res) => {
if (res.ok) {
const result = await res.json()
// Update toast with final status if different
if (
result.status === 'approved' ||
result.status === 'verified'
) {
toast.success(
'KYC verification approved! Your status has been updated.',
)
} else if (result.status === 'rejected') {
toast.error(
'KYC verification was declined. Please review the requirements and try again.',
)
} else if (result.status === 'pending') {
toast.info(
"KYC verification is under review. We will notify you once it's complete.",
)
}
- // Reload page to show updated status (server already updated database)
- window.location.reload()
+ // Remove query params only after a successful update
+ window.history.replaceState({}, '', '/profile')
+ // Reload page to show updated status (server already updated database)
+ window.location.reload()
} else {
console.error('Failed to update KYC status via API')
+ toast.error('Failed to update KYC status. Please try again.')
}
})
.catch((error) => {
console.error('Failed to update KYC status:', error)
+ toast.error('Failed to update KYC status. Please try again.')
})
-
- // Remove query params from URL immediately
- window.history.replaceState({}, '', '/profile')🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/profile-dashboard.tsx` around lines 86 -
160, The code currently clears URL params via window.history.replaceState
immediately after firing the fetch in the useEffect that handles KYC callback,
which loses the retry path on failure; change the flow so you only call
window.history.replaceState({}, '', '/profile') and window.location.reload()
after a successful API response (res.ok and result.status handled), and on
non-ok or fetch.catch paths show a toast.error with the failure message (and do
not clear params) so users can retry or see feedback; modify the block around
fetch('/api/kyc/didit/callback', ...) and move the replaceState/reload into the
success branch while adding toast.error in the else and catch branches
(references: useEffect, fetch('/api/kyc/didit/callback'),
window.history.replaceState, window.location.reload, toast.success/toast.error).
There was a problem hiding this comment.
@coderabbitai, create an issue to address this one
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Create GitHub issue for KYC callback UX improvement
gh issue create \
--repo kindfi-org/kindfi \
--title "Improve KYC callback error handling and retry UX in ProfileDashboard" \
--body "## Description
Currently in the ProfileDashboard component, URL parameters are cleared immediately after initiating the KYC callback API request, before waiting for the response. This creates a poor user experience when the API call fails:
- Users lose the retry path if the callback fails
- No error feedback is provided to the user
- The verification session ID and status are removed from the URL before we know if the update succeeded
## Current Behavior
In \`apps/web/components/sections/profile/profile-dashboard.tsx\`, the code clears URL params via \`window.history.replaceState({}, '', '/profile')\` immediately after firing the fetch request, regardless of the API response.
## Expected Behavior
- Only clear URL params after a successful API response
- Show error toast messages when the callback fails
- Allow users to retry by keeping the URL params on failure
- Reload the page only after successful status update
## Context
This issue was identified during code review of PR `#768`.
Original review comment: https://github.com/kindfi-org/kindfi/pull/768#discussion_r2715836503
Related PR: https://github.com/kindfi-org/kindfi/pull/768
Requested by: `@Bran18`
## Impact
- Affects user experience when KYC verification callback fails
- Users cannot retry failed callbacks without going through the entire verification flow again" \
--assignee Bran18 \
--label "bug,enhancement,webapp"
Length of output: 47
@Bran18, I've created issue #771 to track this improvement: #771
The issue covers:
- Moving URL param clearing to the success branch only
- Adding error toasts for failed callbacks
- Preserving retry path when the API call fails
- Ensuring page reload happens only after successful updates
The issue has been assigned to you and includes backlinks to this PR and the original review comment for context.
🧬 Code Graph Analysis Results
-
File: apps/web/components/sections/profile/modals/role-selection-modal.tsx
Range: 27-164
Description: RoleSelectionModal component. Exposes open, onOpenChange, userId, currentRole, onRoleSelected. Internally manages isSaving and selectedRole state, provides handleRoleSelect to update the user's role via Supabase, show success toast, invoke callback, close modal, and reload page. Includes error handling and loading state UI. -
File: apps/web/components/sections/profile/cards/kyc-card.tsx
Range: 30-350
Description: KYCCard component. Uses useDiditKYC hook to fetch and refresh KYC status, supports starting KYC flow, shows dynamic status badge, messages, and a redirect modal for verification. Handles edge cases like missing userId and error states; includes UI logic for showing Start Verification button, loading, and error states. -
File: apps/web/components/sections/profile/views/creator-profile.tsx
Range: 40-339
Description: CreatorProfile component. Fetches creator's projects, computes escrow balances via useEscrow, polls balances, and derives project balance metrics for display (raised, percentageComplete). Renders header, stats, and project cards; includes balance-fetching logic and memoization of derived data. -
File: apps/web/components/sections/profile/views/donor-profile.tsx
Range: 50-545
Description: DonorProfile component. Fetches projects supported by the donor, obtains escrow balances, polls balances, and computes totals (raised, impact, active/completed counts). Includes donation sync action, balance calculations, and rendering of sections such as Supported Projects and Rewards. -
File: apps/web/components/sections/profile/cards/wallet-card.tsx
Range: 23-229
Description: WalletCard component. Displays Smart Account and External Wallet sections, with copy-to-clipboard actions, status badges, and connect/external wallet behavior. Manages local copy state, handles copying addresses, and provides UI for both addresses with appropriate styling. -
File: apps/web/components/sections/profile/cards/personal-info-card.tsx
Range: 28-169
Description: PersonalInfoCard component. Allows editing of display name, bio, and avatar URL with an isEditing toggle, form submission to update profile in Supabase, toasts for success/failure, and page reload on success. Includes loading state management for save. -
File: apps/web/components/sections/profile/cards/account-info-card.tsx
Range: 24-178
Description: AccountInfoCard component. Allows editing of the slug (handle) with an inline form, updates slug via API, handles errors, toasts, and page reloads on success. Includes read-only Email and Member Since fields and editing toggle. -
File: apps/web/components/sections/profile/profile-header.tsx
Range: 20-121
Description: ProfileHeader component. Displays user avatar, name, email, membership date, and role label chip. Includes avatar fallback logic, gradient/animation accents, and role-label determination for display. Used as the top header in profile dashboard.
Notes:
- The above snippets are identified as potentially relevant to handling the user’s profile, role selection, KYC flow, wallet information, and profile editing, which could inform creating an issue around user onboarding, role assignment, KYC flow reliability, or profile data editing.
🧠 Learnings used
Learnt from: CR
Repo: kindfi-org/kindfi PR: 0
File: .cursor/rules/smart-account-kit.mdc:0-0
Timestamp: 2026-01-15T04:28:05.783Z
Learning: Applies to **/*.{ts,tsx} : Call kit.connectWallet() on page load to silently restore sessions, and only show connection UI if result is null
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 738
File: apps/kyc-server/src/pages/dashboard/customers.tsx:1-3
Timestamp: 2025-09-15T09:24:16.242Z
Learning: In the Kindfi KYC server, the /dashboard/customers route was legacy and has been replaced with /dashboard/users. The users.tsx page component should only contain UserMetricsGrid and UserTable components without header content (h1/p elements).
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 128
File: apps/web/hooks/stellar/use-stellar.ts:11-41
Timestamp: 2025-02-08T06:59:02.588Z
Learning: Storage improvements in this codebase (like IndexedDB implementation) should be coordinated with the NextAuth system updates (issue `#44`) to ensure proper handling of device info and user authentication while maintaining good UX and data management.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 128
File: apps/web/hooks/stellar/use-stellar.ts:11-41
Timestamp: 2025-02-08T06:59:02.588Z
Learning: In this codebase, storage improvements (like IndexedDB) should be implemented after the NextAuth system update (issue `#44`) is complete, focusing on UX and data management while leveraging the updated authentication system.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 128
File: apps/web/hooks/stellar/use-stellar.ts:11-41
Timestamp: 2025-02-08T06:59:02.588Z
Learning: Storage improvements in this codebase (like IndexedDB implementation) should be coordinated with the NextAuth system updates to ensure proper handling of device info and user authentication while maintaining good UX and data management.
Learnt from: Bran18
Repo: kindfi-org/kindfi PR: 0
File: :0-0
Timestamp: 2025-02-22T03:58:54.596Z
Learning: The ProofOfAddressUpload component in apps/web/components/shared/kyc-4-upload.tsx should be broken down into smaller, focused components for better maintainability and reusability. Key components should include DocumentTypeSelector, FileUploadArea, DocumentPreview, OCRProcessor, ValidationDisplay, and ExtractedInfoDisplay.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 738
File: apps/kyc-server/src/pages/dashboard/customers.tsx:1-3
Timestamp: 2025-09-15T09:24:16.242Z
Learning: In the Kindfi KYC server, the /dashboard/customers route was legacy and has been removed. The /dashboard/users route should use UserMetricsGrid and UserTable components without header content (h1/p elements). The clean structure should be: outer container div with "container/main flex flex-1 flex-col gap-2" classes, inner div with "flex flex-col gap-4 py-4 md:gap-6 md:py-6" classes, containing just the UserMetricsGrid and UserTable components.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 207
File: apps/web/components/shared/kyc/kyc-2/hooks/use-document-processor.ts:1-1
Timestamp: 2025-02-23T00:47:53.433Z
Learning: The document utilities used in KYC components will be modularized into a separate library as discussed in issue `#203`. Current implementations should be refactored to use this library once it's available.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 0
File: :0-0
Timestamp: 2025-09-17T06:25:21.390Z
Learning: The Kindfi project has completed a database audit (Issue `#734`) that established ID synchronization between auth.users and next_auth.users tables, with public.profiles containing both user_id and next_auth_id columns (where next_auth_id is redundant and planned for removal).
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@apps/web/app/api/kyc/didit/webhook/route.ts`:
- Around line 158-166: The current catch block returns detailed error messages
to callers; change it so the server still logs the full error (keep
console.error or use a logger) but the NextResponse.json call returns a generic
error payload in production (e.g., { error: 'Failed to process webhook' }) and
only include error.details when not in production by checking
process.env.NODE_ENV !== 'production' (or an isDev flag); update the catch block
around NextResponse.json in route.ts to conditionally include error instanceof
Error ? error.message : String(error) based on that check so external callers do
not receive internal error details.
In `@apps/web/components/sections/profile/modals/kyc-redirect-modal.tsx`:
- Around line 16-22: The KYCRedirectModalProps interface is currently internal;
export it so other components can reuse the type (e.g., KYCCard or parent
components) for prop forwarding and type-safe checks—update the interface
declaration named KYCRedirectModalProps to be exported (export interface
KYCRedirectModalProps { ... }) and ensure any imports/uses elsewhere reference
the exported type.
In `@apps/web/lib/services/didit.ts`:
- Around line 113-132: The fetch call that retrieves Didit session status (the
request to `${DIDIT_API_BASE_URL}/v3/session/${sessionId}/decision/`) needs a
timeout to avoid hanging; wrap the request with an AbortController, pass its
signal into fetch, set a timer to call controller.abort() after a chosen timeout
(e.g., 5–10s), and clear the timer on success. Update error handling in the
surrounding function (the code that awaits response.json()) to treat an aborted
fetch as a timeout error (e.g., throw a descriptive Error like "Didit session
status request timed out") while preserving existing JSON parsing/error
extraction logic for non-timeout failures. Ensure references: the fetch call,
sessionId, DIDIT_API_BASE_URL, and response handling remain intact.
- Around line 1-5: The DIDIT_API_BASE_URL constant should be made configurable
and exported: change it to export const DIDIT_API_BASE_URL =
process.env.DIDIT_API_BASE_URL ?? 'https://verification.didit.me' so callers can
import it and environments can override it (staging/prod); update any internal
references in this module to use the exported DIDIT_API_BASE_URL and ensure
consumers import the named export instead of relying on a hardcoded value.
- Around line 256-274: The signature comparison in verifyDiditWebhookSignature
uses Buffer.from without encoding, causing mismatched binary interpretation;
update the Buffer creation for both the incoming signature and the computed
expectedSignature to use the 'hex' encoding (i.e., Buffer.from(signature, 'hex')
and Buffer.from(expectedSignature, 'hex')) so timingSafeEqual compares the same
byte representations, and ensure lengths match before calling
crypto.timingSafeEqual to avoid exceptions.
- Around line 68-78: The vendor_data assignment in the CreateSessionRequest
(requestBody) uses a blind cast (metadata?.userId as string | undefined); change
it to perform an explicit runtime type check on metadata?.userId (e.g., typeof
metadata?.userId === 'string' ? metadata.userId : undefined) and assign that
result to vendor_data so non-string values (numbers, objects) become undefined;
update any related references (requestBody, CreateSessionRequest, metadata)
accordingly to preserve type safety.
- Around line 80-99: The fetch call that posts to
`${DIDIT_API_BASE_URL}/v3/session/` lacks a timeout and can hang; create an
AbortSignal with a reasonable timeout (e.g. const signal =
AbortSignal.timeout(timeoutMs)) and pass it to fetch({ ..., signal }) in the
same function that constructs requestBody/apiKey (the block referencing
requestBody, apiKey and response), then wrap the fetch/response handling in
try/catch and translate an aborted request (error.name === 'AbortError') into a
clear Error like "Didit session creation timed out" while preserving other
errors as before.
- Around line 147-149: The if-block checking "if (typeof data === 'number' &&
!Number.isInteger(data) && data % 1 === 0) { return Math.trunc(data) }" is dead
code because the condition can never be true; remove this branch from didit.ts
(or replace it with explicit handling for string-encoded numeric values if
webhook sometimes sends numbers as strings—e.g., detect typeof data === 'string'
and parse/normalize accordingly). Locate the exact condition and either delete
it or change the check to handle string inputs (parseFloat + isFinite or a regex
that accepts "5.0") and then return Math.trunc on the parsed value.
♻️ Duplicate comments (4)
apps/web/components/sections/profile/modals/kyc-redirect-modal.tsx (1)
73-75: Consider guarding against empty verification URL.If
verificationUrlis an empty string,handleRedirectNowwould navigate to the current page, causing an unexpected reload. Adding a guard would prevent this edge case.🛡️ Suggested guard
const handleRedirectNow = () => { + if (!verificationUrl) return window.location.href = verificationUrl }apps/web/app/api/kyc/didit/webhook/route.ts (3)
46-48: Wrap JSON.parse in try-catch for malformed payloads.If Didit sends a malformed JSON body,
JSON.parse(rawBody)will throw an unhandled exception, resulting in a 500 error instead of a clear 400 response.🛡️ Suggested defensive parsing
// Get raw body and parse JSON const rawBody = await req.text() - const jsonBody: DiditWebhookEvent = JSON.parse(rawBody) + let jsonBody: DiditWebhookEvent + try { + jsonBody = JSON.parse(rawBody) + } catch { + return NextResponse.json( + { error: 'Invalid JSON payload' }, + { status: 400 }, + ) + }
95-104: Consider using a dedicated indexed column for session ID lookup.Using
LIKEwith wildcards on thenotesfield is inefficient and won't benefit from indexes. For production workloads, storingdidit_session_idas a dedicated column would significantly improve query performance and reliability.
106-110: Add defensive parsing for the notes field.If
kycRecord.notescontains malformed JSON,JSON.parsewill throw and cause the webhook to fail. Consider wrapping in try-catch for resilience.🛡️ Suggested fix
- const notes = - typeof kycRecord.notes === 'string' - ? JSON.parse(kycRecord.notes) - : kycRecord.notes + let notes: Record<string, unknown> = {} + try { + notes = + typeof kycRecord.notes === 'string' + ? JSON.parse(kycRecord.notes) + : kycRecord.notes || {} + } catch { + console.warn('Failed to parse existing notes, starting fresh') + }
| } catch (error) { | ||
| console.error('Error processing Didit webhook:', error) | ||
| return NextResponse.json( | ||
| { | ||
| error: 'Failed to process webhook', | ||
| details: error instanceof Error ? error.message : String(error), | ||
| }, | ||
| { status: 500 }, | ||
| ) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider limiting error details in production responses.
While exposing error details can aid debugging, it may leak internal implementation information to external callers. For a webhook endpoint, you might want to log detailed errors server-side but return a generic message.
💡 Suggested approach
} catch (error) {
console.error('Error processing Didit webhook:', error)
return NextResponse.json(
- {
- error: 'Failed to process webhook',
- details: error instanceof Error ? error.message : String(error),
- },
+ { error: 'Failed to process webhook' },
{ status: 500 },
)
}📝 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.
| } catch (error) { | |
| console.error('Error processing Didit webhook:', error) | |
| return NextResponse.json( | |
| { | |
| error: 'Failed to process webhook', | |
| details: error instanceof Error ? error.message : String(error), | |
| }, | |
| { status: 500 }, | |
| ) | |
| } catch (error) { | |
| console.error('Error processing Didit webhook:', error) | |
| return NextResponse.json( | |
| { error: 'Failed to process webhook' }, | |
| { status: 500 }, | |
| ) | |
| } |
🤖 Prompt for AI Agents
In `@apps/web/app/api/kyc/didit/webhook/route.ts` around lines 158 - 166, The
current catch block returns detailed error messages to callers; change it so the
server still logs the full error (keep console.error or use a logger) but the
NextResponse.json call returns a generic error payload in production (e.g., {
error: 'Failed to process webhook' }) and only include error.details when not in
production by checking process.env.NODE_ENV !== 'production' (or an isDev flag);
update the catch block around NextResponse.json in route.ts to conditionally
include error instanceof Error ? error.message : String(error) based on that
check so external callers do not receive internal error details.
| interface KYCRedirectModalProps { | ||
| open: boolean | ||
| onOpenChange: (open: boolean) => void | ||
| verificationUrl: string | ||
| onCancel: () => void | ||
| countdownSeconds?: number | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider exporting the interface for type reusability.
If consuming components (like KYCCard) need to reference this props type for type-safe prop forwarding or conditional rendering logic, exporting the interface would enhance developer experience.
✨ Suggested enhancement
-interface KYCRedirectModalProps {
+export interface KYCRedirectModalProps {
open: boolean
onOpenChange: (open: boolean) => void
verificationUrl: string
onCancel: () => void
countdownSeconds?: number
}📝 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.
| interface KYCRedirectModalProps { | |
| open: boolean | |
| onOpenChange: (open: boolean) => void | |
| verificationUrl: string | |
| onCancel: () => void | |
| countdownSeconds?: number | |
| } | |
| export interface KYCRedirectModalProps { | |
| open: boolean | |
| onOpenChange: (open: boolean) => void | |
| verificationUrl: string | |
| onCancel: () => void | |
| countdownSeconds?: number | |
| } |
🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/modals/kyc-redirect-modal.tsx` around
lines 16 - 22, The KYCRedirectModalProps interface is currently internal; export
it so other components can reuse the type (e.g., KYCCard or parent components)
for prop forwarding and type-safe checks—update the interface declaration named
KYCRedirectModalProps to be exported (export interface KYCRedirectModalProps {
... }) and ensure any imports/uses elsewhere reference the exported type.
| // lib/services/didit.ts | ||
|
|
||
| import * as crypto from 'crypto' | ||
|
|
||
| const DIDIT_API_BASE_URL = 'https://verification.didit.me' |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Good foundation with clear module organization.
The file comment and imports are well-structured. Consider exporting the DIDIT_API_BASE_URL constant or making it configurable via environment variable for flexibility across different environments (staging vs production).
-const DIDIT_API_BASE_URL = 'https://verification.didit.me'
+const DIDIT_API_BASE_URL =
+ process.env.DIDIT_API_BASE_URL || 'https://verification.didit.me'📝 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.
| // lib/services/didit.ts | |
| import * as crypto from 'crypto' | |
| const DIDIT_API_BASE_URL = 'https://verification.didit.me' | |
| // lib/services/didit.ts | |
| import * as crypto from 'crypto' | |
| const DIDIT_API_BASE_URL = | |
| process.env.DIDIT_API_BASE_URL || 'https://verification.didit.me' |
🤖 Prompt for AI Agents
In `@apps/web/lib/services/didit.ts` around lines 1 - 5, The DIDIT_API_BASE_URL
constant should be made configurable and exported: change it to export const
DIDIT_API_BASE_URL = process.env.DIDIT_API_BASE_URL ??
'https://verification.didit.me' so callers can import it and environments can
override it (staging/prod); update any internal references in this module to use
the exported DIDIT_API_BASE_URL and ensure consumers import the named export
instead of relying on a hardcoded value.
| const requestBody: CreateSessionRequest = { | ||
| workflow_id: workflowId, | ||
| vendor_data: metadata?.userId as string | undefined, | ||
| ...(callbackUrl && { callback: callbackUrl }), | ||
| callback_method: 'both', // Allow either device to trigger callback | ||
| ...(metadata && { metadata: JSON.stringify(metadata) }), | ||
| contact_details: { | ||
| email, | ||
| send_notification_emails: false, | ||
| }, | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding type safety for vendor_data extraction.
The cast metadata?.userId as string | undefined could silently produce unexpected values if userId exists but isn't a string (e.g., a number). We could enhance this with explicit type checking.
💡 Suggested improvement
const requestBody: CreateSessionRequest = {
workflow_id: workflowId,
- vendor_data: metadata?.userId as string | undefined,
+ vendor_data:
+ typeof metadata?.userId === 'string' ? metadata.userId : undefined,
...(callbackUrl && { callback: callbackUrl }),
callback_method: 'both', // Allow either device to trigger callback
...(metadata && { metadata: JSON.stringify(metadata) }),📝 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 requestBody: CreateSessionRequest = { | |
| workflow_id: workflowId, | |
| vendor_data: metadata?.userId as string | undefined, | |
| ...(callbackUrl && { callback: callbackUrl }), | |
| callback_method: 'both', // Allow either device to trigger callback | |
| ...(metadata && { metadata: JSON.stringify(metadata) }), | |
| contact_details: { | |
| email, | |
| send_notification_emails: false, | |
| }, | |
| } | |
| const requestBody: CreateSessionRequest = { | |
| workflow_id: workflowId, | |
| vendor_data: | |
| typeof metadata?.userId === 'string' ? metadata.userId : undefined, | |
| ...(callbackUrl && { callback: callbackUrl }), | |
| callback_method: 'both', // Allow either device to trigger callback | |
| ...(metadata && { metadata: JSON.stringify(metadata) }), | |
| contact_details: { | |
| email, | |
| send_notification_emails: false, | |
| }, | |
| } |
🤖 Prompt for AI Agents
In `@apps/web/lib/services/didit.ts` around lines 68 - 78, The vendor_data
assignment in the CreateSessionRequest (requestBody) uses a blind cast
(metadata?.userId as string | undefined); change it to perform an explicit
runtime type check on metadata?.userId (e.g., typeof metadata?.userId ===
'string' ? metadata.userId : undefined) and assign that result to vendor_data so
non-string values (numbers, objects) become undefined; update any related
references (requestBody, CreateSessionRequest, metadata) accordingly to preserve
type safety.
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/components/sections/profile/views/donor-profile.tsx (1)
140-161: Consider replacingprompt()with a modal component for better UX.The browser's native
prompt()API blocks the main thread and provides a jarring user experience. A custom modal or form component would integrate better with the app's design system and allow for proper input validation, error states, and accessibility features.This could be addressed in a follow-up iteration if the current implementation is intentional for rapid prototyping.
Would you like me to help design a modal component for the sync donation flow?
apps/web/components/shared/layout/header/header.tsx (1)
140-168: Consider extracting the duplicate sign-out logic into a shared hook.The
handleSignOutActionfunction is duplicated betweenUserMenu(lines 140-168) andMobileUserMenu(lines 272-300). This violates DRY principles and creates maintenance overhead if the sign-out flow needs to change.We could enhance maintainability by extracting this into a custom hook:
♻️ Proposed refactor using a custom hook
// In a new file: apps/web/hooks/use-sign-out.ts export function useSignOut() { const router = useRouter() const { t } = useI18n() const { disconnect } = useWallet() const [isSigningOut, setIsSigningOut] = useState(false) const handleSignOut = async () => { if (isSigningOut) return try { setIsSigningOut(true) try { disconnect() } catch (error) { console.error('Error disconnecting wallet:', error) } try { const supabase = createSupabaseBrowserClient() await supabase.auth.signOut() } catch (error) { console.error('Error signing out from Supabase:', error) } await signOut({ callbackUrl: '/sign-in?success=Successfully signed out' }) } catch (error) { console.error('Error signing out:', error) toast.error(t('auth.signOutError')) router.push('/') setIsSigningOut(false) } } return { isSigningOut, handleSignOut } }Also applies to: 272-300
🤖 Fix all issues with AI agents
In `@apps/web/components/pages/auth/passkey-registration.tsx`:
- Around line 86-88: Extract the literal 'kindfi_new_session' into a shared
constant and replace all hard-coded occurrences: create (or add to) a constants
module (e.g., export SESSION_KEYS with NEW_SESSION) and import that constant
into passkey-registration.tsx (where sessionStorage.setItem is called) and into
use-smart-account-auth.ts so both use SESSION_KEYS.NEW_SESSION instead of the
string literal; update imports and remove the duplicated literal from both
files.
In `@apps/web/components/sections/profile/cards/kyc-card.tsx`:
- Around line 287-310: The current JSX uses an unnecessary IIFE to build the
benefits list; replace the IIFE with a direct call to getBenefits() and map over
its result in the JSX (e.g., getBenefits().map(...)) to improve readability.
Keep the existing rendering logic and keys (benefit.text), retain use of
IconComponent, motion.div and the className logic for the colored wrapper and
text, and preserve the transition/initial/animate props and index-based delay.
Ensure there are no extra function wrappers so the component renders the same
elements but without the IIFE.
In `@apps/web/components/sections/profile/cards/wallet-card.tsx`:
- Around line 116-127: Add descriptive aria-label attributes to the icon-only
copy buttons to improve screen-reader accessibility: update the Button that uses
handleCopySmartAccount (and toggles copiedSmartAccount) to include
aria-label="Copy smart account address" (or "Smart account copied" if you prefer
dynamic labels) and similarly add aria-label="Copy external wallet address" to
the external wallet copy Button (the one around lines 184-195). Ensure the
aria-labels stay in sync with the button purpose and are applied to the Button
components wrapping the Copy/CheckCircle2 icons.
- Around line 32-56: handleCopySmartAccount and handleCopyExternal duplicate the
same clipboard/copy logic; extract a reusable helper (e.g.,
copyAddressToClipboard) that accepts the address string, the corresponding state
setter (setCopiedSmartAccount or setCopiedExternal), and the success message;
inside the helper perform the navigator.clipboard.writeText call, call the
setter to true, show toast.success, schedule setTimeout to reset the setter to
false, and catch errors to console.error/toast.error; then replace
handleCopySmartAccount and handleCopyExternal with thin wrappers that call
copyAddressToClipboard(smartAccountAddress, setCopiedSmartAccount, 'Smart
Account address copied!') and copyAddressToClipboard(externalWalletAddress,
setCopiedExternal, 'External wallet address copied!').
In `@apps/web/components/sections/profile/modals/role-selection-modal.tsx`:
- Around line 71-74: The modal text is misleading because RoleCard in this PR
allows changing roles later; update the DialogDescription string in the
RoleSelectionModal component to reflect that users can change their role (e.g.,
"You can change this later in your settings" or similar) so the UI matches
actual behavior and remove the absolute "You can't change this later" wording.
- Around line 78-152: Extract the duplicated Creator/Donor blocks into a
reusable RoleOption component (e.g., function RoleOption({ role, icon, title,
description, isSelected, isSaving, onSelect }: RoleOptionProps)) and replace the
two motion.button blocks by rendering <RoleOption /> for 'creator' and 'donor';
keep all motion props (whileHover/whileTap/initial/animate), preserve disabled
behavior from isSaving, use onSelect to call handleRoleSelect, render the ✓
Selected badge conditionally when isSelected, and centralize the shared
className logic (use CVA or a computed className inside RoleOption) so only
icon/title/description/role differ at call sites.
In `@apps/web/components/sections/profile/profile-dashboard.tsx`:
- Around line 94-98: The localStorage flag 'kindfi_role_chosen' set inside
handleRoleSelected is redundant because modal visibility is already driven by
the `role` state from the DB; remove the
localStorage.setItem('kindfi_role_chosen', 'true') call from the
handleRoleSelected function and any other reads/writes of that key if present,
leaving setShowRoleModal(false) (and related modal logic) intact so the
component relies solely on the `role` state for visibility.
- Around line 250-260: The conditional rendering currently treats admin (and
pending) roles by falling back to DonorProfile; update the branch for clarity by
explicitly handling the "admin" case (or add a clear comment) in the role
conditional around CreatorProfile/DonorProfile so intent is obvious—either add
an explicit else-if branch for role === 'admin' and render the intended
component (or DonorProfile if that is intended) referencing the existing
CreatorProfile and DonorProfile components and the props user.id and
displayName, or insert a concise comment explaining why admin falls back to
DonorProfile.
In `@apps/web/components/sections/profile/profile-header.tsx`:
- Around line 10-18: The Role type and prop are redundantly nullable: remove the
double null by making Role non-nullable and keeping the prop nullable, or vice
versa; for example, change the type alias to "type Role =
Database['public']['Enums']['user_role']" and keep "role: Role | null" in
ProfileHeaderProps (or alternatively leave Role as nullable and change "role:
Role" in ProfileHeaderProps) so you no longer have a | null | null pattern
referencing Role and ProfileHeaderProps.role.
In `@apps/web/components/sections/profile/views/creator-profile.tsx`:
- Around line 361-365: Replace the inline statusColors map with a CVA variant so
badge styles are centralized and extendable: create a cva configuration (e.g.,
creatorProfileStatusCva or extend the existing Badge's cva variants) that
declares variants for active, review, funded, draft (and a default) and map each
variant to the corresponding class strings; then update places using
statusColors to pass the variant name into Badge (or Badge's className via the
cva helper) instead of indexing statusColors, ensuring the component uses the
new variant prop across the CreatorProfile view and any related components.
- Line 160: Replace hardcoded "#000124" occurrences in creator-profile.tsx with
a theme token (e.g., CSS var --brand-dark added to globals.css/Tailwind theme)
and update className usages (e.g., the button with className="bg-[`#000124`]
hover:bg-[`#000124`]/90 text-white shadow-lg") to reference that token
(bg-[var(--brand-dark)] and hover:bg-[var(--brand-dark)]/90) so the color is
centralized and responsive to dark-mode overrides; refactor the local
statusColors object into the project's CVA pattern used by badge/button/alert
(create a statusBadge via cva with variants like "success","warning","danger"
and map existing keys to those variants) and replace usage sites (status
rendering, progress bar, amount displays) to use the new statusBadge; finally,
verify and adjust text color/contrast for uses of the brand-dark token in dark
mode to meet WCAG (especially progress bar and amount displays) and update
classes to use accessible text colors if needed.
In `@apps/web/components/sections/profile/views/donor-profile.tsx`:
- Line 687: The progress bar's fill element currently uses a hardcoded color
class ("bg-[`#000124`]")—replace that with the semantic token class ("bg-primary")
in the JSX element where className="h-full bg-[`#000124`] rounded-full" (the
progress bar fill in donor-profile.tsx) so the component uses the brand primary
token; ensure the resulting className reads like "h-full bg-primary
rounded-full" and that the bg-primary token exists in your Tailwind/theme
config.
- Line 348: There are mixed hardcoded color classes (bg-purple-50/50,
text-purple-600) alongside semantic tokens (text-primary) in donor-profile.tsx;
choose a consistent approach and update the component: replace bg-purple-50/50
on the absolute decorative div, text-purple-600 and any other purple utilities
with the chosen semantic token (e.g., bg-primary/5 or bg-accent/5 and
text-primary or text-accent) and ensure the token names exist in Tailwind/theme
config (add text-accent/bg-accent if using a new semantic). Update all
occurrences referenced (the absolute div, the element using text-primary, and
the element using text-purple-600) so the section uses only semantic tokens.
- Line 279: Replace hardcoded hex Tailwind classes like "bg-[`#000124`]/10" and
"text-[`#000124`]" in the donor-profile component with the project's semantic
color tokens (e.g., use "bg-primary/10", "text-primary", or other design-system
tokens such as "bg-background") so color changes propagate from the theme;
locate the className occurrences in donor-profile.tsx (the element with
className "absolute top-0 right-0 w-24 h-24 bg-[`#000124`]/10 rounded-full ..."
and the other nearby className usages noted in the review) and swap the
hex-based utility classes for the corresponding semantic token utilities,
preserving any opacity suffixes ("/10") and existing layout/transition classes.
In
`@services/supabase/migrations/20260115000000_fix_profile_creation_triggers.sql`:
- Line 48: The migration 20260115000000_fix_profile_creation_triggers.sql
defines a trigger that inserts the role value 'pending' but the user_role enum
doesn't include 'pending' until a later migration, causing runtime failures; fix
by adding the enum values ('pending' and 'admin') to the user_role enum within
the 20260115000000 migration before the trigger function/trigger is created, or
alternatively remove the hardcoded 'pending' in the trigger and instead set the
column default via ALTER TABLE ... ALTER COLUMN role SET DEFAULT 'pending' in
the same 20260115000000 migration so the trigger never writes an invalid enum
value (ensure changes touch user_role enum and the trigger function/trigger
definition in that migration).
In
`@services/supabase/migrations/20260123000000_fix_profile_trigger_role_enum.sql`:
- Around line 81-82: The GRANT statement is overly permissive: remove the
authenticated role from EXECUTE on the trigger function
public.create_profile_and_sync_next_auth() since the function runs as SECURITY
DEFINER and is not directly invoked by authenticated users; update the GRANT to
only include service_role and postgres (or other internal service roles used)
and apply the migration so the authenticated role no longer has EXECUTE
privileges on create_profile_and_sync_next_auth().
♻️ Duplicate comments (16)
apps/web/components/sections/profile/views/donor-profile.tsx (2)
471-471: Same hardcoded color pattern applies here.The Heart icon uses
fill-[#124] text-[#124]. Considerfill-primary text-primaryfor consistency with the earlier suggestion.
584-585: Hardcoded colors in DonationHistory follow the same pattern.These changes align with the rest of the file but share the same maintainability concern. The semantic token approach suggested earlier would apply here as well.
Also applies to: 597-597
apps/web/components/sections/profile/cards/role-card.tsx (1)
33-56: Consider using Next.js router refresh instead of full page reload.The
handleRoleChangeimplementation is solid with proper error handling and toast feedback. However, usingwindow.location.reload()causes a full page refresh which isn't ideal for UX. Additionally,setSelectedRole(newRole)on line 44 is redundant since the page reloads immediately after.We could enhance this by using
useRouter().refresh()for a smoother experience that preserves client state.apps/web/components/sections/profile/profile-header.tsx (1)
27-34: Consider importing the existinggetAvatarFallbackutility instead of duplicating.There's already a more robust
getAvatarFallbackfunction inapps/web/lib/utils.tsthat handles edge cases like empty strings and single-word names better. The local implementation could crash withn[0]ifname.split(' ')produces empty strings (e.g., multiple consecutive spaces).We could enhance maintainability by reusing the shared utility:
import { Avatar, AvatarFallback, AvatarImage } from '~/components/base/avatar' import { Badge } from '~/components/base/badge' import { Card, CardContent } from '~/components/base/card' +import { getAvatarFallback } from '~/lib/utils'Then remove the local function definition entirely.
apps/web/components/sections/profile/cards/account-info-card.tsx (3)
35-39: Consider adding slug format validation.The current validation only checks for empty slugs. We could enhance this by validating the slug format to prevent issues with special characters, reserved words, or length constraints.
52-54: Consider using router.refresh() instead of full page reload.Similar to other components in this PR, using
window.location.reload()causes a full page refresh. UsinguseRouter().refresh()would provide a smoother experience while still fetching fresh server data.
35-39: Fix missing state reset on early return.When the slug is empty, the function returns early without resetting
isSavingtofalse. This leaves the form in a perpetual saving state, preventing further submissions.🐛 Proposed fix
const nextSlug = (formData.get('slug') as string)?.trim().toLowerCase() if (!nextSlug) { toast.error('Slug cannot be empty') + setIsSaving(false) return }apps/web/components/sections/profile/cards/personal-info-card.tsx (3)
25-25: Consider removing the unused_emailprop.The
_emailprop is declared in the interface and destructured but never used within the component. Removing it would reduce unnecessary prop-passing and improve clarity.Also applies to: 33-33
54-56: Consider usingrouter.refresh()instead ofwindow.location.reload().Using
window.location.reload()causes a full page reload which can feel jarring. With Next.js App Router, we could enhance the UX by usinguseRouter().refresh()to revalidate server data while preserving client state and scroll position.
136-144: Consider adding image URL validation for security.The avatar URL input accepts any URL without validation. We could enhance security by implementing validation—such as protocol checking (http/https only) or using the existing
validate-url.tsutility—to reject unsafe schemes likejavascript:ordata:URIs before storing.apps/web/app/(routes)/profile/page.tsx (4)
17-18: Consider removing or sanitizing session logging in production.Logging the full session object may expose sensitive user information in production logs. We could enhance security by removing this log or redacting sensitive fields.
28-45: Consider extracting the Didit-to-KYC status mapping into a shared utility.This status mapping logic appears to be duplicated across multiple files (
webhook/route.ts,check-status/route.ts,callback/route.ts, and this page). We could enhance maintainability by extracting it into a shared function in~/lib/services/didit.ts.
82-85: Consider adding defensive JSON parsing for the notes field.If
kycRecord.notescontains malformed JSON,JSON.parsewill throw an unhandled exception, potentially breaking the profile page load.🛡️ Suggested fix
- const notes = - typeof kycRecord.notes === 'string' - ? JSON.parse(kycRecord.notes) - : kycRecord.notes || {} + let notes = {} + try { + notes = + typeof kycRecord.notes === 'string' + ? JSON.parse(kycRecord.notes) + : kycRecord.notes || {} + } catch { + console.warn('Failed to parse KYC notes, using empty object') + }
108-109: The log message is misleading for non-declined statuses.The log says "Creating new KYC record for declined status" but this code path handles all statuses when no existing record is found.
📝 Suggested fix
} else { // Create a new record if none exists - console.log('📝 Creating new KYC record for declined status') + console.log('📝 Creating new KYC record with status:', kycStatus) await supabaseServiceRole.from('kyc_reviews').insert({apps/web/components/sections/profile/cards/kyc-card.tsx (1)
38-61: Consider guarding refresh effects whenuserIdis missing.Currently
refreshStatus()runs even whenuserIdis empty, which can trigger avoidable API errors before the error card renders. We could enhance this by adding a simple guard in the effects to keep traffic clean and avoid noisy logs.🔧 Suggested fix
// Load status on mount useEffect(() => { + if (!userId) return refreshStatus() - }, [refreshStatus]) + }, [userId, refreshStatus]) // Listen for KYC status update events and refresh when callback completes useEffect(() => { + if (!userId) return const handleStatusUpdate = () => { // Refresh status immediately when callback completes refreshStatus() } window.addEventListener('kyc-status-updated', handleStatusUpdate) return () => { window.removeEventListener('kyc-status-updated', handleStatusUpdate) } - }, [refreshStatus]) + }, [userId, refreshStatus]) // Refresh when shouldRefresh prop changes (triggered by parent after callback) useEffect(() => { - if (shouldRefresh) { + if (!userId || !shouldRefresh) return refreshStatus() - } - }, [shouldRefresh, refreshStatus]) + }, [userId, shouldRefresh, refreshStatus])apps/web/components/sections/profile/profile-dashboard.tsx (1)
131-173: Consider deferring URL cleanup until after the API call succeeds.Currently the URL params are cleared immediately (line 173) before the API response is received. If the callback fails, users lose the retry path and don't see proper feedback. We could enhance reliability by only clearing params after a successful update.
🔧 Suggested fix
fetch('/api/kyc/didit/callback', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ verificationSessionId: sessionId, status: normalizedStatus, }), }) .then(async (res) => { if (res.ok) { const result = await res.json() // Update toast with final status if different if ( result.status === 'approved' || result.status === 'verified' ) { toast.success( 'KYC verification approved! Your status has been updated.', ) } else if (result.status === 'rejected') { toast.error( 'KYC verification was declined. Please review the requirements and try again.', ) } else if (result.status === 'pending') { toast.info( "KYC verification is under review. We will notify you once it's complete.", ) } + // Remove query params only after successful update + window.history.replaceState({}, '', '/profile') // Reload page to show updated status (server already updated database) window.location.reload() } else { console.error('Failed to update KYC status via API') + toast.error('Failed to update KYC status. Please try again.') } }) .catch((error) => { console.error('Failed to update KYC status:', error) + toast.error('Failed to update KYC status. Please try again.') }) - - // Remove query params from URL immediately - window.history.replaceState({}, '', '/profile')
| // Mark this as a new session so role selection modal can be shown | ||
| sessionStorage.setItem('kindfi_new_session', 'true') | ||
| router.push('/profile') |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider extracting the session storage key to a shared constant.
The 'kindfi_new_session' key is used in multiple places (here and in use-smart-account-auth.ts). We could enhance maintainability by defining this as a constant in a shared location.
♻️ Suggested approach
Create a constants file or add to an existing one:
// e.g., ~/lib/constants/session.ts
export const SESSION_KEYS = {
NEW_SESSION: 'kindfi_new_session',
} as constThen import and use:
+import { SESSION_KEYS } from '~/lib/constants/session'
// ...
-sessionStorage.setItem('kindfi_new_session', 'true')
+sessionStorage.setItem(SESSION_KEYS.NEW_SESSION, 'true')🤖 Prompt for AI Agents
In `@apps/web/components/pages/auth/passkey-registration.tsx` around lines 86 -
88, Extract the literal 'kindfi_new_session' into a shared constant and replace
all hard-coded occurrences: create (or add to) a constants module (e.g., export
SESSION_KEYS with NEW_SESSION) and import that constant into
passkey-registration.tsx (where sessionStorage.setItem is called) and into
use-smart-account-auth.ts so both use SESSION_KEYS.NEW_SESSION instead of the
string literal; update imports and remove the duplicated literal from both
files.
| {(() => { | ||
| const benefits = getBenefits() | ||
| return benefits.map((benefit, index) => { | ||
| const IconComponent = benefit.icon | ||
| return ( | ||
| <motion.div | ||
| key={benefit.text} | ||
| initial={{ opacity: 0, x: -10 }} | ||
| animate={{ opacity: 1, x: 0 }} | ||
| transition={{ delay: index * 0.1 }} | ||
| className="flex items-center gap-3" | ||
| > | ||
| <div | ||
| className={`p-1.5 rounded-md ${benefit.color.includes('gray') ? 'bg-gray-100' : 'bg-[#000124]/10'}`} | ||
| > | ||
| <IconComponent className={`h-4 w-4 ${benefit.color}`} /> | ||
| </div> | ||
| <span className={`text-xs font-medium ${benefit.color}`}> | ||
| {benefit.text} | ||
| </span> | ||
| </motion.div> | ||
| ) | ||
| }) | ||
| })()} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider simplifying the benefits list rendering.
The IIFE (Immediately Invoked Function Expression) pattern here adds unnecessary complexity. We could enhance readability by calling getBenefits() directly in the JSX.
♻️ Suggested simplification
{/* Benefits List */}
<div className="space-y-3 mb-6 flex-1">
- {(() => {
- const benefits = getBenefits()
- return benefits.map((benefit, index) => {
- const IconComponent = benefit.icon
- return (
- <motion.div
- key={benefit.text}
- initial={{ opacity: 0, x: -10 }}
- animate={{ opacity: 1, x: 0 }}
- transition={{ delay: index * 0.1 }}
- className="flex items-center gap-3"
- >
- <div
- className={`p-1.5 rounded-md ${benefit.color.includes('gray') ? 'bg-gray-100' : 'bg-[`#000124`]/10'}`}
- >
- <IconComponent className={`h-4 w-4 ${benefit.color}`} />
- </div>
- <span className={`text-xs font-medium ${benefit.color}`}>
- {benefit.text}
- </span>
- </motion.div>
- )
- })
- })()}
+ {getBenefits().map((benefit, index) => {
+ const IconComponent = benefit.icon
+ return (
+ <motion.div
+ key={benefit.text}
+ initial={{ opacity: 0, x: -10 }}
+ animate={{ opacity: 1, x: 0 }}
+ transition={{ delay: index * 0.1 }}
+ className="flex items-center gap-3"
+ >
+ <div
+ className={`p-1.5 rounded-md ${benefit.color.includes('gray') ? 'bg-gray-100' : 'bg-[`#000124`]/10'}`}
+ >
+ <IconComponent className={`h-4 w-4 ${benefit.color}`} />
+ </div>
+ <span className={`text-xs font-medium ${benefit.color}`}>
+ {benefit.text}
+ </span>
+ </motion.div>
+ )
+ })}
</div>🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/cards/kyc-card.tsx` around lines 287 -
310, The current JSX uses an unnecessary IIFE to build the benefits list;
replace the IIFE with a direct call to getBenefits() and map over its result in
the JSX (e.g., getBenefits().map(...)) to improve readability. Keep the existing
rendering logic and keys (benefit.text), retain use of IconComponent, motion.div
and the className logic for the colored wrapper and text, and preserve the
transition/initial/animate props and index-based delay. Ensure there are no
extra function wrappers so the component renders the same elements but without
the IIFE.
| const handleCopySmartAccount = async () => { | ||
| if (!smartAccountAddress) return | ||
| try { | ||
| await navigator.clipboard.writeText(smartAccountAddress) | ||
| setCopiedSmartAccount(true) | ||
| toast.success('Smart Account address copied!') | ||
| setTimeout(() => setCopiedSmartAccount(false), 2000) | ||
| } catch (error) { | ||
| console.error('Failed to copy address:', error) | ||
| toast.error('Failed to copy address') | ||
| } | ||
| } | ||
|
|
||
| const handleCopyExternal = async () => { | ||
| if (!externalWalletAddress) return | ||
| try { | ||
| await navigator.clipboard.writeText(externalWalletAddress) | ||
| setCopiedExternal(true) | ||
| toast.success('External wallet address copied!') | ||
| setTimeout(() => setCopiedExternal(false), 2000) | ||
| } catch (error) { | ||
| console.error('Failed to copy address:', error) | ||
| toast.error('Failed to copy address') | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider consolidating the copy handlers to reduce duplication.
The handleCopySmartAccount and handleCopyExternal functions share identical logic with only the address and state setter differing. We could enhance maintainability by extracting a reusable helper.
♻️ Suggested refactor
+ const handleCopy = async (
+ address: string | null,
+ setCopied: (value: boolean) => void,
+ label: string,
+ ) => {
+ if (!address) return
+ try {
+ await navigator.clipboard.writeText(address)
+ setCopied(true)
+ toast.success(`${label} address copied!`)
+ setTimeout(() => setCopied(false), 2000)
+ } catch (error) {
+ console.error('Failed to copy address:', error)
+ toast.error('Failed to copy address')
+ }
+ }
+
+ const handleCopySmartAccount = () =>
+ handleCopy(smartAccountAddress, setCopiedSmartAccount, 'Smart Account')
+
+ const handleCopyExternal = () =>
+ handleCopy(externalWalletAddress, setCopiedExternal, 'External wallet')
- const handleCopySmartAccount = async () => {
- if (!smartAccountAddress) return
- try {
- await navigator.clipboard.writeText(smartAccountAddress)
- setCopiedSmartAccount(true)
- toast.success('Smart Account address copied!')
- setTimeout(() => setCopiedSmartAccount(false), 2000)
- } catch (error) {
- console.error('Failed to copy address:', error)
- toast.error('Failed to copy address')
- }
- }
-
- const handleCopyExternal = async () => {
- if (!externalWalletAddress) return
- try {
- await navigator.clipboard.writeText(externalWalletAddress)
- setCopiedExternal(true)
- toast.success('External wallet address copied!')
- setTimeout(() => setCopiedExternal(false), 2000)
- } catch (error) {
- console.error('Failed to copy address:', error)
- toast.error('Failed to copy address')
- }
- }🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/cards/wallet-card.tsx` around lines 32 -
56, handleCopySmartAccount and handleCopyExternal duplicate the same
clipboard/copy logic; extract a reusable helper (e.g., copyAddressToClipboard)
that accepts the address string, the corresponding state setter
(setCopiedSmartAccount or setCopiedExternal), and the success message; inside
the helper perform the navigator.clipboard.writeText call, call the setter to
true, show toast.success, schedule setTimeout to reset the setter to false, and
catch errors to console.error/toast.error; then replace handleCopySmartAccount
and handleCopyExternal with thin wrappers that call
copyAddressToClipboard(smartAccountAddress, setCopiedSmartAccount, 'Smart
Account address copied!') and copyAddressToClipboard(externalWalletAddress,
setCopiedExternal, 'External wallet address copied!').
| <Button | ||
| variant="ghost" | ||
| size="sm" | ||
| onClick={handleCopySmartAccount} | ||
| className="h-9 w-9 p-0 rounded-lg text-[#000124] hover:text-[#000124] hover:bg-[#000124]/10 transition-colors" | ||
| > | ||
| {copiedSmartAccount ? ( | ||
| <CheckCircle2 className="h-4 w-4" /> | ||
| ) : ( | ||
| <Copy className="h-4 w-4" /> | ||
| )} | ||
| </Button> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding aria-labels for screen reader accessibility.
The copy buttons use icons without visible text, which may not convey their purpose to screen reader users. We could enhance accessibility by adding descriptive aria-labels.
♿ Suggested improvement
<Button
variant="ghost"
size="sm"
onClick={handleCopySmartAccount}
+ aria-label={copiedSmartAccount ? 'Address copied' : 'Copy Smart Account address'}
className="h-9 w-9 p-0 rounded-lg text-[`#000124`] hover:text-[`#000124`] hover:bg-[`#000124`]/10 transition-colors"
>Apply similar changes to the external wallet copy button.
Also applies to: 184-195
🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/cards/wallet-card.tsx` around lines 116
- 127, Add descriptive aria-label attributes to the icon-only copy buttons to
improve screen-reader accessibility: update the Button that uses
handleCopySmartAccount (and toggles copiedSmartAccount) to include
aria-label="Copy smart account address" (or "Smart account copied" if you prefer
dynamic labels) and similarly add aria-label="Copy external wallet address" to
the external wallet copy Button (the one around lines 184-195). Ensure the
aria-labels stay in sync with the button purpose and are applied to the Button
components wrapping the Copy/CheckCircle2 icons.
| <DialogDescription className="text-center text-base"> | ||
| Select how you'd like to use KindFi. You can't change this | ||
| later in your settings. | ||
| </DialogDescription> |
There was a problem hiding this comment.
UI text may contradict actual functionality.
The description states "You can't change this later in your settings." However, the RoleCard component in this PR allows users to change their role at any time. We could enhance clarity by updating this text to reflect the actual behavior:
<DialogDescription className="text-center text-base">
- Select how you'd like to use KindFi. You can't change this
- later in your settings.
+ Select how you'd like to use KindFi. You can change this
+ later in your profile settings.
</DialogDescription>📝 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.
| <DialogDescription className="text-center text-base"> | |
| Select how you'd like to use KindFi. You can't change this | |
| later in your settings. | |
| </DialogDescription> | |
| <DialogDescription className="text-center text-base"> | |
| Select how you'd like to use KindFi. You can change this | |
| later in your profile settings. | |
| </DialogDescription> |
🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/modals/role-selection-modal.tsx` around
lines 71 - 74, The modal text is misleading because RoleCard in this PR allows
changing roles later; update the DialogDescription string in the
RoleSelectionModal component to reflect that users can change their role (e.g.,
"You can change this later in your settings" or similar) so the UI matches
actual behavior and remove the absolute "You can't change this later" wording.
| <motion.div variants={cardVariants}> | ||
| <Card className="border-0 overflow-hidden bg-card shadow-lg hover:shadow-xl transition-all duration-300 relative group"> | ||
| <div className="absolute top-0 right-0 w-24 h-24 bg-primary/10 rounded-full -mr-12 -mt-12 group-hover:scale-150 transition-transform duration-500" /> | ||
| <div className="absolute top-0 right-0 w-24 h-24 bg-[#000124]/10 rounded-full -mr-12 -mt-12 group-hover:scale-150 transition-transform duration-500" /> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Consider using semantic color tokens instead of hardcoded hex values.
The replacement of bg-primary/10 and text-primary with bg-[#124]/10 and text-[#124] reduces maintainability. If the brand's primary color changes, these hardcoded values won't update automatically, creating potential inconsistencies across the application.
We could enhance this by continuing to use the design system tokens:
♻️ Suggested approach
- <div className="absolute top-0 right-0 w-24 h-24 bg-[`#000124`]/10 rounded-full -mr-12 -mt-12 group-hover:scale-150 transition-transform duration-500" />
+ <div className="absolute top-0 right-0 w-24 h-24 bg-primary/10 rounded-full -mr-12 -mt-12 group-hover:scale-150 transition-transform duration-500" />- className="text-4xl font-extrabold text-[`#000124`]"
+ className="text-4xl font-extrabold text-primary"- <Trophy className="h-8 w-8 text-[`#000124`]" />
+ <Trophy className="h-8 w-8 text-primary" />Based on learnings, the project prefers semantic tokens (e.g., bg-background over bg-white) for better theme consistency.
Also applies to: 299-299, 310-310, 319-319, 336-336
🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/views/donor-profile.tsx` at line 279,
Replace hardcoded hex Tailwind classes like "bg-[`#000124`]/10" and
"text-[`#000124`]" in the donor-profile component with the project's semantic
color tokens (e.g., use "bg-primary/10", "text-primary", or other design-system
tokens such as "bg-background") so color changes propagate from the theme;
locate the className occurrences in donor-profile.tsx (the element with
className "absolute top-0 right-0 w-24 h-24 bg-[`#000124`]/10 rounded-full ..."
and the other nearby className usages noted in the review) and swap the
hex-based utility classes for the corresponding semantic token utilities,
preserving any opacity suffixes ("/10") and existing layout/transition classes.
| <motion.div variants={cardVariants} whileHover={{ y: -2 }}> | ||
| <Card className="border-0 shadow-xl bg-card hover:shadow-2xl transition-all duration-300 overflow-hidden relative group"> | ||
| <div className="absolute top-0 right-0 w-40 h-40 bg-primary/5 rounded-full -mr-20 -mt-20 group-hover:scale-150 transition-transform duration-700" /> | ||
| <div className="absolute top-0 right-0 w-40 h-40 bg-purple-50/50 rounded-full -mr-20 -mt-20 group-hover:scale-150 transition-transform duration-700" /> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Inconsistent color token usage within the same section.
There's a mix of semantic tokens (text-primary on line 365) and hardcoded colors (text-purple-600 on line 402, bg-purple-50/50 on line 348). This inconsistency could lead to visual mismatches if the theme changes.
Consider either:
- Using semantic tokens consistently throughout (e.g.,
text-primary,bg-primary/5) - Or defining new semantic tokens like
text-accentif purple is intentionally a secondary brand color
Also applies to: 365-365, 402-402
🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/views/donor-profile.tsx` at line 348,
There are mixed hardcoded color classes (bg-purple-50/50, text-purple-600)
alongside semantic tokens (text-primary) in donor-profile.tsx; choose a
consistent approach and update the component: replace bg-purple-50/50 on the
absolute decorative div, text-purple-600 and any other purple utilities with the
chosen semantic token (e.g., bg-primary/5 or bg-accent/5 and text-primary or
text-accent) and ensure the token names exist in Tailwind/theme config (add
text-accent/bg-accent if using a new semantic). Update all occurrences
referenced (the absolute div, the element using text-primary, and the element
using text-purple-600) so the section uses only semantic tokens.
| <div className="relative h-3 bg-muted rounded-full overflow-hidden"> | ||
| <motion.div | ||
| className="h-full bg-primary rounded-full" | ||
| className="h-full bg-[#000124] rounded-full" |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Progress bar fill color should use semantic token.
The progress bar is a key UI element that should align with the brand's primary color. Using bg-primary ensures consistency across all progress indicators in the application.
♻️ Suggested fix
- className="h-full bg-[`#000124`] rounded-full"
+ className="h-full bg-primary rounded-full"📝 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.
| className="h-full bg-[#000124] rounded-full" | |
| className="h-full bg-primary rounded-full" |
🤖 Prompt for AI Agents
In `@apps/web/components/sections/profile/views/donor-profile.tsx` at line 687,
The progress bar's fill element currently uses a hardcoded color class
("bg-[`#000124`]")—replace that with the semantic token class ("bg-primary") in
the JSX element where className="h-full bg-[`#000124`] rounded-full" (the progress
bar fill in donor-profile.tsx) so the component uses the brand primary token;
ensure the resulting className reads like "h-full bg-primary rounded-full" and
that the bg-primary token exists in your Tailwind/theme config.
There was a problem hiding this comment.
@coderabbitai, create a new issue for this one that is a minimal task to address
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Create a GitHub issue for the progress bar color refactoring task
gh issue create \
--repo kindfi-org/kindfi \
--title "Use semantic color token for progress bar fill in DonorProfile" \
--body "## Context
A hardcoded color value is being used for the progress bar fill in the DonorProfile component, which should be replaced with the semantic \`bg-primary\` token for better consistency with the design system.
## Location
- **File**: \`apps/web/components/sections/profile/views/donor-profile.tsx\`
- **Line**: 687
## Task
Replace the hardcoded color class \`bg-[`#000124`]\` with the semantic token \`bg-primary\` for the progress bar fill element.
## Backlinks
- **PR**: https://github.com/kindfi-org/kindfi/pull/768
- **Review Comment**: https://github.com/kindfi-org/kindfi/pull/768#discussion_r2719777301
## Requested by
`@Bran18`" \
--assignee Bran18 \
--label "easy,frontend,refactor"
Length of output: 37
🏁 Script executed:
#!/bin/bash
# First, list available labels in the repository to see what's available
echo "=== Available labels in kindfi-org/kindfi repository ==="
gh label list --repo kindfi-org/kindfi --limit 100
echo -e "\n=== Creating issue without labels first ==="
# Create the issue without labels for now
gh issue create \
--repo kindfi-org/kindfi \
--title "Use semantic color token for progress bar fill in DonorProfile" \
--body "## Context
A hardcoded color value is being used for the progress bar fill in the DonorProfile component, which should be replaced with the semantic \`bg-primary\` token for better consistency with the design system.
## Location
- **File**: \`apps/web/components/sections/profile/views/donor-profile.tsx\`
- **Line**: 687
## Task
Replace the hardcoded color class \`bg-[`#000124`]\` with the semantic token \`bg-primary\` for the progress bar fill element.
## Backlinks
- **PR**: https://github.com/kindfi-org/kindfi/pull/768
- **Review Comment**: https://github.com/kindfi-org/kindfi/pull/768#discussion_r2719777301
## Requested by
`@Bran18`" \
--assignee Bran18
Length of output: 2807
🏁 Script executed:
#!/bin/bash
# Add the appropriate labels to the created issue
gh issue edit 772 \
--repo kindfi-org/kindfi \
--add-label "difficulty: easy,frontend,refactor"
echo "Issue `#772` created and labeled successfully!"
echo "Issue URL: https://github.com/kindfi-org/kindfi/issues/772"
Length of output: 151
@Bran18, I've created issue #772 for this refactoring task.
Issue Details:
- Title: Use semantic color token for progress bar fill in DonorProfile
- Assigned to:
@Bran18 - Labels:
difficulty: easy,frontend,refactor - Location:
apps/web/components/sections/profile/views/donor-profile.tsx, line 687 - Task: Replace
bg-[Doc test #124]withbg-primaryfor the progress bar fill element
The issue includes backlinks to this PR and the review comment for easy reference.
🧬 Code Graph Analysis Results
[START_SNIPPETS]
Filename: apps/web/lib/queries/projects/get-user-projects.ts
Lines: 78-166
export async function getUserSupportedProjects(
client: TypedSupabaseClient,
userId: string,
) {
const { data: contributions, error: contributionsError } = await client
.from('contributions')
.select(
`
id,
amount,
created_at,
project_id,
project:project_id (
id,
title,
slug,
description,
image_url,
created_at,
current_amount,
target_amount,
min_investment,
percentage_complete,
kinder_count,
status,
category:category_id ( * ),
project_tag_relationships (
tag:tag_id ( id, name, color )
),
project_escrows:project_escrows!left (
escrow_id
)
)
`,
)
.eq('contributor_id', userId)
.order('created_at', { ascending: false })
if (contributionsError) throw contributionsError
return (
contributions?.map((contribution) => {
const project = contribution.project as unknown as {
id: string
title: string
slug: string
description: string
image_url: string
created_at: string
current_amount: number
target_amount: number
min_investment: number
percentage_complete: number
kinder_count: number
status: string
category: unknown
project_tag_relationships: Array<{
tag: { id: string; name: string; color: string }
}>
project_escrows?: { escrow_id?: string } | Array<{ escrow_id?: string }>
}
const escrowRel = project.project_escrows
const escrowId = Array.isArray(escrowRel)
? escrowRel[0]?.escrow_id
: escrowRel?.escrow_id
return {
id: project.id,
title: project.title,
slug: project.slug,
description: project.description,
image: project.image_url,
goal: project.target_amount,
raised: project.current_amount,
investors: project.kinder_count,
minInvestment: project.min_investment,
createdAt: project.created_at,
status: project.status,
percentageComplete: project.percentage_complete,
category: project.category,
tags: project.project_tag_relationships?.map((r) => r.tag) ?? [],
escrowContractAddress: escrowId,
contributionAmount: contribution.amount,
contributionDate: contribution.created_at,
}
}) ?? []
)
}
Filename: apps/web/lib/hooks/use-supabase-query.hook.ts
Lines: 38-79
export function useSupabaseQuery<TData>(
queryName: string,
queryFn: QueryFn<TData>,
options?: UseSupabaseQueryOptions<TData>,
) {
const queryClient = useQueryClient()
const supabase = createSupabaseBrowserClient()
const queryKey = useMemo(() => {
const baseKey = ['supabase', queryName]
if (options?.additionalKeyValues?.length) {
return [...baseKey, ...options.additionalKeyValues]
}
return baseKey
}, [queryName, options?.additionalKeyValues])
const query = useQuery<TData, Error>({
queryKey,
queryFn: async () => {
try {
return await queryFn(supabase)
} catch (error) {
console.error(`Error in query ${queryName}:`, error)
throw error instanceof Error ? error : new Error(String(error))
}
},
...options,
})
useEffect(() => {
return () => {
if (options?.clearOnUnmount) {
queryClient.removeQueries({ queryKey })
}
}
}, [queryClient, queryKey, options?.clearOnUnmount])
return {
...query,
refresh: () => query.refetch(),
}
}
Filename: apps/web/hooks/contexts/use-escrow.context.tsx
Lines: 177-181
export function useEscrow() {
const ctx = useContext(EscrowContext)
if (!ctx) throw new Error('useEscrow must be used within EscrowProvider')
return ctx
}
Filename: apps/web/lib/constants/animations.ts
Lines: 146-161
export const cardVariants = {
initial: { opacity: 0, y: 20 },
animate: {
opacity: 1,
y: 0,
transition: {
duration: 0.5,
},
},
hover: {
y: -5,
transition: {
duration: 0.2,
},
},
}
Filename: apps/web/lib/constants/animations.ts
Lines: 236-243
export const staggerContainer = {
initial: {},
animate: {
transition: {
staggerChildren: 0.05,
},
},
}
[END_SNIPPETS]
🧠 Learnings used
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 598
File: apps/web/components/sections/projects/pitch/tips-sidebar.tsx:15-15
Timestamp: 2025-08-13T08:48:21.639Z
Learning: In the Kindfi project, use `bg-background` instead of `bg-white` for Card components that need a white background. The `bg-background` token resolves to white in their Tailwind theme configuration and provides better semantic consistency.
Learnt from: CR
Repo: kindfi-org/kindfi PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-04T22:00:57.967Z
Learning: Applies to **/*.{tsx,jsx,css} : Maintain WCAG compliance for color contrast and readability
Learnt from: CR
Repo: kindfi-org/kindfi PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-04T22:00:18.257Z
Learning: Applies to **/*.{tsx,css} : Maintain WCAG compliance for color contrast and readability
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 322
File: apps/web/components/shared/project-card.tsx:12-39
Timestamp: 2025-03-24T10:05:50.568Z
Learning: The tag and category rendering in the project should use a11y color matching to ensure proper contrast between background and text colors. A utility function `getA11yColorMatch` should be implemented using `random-hex-color` and `get-contrast` packages to generate accessible color combinations.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 322
File: apps/web/components/shared/project-card.tsx:12-39
Timestamp: 2025-03-24T10:05:50.568Z
Learning: The tag and category rendering in the project should implement a11y color matching to ensure proper contrast between background and text colors. A utility function `getA11yColorMatch` should be implemented using `random-hex-color` and `get-contrast` packages to generate accessible color combinations.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 86
File: apps/web/lib/validators/escrow.ts:54-64
Timestamp: 2025-01-27T19:06:49.271Z
Learning: When reviewing code, if suggestions are related to a different feature or task, create a separate issue to track them instead of implementing them in the current PR. This helps maintain focus and track related changes effectively.
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 0
File: :0-0
Timestamp: 2025-08-19T10:10:50.052Z
Learning: When creating planning issues for kindfi-org/kindfi: assign all EPICs to user "andlerrl", leave sub-issues unassigned, and apply labels for difficulty (easy/medium/hard), area (frontend/backend/supabase/contract/kyc server/TL), and type (feature/refactor/chore/bug/spike). Also add tracking checklists in EPIC bodies linking sub-issues using "- [ ] #<issue>".
Learnt from: AndlerRL
Repo: kindfi-org/kindfi PR: 0
File: :0-0
Timestamp: 2025-09-17T06:25:21.390Z
Learning: The Kindfi project has completed a database audit (Issue `#734`) that established ID synchronization between auth.users and next_auth.users tables, with public.profiles containing both user_id and next_auth_id columns (where next_auth_id is redundant and planned for removal).
services/supabase/migrations/20260115000000_fix_profile_creation_triggers.sql
Show resolved
Hide resolved
| -- Grant necessary permissions | ||
| GRANT EXECUTE ON FUNCTION public.create_profile_and_sync_next_auth() TO service_role, postgres, authenticated; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if authenticated role is used to directly call this function elsewhere
rg -n "create_profile_and_sync_next_auth" --type sql --type ts --type tsxRepository: kindfi-org/kindfi
Length of output: 88
🏁 Script executed:
#!/bin/bash
# Search for function usages with supported file types
rg -n "create_profile_and_sync_next_auth" --type sql --type tsRepository: kindfi-org/kindfi
Length of output: 1706
🏁 Script executed:
#!/bin/bash
# Get the trigger definition context
sed -n '80,95p' services/supabase/migrations/20260123000000_fix_profile_trigger_role_enum.sqlRepository: kindfi-org/kindfi
Length of output: 435
Consider removing authenticated from the EXECUTE grant for tighter security.
This trigger function is invoked automatically by the database engine on auth.users INSERT and runs with SECURITY DEFINER privileges. Since the authenticated role never directly calls this function, it doesn't need EXECUTE permission. We can enhance security by following the principle of least privilege:
Proposed change
-GRANT EXECUTE ON FUNCTION public.create_profile_and_sync_next_auth() TO service_role, postgres, authenticated;
+GRANT EXECUTE ON FUNCTION public.create_profile_and_sync_next_auth() TO service_role, postgres;📝 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.
| -- Grant necessary permissions | |
| GRANT EXECUTE ON FUNCTION public.create_profile_and_sync_next_auth() TO service_role, postgres, authenticated; | |
| -- Grant necessary permissions | |
| GRANT EXECUTE ON FUNCTION public.create_profile_and_sync_next_auth() TO service_role, postgres; |
🤖 Prompt for AI Agents
In
`@services/supabase/migrations/20260123000000_fix_profile_trigger_role_enum.sql`
around lines 81 - 82, The GRANT statement is overly permissive: remove the
authenticated role from EXECUTE on the trigger function
public.create_profile_and_sync_next_auth() since the function runs as SECURITY
DEFINER and is not directly invoked by authenticated users; update the GRANT to
only include service_role and postgres (or other internal service roles used)
and apply the migration so the authenticated role no longer has EXECUTE
privileges on create_profile_and_sync_next_auth().
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
services/supabase/README.md (1)
89-99: Good addition of Service Role Key documentation.This section provides valuable guidance for obtaining the critical
SUPABASE_SERVICE_ROLE_KEY. Consider adding a security reminder about keeping this key confidential since it grants admin-level access.📝 Suggested enhancement
5. Locate the Service Role Key and reveal and copy it: + +⚠️ **Security Note:** Never commit the Service Role Key to version control or expose it in client-side code. This key provides admin-level access to your Supabase project. ## Useful Commandsapps/contract/README.md (1)
147-160: Consider specifying the actual minimum Rust version or linking to official compatibility docs.Great addition on the
wasm32-unknown-unknowntarget. For the new note, while mentioning SDK 22.0.6 is helpful context, the hardcoded version and vague reference to "compatible Rust version" could become outdated as releases progress. Stellar's official documentation specifies that smart contract development requires Rust 1.84.0 or higher (for thewasm32v1-nonetarget). Consider either:
- Explicitly stating the minimum Rust version (1.84.0+) instead of leaving it implied by SDK version, or
- Linking to the official Stellar environment setup documentation for maintainability.
This approach makes the requirement clear and more resilient to future version updates.
🤖 Fix all issues with AI agents
In `@services/supabase/generate-types.sh`:
- Around line 44-67: Replace the post-command $? checks with direct command
tests and/or enable fail-fast with set -e: run supabase gen types ... >
src/database.types.ts inside an if (or prefix script with set -e) and test the
output file with [ -s src/database.types.ts ] only; likewise call bun run
schemas inside an if bun run schemas then handle success/failure—remove all uses
of `$?` and rely on direct if-command checks (supabase gen types, bun run
schemas) and the file-size test for src/database.types.ts to simplify the flow.
In `@services/supabase/package.json`:
- Around line 20-21: The package.json has a JSON syntax error: the new script
entry "gen:remote:script" is missing a trailing comma before the next property
"migrate"; open package.json and add the missing comma after the
"gen:remote:script" entry so the "migrate" script is a valid sibling property
(verify the scripts object still parses and run a JSON lint or npm/yarn to
confirm).
In `@services/supabase/README.md`:
- Around line 64-69: Wrap the bare URL "https://supabase.com/dashboard" in angle
brackets so Markdown parsers treat it as a link (i.e., change it to
<https://supabase.com/dashboard>); update the line in the README where that URL
appears to use angle brackets for consistent rendering across Markdown engines.
| supabase gen types typescript --project-id "$PROJECT_ID" > src/database.types.ts | ||
|
|
||
| # Check if generation was successful | ||
| if [ $? -eq 0 ] && [ -s src/database.types.ts ]; then | ||
| echo "✅ Types generated successfully!" | ||
|
|
||
| # Generate schemas | ||
| echo "🔵 Generating Zod schemas..." | ||
| bun run schemas | ||
|
|
||
| if [ $? -eq 0 ]; then | ||
| echo "✅ Schemas generated successfully!" | ||
| echo "" | ||
| echo "📝 Next steps:" | ||
| echo " 1. Review the generated types in src/database.types.ts" | ||
| echo " 2. Commit the changes: git add src/database.types.ts src/database.schemas.ts" | ||
| echo " 3. git commit -m 'chore: update Supabase types from remote'" | ||
| else | ||
| echo "⚠️ Types generated but schemas generation failed" | ||
| fi | ||
| else | ||
| echo "❌ Failed to generate types" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider refactoring exit code checks for cleaner shell style.
The script is well-structured with excellent error handling and user guidance! We could enhance the readability by checking exit codes directly rather than using $?, as suggested by Shellcheck. Additionally, with set -e enabled, if the supabase gen types command fails, the script exits immediately—so the $? -eq 0 check on line 47 is somewhat redundant.
♻️ Proposed refactor
-# Generate types
-echo "🔵 Generating types..."
-supabase gen types typescript --project-id "$PROJECT_ID" > src/database.types.ts
-
-# Check if generation was successful
-if [ $? -eq 0 ] && [ -s src/database.types.ts ]; then
+# Generate types
+echo "🔵 Generating types..."
+if supabase gen types typescript --project-id "$PROJECT_ID" > src/database.types.ts && [ -s src/database.types.ts ]; then
echo "✅ Types generated successfully!"
# Generate schemas
echo "🔵 Generating Zod schemas..."
- bun run schemas
-
- if [ $? -eq 0 ]; then
+ if bun run schemas; then
echo "✅ Schemas generated successfully!"🧰 Tools
🪛 Shellcheck (0.11.0)
[style] 47-47: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.
(SC2181)
[style] 54-54: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.
(SC2181)
🤖 Prompt for AI Agents
In `@services/supabase/generate-types.sh` around lines 44 - 67, Replace the
post-command $? checks with direct command tests and/or enable fail-fast with
set -e: run supabase gen types ... > src/database.types.ts inside an if (or
prefix script with set -e) and test the output file with [ -s
src/database.types.ts ] only; likewise call bun run schemas inside an if bun run
schemas then handle success/failure—remove all uses of `$?` and rely on direct
if-command checks (supabase gen types, bun run schemas) and the file-size test
for src/database.types.ts to simplify the flow.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@services/supabase/package.json`:
- Line 28: Add documentation describing the PROJECT_ID / SUPABASE_PROJECT_ID
environment variable that the "types:remote" npm script relies on (the script
key "types:remote" in package.json), including the expected variable names, that
PROJECT_ID falls back to SUPABASE_PROJECT_ID, and an example of how to set them
in local and CI environments; update the repository's environment/setup docs
(e.g., README or env setup doc) to include these details and mention where the
generated src/database.types.ts will be written so new contributors can run the
command successfully.
- Line 20: Update the "gen:remote:script" npm script so it invokes the shell
explicitly instead of relying on the executable bit; change the script value for
the gen:remote:script entry to call a shell (e.g., sh or bash) with
./generate-types.sh so the command runs portably across environments and doesn't
depend on file mode.
| "db:push": "supabase db push --local", | ||
| "gen": "bun run types && bun run schemas", | ||
| "gen:remote": "bun run types:remote && bun run schemas", | ||
| "gen:remote:script": "./generate-types.sh", |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Nice addition of the remote-generation entrypoint; consider making execution explicit.
We could enhance this by invoking the script via a shell to avoid relying on the executable bit and to make the runtime requirement explicit.
♻️ Suggested tweak
- "gen:remote:script": "./generate-types.sh",
+ "gen:remote:script": "bash ./generate-types.sh",📝 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.
| "gen:remote:script": "./generate-types.sh", | |
| "gen:remote:script": "bash ./generate-types.sh", |
🤖 Prompt for AI Agents
In `@services/supabase/package.json` at line 20, Update the "gen:remote:script"
npm script so it invokes the shell explicitly instead of relying on the
executable bit; change the script value for the gen:remote:script entry to call
a shell (e.g., sh or bash) with ./generate-types.sh so the command runs portably
across environments and doesn't depend on file mode.
| "stop": "supabase stop", | ||
| "types": "supabase gen types typescript --local > src/database.types.ts", | ||
| "types:remote": "supabase gen types typescript --project-id ${PROJECT_ID} > src/database.types.ts" | ||
| "types:remote": "supabase gen types typescript --project-id ${PROJECT_ID:-${SUPABASE_PROJECT_ID}} > src/database.types.ts" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Great fallback for remote project IDs.
Consider documenting the PROJECT_ID / SUPABASE_PROJECT_ID expectation in the env setup docs to reduce onboarding friction.
🤖 Prompt for AI Agents
In `@services/supabase/package.json` at line 28, Add documentation describing the
PROJECT_ID / SUPABASE_PROJECT_ID environment variable that the "types:remote"
npm script relies on (the script key "types:remote" in package.json), including
the expected variable names, that PROJECT_ID falls back to SUPABASE_PROJECT_ID,
and an example of how to set them in local and CI environments; update the
repository's environment/setup docs (e.g., README or env setup doc) to include
these details and mention where the generated src/database.types.ts will be
written so new contributors can run the command successfully.
* feat: add didit KYC user flow (#768) * feat: add dididt KYC user flow * fix: build issues * fix(auth): add pending role state and fix user signup default role * chore: generate Supabase types from remote instance * fix: configure Vercel for monorepo and add Supabase types * feat: kindfi nft open zeppelin (#769) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: Reputation Contract for NFT Level Management (#773) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: Comprehensive Tests and Integration for NFT & Reputation Contracts (#774) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation * test(reputation): add reputation contract tests * test(nft): add NFT contract tests --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: Deployment Scripts and Configuration for NFT & Reputation Contracts (#775) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation * test(reputation): add reputation contract tests * test(nft): add NFT contract tests * chore: update cargo lock file * docs: update contracts README * script: add NFT contract deployment script * script: add reputation contract deployment script * chore: add environment example file --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: refactor nft-kindfi with SEP-0050 compatibility (#778) * feat: add SEP-0050 compliant NFTAttribute struct to nft-kindfi types Replace Vec<String> attributes with Vec<NFTAttribute> using structured fields (trait_type, value, display_type, max_value) to comply with the SEP-0050 Non-Fungible Metadata JSON Schema. * feat: update nft-kindfi tests to use NFTAttribute struct Adapt all test helpers and test cases to use the new SEP-0050 compliant NFTAttribute struct instead of plain strings for metadata attributes. * feat: update reputation nft_client to SEP-0050 attribute format Refactor NFTMetadata mirror and level attribute helpers to use the new NFTAttribute struct. Update cross-contract call types and all related tests for SEP-0050 compliance. * fix(kyc): prevent URL params cleanup on failure and add error toasts (#777) * fix(kyc): prevent URL params cleanup on failure and add error toasts * refactor(kyc): deduplicate error message string per code review * fix(kyc): sync with develop and add error logging per code review * refactor: centralize didit kyc status mapping logic (#779) * refactor: centralize didit kyc status mapping logic * refactor: apply code review feedback (arrow functions, type-only imports, and alias paths) * style(ui): use bg-primary token instead of hex code in progress bar (#780) Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * chore: add vercel skills set for OSS * fix: add new migration strategy * feat: add fundation support * chore: generate Supabase database types using MCP tool - Generated complete TypeScript types from Supabase database - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add complete Supabase database types - Generated TypeScript types from Supabase database using MCP tool - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add complete Supabase database types - Generated TypeScript types from Supabase database using MCP tool - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Matias Aguilar <aaguilar1x@gmail.com> Co-authored-by: Karen Giannetto <karengiannetto99@gmail.com> Co-authored-by: Delfina luna Corradini <105253541+delfinacorr@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: add didit KYC user flow (#768) * feat: add dididt KYC user flow * fix: build issues * fix(auth): add pending role state and fix user signup default role * chore: generate Supabase types from remote instance * fix: configure Vercel for monorepo and add Supabase types * feat: kindfi nft open zeppelin (#769) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: Reputation Contract for NFT Level Management (#773) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: Comprehensive Tests and Integration for NFT & Reputation Contracts (#774) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation * test(reputation): add reputation contract tests * test(nft): add NFT contract tests --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: Deployment Scripts and Configuration for NFT & Reputation Contracts (#775) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation * test(reputation): add reputation contract tests * test(nft): add NFT contract tests * chore: update cargo lock file * docs: update contracts README * script: add NFT contract deployment script * script: add reputation contract deployment script * chore: add environment example file --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: refactor nft-kindfi with SEP-0050 compatibility (#778) * feat: add SEP-0050 compliant NFTAttribute struct to nft-kindfi types Replace Vec<String> attributes with Vec<NFTAttribute> using structured fields (trait_type, value, display_type, max_value) to comply with the SEP-0050 Non-Fungible Metadata JSON Schema. * feat: update nft-kindfi tests to use NFTAttribute struct Adapt all test helpers and test cases to use the new SEP-0050 compliant NFTAttribute struct instead of plain strings for metadata attributes. * feat: update reputation nft_client to SEP-0050 attribute format Refactor NFTMetadata mirror and level attribute helpers to use the new NFTAttribute struct. Update cross-contract call types and all related tests for SEP-0050 compliance. * fix(kyc): prevent URL params cleanup on failure and add error toasts (#777) * fix(kyc): prevent URL params cleanup on failure and add error toasts * refactor(kyc): deduplicate error message string per code review * fix(kyc): sync with develop and add error logging per code review * refactor: centralize didit kyc status mapping logic (#779) * refactor: centralize didit kyc status mapping logic * refactor: apply code review feedback (arrow functions, type-only imports, and alias paths) * style(ui): use bg-primary token instead of hex code in progress bar (#780) Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * chore: add vercel skills set for OSS * fix: add new migration strategy * feat: add fundation support * chore: generate Supabase database types using MCP tool - Generated complete TypeScript types from Supabase database - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add complete Supabase database types - Generated TypeScript types from Supabase database using MCP tool - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add complete Supabase database types - Generated TypeScript types from Supabase database using MCP tool - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: WebAuthn cross environments --------- Co-authored-by: Matias Aguilar <aaguilar1x@gmail.com> Co-authored-by: Karen Giannetto <karengiannetto99@gmail.com> Co-authored-by: Delfina luna Corradini <105253541+delfinacorr@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: add didit KYC user flow (#768) * feat: add dididt KYC user flow * fix: build issues * fix(auth): add pending role state and fix user signup default role * chore: generate Supabase types from remote instance * fix: configure Vercel for monorepo and add Supabase types * feat: kindfi nft open zeppelin (#769) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: Reputation Contract for NFT Level Management (#773) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: Comprehensive Tests and Integration for NFT & Reputation Contracts (#774) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation * test(reputation): add reputation contract tests * test(nft): add NFT contract tests --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: Deployment Scripts and Configuration for NFT & Reputation Contracts (#775) * feat: add KindFi NFT contract structure and types - Add Cargo.toml for nft-kindfi contract with OpenZeppelin dependencies - Add types.rs with NFTMetadata struct and StorageKey enum - Add errors.rs with custom error definitions - Add events.rs with metadata update events * feat: implement KindFi NFT core functionality - Add lib.rs with main contract implementing NonFungibleToken, AccessControl, and custom metadata functions - Add metadata.rs for on-chain metadata storage and retrieval - Add mint.rs for sequential token ID minting with metadata - Add burn.rs for token burning functionality - Implement role-based access control (minter, burner, metadata_manager) - Add OpenZeppelin Stellar Contracts integration * fix: update workspace members for compilation - Comment out non-existent contract members to allow compilation - Add nft-kindfi to implemented contracts - Reorganize workspace members with clear sections * chore: update Cargo.lock with new contract dependencies * feat: update to OpenZeppelin Stellar Contracts v0.6.0 - Update soroban-sdk from 22.0.6 to 23.4.0 - Update all OpenZeppelin dependencies from v0.3.0 to v0.6.0 - Restructure dependencies: stellar-access-control → stellar-access, stellar-non-fungible → stellar-tokens * feat: update nft-kindfi dependencies for v0.6.0 - Update contract dependencies to use new package structure - stellar-access-control → stellar-access - stellar-access-control-macros → stellar-macros - stellar-non-fungible → stellar-tokens * refactor: update imports for OpenZeppelin v0.6.0 - Update stellar_access_control imports to stellar_access::access_control - Update stellar_non_fungible imports to stellar_tokens::non_fungible - Update stellar_access_control_macros to stellar_macros - Fix AccessControl trait method signatures to match v0.6.0 API * chore: kindfi information in the workspace.package and comments added in members * fix: target changed to wasm32v1-none and leaving necessary components * chore: Add additional information about kindfi to contract cargo.toml and change crate-type to just (cdylib) * fix: config.toml file removed since it generated conflicts with rust base and the tests did not compile * refactor: change in the use of events, since due to the sdk update they are now used with the contractevent and topic macro * refactor: structure change in the use of events of the form .publish(e) for the different files that propagate the events * doc: detailed documentation about the nft-kindfi contract * chore: add admin.require_auth() add admin.require_auth() for validation and security of contract initialization * fix: validation was added for the metadata and the event was created to send this * feat: add root cargo manifest for contracts workspace * feat(reputation): add cargo manifest for reputation contract * feat(reputation): add reputation contract main library * feat(reputation): add reputation contract types and data structures * feat(reputation): add reputation contract storage layer * feat(reputation): add reputation contract error types * feat(reputation): add reputation contract events * feat(reputation): add NFT client integration for reputation contract * docs(reputation): add reputation contract documentation * test(reputation): add reputation contract tests * test(nft): add NFT contract tests * chore: update cargo lock file * docs: update contracts README * script: add NFT contract deployment script * script: add reputation contract deployment script * chore: add environment example file --------- Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * feat: refactor nft-kindfi with SEP-0050 compatibility (#778) * feat: add SEP-0050 compliant NFTAttribute struct to nft-kindfi types Replace Vec<String> attributes with Vec<NFTAttribute> using structured fields (trait_type, value, display_type, max_value) to comply with the SEP-0050 Non-Fungible Metadata JSON Schema. * feat: update nft-kindfi tests to use NFTAttribute struct Adapt all test helpers and test cases to use the new SEP-0050 compliant NFTAttribute struct instead of plain strings for metadata attributes. * feat: update reputation nft_client to SEP-0050 attribute format Refactor NFTMetadata mirror and level attribute helpers to use the new NFTAttribute struct. Update cross-contract call types and all related tests for SEP-0050 compliance. * fix(kyc): prevent URL params cleanup on failure and add error toasts (#777) * fix(kyc): prevent URL params cleanup on failure and add error toasts * refactor(kyc): deduplicate error message string per code review * fix(kyc): sync with develop and add error logging per code review * refactor: centralize didit kyc status mapping logic (#779) * refactor: centralize didit kyc status mapping logic * refactor: apply code review feedback (arrow functions, type-only imports, and alias paths) * style(ui): use bg-primary token instead of hex code in progress bar (#780) Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * chore: add vercel skills set for OSS * fix: add new migration strategy * feat: add fundation support * chore: generate Supabase database types using MCP tool - Generated complete TypeScript types from Supabase database - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add complete Supabase database types - Generated TypeScript types from Supabase database using MCP tool - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add complete Supabase database types - Generated TypeScript types from Supabase database using MCP tool - Includes all tables, enums, functions, and relationships - Fixes Vercel build error: Module '@services/supabase' has no exported member 'Database' Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: WebAuthn cross environments * Fix: WebAuthn non dynamic * Fix: add path alias --------- Co-authored-by: Matias Aguilar <aaguilar1x@gmail.com> Co-authored-by: Karen Giannetto <karengiannetto99@gmail.com> Co-authored-by: Delfina luna Corradini <105253541+delfinacorr@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Summary by CodeRabbit
New Features
Removals
Documentation
Other
✏️ Tip: You can customize this high-level summary in your review settings.