feat: refactor authentication panels and add AdminOnlyNotice component
This commit is contained in:
@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
|
|||||||
import { useState } from "react";
|
import { 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,10 +18,11 @@ import {
|
|||||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||||
} from "@/api/generated/organizations/organizations";
|
} from "@/api/generated/organizations/organizations";
|
||||||
import type { GatewayUpdate } from "@/api/generated/model";
|
import type { GatewayUpdate } from "@/api/generated/model";
|
||||||
|
import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice";
|
||||||
|
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
|
||||||
import { GatewayForm } from "@/components/gateways/GatewayForm";
|
import { GatewayForm } from "@/components/gateways/GatewayForm";
|
||||||
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 {
|
import {
|
||||||
DEFAULT_MAIN_SESSION_KEY,
|
DEFAULT_MAIN_SESSION_KEY,
|
||||||
DEFAULT_WORKSPACE_ROOT,
|
DEFAULT_WORKSPACE_ROOT,
|
||||||
@@ -180,17 +181,10 @@ export default function EditGatewayPage() {
|
|||||||
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 a gateway."
|
||||||
<p className="text-sm text-slate-600">Sign in to edit a gateway.</p>
|
|
||||||
<SignInButton
|
|
||||||
mode="modal"
|
|
||||||
forceRedirectUrl={`/gateways/${gatewayId}/edit`}
|
forceRedirectUrl={`/gateways/${gatewayId}/edit`}
|
||||||
>
|
/>
|
||||||
<Button className="mt-4">Sign in</Button>
|
|
||||||
</SignInButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SignedOut>
|
</SignedOut>
|
||||||
<SignedIn>
|
<SignedIn>
|
||||||
<DashboardSidebar />
|
<DashboardSidebar />
|
||||||
@@ -210,9 +204,7 @@ export default function EditGatewayPage() {
|
|||||||
|
|
||||||
<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 gateways." />
|
||||||
Only organization owners and admins can edit gateways.
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<GatewayForm
|
<GatewayForm
|
||||||
name={resolvedName}
|
name={resolvedName}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } 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 {
|
||||||
@@ -22,6 +22,8 @@ import {
|
|||||||
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||||
} from "@/api/generated/organizations/organizations";
|
} from "@/api/generated/organizations/organizations";
|
||||||
|
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";
|
||||||
@@ -128,17 +130,10 @@ export default function GatewayDetailPage() {
|
|||||||
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 a gateway."
|
||||||
<p className="text-sm text-slate-600">Sign in to view a gateway.</p>
|
|
||||||
<SignInButton
|
|
||||||
mode="modal"
|
|
||||||
forceRedirectUrl={`/gateways/${gatewayId}`}
|
forceRedirectUrl={`/gateways/${gatewayId}`}
|
||||||
>
|
/>
|
||||||
<Button className="mt-4">Sign in</Button>
|
|
||||||
</SignInButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SignedOut>
|
</SignedOut>
|
||||||
<SignedIn>
|
<SignedIn>
|
||||||
<DashboardSidebar />
|
<DashboardSidebar />
|
||||||
@@ -173,9 +168,7 @@ export default function GatewayDetailPage() {
|
|||||||
|
|
||||||
<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 gateways." />
|
||||||
Only organization owners and admins can access gateways.
|
|
||||||
</div>
|
|
||||||
) : gatewayQuery.isLoading ? (
|
) : gatewayQuery.isLoading ? (
|
||||||
<div className="rounded-xl border border-slate-200 bg-white p-6 text-sm text-slate-500 shadow-sm">
|
<div className="rounded-xl border border-slate-200 bg-white p-6 text-sm text-slate-500 shadow-sm">
|
||||||
Loading gateway…
|
Loading gateway…
|
||||||
|
|||||||
@@ -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 { useCreateGatewayApiV1GatewaysPost } from "@/api/generated/gateways/gateways";
|
import { useCreateGatewayApiV1GatewaysPost } from "@/api/generated/gateways/gateways";
|
||||||
@@ -13,10 +13,11 @@ import {
|
|||||||
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||||
} from "@/api/generated/organizations/organizations";
|
} from "@/api/generated/organizations/organizations";
|
||||||
|
import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice";
|
||||||
|
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
|
||||||
import { GatewayForm } from "@/components/gateways/GatewayForm";
|
import { GatewayForm } from "@/components/gateways/GatewayForm";
|
||||||
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 {
|
import {
|
||||||
DEFAULT_MAIN_SESSION_KEY,
|
DEFAULT_MAIN_SESSION_KEY,
|
||||||
DEFAULT_WORKSPACE_ROOT,
|
DEFAULT_WORKSPACE_ROOT,
|
||||||
@@ -141,16 +142,10 @@ export default function NewGatewayPage() {
|
|||||||
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 gateway."
|
||||||
<p className="text-sm text-slate-600">
|
forceRedirectUrl="/gateways/new"
|
||||||
Sign in to create a gateway.
|
/>
|
||||||
</p>
|
|
||||||
<SignInButton mode="modal" forceRedirectUrl="/gateways/new">
|
|
||||||
<Button className="mt-4">Sign in</Button>
|
|
||||||
</SignInButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SignedOut>
|
</SignedOut>
|
||||||
<SignedIn>
|
<SignedIn>
|
||||||
<DashboardSidebar />
|
<DashboardSidebar />
|
||||||
@@ -168,9 +163,7 @@ export default function NewGatewayPage() {
|
|||||||
|
|
||||||
<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 gateways." />
|
||||||
Only organization owners and admins can create gateways.
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<GatewayForm
|
<GatewayForm
|
||||||
name={name}
|
name={name}
|
||||||
|
|||||||
@@ -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,
|
||||||
type SortingState,
|
type SortingState,
|
||||||
@@ -40,6 +40,8 @@ import {
|
|||||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||||
} from "@/api/generated/organizations/organizations";
|
} from "@/api/generated/organizations/organizations";
|
||||||
import type { GatewayRead } from "@/api/generated/model";
|
import type { GatewayRead } from "@/api/generated/model";
|
||||||
|
import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice";
|
||||||
|
import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
|
||||||
|
|
||||||
const truncate = (value?: string | null, max = 24) => {
|
const truncate = (value?: string | null, max = 24) => {
|
||||||
if (!value) return "—";
|
if (!value) return "—";
|
||||||
@@ -235,14 +237,10 @@ export default function GatewaysPage() {
|
|||||||
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 gateways."
|
||||||
<p className="text-sm text-slate-600">Sign in to view gateways.</p>
|
forceRedirectUrl="/gateways"
|
||||||
<SignInButton mode="modal" forceRedirectUrl="/gateways">
|
/>
|
||||||
<Button className="mt-4">Sign in</Button>
|
|
||||||
</SignInButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SignedOut>
|
</SignedOut>
|
||||||
<SignedIn>
|
<SignedIn>
|
||||||
<DashboardSidebar />
|
<DashboardSidebar />
|
||||||
@@ -275,9 +273,7 @@ export default function GatewaysPage() {
|
|||||||
|
|
||||||
<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 gateways." />
|
||||||
Only organization owners and admins can access gateways.
|
|
||||||
</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">
|
||||||
|
|||||||
11
frontend/src/components/auth/AdminOnlyNotice.tsx
Normal file
11
frontend/src/components/auth/AdminOnlyNotice.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
type AdminOnlyNoticeProps = {
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function AdminOnlyNotice({ message }: AdminOnlyNoticeProps) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm">
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
frontend/src/components/auth/SignedOutPanel.tsx
Normal file
26
frontend/src/components/auth/SignedOutPanel.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { SignInButton } from "@/auth/clerk";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
type SignedOutPanelProps = {
|
||||||
|
message: string;
|
||||||
|
forceRedirectUrl: string;
|
||||||
|
buttonLabel?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SignedOutPanel({
|
||||||
|
message,
|
||||||
|
forceRedirectUrl,
|
||||||
|
buttonLabel = "Sign in",
|
||||||
|
}: SignedOutPanelProps) {
|
||||||
|
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="rounded-xl border border-slate-200 bg-white px-8 py-6 shadow-sm">
|
||||||
|
<p className="text-sm text-slate-600">{message}</p>
|
||||||
|
<SignInButton mode="modal" forceRedirectUrl={forceRedirectUrl}>
|
||||||
|
<Button className="mt-4">{buttonLabel}</Button>
|
||||||
|
</SignInButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user