Skip to content

Conversation

@Saahi30
Copy link
Collaborator

@Saahi30 Saahi30 commented Nov 16, 2025

📝 Description

This pull request introduces the profile feature for both brand and creator users. It adds new profile pages, reusable profile components, and the corresponding API client integration for frontend and backend. The backend is updated to support profile-related endpoints.

🔧 Changes Made

  • Added new API route: backend/app/api/routes/profiles.py
  • Updated backend/app/main.py to include profile endpoints
  • Created profile pages for brand and creator: frontend/app/brand/profile/, frontend/app/creator/profile/
  • Added reusable profile components: frontend/components/profile/
  • Implemented profile API client: frontend/lib/api/profile.ts
  • Updated brand and creator home pages to integrate profile features

📷 Screenshots or Visual Changes (if applicable)

Screenshot 2025-11-16 at 11 28 43 PM Screenshot 2025-11-16 at 11 28 51 PM Screenshot 2025-11-16 at 11 28 59 PM

✅ Checklist

  • I have read the contributing guidelines.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation (if applicable).
  • Any dependent changes have been merged and published in downstream modules.

Summary by CodeRabbit

  • New Features
    • Added comprehensive profile management system for brands and creators with view, edit, and save capabilities.
    • Introduced profile completion percentage tracking displayed with progress visualization.
    • Implemented AI-assisted profile filling to help users complete profile information.
    • Added profile access button to brand and creator home headers.
    • New editable profile pages with multi-section forms and diverse input controls.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 16, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces comprehensive profile management for brands and creators with backend API endpoints for CRUD operations, profile completion tracking, and AI-assisted profile population via Gemini. The frontend provides complete profile pages with editing capabilities, form management, and UI components for rendering profile data.

Changes

Cohort / File(s) Summary
Backend Profile API
backend/app/api/routes/profiles.py
New module providing endpoints for GET/PUT brand and creator profiles, AI-fill operations via Gemini, profile completion percentage calculations with weighted field scoring, request/response models, and robust error handling with Supabase integration.
Backend Router Integration
backend/app/main.py
Imports and registers the profiles router to expose profile endpoints in the main FastAPI application.
Frontend Profile API Client
frontend/lib/api/profile.ts
New module defining BrandProfile and CreatorProfile TypeScript interfaces and providing client functions for fetching, updating, and AI-filling brand and creator profiles.
Frontend Profile Pages
frontend/app/brand/profile/page.tsx, frontend/app/creator/profile/page.tsx
Complete profile editor pages with form state management, edit mode toggles, save/cancel actions, AI-fill modal workflows, logout handling, collapsible form sections, and multi-field input controls.
Frontend Header Updates
frontend/app/brand/home/page.tsx, frontend/app/creator/home/page.tsx
Add ProfileButton component alongside logout button in brand and creator home headers.
Frontend Shared Profile Components
frontend/components/profile/{ArrayInput,CollapsibleSection,JsonInput,ProfileButton}.tsx
New reusable UI components: ArrayInput (editable string list with add/remove), CollapsibleSection (toggleable sections), JsonInput (JSON object editor), and ProfileButton (avatar/profile link with role-based navigation).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Frontend as Frontend App
    participant API as Backend API
    participant DB as Supabase DB
    participant Gemini as Gemini API

    User->>Frontend: View Profile Page
    Frontend->>API: GET /brand/profile
    API->>DB: Fetch brand record
    DB-->>API: Return profile + completion %
    API-->>Frontend: Profile data
    Frontend->>Frontend: Display profile & form

    User->>Frontend: Enter edit mode & modify fields
    Frontend->>Frontend: Update local formData state

    User->>Frontend: Click Save
    Frontend->>API: PUT /brand/profile
    API->>API: Sanitize restricted fields
    API->>API: Calculate completion %
    API->>DB: Update brand record
    DB-->>API: Confirm update
    API-->>Frontend: Updated profile
    Frontend->>Frontend: Exit edit mode, show success

    rect rgb(200, 220, 240)
    note over User,Gemini: AI-Fill Flow
    User->>Frontend: Click AI Fill
    Frontend->>Frontend: Open modal, accept input
    User->>Frontend: Enter context + submit
    Frontend->>API: POST /brand/profile/ai-fill
    API->>Gemini: Send user input + context
    Gemini-->>API: Return JSON with filled fields
    API->>API: Parse & merge with existing profile
    API-->>Frontend: Merged profile data
    Frontend->>Frontend: Update formData, enable edit, show alert
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • backend/app/api/routes/profiles.py: Dense logic including profile completion calculations with weighted field sets, Gemini JSON parsing and error handling, field sanitization logic, and multiple endpoint implementations. Verify completion percentage weights and field validation rules.
  • frontend/app/brand/profile/page.tsx and frontend/app/creator/profile/page.tsx: Large, feature-rich components with complex state management (profile state, formData state, editing modes, loading states). Review form data merging logic in AI-fill flow, edit mode transitions, and error handling paths.
  • Integration points: Verify that sanitized restricted fields are consistently applied, that AI-fill merging logic preserves existing non-empty fields as intended, and that completion percentage persistence is correct across all update paths.

