feat: add organization-related models and update schemas for organization management
This commit is contained in:
@@ -15,6 +15,10 @@ import {
|
||||
useGetGatewayApiV1GatewaysGatewayIdGet,
|
||||
useUpdateGatewayApiV1GatewaysGatewayIdPatch,
|
||||
} from "@/api/generated/gateways/gateways";
|
||||
import {
|
||||
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||
} from "@/api/generated/organizations/organizations";
|
||||
import type { GatewayUpdate } from "@/api/generated/model";
|
||||
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
|
||||
import { DashboardShell } from "@/components/templates/DashboardShell";
|
||||
@@ -50,6 +54,20 @@ export default function EditGatewayPage() {
|
||||
? gatewayIdParam[0]
|
||||
: gatewayIdParam;
|
||||
|
||||
const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet<
|
||||
getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
ApiError
|
||||
>({
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn),
|
||||
refetchOnMount: "always",
|
||||
retry: false,
|
||||
},
|
||||
});
|
||||
const member =
|
||||
membershipQuery.data?.status === 200 ? membershipQuery.data.data : null;
|
||||
const isAdmin = member ? ["owner", "admin"].includes(member.role) : false;
|
||||
|
||||
const [name, setName] = useState<string | undefined>(undefined);
|
||||
const [gatewayUrl, setGatewayUrl] = useState<string | undefined>(undefined);
|
||||
const [gatewayToken, setGatewayToken] = useState<string | undefined>(
|
||||
@@ -77,7 +95,7 @@ export default function EditGatewayPage() {
|
||||
ApiError
|
||||
>(gatewayId ?? "", {
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn && gatewayId),
|
||||
enabled: Boolean(isSignedIn && isAdmin && gatewayId),
|
||||
refetchOnMount: "always",
|
||||
retry: false,
|
||||
},
|
||||
@@ -230,21 +248,26 @@ export default function EditGatewayPage() {
|
||||
</div>
|
||||
|
||||
<div className="p-8">
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-900">
|
||||
Gateway name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
value={resolvedName}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
placeholder="Primary gateway"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
{!isAdmin ? (
|
||||
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm">
|
||||
Only organization owners and admins can edit gateways.
|
||||
</div>
|
||||
) : (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-900">
|
||||
Gateway name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
value={resolvedName}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
placeholder="Primary gateway"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
@@ -361,6 +384,7 @@ export default function EditGatewayPage() {
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</SignedIn>
|
||||
|
||||
@@ -18,6 +18,10 @@ import {
|
||||
type listAgentsApiV1AgentsGetResponse,
|
||||
useListAgentsApiV1AgentsGet,
|
||||
} from "@/api/generated/agents/agents";
|
||||
import {
|
||||
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||
} from "@/api/generated/organizations/organizations";
|
||||
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
|
||||
import { DashboardShell } from "@/components/templates/DashboardShell";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -49,12 +53,26 @@ export default function GatewayDetailPage() {
|
||||
? gatewayIdParam[0]
|
||||
: gatewayIdParam;
|
||||
|
||||
const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet<
|
||||
getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
ApiError
|
||||
>({
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn),
|
||||
refetchOnMount: "always",
|
||||
retry: false,
|
||||
},
|
||||
});
|
||||
const member =
|
||||
membershipQuery.data?.status === 200 ? membershipQuery.data.data : null;
|
||||
const isAdmin = member ? ["owner", "admin"].includes(member.role) : false;
|
||||
|
||||
const gatewayQuery = useGetGatewayApiV1GatewaysGatewayIdGet<
|
||||
getGatewayApiV1GatewaysGatewayIdGetResponse,
|
||||
ApiError
|
||||
>(gatewayId ?? "", {
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn && gatewayId),
|
||||
enabled: Boolean(isSignedIn && isAdmin && gatewayId),
|
||||
refetchInterval: 30_000,
|
||||
},
|
||||
});
|
||||
@@ -67,7 +85,7 @@ export default function GatewayDetailPage() {
|
||||
ApiError
|
||||
>(gatewayId ? { gateway_id: gatewayId } : undefined, {
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn && gatewayId),
|
||||
enabled: Boolean(isSignedIn && isAdmin && gatewayId),
|
||||
refetchInterval: 15_000,
|
||||
},
|
||||
});
|
||||
@@ -85,7 +103,7 @@ export default function GatewayDetailPage() {
|
||||
ApiError
|
||||
>(statusParams, {
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn && statusParams),
|
||||
enabled: Boolean(isSignedIn && isAdmin && statusParams),
|
||||
refetchInterval: 15_000,
|
||||
},
|
||||
});
|
||||
@@ -142,7 +160,7 @@ export default function GatewayDetailPage() {
|
||||
>
|
||||
Back to gateways
|
||||
</Button>
|
||||
{gatewayId ? (
|
||||
{isAdmin && gatewayId ? (
|
||||
<Button
|
||||
onClick={() => router.push(`/gateways/${gatewayId}/edit`)}
|
||||
>
|
||||
@@ -154,7 +172,11 @@ export default function GatewayDetailPage() {
|
||||
</div>
|
||||
|
||||
<div className="p-8">
|
||||
{gatewayQuery.isLoading ? (
|
||||
{!isAdmin ? (
|
||||
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm">
|
||||
Only organization owners and admins can access gateways.
|
||||
</div>
|
||||
) : gatewayQuery.isLoading ? (
|
||||
<div className="rounded-xl border border-slate-200 bg-white p-6 text-sm text-slate-500 shadow-sm">
|
||||
Loading gateway…
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,10 @@ import {
|
||||
gatewaysStatusApiV1GatewaysStatusGet,
|
||||
useCreateGatewayApiV1GatewaysPost,
|
||||
} from "@/api/generated/gateways/gateways";
|
||||
import {
|
||||
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||
} from "@/api/generated/organizations/organizations";
|
||||
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
|
||||
import { DashboardShell } from "@/components/templates/DashboardShell";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -42,6 +46,20 @@ export default function NewGatewayPage() {
|
||||
const { isSignedIn } = useAuth();
|
||||
const router = useRouter();
|
||||
|
||||
const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet<
|
||||
getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
ApiError
|
||||
>({
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn),
|
||||
refetchOnMount: "always",
|
||||
retry: false,
|
||||
},
|
||||
});
|
||||
const member =
|
||||
membershipQuery.data?.status === 200 ? membershipQuery.data.data : null;
|
||||
const isAdmin = member ? ["owner", "admin"].includes(member.role) : false;
|
||||
|
||||
const [name, setName] = useState("");
|
||||
const [gatewayUrl, setGatewayUrl] = useState("");
|
||||
const [gatewayToken, setGatewayToken] = useState("");
|
||||
@@ -191,21 +209,26 @@ export default function NewGatewayPage() {
|
||||
</div>
|
||||
|
||||
<div className="p-8">
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-900">
|
||||
Gateway name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
placeholder="Primary gateway"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
{!isAdmin ? (
|
||||
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm">
|
||||
Only organization owners and admins can create gateways.
|
||||
</div>
|
||||
) : (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-900">
|
||||
Gateway name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
placeholder="Primary gateway"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
@@ -320,6 +343,7 @@ export default function NewGatewayPage() {
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</SignedIn>
|
||||
|
||||
@@ -35,6 +35,10 @@ import {
|
||||
useDeleteGatewayApiV1GatewaysGatewayIdDelete,
|
||||
useListGatewaysApiV1GatewaysGet,
|
||||
} from "@/api/generated/gateways/gateways";
|
||||
import {
|
||||
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
useGetMyMembershipApiV1OrganizationsMeMemberGet,
|
||||
} from "@/api/generated/organizations/organizations";
|
||||
import type { GatewayRead } from "@/api/generated/model";
|
||||
|
||||
const truncate = (value?: string | null, max = 24) => {
|
||||
@@ -58,6 +62,20 @@ const formatTimestamp = (value?: string | null) => {
|
||||
export default function GatewaysPage() {
|
||||
const { isSignedIn } = useAuth();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet<
|
||||
getMyMembershipApiV1OrganizationsMeMemberGetResponse,
|
||||
ApiError
|
||||
>({
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn),
|
||||
refetchOnMount: "always",
|
||||
retry: false,
|
||||
},
|
||||
});
|
||||
const member =
|
||||
membershipQuery.data?.status === 200 ? membershipQuery.data.data : null;
|
||||
const isAdmin = member ? ["owner", "admin"].includes(member.role) : false;
|
||||
const [sorting, setSorting] = useState<SortingState>([
|
||||
{ id: "name", desc: false },
|
||||
]);
|
||||
@@ -69,7 +87,7 @@ export default function GatewaysPage() {
|
||||
ApiError
|
||||
>(undefined, {
|
||||
query: {
|
||||
enabled: Boolean(isSignedIn),
|
||||
enabled: Boolean(isSignedIn && isAdmin),
|
||||
refetchInterval: 30_000,
|
||||
refetchOnMount: "always",
|
||||
},
|
||||
@@ -240,7 +258,7 @@ export default function GatewaysPage() {
|
||||
Manage OpenClaw gateway connections used by boards
|
||||
</p>
|
||||
</div>
|
||||
{gateways.length > 0 ? (
|
||||
{isAdmin && gateways.length > 0 ? (
|
||||
<Link
|
||||
href="/gateways/new"
|
||||
className={buttonVariants({
|
||||
@@ -256,9 +274,15 @@ export default function GatewaysPage() {
|
||||
</div>
|
||||
|
||||
<div className="p-8">
|
||||
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
{!isAdmin ? (
|
||||
<div className="rounded-xl border border-slate-200 bg-white px-6 py-5 text-sm text-slate-600 shadow-sm">
|
||||
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-x-auto">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead className="sticky top-0 z-10 bg-slate-50 text-xs font-semibold uppercase tracking-wider text-slate-500">
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
@@ -347,11 +371,13 @@ export default function GatewaysPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{gatewaysQuery.error ? (
|
||||
<p className="mt-4 text-sm text-red-500">
|
||||
{gatewaysQuery.error.message}
|
||||
</p>
|
||||
) : null}
|
||||
{gatewaysQuery.error ? (
|
||||
<p className="mt-4 text-sm text-red-500">
|
||||
{gatewaysQuery.error.message}
|
||||
</p>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</SignedIn>
|
||||
|
||||
Reference in New Issue
Block a user