Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions apps/web/app/(admin)/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,16 @@ export default async function Layout({ children }: { children: React.ReactNode }
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<Button variant="outline" size="sm" asChild>
<Link href="/">
<Home className="mr-2 h-4 w-4" />
Home
</Link>
</Button>
<Button
variant="outline"
size="sm"
render={(props) => (
<Link href="/" {...props}>
<Home className="mr-2 h-4 w-4" />
Home
</Link>
)}
/>
</div>
</header>
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">{children}</div>
Expand Down
93 changes: 60 additions & 33 deletions apps/web/app/(admin)/admin/projects/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
'use client';

import { RepositoryChangeDialog } from '@/components/admin/repository-change-dialog';
import { ProjectClassification } from '@/components/admin/project-classification';
import { ProjectSocialLinks } from '@/components/admin/project-social-links';
import { ProjectBasicInfo } from '@/components/admin/project-basic-info';
import { ProjectEditForm } from '@/components/admin/project-edit-form';
import { ProjectSettings } from '@/components/admin/project-settings';
import { Separator } from '@workspace/ui/components/separator';
import { Button } from '@workspace/ui/components/button';
import { ArrowLeft, Loader2, Save } from 'lucide-react';
import { Form } from '@workspace/ui/components/form';
import Link from '@workspace/ui/components/link';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@workspace/ui/components/button';
import { Separator } from '@workspace/ui/components/separator';
import { useParams } from 'next/navigation';
import { isProject } from '@/types/project';
import { useTRPC } from '@/hooks/use-trpc';
import Link from '@workspace/ui/components/link';
import React from 'react';
import { Form } from '@workspace/ui/components/form';
import { ProjectEditForm } from '@/components/admin/project-edit-form';
import { ProjectBasicInfo } from '@/components/admin/project-basic-info';
import { ProjectClassification } from '@/components/admin/project-classification';
import { ProjectSocialLinks } from '@/components/admin/project-social-links';
import { ProjectSettings } from '@/components/admin/project-settings';
import { RepositoryChangeDialog } from '@/components/admin/repository-change-dialog';
import { isProject } from '@/types/project';

export default function AdminProjectEditPage() {
const params = useParams();
Expand Down Expand Up @@ -52,13 +52,20 @@ export default function AdminProjectEditPage() {
onSubmit,
} = ProjectEditForm({
projectData: isProject(projectData) ? projectData : null,
projectId
projectId,
});

// Set form values when project data loads
React.useEffect(() => {
if (projectData && isProject(projectData) && !projectStatusesLoading && !projectTypesLoading && !tagsLoading) {
const currentTags = projectData.tagRelations?.map(relation => relation.tag?.name).filter(Boolean) ?? [];
if (
projectData &&
isProject(projectData) &&
!projectStatusesLoading &&
!projectTypesLoading &&
!tagsLoading
) {
const currentTags =
projectData.tagRelations?.map((relation) => relation.tag?.name).filter(Boolean) ?? [];

const formData = {
name: projectData.name,
Expand Down Expand Up @@ -91,21 +98,28 @@ export default function AdminProjectEditPage() {

if (projectLoading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<div className="flex min-h-[400px] items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin" />
</div>
);
}

if (!projectData || !isProject(projectData)) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<div className="flex min-h-[400px] items-center justify-center">
<div className="text-center">
<h2 className="text-xl font-semibold">Project not found</h2>
<p className="text-muted-foreground">The project you&apos;re looking for doesn&apos;t exist.</p>
<Button asChild className="mt-4">
<Link href="/admin/projects">Back to Projects</Link>
</Button>
<p className="text-muted-foreground">
The project you&apos;re looking for doesn&apos;t exist.
</p>
<Button
className="mt-4"
render={(props) => (
<Link href="/admin/projects" {...props}>
Back to Projects
</Link>
)}
/>
Comment on lines +115 to +122
Copy link

Choose a reason for hiding this comment

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

πŸ’‘ Verification agent

🧩 Analysis chain

Button render + Link must forward ref to avoid broken focus/disabled semantics

9ui’s Button render prop will pass a ref and stateful attrs (e.g., data-disabled). Your Link component (packages/ui/src/components/link.tsx) is a function component without forwardRef, so the ref is dropped. This can break focus rings, keyboard focus management, and disabled behavior, and may emit React warnings. Convert Link to forwardRef and pass the ref to NextLink’s underlying anchor.

Update Link (outside this file) along these lines:

import NextLink from 'next/link';
import * as React from 'react';
import { cn } from '../lib/cn'; // adjust import to your utils

type Props = React.ComponentPropsWithoutRef<typeof NextLink> & {
  event?: string;
  eventObject?: Record<string, unknown>;
};

const Link = React.forwardRef<HTMLAnchorElement, Props>(function Link(
  { className, href, children, event, eventObject, onClick, ...props },
  ref,
) {
  return (
    <NextLink
      href={href}
      ref={ref}
      className={cn(className)}
      {...props}
      onClick={(e) => {
        if (event) {
          track(event, eventObject);
        }
        onClick?.(e);
      }}
    >
      {children}
    </NextLink>
  );
});

export default Link;

Also applies to: 132-142, 193-201


🏁 Script executed:

#!/bin/bash
# Search for Link component files in the UI package
echo "Searching for link.tsx files..."
find packages/ui -type f -iname "link.tsx"

# Inspect the first Link component found
LINK_FILE=$(find packages/ui -type f -iname "link.tsx" | head -n1)
if [ -n "$LINK_FILE" ]; then
  echo "---- $LINK_FILE (first 200 lines) ----"
  sed -n '1,200p' "$LINK_FILE"
  echo "---- Checking for forwardRef usage ----"
  rg -n "forwardRef" -C3 "$LINK_FILE" || echo "No forwardRef usage found in $LINK_FILE"
else
  echo "No Link component file found"
fi

# Search for Button component files in the UI package
echo "Searching for button component files..."
BUTTON_FILES=$(find packages/ui -type f -iname "button*.tsx")
if [ -n "$BUTTON_FILES" ]; then
  for file in $BUTTON_FILES; do
    echo "---- $file (first 200 lines) ----"
    sed -n '1,200p' "$file"
    echo "---- Searching for render prop usage in $file ----"
    rg -n "render" -C3 "$file" || echo "No render usage found in $file"
  done
else
  echo "No Button component files found"
fi

Length of output: 5319


Forward Link ref to support Button render
Wrap Link (packages/ui/src/components/link.tsx) in React.forwardRef<HTMLAnchorElement, Props> and forward the passed-in ref to <NextLink> so Button’s render-prop ref and data-disabled attributes aren’t dropped. Apply the same change for all other Button render usages (lines 115–122, 132–142, 193–201).

</div>
</div>
);
Expand All @@ -115,17 +129,20 @@ export default function AdminProjectEditPage() {
<div className="px-6">
<div className="mx-auto max-w-[1080px] py-4">
<div className="mb-6">
<Button variant="ghost" size="sm" asChild className="mb-4">
<Link href="/admin/projects">
<ArrowLeft className="h-4 w-4 mr-2" />
Back to Projects
</Link>
</Button>
<Button
variant="ghost"
size="sm"
className="mb-4"
render={(props) => (
<Link href="/admin/projects" {...props}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Projects
</Link>
)}
/>
<div>
<h1 className="text-3xl font-bold tracking-tight text-white">Edit Project</h1>
<p className="text-neutral-400">
Update project details and settings
</p>
<p className="text-neutral-400">Update project details and settings</p>
</div>
</div>

Expand Down Expand Up @@ -173,10 +190,20 @@ export default function AdminProjectEditPage() {
</div>

<div className="flex justify-end gap-4">
<Button variant="outline" asChild className="rounded-none">
<Link href="/admin/projects">Cancel</Link>
</Button>
<Button type="submit" disabled={updateProjectMutation.isPending} className="rounded-none">
<Button
variant="outline"
className="rounded-none"
render={(props) => (
<Link href="/admin/projects" {...props}>
Cancel
</Link>
)}
/>
<Button
type="submit"
disabled={updateProjectMutation.isPending}
className="rounded-none"
>
{updateProjectMutation.isPending && (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
)}
Expand Down
28 changes: 12 additions & 16 deletions apps/web/app/(public)/launches/[id]/components/launch-comments.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
'use client';

import { MessageCircle } from 'lucide-react';
import CommentThread from '@/components/comments/comment-thread';
import ReplyForm from '@/components/comments/reply-form';
import { Button } from '@workspace/ui/components/button';
import { authClient } from '@workspace/auth/client';
import Link from '@workspace/ui/components/link';
import CommentThread from '@/components/comments/comment-thread';
import ReplyForm from '@/components/comments/reply-form';



import { MessageCircle } from 'lucide-react';

interface LaunchCommentsProps {
projectId: string;
Expand Down Expand Up @@ -42,22 +39,21 @@ export default function LaunchComments({

{/* Comment Form */}
{session?.user && (
<ReplyForm
projectId={projectId}
placeholder="Add your comment..."
className="w-full"
/>
<ReplyForm projectId={projectId} placeholder="Add your comment..." className="w-full" />
)}

{!session?.user && (
<div className="mt-6 rounded-none border border-neutral-800 bg-neutral-800/30 p-4">
<div className="flex items-center justify-between">
<p className="text-neutral-300">Login to join the conversation.</p>
<Button asChild>
<Link href={`/login?redirect=/launches/${projectId}`} className="rounded-none">
Login
</Link>
</Button>
<Button
className="rounded-none"
render={(props) => (
<Link href={`/login?redirect=/launches/${projectId}`} {...props}>
Login
</Link>
)}
/>
</div>
</div>
)}
Expand Down
18 changes: 11 additions & 7 deletions apps/web/app/(public)/launches/[id]/components/launch-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ import { Input } from '@workspace/ui/components/input';
import { useCountdown } from '@/hooks/use-countdown';
import { authClient } from '@workspace/auth/client';
import Link from '@workspace/ui/components/link';
import { isValidProvider } from '@/lib/constants';
import { formatDistanceToNow } from 'date-fns';
import { useTRPC } from '@/hooks/use-trpc';
import { useState } from 'react';
import { toast } from 'sonner';
import {isValidProvider} from '@/lib/constants'

interface LaunchSidebarProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -381,12 +381,16 @@ export default function LaunchSidebar({ launch, project, projectId }: LaunchSide
)}

<div className="space-y-3">
<Button variant="outline" className="w-full gap-2 rounded-none" asChild>
<Link href={`/projects/${projectId}`}>
<ExternalLink className="h-4 w-4" />
View Project
</Link>
</Button>
<Button
variant="outline"
className="w-full gap-2 rounded-none"
render={(props) => (
<Link href={`/projects/${projectId}`} {...props}>
<ExternalLink className="h-4 w-4" />
View Project
</Link>
)}
/>
{isOwner && (
<>
<Button
Expand Down
14 changes: 7 additions & 7 deletions apps/web/app/(public)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ export default function Page() {
<Button
variant="outline"
className="mx-auto flex w-min items-center gap-2 rounded-none"
asChild
>
<Link href="/early-submission">
<span>Early Submission</span>
<ArrowRight className="h-4 w-4" />
</Link>
</Button>
render={(props) => (
<Link href="/early-submission" {...props}>
<span>Early Submission</span>
<ArrowRight className="h-4 w-4" />
</Link>
)}
/>

<div>
<GitAvatars />
Expand Down
33 changes: 19 additions & 14 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NextSSRPlugin } from '@uploadthing/react/next-ssr-plugin';
import { ourFileRouter } from '@/app/api/uploadthing/core';
import { extractRouterConfig } from 'uploadthing/server';
import { TempNav } from '@/components/layout/temp-nav';
import { Geist, Geist_Mono } from 'next/font/google';
import { Providers } from '@/components/providers';
import Footer from '@/components/layout/footer';
Expand Down Expand Up @@ -54,21 +55,25 @@ export default function RootLayout({
<body
className={`${fontSans.variable} ${fontMono.variable} overscroll-none bg-[#101010] font-sans antialiased`}
>
<Suspense>
<UTSSR />
</Suspense>
<Providers>
{children}
<Footer />
<main className="root">
<Suspense>
<UTSSR />
</Suspense>
<Providers>
{children}
<Footer />

<Databuddy
clientId={env.DATABUDDY_CLIENT_ID}
enableBatching={true}
trackErrors
trackOutgoingLinks
/>
<Toaster />
</Providers>
<TempNav />

<Databuddy
clientId={env.DATABUDDY_CLIENT_ID}
enableBatching={true}
trackErrors
trackOutgoingLinks
/>
<Toaster />
</Providers>
</main>
</body>
</html>
);
Expand Down
40 changes: 22 additions & 18 deletions apps/web/components/layout/public-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,22 @@ export default function PublicNav({ session }: { session: any }) {
<div className="absolute left-1/2 flex -translate-x-1/2 items-center gap-2">
{navItems.map((item) => (
<React.Fragment key={item.href}>
<Button variant="ghost" asChild>
<Link
href={item.href}
event={`${item.label.toLowerCase()}_nav_click`}
className={cn(
'text-muted-foreground rounded-none p-2 text-sm hover:bg-neutral-900',
isActive(item.href) && 'text-primary bg-neutral-900',
)}
>
{item.label}
</Link>
</Button>
<Button
variant="ghost"
render={(props) => (
<Link
{...props}
href={item.href}
event={`${item.label.toLowerCase()}_nav_click`}
className={cn(
'text-muted-foreground rounded-none p-2 text-sm hover:bg-neutral-900',
isActive(item.href) && 'text-primary bg-neutral-900',
)}
>
{item.label}
</Link>
)}
/>
</React.Fragment>
))}
</div>
Expand All @@ -49,12 +53,12 @@ export default function PublicNav({ session }: { session: any }) {
) : (
<Button
className="rounded-none border border-neutral-800 bg-transparent px-4 py-2 text-sm text-white hover:border-neutral-700 hover:bg-neutral-900"
asChild
>
<Link href="/login" event="login_nav_click">
Login
</Link>
</Button>
render={(props) => (
<Link href="/login" event="login_nav_click" {...props}>
Login
</Link>
)}
/>
)}
<SubmissionDialog />
</>
Expand Down
Loading