Possibly related PRs

Suggested reviewers

  • chandansgowda

Poem

🐰 A profile's tale, now complete with care,
Fields filled by form and Gemini's flair,
Completion bars dance as data flows free,
Brand and creator profiles, a sight to see!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding profile pages, components, and API integration for brands and creators. It accurately summarizes the primary objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Saahi30
Copy link
Collaborator Author

Saahi30 commented Nov 16, 2025

@CodeRabbit review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
backend/app/api/routes/profiles.py (1)

42-44: Remove unused locals to satisfy Ruff F841.

Both completion helpers assign total = ... (Line 44 / Line 86) but never use it. Ruff flags this as F841, which fails CI. Please drop those assignments.

-    completed = 0
-    total = len(required_fields) + len(important_fields) + len(nice_to_have_fields)
+    completed = 0

Repeat in calculate_creator_completion_percentage.

Also applies to: 85-87

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26121e0 and 6e1ec90.

📒 Files selected for processing (11)
  • backend/app/api/routes/profiles.py (1 hunks)
  • backend/app/main.py (2 hunks)
  • frontend/app/brand/home/page.tsx (2 hunks)
  • frontend/app/brand/profile/page.tsx (1 hunks)
  • frontend/app/creator/home/page.tsx (2 hunks)
  • frontend/app/creator/profile/page.tsx (1 hunks)
  • frontend/components/profile/ArrayInput.tsx (1 hunks)
  • frontend/components/profile/CollapsibleSection.tsx (1 hunks)
  • frontend/components/profile/JsonInput.tsx (1 hunks)
  • frontend/components/profile/ProfileButton.tsx (1 hunks)
  • frontend/lib/api/profile.ts (1 hunks)
🧰 Additional context used
🪛 Ruff (0.14.4)
backend/app/api/routes/profiles.py

43-43: Local variable total is assigned to but never used

Remove assignment to unused variable total

(F841)


85-85: Local variable total is assigned to but never used

Remove assignment to unused variable total

(F841)


110-110: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


125-125: Consider moving this statement to an else block

(TRY300)


129-129: Use explicit conversion flag

Replace with conversion flag

(RUF010)


136-136: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


158-161: Abstract raise to an inner function

(TRY301)


176-176: Consider moving this statement to an else block

(TRY300)


182-182: Use explicit conversion flag

Replace with conversion flag

(RUF010)


188-188: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


203-203: Consider moving this statement to an else block

(TRY300)


207-207: Use explicit conversion flag

Replace with conversion flag

(RUF010)


214-214: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


236-239: Abstract raise to an inner function

(TRY301)


254-254: Consider moving this statement to an else block

(TRY300)


260-260: Use explicit conversion flag

Replace with conversion flag

(RUF010)


273-273: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


435-438: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


437-437: Use explicit conversion flag

Replace with conversion flag

(RUF010)


440-443: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


442-442: Use explicit conversion flag

Replace with conversion flag

(RUF010)


447-447: Use explicit conversion flag

Replace with conversion flag

(RUF010)


454-454: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


619-622: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


621-621: Use explicit conversion flag

Replace with conversion flag

(RUF010)


624-627: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


626-626: Use explicit conversion flag

Replace with conversion flag

(RUF010)


631-631: Use explicit conversion flag

Replace with conversion flag

(RUF010)

🔇 Additional comments (1)
frontend/app/creator/home/page.tsx (1)

51-65: Header integration looks solid

ProfileButton slots neatly alongside the existing logout control and keeps state handling untouched. No issues spotted.

Comment on lines 340 to 357
<label className="mb-1 block text-sm font-medium text-gray-700">Founded Year</label>
<input
type="number"
value={formData.founded_year || ""}
onChange={(e) => updateField("founded_year", parseInt(e.target.value) || undefined)}
disabled={!isEditing}
className="w-full rounded-lg border border-gray-300 px-3 py-2 disabled:bg-gray-50"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-gray-700">Headquarters Location</label>
<input
type="text"
value={formData.headquarters_location || ""}
onChange={(e) => updateField("headquarters_location", e.target.value)}
disabled={!isEditing}
className="w-full rounded-lg border border-gray-300 px-3 py-2 disabled:bg-gray-50"
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle empty numeric inputs without breaking zero values

