diff --git a/apps/web/app/(authenticated)/projects/new/page.tsx b/apps/web/app/(authenticated)/projects/new/page.tsx index d426022..ca958e7 100644 --- a/apps/web/app/(authenticated)/projects/new/page.tsx +++ b/apps/web/app/(authenticated)/projects/new/page.tsx @@ -147,9 +147,9 @@ export default function NewProjectPage() { className="flex flex-col overflow-hidden transition-colors hover:border-primary/50" > -
+
{repo.name} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 86be27e..351b606 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -25,7 +25,12 @@ export default function RootLayout({ return ( - + {children} diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx index d206b28..e7636af 100644 --- a/apps/web/app/login/page.tsx +++ b/apps/web/app/login/page.tsx @@ -7,7 +7,11 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { toast } from 'sonner'; import { Loader2 } from 'lucide-react'; -import { loginRequestSchema, type LoginRequest } from '@repo/contracts'; +import { + loginRequestSchema, + type ExploreProjectResponse, + type LoginRequest, +} from '@repo/contracts'; import { AuthShell } from '@/components/auth-shell'; import { PasswordInput } from '@/components/password-input'; import { useAuth } from '@/hooks/use-auth'; @@ -25,6 +29,38 @@ const THUMB_GRADIENTS = [ 'from-orange to-accent', ]; +function ProjectThumbnail({ + project, + index, +}: { + project: ExploreProjectResponse; + index: number; +}) { + const [imageFailed, setImageFailed] = useState(false); + const coverUrl = project.media[0]?.publicUrl; + const imageUrl = coverUrl ?? project.logoUrl; + + return ( +
+ {imageUrl && !imageFailed && ( + // eslint-disable-next-line @next/next/no-img-element + {`${project.title} setImageFailed(true)} + className={ + coverUrl + ? 'absolute inset-0 h-full w-full object-cover' + : 'h-10 w-10 rounded-md bg-white/90 object-contain p-1 shadow-sm' + } + /> + )} +
+ ); +} + // Component to handle fetching & randomizing projects function RandomProjects() { // Fetch up to 50 projects for a healthy randomization pool @@ -80,9 +116,7 @@ function RandomProjects() { className="block h-full" > -
+

diff --git a/apps/web/components/onboarding-wizard.tsx b/apps/web/components/onboarding-wizard.tsx index 8e3de31..74e19da 100644 --- a/apps/web/components/onboarding-wizard.tsx +++ b/apps/web/components/onboarding-wizard.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { useRouter } from 'next/navigation'; import { useForm, Controller, type Path } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -92,6 +92,12 @@ export function OnboardingWizard({ user }: OnboardingWizardProps) { user.developerProfile?.publicSlug, ) : ''; + const slugWasManuallyEdited = useRef( + Boolean( + user.developerProfile?.publicSlug && + !user.developerProfile.publicSlug.startsWith('dev-'), + ), + ); const { register, @@ -134,12 +140,20 @@ export function OnboardingWizard({ user }: OnboardingWizardProps) { const name = e.target.value; setValue('displayName', name, { shouldValidate: true }); - if (isDev) { + if (isDev && !slugWasManuallyEdited.current) { const generated = slugify(name); setValue('publicSlug', generated, { shouldValidate: true }); } }; + const handlePublicSlugChange = (e: React.ChangeEvent) => { + slugWasManuallyEdited.current = true; + setValue('publicSlug', e.target.value, { + shouldDirty: true, + shouldValidate: true, + }); + }; + const handleNextStep = async (e?: React.MouseEvent) => { e?.preventDefault(); @@ -151,11 +165,9 @@ export function OnboardingWizard({ user }: OnboardingWizardProps) { ? ['displayName', 'publicSlug'] : ['organizationName', 'organizationType']; - console.log('[DEBUG] Wizard - Validating Step 1:', fieldsToValidate); const isValid = await trigger(fieldsToValidate); if (!isValid) { - console.log('[DEBUG] Wizard - Validation failed on Step 1'); return; } @@ -202,7 +214,6 @@ export function OnboardingWizard({ user }: OnboardingWizardProps) { const isStep2Valid = await trigger(step2Fields); if (!isStep2Valid) { - console.log('[DEBUG] Wizard - Validation failed on Step 2 URLs/Fields'); return; } @@ -214,7 +225,6 @@ export function OnboardingWizard({ user }: OnboardingWizardProps) { return; } - console.log('[DEBUG] Wizard - Submission started...'); setIsSubmitting(true); try { await updateProfile(data); @@ -294,6 +304,7 @@ export function OnboardingWizard({ user }: OnboardingWizardProps) { className="rounded-l-none" placeholder="jane-doe" {...register('publicSlug')} + onChange={handlePublicSlugChange} />

{errors.publicSlug && ( diff --git a/apps/web/components/theme-toggle.tsx b/apps/web/components/theme-toggle.tsx index 3e1649d..f90dd24 100644 --- a/apps/web/components/theme-toggle.tsx +++ b/apps/web/components/theme-toggle.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -import { Moon, Sun, Laptop } from 'lucide-react'; +import { Moon, Sun } from 'lucide-react'; import { useTheme } from 'next-themes'; import { Button } from '@/components/ui/button'; import { @@ -37,10 +37,6 @@ export function ThemeToggle() { Dark - setTheme('system')}> - - System - );