-
Notifications
You must be signed in to change notification settings - Fork 121
[Multi-Tenancy] Claim your Custom Domain #289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
3 Skipped Deployments
|
WalkthroughThis update introduces domain name management for web projects, including backend, frontend, and database changes. The frontend now allows users to view and update their site's domain name, checking availability and applying changes via new RPC calls. The backend integrates with the Vercel SDK for domain verification, exposing a new API route for this purpose. The database schema is updated to enforce stricter domain name constraints, automate random name assignment, and provide functions for checking and changing domain names. New environment variables and dependencies are added to support Vercel integration and these new features. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ProjectWWWSettingsPage
participant FormSiteDomain
participant SiteDomainsSection
participant BackendAPI
participant Database
User->>ProjectWWWSettingsPage: Open site settings
ProjectWWWSettingsPage->>FormSiteDomain: Render with current domain
FormSiteDomain->>SiteDomainsSection: Pass domain and callbacks
User->>SiteDomainsSection: Click edit, enter new domain, click save
SiteDomainsSection->>FormSiteDomain: onSubmit(newDomain)
FormSiteDomain->>BackendAPI: check_www_name_available(newDomain)
BackendAPI->>Database: Run check_www_name_available
Database-->>BackendAPI: Return availability
BackendAPI-->>FormSiteDomain: Return boolean
alt Domain available
FormSiteDomain->>BackendAPI: change_www_name(newDomain)
BackendAPI->>Database: Run change_www_name
Database-->>BackendAPI: Update and confirm
BackendAPI-->>FormSiteDomain: Success
FormSiteDomain->>SiteDomainsSection: Show success, update UI
else Domain unavailable
FormSiteDomain->>SiteDomainsSection: Show error
end
sequenceDiagram
participant APIClient
participant VercelClient
participant VercelAPI
APIClient->>VercelClient: projectsVerifyProjectDomain(domain)
VercelClient->>VercelAPI: Verify domain with credentials
VercelAPI-->>VercelClient: Verification result
VercelClient-->>APIClient: Return result
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
…nant/custom-domain
There was a problem hiding this 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
♻️ Duplicate comments (4)
supabase/migrations/20250418050733_www_name.sql (1)
86-94: Duplicate of domain-pattern mismatch already flagged.The availability regex
{2,32}is misaligned with the domain’s constraints. See first comment for the unified pattern.supabase/schemas/grida_www.sql (3)
30-37: Duplicate domain‐constraint mismatch.The
grida_www.www_namedefinition here uses the same loose pattern as the migration. Align it with the revised pattern from the migration review.
43-58: Samegen_random_www_namelogic appears here—apply the truncation and collision‐retry refactor.
218-225: Availability regex mismatch mirrored here.Ensure
check_www_name_availableuses the same pattern bounds as the domain.
🧹 Nitpick comments (12)
editor/.env.example (1)
31-38: Ensure.env.exampleplaceholders and security boundaries are clear.
- The new VERCEL_* variables are correctly added for Vercel integration.
- Recommend using descriptive placeholders (e.g.
<YOUR_TOKEN>) instead of empty strings to guide users.- Remind users that
VERCEL_AUTH_BEARER_TOKENshould remain server‐side (not exposed in client bundles) and be stored in.env.local, not committed.supabase/migrations/20250418050733_www_name.sql (1)
32-38: Optional: retry on duplicate random names.Right now, if
gen_random_www_nameproduces an existing name, the insert will error. For better UX, consider wrapping generation in a loop that retries when a collision occurs.supabase/schemas/grida_www.sql (2)
64-70: Assign trigger logic is fine; consider collision handling.
98-99: Consider idempotent trigger creation.Add a
DROP TRIGGER IF EXISTS set_www_name ON grida_www.www;before this to avoid errors on repeated deployments.editor/app/(api)/private/domains/[domain]/verify/route.ts (1)
5-5: Remove or implement commented codeThis commented line suggests you intended to extract JSON from the request body but didn't implement it.
Either implement this functionality if needed or remove the commented line to keep the codebase clean.
editor/clients/vercel/index.ts (2)
13-17: Decide on commented out functionalityThis commented-out function suggests incomplete implementation for domain deletion functionality.
Either complete and enable this functionality if needed for future use, or remove it to avoid confusion.
19-24: Add JSDoc documentationThe exported function lacks documentation which would help other developers understand its purpose and usage.
Consider adding JSDoc comments:
+/** + * Verifies a domain with Vercel for the configured project + * @param domain - The domain to verify + * @returns Promise with verification result from Vercel API + */ export const projectsVerifyProjectDomain = (domain: string) => _projectsVerifyProjectDomain(__vercel_core, { teamId: VERCEL_TEAM_ID, idOrName: VERCEL_PROJECT_ID, domain, });editor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/section-domain.tsx (3)
83-86: Provide more specific error messagesThe error message combines two distinct error cases: "already taken" and "not allowed". This makes it harder for users to understand the specific issue.
Consider distinguishing these cases in the backend and providing more specific error messages based on the actual error case:
- setError( - "This domain is either already taken or not allowed. Please try a different name using only letters, numbers, or dashes." - ); + // Example of more specific error handling + if (!ok) { + // Assuming onSubmit could return different error types + if (error === "TAKEN") { + setError("This domain is already taken. Please try a different name."); + } else if (error === "INVALID_FORMAT") { + setError("Domain names can only contain lowercase letters, numbers, or dashes."); + } else { + setError("Failed to update domain. Please try again."); + } + }
75-87: Improve error handling in form submissionThe current implementation doesn't distinguish between different types of errors and doesn't handle network failures.
Add proper try/catch and more detailed error handling:
const onSubmitHandler = async () => { setBusy(true); + try { const ok = await onSubmit(name); - setBusy(false); if (ok) { toast.success("Domain name updated successfully"); props.onOpenChange?.(false); } else { setError( "This domain is either already taken or not allowed. Please try a different name using only letters, numbers, or dashes." ); } + } catch (error) { + console.error("Error updating domain:", error); + setError("Failed to update domain due to a network error. Please try again."); + } finally { + setBusy(false); + } };
102-126: Improve form accessibilityThe current implementation lacks proper form semantics and accessibility enhancements.
Consider these improvements:
- Wrap the form fields in a
<form>element- Add proper labels with
htmlForattributes- Add
aria-invalidattributes for error states- Auto-focus the input field when the dialog opens
- <div className="py-4 flex flex-col gap-2"> + <form + className="py-4 flex flex-col gap-2" + onSubmit={(e) => { + e.preventDefault(); + onSubmitHandler(); + }} + > + <label htmlFor="domain-name" className="text-sm font-medium">Domain Name</label> <div className="flex h-9 items-center border rounded-md px-3 py-1 focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 bg-muted"> <Input + id="domain-name" + autoFocus + aria-invalid={!!error} className="border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-0 shadow-none" placeholder="your-domain" disabled={busy} value={name} onChange={(e) => { setName(e.target.value); setError(null); }} /> <span className="ml-2 text-muted-foreground text-sm"> .grida.site </span> </div> <p + id="domain-name-error" + aria-live="polite" data-error={!!error} className="text-xs text-muted-foreground data-[error=true]:text-destructive" > {error ? error : "lowercase letters, numbers, and dashes are allowed"} </p> - </div> + </form>editor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/page.tsx (2)
82-96: Consider adding more detailed error handling.The
changeDomainNamefunction correctly validates data existence, calls the RPC function, and refreshes the UI through SWR cache invalidation. However, the error handling is minimal - it only returns false when an error occurs.Consider enhancing error handling to provide more specific feedback:
- const { error } = await client.rpc("change_www_name", { + const { data: result, error } = await client.rpc("change_www_name", { p_www_id: data.id, p_name: name, }); - if (error) return false; + if (error) { + console.error("Failed to change domain name:", error.message); + return false; + }
303-331: Consider adding loading state and error feedback to the domain form.The
FormSiteDomaincomponent correctly implements the domain management functionality, but unlike theFormSiteGeneralcomponent (lines 252-301), it doesn't provide visual feedback during the domain change operation or display error messages.Consider enhancing the user experience by adding:
- Loading indicators during the availability check and domain change
- Error messages when operations fail
- Success confirmation when the operation completes
function FormSiteDomain({ defaultValues, changeDomainName, checkDomainName, }: { checkDomainName: (name: string) => Promise<boolean>; changeDomainName: (name: string) => Promise<boolean>; defaultValues: { name: string; }; }) { + const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState<string | null>(null); + const [successMessage, setSuccessMessage] = useState<string | null>(null); return ( <Card> <CardHeader> <CardTitle>Domains</CardTitle> </CardHeader> <CardContent className="space-y-6"> + {errorMessage && <div className="text-red-500 mb-2">{errorMessage}</div>} + {successMessage && <div className="text-green-500 mb-2">{successMessage}</div>} <SiteDomainsSection onDomainNameChange={async (name) => { + setIsLoading(true); + setErrorMessage(null); + setSuccessMessage(null); + const available = await checkDomainName(name); - if (!available) return false; - return await changeDomainName(name); + if (!available) { + setErrorMessage(`Domain name "${name}" is not available.`); + setIsLoading(false); + return false; + } + + const success = await changeDomainName(name); + setIsLoading(false); + if (success) { + setSuccessMessage("Domain name updated successfully!"); + } else { + setErrorMessage("Failed to update domain name. Please try again."); + } + return success; }} name={defaultValues.name} + isLoading={isLoading} /> </CardContent> </Card> ); }Note: You would need to import
useStatefrom React and update theSiteDomainsSectioncomponent to handle theisLoadingprop.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (10)
editor/.env.example(1 hunks)editor/app/(api)/private/domains/[domain]/verify/route.ts(1 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/page.tsx(6 hunks)editor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/section-domain.tsx(1 hunks)editor/clients/vercel/index.ts(1 hunks)editor/database-generated.types.ts(1 hunks)editor/package.json(1 hunks)supabase/migrations/20250418050733_www_name.sql(1 hunks)supabase/migrations/20250418063117_www_name_drop_default.sql(1 hunks)supabase/schemas/grida_www.sql(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
editor/app/(api)/private/domains/[domain]/verify/route.ts (1)
editor/clients/vercel/index.ts (1)
projectsVerifyProjectDomain(19-24)
🔇 Additional comments (12)
supabase/migrations/20250418050733_www_name.sql (2)
56-82: Views re-created correctly after schema change.The drop/create sequence ensures no broken dependencies. The column selection remains unchanged.
99-117: Change function enforces RLS and availability correctly.The implementation properly checks ownership (
rls_www), name validity, and updates the table. Nothing to refactor here.supabase/schemas/grida_www.sql (2)
80-80: Domain‐typed column added correctly.
231-249: Change‐name RPC remains correct.supabase/migrations/20250418063117_www_name_drop_default.sql (1)
1-2: Dropping the old default is straightforward.editor/package.json (1)
97-97: New dependency looks correct.
@vercel/sdk@^1.5.0is added to support Vercel integration. Verify this version meets your compatibility and security requirements.editor/database-generated.types.ts (1)
3202-3213: New domain management functions look goodThe type definitions for the new domain management functions are correctly defined, providing a type-safe interface for domain name operations.
editor/app/(workbench)/[org]/[proj]/(console)/(resources)/www/page.tsx (5)
21-21: New import for domain management functionality.The import of
SiteDomainsSectionfrom "./section-domain" supports the new domain management feature being added to this page.
67-80: Implementation of domain availability checker looks good.This function makes an RPC call to check if a domain name is available, with appropriate error handling. It returns a boolean result which simplifies usage in the UI.
165-167: Properly exposing new domain management functions.The hook correctly exposes the new domain name management functions alongside existing functions.
178-180: New domain management functions are properly destructured.The component correctly destructures the new domain name management functions from the hook.
213-220: Good implementation of the new domain form in the page layout.The
FormSiteDomaincomponent is properly integrated into the page layout with the right props.
| CREATE OR REPLACE FUNCTION grida_www.gen_random_www_name(p_project_id INTEGER) | ||
| RETURNS TEXT AS $$ | ||
| DECLARE | ||
| org_name TEXT; | ||
| project_name TEXT; | ||
| suffix TEXT := encode(gen_random_bytes(2), 'hex'); | ||
| BEGIN | ||
| SELECT o.name, p.name INTO org_name, project_name | ||
| FROM public.project p | ||
| JOIN public.organization o ON o.id = p.organization_id | ||
| WHERE p.id = p_project_id; | ||
|
|
||
| RETURN lower(regexp_replace(org_name, '[^a-zA-Z0-9]+', '', 'g')) || '-' || | ||
| lower(regexp_replace(project_name, '[^a-zA-Z0-9]+', '', 'g')) || '-' || suffix; | ||
| END; | ||
| $$ LANGUAGE plpgsql STABLE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Guard against over‑long random names and possible collisions.
- The concatenation of full org/project names + suffix may exceed the 32‑char limit and cause migration failures.
- A 2‑byte suffix (4 hex chars) yields ~65k possibilities; collisions are possible in high‑volume setups.
Consider truncating sanitized org_name/project_name segments to ensure the final string ≤32 chars, and loop on collision:
DECLARE
max_base_len INT := 32 - length(suffix) - 2; -- 2 hyphens
base TEXT := lower(regexp_replace(org_name, '[^a-zA-Z0-9]+', '', 'g'))
|| '-' || lower(regexp_replace(project_name, '[^a-zA-Z0-9]+', '', 'g'));
truncated_base TEXT := substring(base from 1 for max_base_len);
BEGIN
RETURN truncated_base || '-' || suffix;
END;This keeps names within constraints and reduces collision risk.
| -- [www name type] -- | ||
| --------------------------------------------------------------------- | ||
| CREATE DOMAIN grida_www.www_name AS TEXT | ||
| CHECK ( | ||
| length(VALUE) <= 32 AND | ||
| VALUE ~ '^[a-zA-Z0-9][a-zA-Z0-9\-]*$' | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Align domain constraints with availability checks to avoid mismatches.
The grida_www.www_name domain currently allows names as short as 1 character and up to 32 (length(VALUE) <= 32), whereas the check_www_name_available function enforces a 3–32 character length (^[a-zA-Z0-9][a-zA-Z0-9\-]{2,32}$)—leading to inconsistent validation.
Proposed diff to unify both rules (3–32 alphanumeric/hyphen, starting with alphanumeric):
CREATE DOMAIN grida_www.www_name AS TEXT
-CHECK (
- length(VALUE) <= 32 AND
- VALUE ~ '^[a-zA-Z0-9][a-zA-Z0-9\-]*$'
-);
+CHECK (
+ length(VALUE) BETWEEN 3 AND 32 AND
+ VALUE ~ '^[a-zA-Z0-9][a-zA-Z0-9\-]{2,31}$'
+);This ensures domain and RPC validations remain consistent.
📝 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.
| -- [www name type] -- | |
| --------------------------------------------------------------------- | |
| CREATE DOMAIN grida_www.www_name AS TEXT | |
| CHECK ( | |
| length(VALUE) <= 32 AND | |
| VALUE ~ '^[a-zA-Z0-9][a-zA-Z0-9\-]*$' | |
| ); | |
| -- [www name type] -- | |
| --------------------------------------------------------------------- | |
| CREATE DOMAIN grida_www.www_name AS TEXT | |
| CHECK ( | |
| length(VALUE) BETWEEN 3 AND 32 AND | |
| VALUE ~ '^[a-zA-Z0-9][a-zA-Z0-9\\-]{2,31}$' | |
| ); |
| DROP VIEW IF EXISTS grida_www.www_public; | ||
| DROP VIEW IF EXISTS grida_www.public_route; | ||
|
|
||
| -- alter the column to use the new domain | ||
| ALTER TABLE grida_www.www ALTER COLUMN name TYPE grida_www.www_name USING name::TEXT; | ||
|
|
||
| -- add the trigger | ||
| CREATE TRIGGER set_www_name BEFORE INSERT ON grida_www.www FOR EACH ROW WHEN (NEW.name IS NULL) EXECUTE FUNCTION grida_www.assign_random_www_name(); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify existing data compatibility and drop stale triggers/views.
- Altering
nametogrida_www.www_namewill fail if existing values violate the new domain constraints. Ensure a data‑cleanup step precedes this migration. - You currently add the trigger without dropping any prior
set_www_nametrigger. Add:before creating the new trigger to prevent duplicate‑trigger errors.DROP TRIGGER IF EXISTS set_www_name ON grida_www.www;
Run this script to confirm all existing name values conform to the new regex:
🏁 Script executed:
#!/bin/bash
rg -e "^[a-zA-Z0-9][a-zA-Z0-9\\-]{2,31}$" --invert-match -U supabase/migrations/20250418050733_www_name.sqlLength of output: 3557
Ensure data cleanup and idempotent trigger creation
-
Before you alter the column to use the new
grida_www.www_namedomain, verify that no existing rows violate its CHECK constraint. For example, in psql run:SELECT id, name FROM grida_www.www WHERE name !~ '^[a-zA-Z0-9][a-zA-Z0-9-]{2,31}$';
Fix or remove any rows returned so the migration won’t fail.
-
Prevent duplicate‑trigger errors by dropping the old trigger first. Add this immediately before your
CREATE TRIGGER:DROP TRIGGER IF EXISTS set_www_name ON grida_www.www;
| export async function GET(req: NextRequest) { | ||
| // const reqjson = await req.json() | ||
| const r = await projectsVerifyProjectDomain("domain-name.com"); | ||
| console.log(r); | ||
|
|
||
| return NextResponse.json(r, { | ||
| status: 200, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use dynamic route parameter instead of hardcoded domain
The route handler uses a hardcoded domain string "domain-name.com" instead of utilizing the dynamic route parameter [domain] available in the URL. This defeats the purpose of having a dynamic route.
- const r = await projectsVerifyProjectDomain("domain-name.com");
+ // Extract domain from route parameters
+ const domain = req.nextUrl.pathname.split('/').pop();
+ const r = await projectsVerifyProjectDomain(domain);Additionally, there's no error handling for the API call to the Vercel SDK. Consider adding a try/catch block to handle potential failures.
Committable suggestion skipped: line range outside the PR's diff.
| const VERCEL_AUTH_BEARER_TOKEN = process.env.VERCEL_AUTH_BEARER_TOKEN || ""; | ||
| const VERCEL_TEAM_ID = process.env.VERCEL_TEAM_ID || ""; | ||
| const VERCEL_PROJECT_ID = process.env.VERCEL_PROJECT_ID || ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for environment variables
The environment variables lack validation and default to empty strings, which could lead to silent failures when making API calls if they're not configured properly.
- const VERCEL_AUTH_BEARER_TOKEN = process.env.VERCEL_AUTH_BEARER_TOKEN || "";
- const VERCEL_TEAM_ID = process.env.VERCEL_TEAM_ID || "";
- const VERCEL_PROJECT_ID = process.env.VERCEL_PROJECT_ID || "";
+ const VERCEL_AUTH_BEARER_TOKEN = process.env.VERCEL_AUTH_BEARER_TOKEN;
+ const VERCEL_TEAM_ID = process.env.VERCEL_TEAM_ID;
+ const VERCEL_PROJECT_ID = process.env.VERCEL_PROJECT_ID;
+
+ if (!VERCEL_AUTH_BEARER_TOKEN || !VERCEL_TEAM_ID || !VERCEL_PROJECT_ID) {
+ console.error("Missing required Vercel environment variables");
+ }📝 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 VERCEL_AUTH_BEARER_TOKEN = process.env.VERCEL_AUTH_BEARER_TOKEN || ""; | |
| const VERCEL_TEAM_ID = process.env.VERCEL_TEAM_ID || ""; | |
| const VERCEL_PROJECT_ID = process.env.VERCEL_PROJECT_ID || ""; | |
| const VERCEL_AUTH_BEARER_TOKEN = process.env.VERCEL_AUTH_BEARER_TOKEN; | |
| const VERCEL_TEAM_ID = process.env.VERCEL_TEAM_ID; | |
| const VERCEL_PROJECT_ID = process.env.VERCEL_PROJECT_ID; | |
| if (!VERCEL_AUTH_BEARER_TOKEN || !VERCEL_TEAM_ID || !VERCEL_PROJECT_ID) { | |
| console.error("Missing required Vercel environment variables"); | |
| } |
| <Input | ||
| className="border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-0 shadow-none" | ||
| placeholder="your-domain" | ||
| disabled={busy} | ||
| value={name} | ||
| onChange={(e) => { | ||
| setName(e.target.value); | ||
| setError(null); | ||
| }} | ||
| /> | ||
| <span className="ml-2 text-muted-foreground text-sm"> | ||
| .grida.site | ||
| </span> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enforce input validation on domain name
The help text mentions restrictions (lowercase letters, numbers, and dashes), but there's no enforcement in the input field itself.
<Input
className="border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-0 shadow-none"
placeholder="your-domain"
disabled={busy}
value={name}
+ pattern="^[a-z0-9-]+$"
onChange={(e) => {
+ // Only allow lowercase letters, numbers, and dashes
+ const value = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '');
- setName(e.target.value);
+ setName(value);
setError(null);
}}
/>📝 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.
| <Input | |
| className="border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-0 shadow-none" | |
| placeholder="your-domain" | |
| disabled={busy} | |
| value={name} | |
| onChange={(e) => { | |
| setName(e.target.value); | |
| setError(null); | |
| }} | |
| /> | |
| <span className="ml-2 text-muted-foreground text-sm"> | |
| .grida.site | |
| </span> | |
| </div> | |
| <Input | |
| className="border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-0 shadow-none" | |
| placeholder="your-domain" | |
| disabled={busy} | |
| value={name} | |
| pattern="^[a-z0-9-]+$" | |
| onChange={(e) => { | |
| // Only allow lowercase letters, numbers, and dashes | |
| const value = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''); | |
| setName(value); | |
| setError(null); | |
| }} | |
| /> |
Allow user to customize default www name
day-221-grida-sites-claim-grida-site-domain.mp4
Summary by CodeRabbit
New Features
Integration
Database
Chores