Here (and in the other numeric inputs across this page) the parseInt/parseFloat(... ) || … pattern forces zero to become undefined/default values, and even clearing a field can snap back to 1/0 due to the fallback. Please gate on the empty string instead, e.g.:

const raw = e.target.value;
updateField("founded_year", raw === "" ? undefined : parseInt(raw, 10));

Apply the same fix to every numeric field so legitimate zero entries persist.

🤖 Prompt for AI Agents
In frontend/app/brand/profile/page.tsx around lines 340 to 357, the current use
of parseInt(...) || undefined (and similar parseFloat(...) || ...) causes valid
zero values to be coerced to undefined and cleared inputs to fallback to
non-empty defaults; change the onChange handlers for all numeric inputs to first
read the raw string value and set undefined when raw === "" otherwise pass the
parsed number (use parseInt(raw, 10) or parseFloat(raw) as appropriate), and
apply this same empty-string gating fix to every numeric field on the page so
zeros are preserved and clearing a field results in undefined.

Comment on lines 392 to 405
type="number"
value={formData.instagram_followers || ""}
onChange={(e) => updateField("instagram_followers", parseInt(e.target.value) || undefined)}
disabled={!isEditing}
className="w-full rounded-lg border border-gray-300 px-3 py-2 disabled:bg-gray-50"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-gray-700">YouTube Handle</label>
<input
type="text"
value={formData.youtube_handle || ""}
onChange={(e) => updateField("youtube_handle", e.target.value)}
disabled={!isEditing}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t drop legitimate zero values from numeric fields

Using parseInt/parseFloat(... ) || undefined (and similar || fallbacks) means a user-entered 0 becomes falsy and gets replaced with undefined, so we can’t store zero followers, zero budget, etc. The same pattern appears throughout this file. Please switch to an explicit empty-string check so that zero remains zero—for example:

const raw = e.target.value;
updateField("instagram_followers", raw === "" ? undefined : parseInt(raw, 10));

Apply the same approach for every integer/decimal field (parseFloat respectively).

🤖 Prompt for AI Agents
In frontend/app/creator/profile/page.tsx around lines 392 to 405, numeric inputs
use expressions like parseInt(e.target.value) || undefined which turn legitimate
0 values into undefined; change each handler to first read const raw =
e.target.value and then call updateField(field, raw === "" ? undefined :
parseInt(raw, 10)) for integer fields (and parseFloat(raw) for decimal fields)
so empty strings map to undefined but numeric zero is preserved; apply this same
explicit empty-string check pattern for every integer/decimal input handler
throughout the file.

Comment on lines 21 to 33
<button
onClick={() => setIsOpen(!isOpen)}
className="flex w-full items-center justify-between px-6 py-4 text-left transition hover:bg-gray-50"
>
<h3 className="text-lg font-semibold text-gray-900">{title}</h3>
{isOpen ? (
<ChevronUp className="h-5 w-5 text-gray-500" />
) : (
<ChevronDown className="h-5 w-5 text-gray-500" />
)}
</button>
{isOpen && <div className="border-t border-gray-200 px-6 py-4">{children}</div>}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Prevent collapsible toggle from submitting parent forms.

Line 24 uses a plain <button> inside a component that will live within profile forms. In HTML the default type is "submit", so clicking the collapsible header will fire a form submission instead of just toggling the section. That breaks the editing experience. Please set the button’s type to "button" (and optionally drive aria-expanded) so it behaves purely as a disclosure control.

-      <button
-        onClick={() => setIsOpen(!isOpen)}
+      <button
+        type="button"
+        onClick={() => setIsOpen((prev) => !prev)}
+        aria-expanded={isOpen}
         className="flex w-full items-center justify-between px-6 py-4 text-left transition hover:bg-gray-50"
       >
🤖 Prompt for AI Agents
In frontend/components/profile/CollapsibleSection.tsx around lines 21 to 33, the
button lacks an explicit type so clicking it inside a form will submit the
parent form; change the button to have type="button" to prevent form submission
and add aria-expanded={isOpen} to reflect its disclosure state for
accessibility. Ensure you keep the onClick handler and existing classes/contents
unchanged and only add the type and aria-expanded attributes.

Comment on lines 18 to 36
const [textValue, setTextValue] = useState(
value ? JSON.stringify(value, null, 2) : ""
);
const [error, setError] = useState<string | null>(null);

