feat: refactor sign-in prompts to use SignedOutPanel component

This commit is contained in:
Abhimanyu Saharan
2026-02-08 23:40:11 +05:30
parent 86d93d94fe
commit 840d5a050f
15 changed files with 117 additions and 191 deletions

View File

@@ -34,6 +34,8 @@ from app.models.board_memory import BoardMemory
from app.models.board_onboarding import BoardOnboardingSession from app.models.board_onboarding import BoardOnboardingSession
from app.models.boards import Board from app.models.boards import Board
from app.models.gateways import Gateway from app.models.gateways import Gateway
from app.models.organization_board_access import OrganizationBoardAccess
from app.models.organization_invite_board_access import OrganizationInviteBoardAccess
from app.models.task_dependencies import TaskDependency from app.models.task_dependencies import TaskDependency
from app.models.task_fingerprints import TaskFingerprint from app.models.task_fingerprints import TaskFingerprint
from app.models.tasks import Task from app.models.tasks import Task
@@ -316,6 +318,10 @@ async def delete_board(
await session.execute( await session.execute(
delete(BoardOnboardingSession).where(col(BoardOnboardingSession.board_id) == board.id) delete(BoardOnboardingSession).where(col(BoardOnboardingSession.board_id) == board.id)
) )
await session.execute(delete(OrganizationBoardAccess).where(col(OrganizationBoardAccess.board_id) == board.id))
await session.execute(
delete(OrganizationInviteBoardAccess).where(col(OrganizationInviteBoardAccess.board_id) == board.id)
)
# Tasks reference agents (assigned_agent_id) and have dependents (fingerprints/dependencies), so # Tasks reference agents (assigned_agent_id) and have dependents (fingerprints/dependencies), so
# delete tasks before agents. # delete tasks before agents.

View File

@@ -3,7 +3,7 @@
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { ArrowUpRight, Activity as ActivityIcon } from "lucide-react"; import { ArrowUpRight, Activity as ActivityIcon } from "lucide-react";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
@@ -15,9 +15,9 @@ import {
import type { ActivityTaskCommentFeedItemRead } from "@/api/generated/model"; import type { ActivityTaskCommentFeedItemRead } from "@/api/generated/model";
import { Markdown } from "@/components/atoms/Markdown"; import { Markdown } from "@/components/atoms/Markdown";
import { ActivityFeed } from "@/components/activity/ActivityFeed"; import { ActivityFeed } from "@/components/activity/ActivityFeed";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { createExponentialBackoff } from "@/lib/backoff"; import { createExponentialBackoff } from "@/lib/backoff";
import { apiDatetimeToMs, parseApiDatetime } from "@/lib/datetime"; import { apiDatetimeToMs, parseApiDatetime } from "@/lib/datetime";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -295,20 +295,13 @@ export default function ActivityPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to view the feed."
<p className="text-sm text-slate-600">Sign in to view the feed.</p> forceRedirectUrl="/activity"
<SignInButton signUpForceRedirectUrl="/activity"
mode="redirect" mode="redirect"
forceRedirectUrl="/activity" buttonTestId="activity-signin"
signUpForceRedirectUrl="/activity" />
>
<Button className="mt-4" data-testid="activity-signin">
Sign in
</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
import { import {
@@ -18,6 +18,7 @@ import {
useListBoardsApiV1BoardsGet, useListBoardsApiV1BoardsGet,
} from "@/api/generated/boards/boards"; } from "@/api/generated/boards/boards";
import type { AgentRead, AgentUpdate, BoardRead } from "@/api/generated/model"; import type { AgentRead, AgentUpdate, BoardRead } from "@/api/generated/model";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -273,18 +274,11 @@ export default function EditAgentPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to edit agents."
<p className="text-sm text-slate-600">Sign in to edit agents.</p> forceRedirectUrl={`/agents/${agentId}/edit`}
<SignInButton signUpForceRedirectUrl={`/agents/${agentId}/edit`}
mode="modal" />
forceRedirectUrl={`/agents/${agentId}/edit`}
signUpForceRedirectUrl={`/agents/${agentId}/edit`}
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
import { useState } from "react"; import { useState } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
import { import {
@@ -18,6 +18,8 @@ import {
useGetMyMembershipApiV1OrganizationsMeMemberGet, useGetMyMembershipApiV1OrganizationsMeMemberGet,
} from "@/api/generated/organizations/organizations"; } from "@/api/generated/organizations/organizations";
import type { BoardRead } from "@/api/generated/model"; import type { BoardRead } from "@/api/generated/model";
import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -170,20 +172,11 @@ export default function NewAgentPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to create an agent."
<p className="text-sm text-slate-600"> forceRedirectUrl="/agents/new"
Sign in to create an agent. signUpForceRedirectUrl="/agents/new"
</p> />
<SignInButton
mode="modal"
forceRedirectUrl="/agents/new"
signUpForceRedirectUrl="/agents/new"
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />
@@ -201,9 +194,7 @@ export default function NewAgentPage() {
<div className="p-8"> <div className="p-8">
{!isAdmin ? ( {!isAdmin ? (
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm"> <AdminOnlyNotice message="Only organization owners and admins can create agents." />
Only organization owners and admins can create agents.
</div>
) : ( ) : (
<form <form
onSubmit={handleSubmit} onSubmit={handleSubmit}

View File

@@ -6,7 +6,7 @@ import { useMemo, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { import {
type ColumnDef, type ColumnDef,
type SortingState, type SortingState,
@@ -47,6 +47,8 @@ import {
useGetMyMembershipApiV1OrganizationsMeMemberGet, useGetMyMembershipApiV1OrganizationsMeMemberGet,
} from "@/api/generated/organizations/organizations"; } from "@/api/generated/organizations/organizations";
import type { AgentRead } from "@/api/generated/model"; import type { AgentRead } from "@/api/generated/model";
import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
const parseTimestamp = (value?: string | null) => { const parseTimestamp = (value?: string | null) => {
if (!value) return null; if (!value) return null;
@@ -304,18 +306,11 @@ export default function AgentsPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to view agents."
<p className="text-sm text-slate-600">Sign in to view agents.</p> forceRedirectUrl="/agents"
<SignInButton signUpForceRedirectUrl="/agents"
mode="modal" />
forceRedirectUrl="/agents"
signUpForceRedirectUrl="/agents"
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />
@@ -342,9 +337,7 @@ export default function AgentsPage() {
<div className="p-8"> <div className="p-8">
{!isAdmin ? ( {!isAdmin ? (
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm"> <AdminOnlyNotice message="Only organization owners and admins can access agents." />
Only organization owners and admins can access agents.
</div>
) : ( ) : (
<> <>
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm"> <div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">

View File

@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useParams, useRouter, useSearchParams } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
import { import {
@@ -23,6 +23,7 @@ import type {
BoardGroupUpdate, BoardGroupUpdate,
BoardRead, BoardRead,
} from "@/api/generated/model"; } from "@/api/generated/model";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -281,19 +282,10 @@ export default function EditBoardGroupPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to edit board groups."
<p className="text-sm text-slate-600"> forceRedirectUrl={`/board-groups/${groupId ?? ""}/edit`}
Sign in to edit board groups. />
</p>
<SignInButton
mode="modal"
forceRedirectUrl={`/board-groups/${groupId ?? ""}/edit`}
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -6,7 +6,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { import {
ArrowUpRight, ArrowUpRight,
MessageSquare, MessageSquare,
@@ -38,6 +38,7 @@ import type {
} from "@/api/generated/model"; } from "@/api/generated/model";
import type { BoardGroupBoardSnapshot } from "@/api/generated/model"; import type { BoardGroupBoardSnapshot } from "@/api/generated/model";
import { Markdown } from "@/components/atoms/Markdown"; import { Markdown } from "@/components/atoms/Markdown";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { BoardChatComposer } from "@/components/BoardChatComposer"; import { BoardChatComposer } from "@/components/BoardChatComposer";
@@ -721,19 +722,10 @@ export default function BoardGroupDetailPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to view board groups."
<p className="text-sm text-slate-600"> forceRedirectUrl={`/board-groups/${groupId ?? ""}`}
Sign in to view board groups. />
</p>
<SignInButton
mode="modal"
forceRedirectUrl={`/board-groups/${groupId ?? ""}`}
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -6,7 +6,7 @@ import { useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
import { import {
@@ -16,6 +16,7 @@ import {
} from "@/api/generated/boards/boards"; } from "@/api/generated/boards/boards";
import { useCreateBoardGroupApiV1BoardGroupsPost } from "@/api/generated/board-groups/board-groups"; import { useCreateBoardGroupApiV1BoardGroupsPost } from "@/api/generated/board-groups/board-groups";
import type { BoardRead } from "@/api/generated/model"; import type { BoardRead } from "@/api/generated/model";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -126,16 +127,10 @@ export default function NewBoardGroupPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to create a board group."
<p className="text-sm text-slate-600"> forceRedirectUrl="/board-groups/new"
Sign in to create a board group. />
</p>
<SignInButton mode="modal" forceRedirectUrl="/board-groups/new">
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { import {
type ColumnDef, type ColumnDef,
flexRender, flexRender,
@@ -33,6 +33,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
const formatTimestamp = (value?: string | null) => { const formatTimestamp = (value?: string | null) => {
if (!value) return "—"; if (!value) return "—";
@@ -190,16 +191,10 @@ export default function BoardGroupsPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to view board groups."
<p className="text-sm text-slate-600"> forceRedirectUrl="/board-groups"
Sign in to view board groups. />
</p>
<SignInButton mode="modal" forceRedirectUrl="/board-groups">
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useParams, useRouter, useSearchParams } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
@@ -32,6 +32,8 @@ import type {
BoardUpdate, BoardUpdate,
} from "@/api/generated/model"; } from "@/api/generated/model";
import { BoardOnboardingChat } from "@/components/BoardOnboardingChat"; import { BoardOnboardingChat } from "@/components/BoardOnboardingChat";
import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -308,18 +310,11 @@ export default function EditBoardPage() {
<> <>
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to edit boards."
<p className="text-sm text-slate-600">Sign in to edit boards.</p> forceRedirectUrl={`/boards/${boardId}/edit`}
<SignInButton signUpForceRedirectUrl={`/boards/${boardId}/edit`}
mode="modal" />
forceRedirectUrl={`/boards/${boardId}/edit`}
signUpForceRedirectUrl={`/boards/${boardId}/edit`}
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />
@@ -337,9 +332,7 @@ export default function EditBoardPage() {
<div className="p-8"> <div className="p-8">
{!isAdmin ? ( {!isAdmin ? (
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm"> <AdminOnlyNotice message="Only organization owners and admins can edit board settings." />
Only organization owners and admins can edit board settings.
</div>
) : ( ) : (
<div className="space-y-6"> <div className="space-y-6">
<form <form

View File

@@ -6,7 +6,7 @@ import { useMemo, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
import { useCreateBoardApiV1BoardsPost } from "@/api/generated/boards/boards"; import { useCreateBoardApiV1BoardsPost } from "@/api/generated/boards/boards";
@@ -23,6 +23,8 @@ import {
useGetMyMembershipApiV1OrganizationsMeMemberGet, useGetMyMembershipApiV1OrganizationsMeMemberGet,
} from "@/api/generated/organizations/organizations"; } from "@/api/generated/organizations/organizations";
import type { BoardGroupRead } from "@/api/generated/model"; import type { BoardGroupRead } from "@/api/generated/model";
import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -156,18 +158,11 @@ export default function NewBoardPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to create a board."
<p className="text-sm text-slate-600">Sign in to create a board.</p> forceRedirectUrl="/boards/new"
<SignInButton signUpForceRedirectUrl="/boards/new"
mode="modal" />
forceRedirectUrl="/boards/new"
signUpForceRedirectUrl="/boards/new"
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />
@@ -185,9 +180,7 @@ export default function NewBoardPage() {
<div className="p-8"> <div className="p-8">
{!isAdmin ? ( {!isAdmin ? (
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm"> <AdminOnlyNotice message="Only organization owners and admins can create boards." />
Only organization owners and admins can create boards.
</div>
) : ( ) : (
<form <form
onSubmit={handleSubmit} onSubmit={handleSubmit}

View File

@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { import {
type ColumnDef, type ColumnDef,
flexRender, flexRender,
@@ -41,6 +41,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
const formatTimestamp = (value?: string | null) => { const formatTimestamp = (value?: string | null) => {
if (!value) return "—"; if (!value) return "—";
@@ -254,18 +255,11 @@ export default function BoardsPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to view boards."
<p className="text-sm text-slate-600">Sign in to view boards.</p> forceRedirectUrl="/boards"
<SignInButton signUpForceRedirectUrl="/boards"
mode="modal" />
forceRedirectUrl="/boards"
signUpForceRedirectUrl="/boards"
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -4,7 +4,7 @@ export const dynamic = "force-dynamic";
import { useMemo } from "react"; import { useMemo } from "react";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { import {
Area, Area,
AreaChart, AreaChart,
@@ -22,8 +22,8 @@ import { Activity, Clock, PenSquare, Timer, Users } from "lucide-react";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import MetricSparkline from "@/components/charts/metric-sparkline"; import MetricSparkline from "@/components/charts/metric-sparkline";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { ApiError } from "@/api/mutator"; import { ApiError } from "@/api/mutator";
import { import {
type dashboardMetricsApiV1MetricsDashboardGetResponse, type dashboardMetricsApiV1MetricsDashboardGetResponse,
@@ -319,20 +319,11 @@ export default function DashboardPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to access the dashboard."
<p className="text-sm text-slate-600"> forceRedirectUrl="/onboarding"
Sign in to access the dashboard. signUpForceRedirectUrl="/onboarding"
</p> />
<SignInButton
mode="modal"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -4,7 +4,7 @@ export const dynamic = "force-dynamic";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { Building2, Copy, UserPlus, Users } from "lucide-react"; import { Building2, Copy, UserPlus, Users } from "lucide-react";
@@ -38,6 +38,7 @@ import type {
OrganizationInviteRead, OrganizationInviteRead,
OrganizationMemberRead, OrganizationMemberRead,
} from "@/api/generated/model"; } from "@/api/generated/model";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -721,20 +722,11 @@ export default function OrganizationPage() {
return ( return (
<DashboardShell> <DashboardShell>
<SignedOut> <SignedOut>
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <SignedOutPanel
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> message="Sign in to manage your organization."
<p className="text-sm text-slate-600"> forceRedirectUrl="/organization"
Sign in to manage your organization. signUpForceRedirectUrl="/organization"
</p> />
<SignInButton
mode="modal"
forceRedirectUrl="/organization"
signUpForceRedirectUrl="/organization"
>
<Button className="mt-4">Sign in</Button>
</SignInButton>
</div>
</div>
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />

View File

@@ -5,20 +5,32 @@ import { Button } from "@/components/ui/button";
type SignedOutPanelProps = { type SignedOutPanelProps = {
message: string; message: string;
forceRedirectUrl: string; forceRedirectUrl: string;
signUpForceRedirectUrl?: string;
mode?: "modal" | "redirect";
buttonLabel?: string; buttonLabel?: string;
buttonTestId?: string;
}; };
export function SignedOutPanel({ export function SignedOutPanel({
message, message,
forceRedirectUrl, forceRedirectUrl,
signUpForceRedirectUrl,
mode = "modal",
buttonLabel = "Sign in", buttonLabel = "Sign in",
buttonTestId,
}: SignedOutPanelProps) { }: SignedOutPanelProps) {
return ( return (
<div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center"> <div className="col-span-2 flex min-h-[calc(100vh-64px)] items-center justify-center bg-slate-50 p-10 text-center">
<div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm"> <div className="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm">
<p className="text-sm text-slate-600">{message}</p> <p className="text-sm text-slate-600">{message}</p>
<SignInButton mode="modal" forceRedirectUrl={forceRedirectUrl}> <SignInButton
<Button className="mt-4">{buttonLabel}</Button> mode={mode}
forceRedirectUrl={forceRedirectUrl}
signUpForceRedirectUrl={signUpForceRedirectUrl}
>
<Button className="mt-4" data-testid={buttonTestId}>
{buttonLabel}
</Button>
</SignInButton> </SignInButton>
</div> </div>
</div> </div>