Skip to content

fix: Add Social Network modal with URL validation#13

Merged
nathanialhenniges merged 2 commits intomainfrom
claude/ecstatic-neumann
Apr 15, 2026
Merged

fix: Add Social Network modal with URL validation#13
nathanialhenniges merged 2 commits intomainfrom
claude/ecstatic-neumann

Conversation

@nathanialhenniges
Copy link
Copy Markdown
Member

@nathanialhenniges nathanialhenniges commented Apr 15, 2026

Summary

  • Replace broken handleActivateNetwork (set isActive:true with empty URL) with a modal flow that requires a valid URL before activating — fixes 4-layer silent failure chain: activeNetworks filter, socialDirty gate, handleSaveSocial skip, and D1 url NOT NULL constraint
  • Remove 20-network hard cap in available list (scroll container already limits viewport)
  • Fix touch-invisible remove button: drop hidden group-hover:flex, use always-visible opacity-60 with hover/focus ramp

Root cause

handleActivateNetwork only set isActive: true — URL stayed empty. Every downstream check required a truthy URL, so the network silently disappeared from both lists after click.

Test plan

  • Click any brand in "Add Social Network" → modal opens with brand name + URL input
  • Empty/invalid URL → Add button disabled, inline error shown
  • Valid URL → modal closes, network appears in Active list, Save/Discard bar appears
  • Save → toast confirms, row persists after reload
  • Scroll "Add Social Network" past 20 networks (search "bandcamp" etc.)
  • Remove button visible on mobile viewport (no hover required)
  • bun run check-types passes
  • Dark + light mode visual check

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a modal dialog for adding social networks with URL validation (requires valid HTTP/HTTPS URL).
    • Removed the 20-network display limit—all available networks can now be accessed.
    • Network add/remove buttons now have improved visibility and keyboard focus support.
    • URL input fields include placeholder text and inline validation messaging for clearer guidance.

Replace broken handleActivateNetwork (set isActive with empty URL) with
modal flow that requires valid URL before activating — fixes silent failure
chain through activeNetworks filter, socialDirty gate, and handleSaveSocial skip.

Also removes 20-network hard cap and fixes touch-invisible remove button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 720e00e8-2bc2-4df4-8ab2-f2cd694e57b8

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The social tab component was refactored to implement a modal-based flow for adding social networks. Users now enter a URL via dialog before activation, with validation for HTTP/HTTPS format. The 20-network limit was removed, and button styling was updated for improved accessibility and interaction states.

Changes

Cohort / File(s) Summary
Social Tab Enhancement
apps/web/src/components/admin/builder/social-tab.tsx
Replaced direct network activation with modal-based "Add Social Network" dialog. Added pendingSlug, pendingUrl, and pendingUrlValid state management with URL validation (requiring non-empty, parseable HTTP/HTTPS URLs). Introduced Dialog UI with per-network URL placeholders, inline validation messaging, and disabled Add action for invalid URLs. Removed 20-network limit on available networks. Updated remove button styling from hover-hidden to persistent opacity-based visibility. Enhanced add button with focus-visible styling support. On confirm, clears search state and scrolls to newly added network.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit's delight, a dialog so bright! ✨
Add networks with URLs, no limits in sight!
Modal flows dance, validation runs true,
Focus and hover, accessibility shines through!
Social tabs flourish, our configs take flight! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: Add Social Network modal with URL validation' directly and specifically describes the main change: introducing a modal-based flow with URL validation for adding social networks, addressing the root cause of the broken handleActivateNetwork flow.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/ecstatic-neumann

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.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
apps/web/src/components/admin/builder/social-tab.tsx (2)

396-413: Consider extracting the IIFE to a variable for cleaner JSX.

The immediately invoked function expression inside JSX works but is harder to read. Extract it to a const before the return for clarity.

♻️ Proposed refactor
+	const brandIcon = pendingBrand && (() => {
+		const { bg, fg } = getAdminThemeColors(resolvedTheme);
+		const fill = getAccessibleIconFill(pendingBrand.hex, bg, fg);
+		const needsRing = isLowLuminance(pendingBrand.hex);
+		return (
+			<div
+				className={cn(
+					"flex h-8 w-8 shrink-0 items-center justify-center rounded-full",
+					needsRing && "ring-1 ring-border dark:ring-white/20",
+				)}
+				style={{ backgroundColor: `${pendingBrand.hex}20` }}
+			>
+				<svg viewBox="0 0 24 24" className="h-4 w-4" aria-hidden="true">
+					<path d={pendingBrand.svgPath} fill={fill} />
+				</svg>
+			</div>
+		);
+	})();

 // Then in JSX:
 <DialogTitle className="flex items-center gap-3">
-	{pendingBrand && (() => {
-		const { bg, fg } = getAdminThemeColors(resolvedTheme);
-		const fill = getAccessibleIconFill(pendingBrand.hex, bg, fg);
-		const needsRing = isLowLuminance(pendingBrand.hex);
-		return (
-			<div
-				className={cn(
-					"flex h-8 w-8 shrink-0 items-center justify-center rounded-full",
-					needsRing && "ring-1 ring-border dark:ring-white/20",
-				)}
-				style={{ backgroundColor: `${pendingBrand.hex}20` }}
-			>
-				<svg viewBox="0 0 24 24" className="h-4 w-4" aria-hidden="true">
-					<path d={pendingBrand.svgPath} fill={fill} />
-				</svg>
-			</div>
-		);
-	})()}
+	{brandIcon}
 	<span>Add {pendingBrand?.name ?? "Network"}</span>
 </DialogTitle>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/admin/builder/social-tab.tsx` around lines 396 - 413,
The JSX contains an IIFE that renders the pending brand icon; extract that block
into a const (e.g., pendingBrandIcon) just above the return so the JSX becomes
{pendingBrandIcon} for readability. Compute the const using the same symbols:
check pendingBrand, call getAdminThemeColors(resolvedTheme),
getAccessibleIconFill(pendingBrand.hex, bg, fg),
isLowLuminance(pendingBrand.hex), and cn to build the className and style, and
return the same <div>…<svg> structure; ensure the const is null/undefined when
pendingBrand is falsy so JSX renders nothing.

431-446: Link the error message to the input for screen reader accessibility.

The input has aria-invalid but the error message isn't programmatically associated with it. Add an id to the error and reference it via aria-describedby for better screen reader support.

♿ Proposed accessibility improvement
 <Input
 	id="add-network-url"
 	type="url"
 	autoFocus
 	value={pendingUrl}
 	onChange={(e) => setPendingUrl(e.target.value)}
 	placeholder={
 		pendingSlug ? URL_PLACEHOLDERS[pendingSlug] ?? "https://" : "https://"
 	}
 	aria-invalid={pendingUrl.length > 0 && !pendingUrlValid}
+	aria-describedby={pendingUrl.length > 0 && !pendingUrlValid ? "add-network-url-error" : undefined}
 />
 {pendingUrl.length > 0 && !pendingUrlValid && (
-	<p className="text-xs text-destructive">
+	<p id="add-network-url-error" className="text-xs text-destructive">
 		Enter a valid URL starting with http:// or https://
 	</p>
 )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/admin/builder/social-tab.tsx` around lines 431 - 446,
The error text under the Input is not associated with the input for screen
readers: give the error element a stable id (e.g. "add-network-url-error") and
update the Input to set aria-describedby to that id only when the error is shown
(i.e., when pendingUrl.length > 0 && !pendingUrlValid); keep aria-invalid as-is
and ensure aria-describedby is omitted or null when there is no error so screen
readers don't reference a non-existent element. Use the existing Input component
and the pendingUrl/pendingUrlValid state to control this.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/src/components/admin/builder/social-tab.tsx`:
- Around line 396-413: The JSX contains an IIFE that renders the pending brand
icon; extract that block into a const (e.g., pendingBrandIcon) just above the
return so the JSX becomes {pendingBrandIcon} for readability. Compute the const
using the same symbols: check pendingBrand, call
getAdminThemeColors(resolvedTheme), getAccessibleIconFill(pendingBrand.hex, bg,
fg), isLowLuminance(pendingBrand.hex), and cn to build the className and style,
and return the same <div>…<svg> structure; ensure the const is null/undefined
when pendingBrand is falsy so JSX renders nothing.
- Around line 431-446: The error text under the Input is not associated with the
input for screen readers: give the error element a stable id (e.g.
"add-network-url-error") and update the Input to set aria-describedby to that id
only when the error is shown (i.e., when pendingUrl.length > 0 &&
!pendingUrlValid); keep aria-invalid as-is and ensure aria-describedby is
omitted or null when there is no error so screen readers don't reference a
non-existent element. Use the existing Input component and the
pendingUrl/pendingUrlValid state to control this.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f6fa80ea-617e-4f2a-9130-47e05ef0964c

📥 Commits

Reviewing files that changed from the base of the PR and between 9ea7c65 and 3eeed6b.

📒 Files selected for processing (1)
  • apps/web/src/components/admin/builder/social-tab.tsx

@nathanialhenniges nathanialhenniges merged commit d322087 into main Apr 15, 2026
3 checks passed
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.

1 participant