const handleBlur = () => {
if (!textValue.trim()) {
onChange(null);
setError(null);
return;
}

try {
const parsed = JSON.parse(textValue);
onChange(parsed);
setError(null);
} catch (e) {
setError("Invalid JSON format");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Sync local text with incoming value prop

textValue is initialized from value once, but we never update it when the parent supplies fresh data (initial fetch, cancel, AI fill, etc.). That leaves existing JSON fields blank in the UI and risks overwriting server state with an empty object on save. Please watch the value prop and refresh textValue (and clear any stale error) whenever it changes.

-  const [textValue, setTextValue] = useState(
-    value ? JSON.stringify(value, null, 2) : ""
-  );
+  const [textValue, setTextValue] = useState(
+    value ? JSON.stringify(value, null, 2) : ""
+  );
   const [error, setError] = useState<string | null>(null);
+
+  useEffect(() => {
+    setTextValue(value ? JSON.stringify(value, null, 2) : "");
+    setError(null);
+  }, [value]);
🤖 Prompt for AI Agents
In frontend/components/profile/JsonInput.tsx around lines 18 to 36, textValue is
only initialized from the value prop and not updated when value changes; add a
useEffect that watches value and calls setTextValue(value ?
JSON.stringify(value, null, 2) : "") and setError(null) so the textarea reflects
incoming updates (initial fetch, cancel, AI fill) and clears stale errors.

Comment on lines 105 to 117
<div className="relative h-full w-full overflow-hidden rounded-full bg-gray-200">
<Image
src={profileImageUrl}
alt={userName || "Profile"}
fill
className="object-cover"
onError={() => {
setImageError(true);
setImageLoading(false);
}}
onLoad={() => setImageLoading(false)}
/>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clear imageError when the image eventually loads

If the avatar takes longer than three seconds, the timeout flips imageError to true, and we never set it back to false inside onLoad. Slow-but-successful loads will therefore keep showing the fallback monogram forever. Please reset the flag when the image finishes loading.

-              onLoad={() => setImageLoading(false)}
+              onLoad={() => {
+                setImageLoading(false);
+                setImageError(false);
+              }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="relative h-full w-full overflow-hidden rounded-full bg-gray-200">
<Image
src={profileImageUrl}
alt={userName || "Profile"}
fill
className="object-cover"
onError={() => {
setImageError(true);
setImageLoading(false);
}}
onLoad={() => setImageLoading(false)}
/>
</div>
<div className="relative h-full w-full overflow-hidden rounded-full bg-gray-200">
<Image
src={profileImageUrl}
alt={userName || "Profile"}
fill
className="object-cover"
onError={() => {
setImageError(true);
setImageLoading(false);
}}
onLoad={() => {
setImageLoading(false);
setImageError(false);
}}
/>
</div>
🤖 Prompt for AI Agents
In frontend/components/profile/ProfileButton.tsx around lines 105 to 117, the
timeout can set imageError to true for slow-but-successful loads and onLoad
currently only clears imageLoading; update the onLoad handler to also reset
imageError to false (i.e., call setImageError(false) before or alongside
setImageLoading(false)) so a late-arriving image will replace the fallback; if
you track a timeout ID elsewhere, also clear that timeout when the image loads
to avoid race conditions.

Comment on lines 5 to 168
export interface BrandProfile {
id: string;
user_id: string;
company_name: string;
company_tagline?: string;
company_description?: string;
company_logo_url?: string;
company_cover_image_url?: string;
industry: string;
sub_industry?: string[];
company_size?: string;
founded_year?: number;
headquarters_location?: string;
company_type?: string;
website_url: string;
contact_email?: string;
contact_phone?: string;
social_media_links?: Record<string, any>;
target_audience_age_groups?: string[];
target_audience_gender?: string[];
target_audience_locations?: string[];
target_audience_interests?: string[];
target_audience_income_level?: string[];
target_audience_description?: string;
brand_values?: string[];
brand_personality?: string[];
brand_voice?: string;
brand_colors?: Record<string, any>;
marketing_goals?: string[];
campaign_types_interested?: string[];
preferred_content_types?: string[];
preferred_platforms?: string[];
campaign_frequency?: string;
monthly_marketing_budget?: number;
influencer_budget_percentage?: number;
budget_per_campaign_min?: number;
budget_per_campaign_max?: number;
typical_deal_size?: number;
payment_terms?: string;
offers_product_only_deals?: boolean;
offers_affiliate_programs?: boolean;
affiliate_commission_rate?: number;
preferred_creator_niches?: string[];
preferred_creator_size?: string[];
preferred_creator_locations?: string[];
minimum_followers_required?: number;
minimum_engagement_rate?: number;
content_dos?: string[];
content_donts?: string[];
brand_safety_requirements?: string[];
competitor_brands?: string[];
exclusivity_required?: boolean;
exclusivity_duration_months?: number;
past_campaigns_count?: number;
successful_partnerships?: string[];
case_studies?: any[];
average_campaign_roi?: number;
products_services?: string[];
product_price_range?: string;
product_categories?: string[];
seasonal_products?: boolean;
product_catalog_url?: string;
business_verified?: boolean;
payment_verified?: boolean;
tax_id_verified?: boolean;
profile_completion_percentage: number;
is_active?: boolean;
is_featured?: boolean;
is_verified_brand?: boolean;
subscription_tier?: string;
featured_until?: string;
ai_profile_summary?: string;
search_keywords?: string[];
matching_score_base?: number;
total_deals_posted?: number;
total_deals_completed?: number;
total_spent?: number;
average_deal_rating?: number;
created_at?: string;
updated_at?: string;
last_active_at?: string;
[key: string]: any;
}

export interface CreatorProfile {
id: string;
user_id: string;
display_name: string;
bio?: string;
tagline?: string;
profile_picture_url?: string;
cover_image_url?: string;
website_url?: string;
youtube_url?: string;
youtube_handle?: string;
youtube_subscribers?: number;
instagram_url?: string;
instagram_handle?: string;
instagram_followers?: number;
tiktok_url?: string;
tiktok_handle?: string;
tiktok_followers?: number;
twitter_url?: string;
twitter_handle?: string;
twitter_followers?: number;
twitch_url?: string;
twitch_handle?: string;
twitch_followers?: number;
linkedin_url?: string;
facebook_url?: string;
primary_niche: string;
secondary_niches?: string[];
content_types?: string[];
content_language?: string[];
total_followers: number;
total_reach?: number;
average_views?: number;
engagement_rate?: number;
audience_age_primary?: string;
audience_age_secondary?: string[];
audience_gender_split?: Record<string, any>;
audience_locations?: Record<string, any>;
audience_interests?: string[];
average_engagement_per_post?: number;
posting_frequency?: string;
best_performing_content_type?: string;
peak_posting_times?: Record<string, any>;
years_of_experience?: number;
content_creation_full_time: boolean;
team_size: number;
equipment_quality?: string;
editing_software?: string[];
collaboration_types?: string[];
preferred_brands_style?: string[];
not_interested_in?: string[];
rate_per_post?: number;
rate_per_video?: number;
rate_per_story?: number;
rate_per_reel?: number;
rate_negotiable: boolean;
accepts_product_only_deals: boolean;
minimum_deal_value?: number;
preferred_payment_terms?: string;
portfolio_links?: string[];
past_brand_collaborations?: string[];
case_study_links?: string[];
media_kit_url?: string;
email_verified?: boolean;
phone_verified?: boolean;
identity_verified?: boolean;
profile_completion_percentage: number;
is_active?: boolean;
is_featured?: boolean;
is_verified_creator?: boolean;
featured_until?: string;
ai_profile_summary?: string;
search_keywords?: string[];
matching_score_base?: number;
social_platforms?: Record<string, any>;
created_at?: string;
updated_at?: string;
last_active_at?: string;
[key: string]: any;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Align profile typings with nullable Supabase fields.

The brand/creator interfaces (e.g., Line 13 company_tagline?: string;, Line 116 secondary_niches?: string[];) model nullable columns as plain string, number, string[], etc. Supabase returns null for unset nullable columns, so at runtime these properties are often null. Because the type system claims they are concrete strings/arrays, downstream code can safely call methods like .trim() or iterate the array and we will crash when the value is actually null. Please widen these optional fields to include null (or introduce a helper like type Nullable<T> = T | null;) so the TypeScript contract matches the API payload.

+type Nullable<T> = T | null;
+
 export interface BrandProfile {
   id: string;
   user_id: string;
-  company_tagline?: string;
-  company_description?: string;
+  company_tagline?: Nullable<string>;
+  company_description?: Nullable<string>;
   ...
-  sub_industry?: string[];
+  sub_industry?: Nullable<string[]>;
   ...
-  monthly_marketing_budget?: number;
+  monthly_marketing_budget?: Nullable<number>;

Please apply the same treatment across the rest of the nullable brand and creator fields.

Committable suggestion skipped: line range outside the PR's diff.

@Saahi30 Saahi30 merged commit 83dd53e into dev Nov 16, 2025
1 check passed
@Saahi30 Saahi30 deleted the profile branch November 17, 2025 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants