Migrate current organization from in memory to url state
Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
@@ -4,7 +4,13 @@ import { lazy, StrictMode, Suspense } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { HelmetProvider } from "react-helmet-async";
|
||||
import { RelayEnvironmentProvider } from "react-relay";
|
||||
import { BrowserRouter, Route, Routes, useLocation } from "react-router";
|
||||
import {
|
||||
BrowserRouter,
|
||||
Route,
|
||||
Routes,
|
||||
useLocation,
|
||||
Navigate,
|
||||
} from "react-router";
|
||||
import "App.css";
|
||||
import ErrorBoundary from "./components/ErrorBoundary";
|
||||
import ConsoleLayout from "./layouts/ConsoleLayout";
|
||||
@@ -12,7 +18,6 @@ import AuthLayout from "./layouts/AuthLayout";
|
||||
import { RelayEnvironment } from "./RelayEnvironment";
|
||||
import { AuthProvider } from "./contexts/AuthContext";
|
||||
import { ProtectedRoute } from "./components/ProtectedRoute";
|
||||
import { OrganizationProvider } from "./contexts/OrganizationContext";
|
||||
|
||||
posthog.init(process.env.POSTHOG_KEY!, {
|
||||
api_host: process.env.POSTHOG_HOST,
|
||||
@@ -24,6 +29,9 @@ posthog.init(process.env.POSTHOG_KEY!, {
|
||||
},
|
||||
});
|
||||
|
||||
const OrganizationSelectionPage = lazy(
|
||||
() => import("./pages/OrganizationSelectionPage")
|
||||
);
|
||||
const HomePage = lazy(() => import("./pages/HomePage"));
|
||||
const NotFoundPage = lazy(() => import("./pages/NotFoundPage"));
|
||||
const PeopleListPage = lazy(() => import("./pages/PeopleListPage"));
|
||||
@@ -52,181 +60,194 @@ function App() {
|
||||
<HelmetProvider>
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<OrganizationProvider>
|
||||
<Routes>
|
||||
{/* Authentication Routes - Accessible without login */}
|
||||
<Routes>
|
||||
{/* Authentication Routes - Accessible without login */}
|
||||
<Route
|
||||
path="/login"
|
||||
element={
|
||||
<ErrorBoundaryWithLocation>
|
||||
<AuthLayout />
|
||||
</ErrorBoundaryWithLocation>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
path="/login"
|
||||
index
|
||||
element={
|
||||
<ErrorBoundaryWithLocation>
|
||||
<AuthLayout />
|
||||
</ErrorBoundaryWithLocation>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<LoginPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route
|
||||
path="/register"
|
||||
element={
|
||||
<ErrorBoundaryWithLocation>
|
||||
<AuthLayout />
|
||||
</ErrorBoundaryWithLocation>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<RegisterPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
{/* Protected Routes - Require authentication */}
|
||||
<Route element={<ProtectedRoute />}>
|
||||
<Route
|
||||
path="/*"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<ConsoleLayout />
|
||||
<LoginPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route
|
||||
path="/register"
|
||||
element={
|
||||
<ErrorBoundaryWithLocation>
|
||||
<AuthLayout />
|
||||
</ErrorBoundaryWithLocation>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<RegisterPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
{/* Protected Routes - Require authentication */}
|
||||
<Route element={<ProtectedRoute />}>
|
||||
{/* Organization Selection Route */}
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<OrganizationSelectionPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Organization Creation Route */}
|
||||
<Route
|
||||
path="/organizations/create"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<CreateOrganizationPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Organization-specific Routes */}
|
||||
<Route
|
||||
path="/organizations/:organizationId/*"
|
||||
element={
|
||||
<ErrorBoundaryWithLocation>
|
||||
<ConsoleLayout />
|
||||
</ErrorBoundaryWithLocation>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<HomePage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<HomePage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="peoples"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<PeopleListPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="peoples/create"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<CreatePeoplePage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="peoples/:peopleId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<PeopleOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="vendors"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<VendorListPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="frameworks"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<FrameworkListPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="frameworks/:frameworkId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<FrameworkOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="frameworks/:frameworkId/controls/:controlId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<ControlOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="vendors/:vendorId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<VendorOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="settings"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<SettingsPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="organizations/create"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<CreateOrganizationPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<Suspense>
|
||||
<NotFoundPage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
/>
|
||||
<Route
|
||||
path="peoples"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<PeopleListPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="peoples/create"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<CreatePeoplePage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="peoples/:peopleId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<PeopleOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="vendors"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<VendorListPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="frameworks"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<FrameworkListPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="frameworks/:frameworkId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<FrameworkOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="frameworks/:frameworkId/controls/:controlId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<ControlOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="vendors/:vendorId"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<VendorOverviewPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="settings"
|
||||
element={
|
||||
<Suspense>
|
||||
<ErrorBoundaryWithLocation>
|
||||
<SettingsPage />
|
||||
</ErrorBoundaryWithLocation>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<Suspense>
|
||||
<NotFoundPage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</OrganizationProvider>
|
||||
</Route>
|
||||
</Routes>
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</HelmetProvider>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import {
|
||||
BookOpen,
|
||||
Users,
|
||||
@@ -31,55 +32,52 @@ import {
|
||||
} from "@/components/ui/sidebar";
|
||||
import type { AppSidebarQuery as AppSidebarQueryType } from "./__generated__/AppSidebarQuery.graphql";
|
||||
import { TeamSwitcher } from "@/components/TeamSwitcher";
|
||||
import { url } from "inspector";
|
||||
const staticData = {
|
||||
user: {
|
||||
name: "shadcn",
|
||||
email: "m@example.com",
|
||||
avatar: "/avatars/shadcn.jpg",
|
||||
},
|
||||
navMain: [
|
||||
{
|
||||
title: "Frameworks",
|
||||
url: "/frameworks",
|
||||
icon: BookOpen,
|
||||
},
|
||||
{
|
||||
title: "Organizations",
|
||||
icon: Building,
|
||||
url: "/peoples",
|
||||
items: [
|
||||
{
|
||||
title: "Peoples",
|
||||
url: "/peoples",
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
title: "Vendors",
|
||||
url: "/vendors",
|
||||
icon: ToyBrick,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
url: "/settings",
|
||||
icon: Settings,
|
||||
},
|
||||
],
|
||||
navSecondary: [
|
||||
{
|
||||
title: "Support",
|
||||
url: "#",
|
||||
icon: LifeBuoy,
|
||||
},
|
||||
{
|
||||
title: "Feedback",
|
||||
url: "#",
|
||||
icon: Send,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function getNavItems(organizationId: string) {
|
||||
return {
|
||||
navMain: [
|
||||
{
|
||||
title: "Frameworks",
|
||||
url: `/organizations/${organizationId}/frameworks`,
|
||||
icon: BookOpen,
|
||||
},
|
||||
{
|
||||
title: "Organizations",
|
||||
icon: Building,
|
||||
url: `/organizations/${organizationId}/peoples`,
|
||||
items: [
|
||||
{
|
||||
title: "Peoples",
|
||||
url: `/organizations/${organizationId}/peoples`,
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
title: "Vendors",
|
||||
url: `/organizations/${organizationId}/vendors`,
|
||||
icon: ToyBrick,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
url: `/organizations/${organizationId}/settings`,
|
||||
icon: Settings,
|
||||
},
|
||||
],
|
||||
navSecondary: [
|
||||
{
|
||||
title: "Support",
|
||||
url: "#",
|
||||
icon: LifeBuoy,
|
||||
},
|
||||
{
|
||||
title: "Feedback",
|
||||
url: "#",
|
||||
icon: Send,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const AppSidebarQuery = graphql`
|
||||
query AppSidebarQuery {
|
||||
@@ -97,7 +95,9 @@ function AppSidebarContent({
|
||||
}: React.ComponentProps<typeof Sidebar> & {
|
||||
queryRef: PreloadedQuery<AppSidebarQueryType>;
|
||||
}) {
|
||||
const { organizationId } = useParams();
|
||||
const data = usePreloadedQuery(AppSidebarQuery, queryRef);
|
||||
const navItems = getNavItems(organizationId!);
|
||||
|
||||
return (
|
||||
<Sidebar variant="inset" {...props}>
|
||||
@@ -105,8 +105,8 @@ function AppSidebarContent({
|
||||
<TeamSwitcher organizations={data.viewer} />
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<NavMain items={staticData.navMain} />
|
||||
<NavSecondary items={staticData.navSecondary} className="mt-auto" />
|
||||
<NavMain items={navItems.navMain} />
|
||||
<NavSecondary items={navItems.navSecondary} className="mt-auto" />
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<NavUser viewer={data.viewer} />
|
||||
@@ -121,39 +121,37 @@ function AppSidebarFallback(props: React.ComponentProps<typeof Sidebar>) {
|
||||
<SidebarHeader>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton size="lg" asChild>
|
||||
<a href="#">
|
||||
<div className="size-8 rounded-lg bg-muted animate-pulse" />
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-semibold">
|
||||
<div className="h-4 w-24 animate-pulse rounded bg-muted" />
|
||||
</span>
|
||||
<span className="truncate text-xs">Enterprise</span>
|
||||
</div>
|
||||
</a>
|
||||
<SidebarMenuButton size="lg">
|
||||
<div className="h-8 w-8 rounded-lg bg-sidebar-muted" />
|
||||
<div className="flex-1 space-y-1">
|
||||
<div className="h-4 w-3/4 rounded-lg bg-sidebar-muted" />
|
||||
<div className="h-3 w-1/2 rounded-lg bg-sidebar-muted" />
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<NavMain items={staticData.navMain} />
|
||||
<NavSecondary items={staticData.navSecondary} className="mt-auto" />
|
||||
<SidebarMenu>
|
||||
{Array.from({ length: 3 }).map((_, i) => (
|
||||
<SidebarMenuItem key={i}>
|
||||
<SidebarMenuButton>
|
||||
<div className="h-4 w-4 rounded-lg bg-sidebar-muted" />
|
||||
<div className="h-4 w-24 rounded-lg bg-sidebar-muted" />
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton size="lg" asChild>
|
||||
<a href="#">
|
||||
<div className="h-8 w-8 rounded-lg bg-muted animate-pulse" />
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-semibold">
|
||||
<div className="h-4 w-24 animate-pulse rounded bg-muted" />
|
||||
</span>
|
||||
<span className="truncate text-xs">
|
||||
<div className="h-3 w-32 animate-pulse rounded bg-muted" />
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<SidebarMenuButton size="lg">
|
||||
<div className="h-8 w-8 rounded-lg bg-sidebar-muted" />
|
||||
<div className="flex-1 space-y-1">
|
||||
<div className="h-4 w-3/4 rounded-lg bg-sidebar-muted" />
|
||||
<div className="h-3 w-1/2 rounded-lg bg-sidebar-muted" />
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Sparkles,
|
||||
} from "lucide-react";
|
||||
import { graphql, useFragment } from "react-relay";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
@@ -26,6 +27,7 @@ import {
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import {
|
||||
NavUser_viewer$key,
|
||||
NavUser_viewer$data,
|
||||
@@ -42,6 +44,13 @@ export const navUserFragment = graphql`
|
||||
export function NavUser({ viewer }: { viewer: NavUser_viewer$key }) {
|
||||
const { isMobile } = useSidebar();
|
||||
const currentUser = useFragment(navUserFragment, viewer);
|
||||
const { logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
@@ -116,7 +125,7 @@ export function NavUser({ viewer }: { viewer: NavUser_viewer$key }) {
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleLogout}>
|
||||
<LogOut />
|
||||
Log out
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { ChevronsUpDown, Plus } from "lucide-react";
|
||||
import { graphql, useFragment } from "react-relay";
|
||||
import { useOrganization } from "@/contexts/OrganizationContext";
|
||||
import { Link } from "react-router";
|
||||
import { Link, useNavigate, useParams } from "react-router";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -54,20 +53,22 @@ export function TeamSwitcher({
|
||||
}) {
|
||||
const { isMobile } = useSidebar();
|
||||
const data = useFragment(teamSwitcherFragment, organizations);
|
||||
const { currentOrganization, setCurrentOrganization } = useOrganization();
|
||||
const navigate = useNavigate();
|
||||
const { organizationId } = useParams();
|
||||
const [currentOrganization, setCurrentOrganization] =
|
||||
useState<Organization | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(data.organizations);
|
||||
|
||||
if (
|
||||
data.organizations &&
|
||||
data.organizations.edges.length > 0 &&
|
||||
!currentOrganization
|
||||
) {
|
||||
// Type assertion to include plan field
|
||||
setCurrentOrganization(data.organizations.edges[0].node);
|
||||
if (data.organizations && data.organizations.edges.length > 0) {
|
||||
// Find the current organization based on the URL parameter
|
||||
const org = data.organizations.edges.find(
|
||||
(edge) => edge.node.id === organizationId
|
||||
);
|
||||
if (org) {
|
||||
setCurrentOrganization(org.node);
|
||||
}
|
||||
}
|
||||
}, [data.organizations, currentOrganization, setCurrentOrganization]);
|
||||
}, [data.organizations, organizationId]);
|
||||
|
||||
// If no organizations or data is still loading
|
||||
if (!currentOrganization) {
|
||||
@@ -88,6 +89,11 @@ export function TeamSwitcher({
|
||||
return null;
|
||||
};
|
||||
|
||||
const handleOrganizationSwitch = (org: Organization) => {
|
||||
// Navigate to the selected organization
|
||||
navigate(`/organizations/${org.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
@@ -121,7 +127,7 @@ export function TeamSwitcher({
|
||||
{data.organizations.edges.map((edge, index) => (
|
||||
<DropdownMenuItem
|
||||
key={edge.node.id}
|
||||
onClick={() => setCurrentOrganization(edge.node)}
|
||||
onClick={() => handleOrganizationSwitch(edge.node)}
|
||||
className="gap-2 p-2"
|
||||
>
|
||||
<div className="flex size-6 items-center justify-center rounded-sm border">
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import React, { createContext, useContext, useState, ReactNode } from "react";
|
||||
|
||||
interface Organization {
|
||||
id: string;
|
||||
name: string;
|
||||
logoUrl: string;
|
||||
}
|
||||
|
||||
interface OrganizationContextType {
|
||||
currentOrganization: Organization | null;
|
||||
setCurrentOrganization: (organization: Organization) => void;
|
||||
}
|
||||
|
||||
const OrganizationContext = createContext<OrganizationContextType | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
export function useOrganization() {
|
||||
const context = useContext(OrganizationContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
"useOrganization must be used within an OrganizationProvider",
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
interface OrganizationProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function OrganizationProvider({ children }: OrganizationProviderProps) {
|
||||
const [currentOrganization, setCurrentOrganization] =
|
||||
useState<Organization | null>(null);
|
||||
|
||||
return (
|
||||
<OrganizationContext.Provider
|
||||
value={{ currentOrganization, setCurrentOrganization }}
|
||||
>
|
||||
{children}
|
||||
</OrganizationContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -29,14 +29,32 @@ import { ConsoleLayoutBreadcrumbFrameworkOverviewQuery } from "./__generated__/C
|
||||
import { ConsoleLayoutBreadcrumbPeopleOverviewQuery } from "./__generated__/ConsoleLayoutBreadcrumbPeopleOverviewQuery.graphql";
|
||||
import { ConsoleLayoutBreadcrumbVendorOverviewQuery } from "./__generated__/ConsoleLayoutBreadcrumbVendorOverviewQuery.graphql";
|
||||
import { ConsoleLayoutBreadcrumbControlOverviewQuery } from "./__generated__/ConsoleLayoutBreadcrumbControlOverviewQuery.graphql";
|
||||
import { ConsoleLayoutOrganizationQuery } from "./__generated__/ConsoleLayoutOrganizationQuery.graphql";
|
||||
|
||||
function BreadcrumbHome({ children }: { children: React.ReactNode }) {
|
||||
const { organizationId } = useParams();
|
||||
const data = useLazyLoadQuery<ConsoleLayoutOrganizationQuery>(
|
||||
graphql`
|
||||
query ConsoleLayoutOrganizationQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
... on Organization {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ organizationId: organizationId! },
|
||||
{ fetchPolicy: "store-or-network" }
|
||||
);
|
||||
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to="/">Home</Link>
|
||||
<Link to={`/organizations/${organizationId}`}>
|
||||
{data.organization?.name || "Home"}
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
{children}
|
||||
@@ -46,12 +64,15 @@ function BreadcrumbHome({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
function BreadcrumbFrameworkList() {
|
||||
const { organizationId } = useParams();
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to="/frameworks">Frameworks</Link>
|
||||
<Link to={`/organizations/${organizationId}/frameworks`}>
|
||||
Frameworks
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
@@ -60,7 +81,7 @@ function BreadcrumbFrameworkList() {
|
||||
}
|
||||
|
||||
function BreadcrumbFrameworkOverview() {
|
||||
const { frameworkId } = useParams();
|
||||
const { organizationId, frameworkId } = useParams();
|
||||
const data = useLazyLoadQuery<ConsoleLayoutBreadcrumbFrameworkOverviewQuery>(
|
||||
graphql`
|
||||
query ConsoleLayoutBreadcrumbFrameworkOverviewQuery($frameworkId: ID!) {
|
||||
@@ -81,7 +102,9 @@ function BreadcrumbFrameworkOverview() {
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to={`/frameworks/${data.framework.id}`}>
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/frameworks/${data.framework.id}`}
|
||||
>
|
||||
{data.framework.name}
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
@@ -91,71 +114,14 @@ function BreadcrumbFrameworkOverview() {
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbPeopleList() {
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to="/peoples">People</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbCreatePeople() {
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to="/peoples/create">Create People</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbPeopleOverview() {
|
||||
const { peopleId } = useParams();
|
||||
const data = useLazyLoadQuery<ConsoleLayoutBreadcrumbPeopleOverviewQuery>(
|
||||
graphql`
|
||||
query ConsoleLayoutBreadcrumbPeopleOverviewQuery($peopleId: ID!) {
|
||||
people: node(id: $peopleId) {
|
||||
id
|
||||
... on People {
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ peopleId: peopleId! },
|
||||
{ fetchPolicy: "store-or-network" }
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to={`/peoples/${data.people.id}`}>{data.people.fullName}</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbVendorList() {
|
||||
const { organizationId } = useParams();
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to="/vendors">Vendors</Link>
|
||||
<Link to={`/organizations/${organizationId}/vendors`}>Vendors</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
@@ -164,7 +130,7 @@ function BreadcrumbVendorList() {
|
||||
}
|
||||
|
||||
function BreadcrumbVendorOverview() {
|
||||
const { vendorId } = useParams();
|
||||
const { organizationId, vendorId } = useParams();
|
||||
const data = useLazyLoadQuery<ConsoleLayoutBreadcrumbVendorOverviewQuery>(
|
||||
graphql`
|
||||
query ConsoleLayoutBreadcrumbVendorOverviewQuery($vendorId: ID!) {
|
||||
@@ -185,15 +151,91 @@ function BreadcrumbVendorOverview() {
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to={`/vendors/${data.vendor.id}`}>{data.vendor.name}</Link>
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/vendors/${data.vendor.id}`}
|
||||
>
|
||||
{data.vendor.name}
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbPeopleList() {
|
||||
const { organizationId } = useParams();
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to={`/organizations/${organizationId}/peoples`}>People</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbCreatePeople() {
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Create</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbPeopleOverview() {
|
||||
const { organizationId, peopleId } = useParams();
|
||||
const data = useLazyLoadQuery<ConsoleLayoutBreadcrumbPeopleOverviewQuery>(
|
||||
graphql`
|
||||
query ConsoleLayoutBreadcrumbPeopleOverviewQuery($peopleId: ID!) {
|
||||
people: node(id: $peopleId) {
|
||||
id
|
||||
... on People {
|
||||
fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ peopleId: peopleId! },
|
||||
{ fetchPolicy: "store-or-network" }
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/peoples/${data.people.id}`}
|
||||
>
|
||||
{data.people.fullName}
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbCreateOrganization() {
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Create Organization</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbControlOverview() {
|
||||
const { frameworkId, controlId } = useParams();
|
||||
const { organizationId, frameworkId, controlId } = useParams();
|
||||
const data = useLazyLoadQuery<ConsoleLayoutBreadcrumbControlOverviewQuery>(
|
||||
graphql`
|
||||
query ConsoleLayoutBreadcrumbControlOverviewQuery(
|
||||
@@ -223,7 +265,9 @@ function BreadcrumbControlOverview() {
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to={`/frameworks/${data.framework.id}`}>
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/frameworks/${data.framework.id}`}
|
||||
>
|
||||
{data.framework.name}
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
@@ -232,7 +276,7 @@ function BreadcrumbControlOverview() {
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link
|
||||
to={`/frameworks/${data.framework.id}/controls/${data.control.id}`}
|
||||
to={`/organizations/${organizationId}/frameworks/${data.framework.id}/controls/${data.control.id}`}
|
||||
>
|
||||
{data.control.name}
|
||||
</Link>
|
||||
@@ -243,21 +287,9 @@ function BreadcrumbControlOverview() {
|
||||
);
|
||||
}
|
||||
|
||||
function BreadcrumbCreateOrganization() {
|
||||
return (
|
||||
<>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to="/organizations/create">Create Organization</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ConsoleLayout() {
|
||||
const { organizationId } = useParams();
|
||||
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<AppSidebar />
|
||||
@@ -268,36 +300,29 @@ export default function ConsoleLayout() {
|
||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||
<BreadcrumbHome>
|
||||
<Routes>
|
||||
<Route path="/frameworks" element={<BreadcrumbFrameworkList />}>
|
||||
<Route path="frameworks" element={<BreadcrumbFrameworkList />}>
|
||||
<Route
|
||||
path="/frameworks/:frameworkId"
|
||||
path=":frameworkId"
|
||||
element={<BreadcrumbFrameworkOverview />}
|
||||
/>
|
||||
<Route
|
||||
path="/frameworks/:frameworkId/controls/:controlId"
|
||||
path=":frameworkId/controls/:controlId"
|
||||
element={<BreadcrumbControlOverview />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/peoples" element={<BreadcrumbPeopleList />}>
|
||||
<Route path="peoples" element={<BreadcrumbPeopleList />}>
|
||||
<Route
|
||||
path="/peoples/:peopleId"
|
||||
path=":peopleId"
|
||||
element={<BreadcrumbPeopleOverview />}
|
||||
/>
|
||||
<Route
|
||||
path="/peoples/create"
|
||||
element={<BreadcrumbCreatePeople />}
|
||||
/>
|
||||
<Route path="create" element={<BreadcrumbCreatePeople />} />
|
||||
</Route>
|
||||
<Route path="/vendors" element={<BreadcrumbVendorList />}>
|
||||
<Route path="vendors" element={<BreadcrumbVendorList />}>
|
||||
<Route
|
||||
path="/vendors/:vendorId"
|
||||
path=":vendorId"
|
||||
element={<BreadcrumbVendorOverview />}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="/organizations/create"
|
||||
element={<BreadcrumbCreateOrganization />}
|
||||
/>
|
||||
</Routes>
|
||||
</BreadcrumbHome>
|
||||
</div>
|
||||
|
||||
124
apps/console/src/layouts/__generated__/ConsoleLayoutOrganizationQuery.graphql.ts
generated
Normal file
124
apps/console/src/layouts/__generated__/ConsoleLayoutOrganizationQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @generated SignedSource<<93800eee21fa3f30cc45da37370359fd>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type ConsoleLayoutOrganizationQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type ConsoleLayoutOrganizationQuery$data = {
|
||||
readonly organization: {
|
||||
readonly name?: string;
|
||||
};
|
||||
};
|
||||
export type ConsoleLayoutOrganizationQuery = {
|
||||
response: ConsoleLayoutOrganizationQuery$data;
|
||||
variables: ConsoleLayoutOrganizationQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "organizationId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "organizationId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "ConsoleLayoutOrganizationQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "ConsoleLayoutOrganizationQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "e275faa366380b1b423251e6a06ab743",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "ConsoleLayoutOrganizationQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query ConsoleLayoutOrganizationQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n name\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "3310c40eb8f1e3874139c934fb08cbb1";
|
||||
|
||||
export default node;
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from "react-relay";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useOrganization } from "@/contexts/OrganizationContext";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
@@ -84,7 +83,7 @@ function EditableField({
|
||||
|
||||
export default function CreateOrganizationPage() {
|
||||
const navigate = useNavigate();
|
||||
const { setCurrentOrganization } = useOrganization();
|
||||
const { toast } = useToast();
|
||||
const data = useLazyLoadQuery<CreateOrganizationPageViewerQuery>(
|
||||
viewerQuery,
|
||||
{}
|
||||
@@ -93,7 +92,6 @@ export default function CreateOrganizationPage() {
|
||||
useMutation<CreateOrganizationPageCreateOrganizationMutation>(
|
||||
createOrganizationMutation
|
||||
);
|
||||
const { toast } = useToast();
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
});
|
||||
@@ -122,13 +120,12 @@ export default function CreateOrganizationPage() {
|
||||
},
|
||||
onCompleted: (response) => {
|
||||
const newOrg = response.createOrganization.organizationEdge.node;
|
||||
setCurrentOrganization(newOrg);
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Organization created successfully",
|
||||
variant: "default",
|
||||
});
|
||||
navigate("/");
|
||||
navigate(`/organizations/${newOrg.id}`);
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
import {
|
||||
graphql,
|
||||
PreloadedQuery,
|
||||
@@ -18,7 +18,6 @@ import { useToast } from "@/hooks/use-toast";
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CreatePeoplePageCreatePeopleMutation } from "./__generated__/CreatePeoplePageCreatePeopleMutation.graphql";
|
||||
import { useOrganization } from "@/contexts/OrganizationContext";
|
||||
|
||||
const createPeopleMutation = graphql`
|
||||
mutation CreatePeoplePageCreatePeopleMutation(
|
||||
@@ -75,7 +74,7 @@ function EditableField({
|
||||
|
||||
function CreatePeoplePageContent() {
|
||||
const navigate = useNavigate();
|
||||
const { currentOrganization } = useOrganization();
|
||||
const { organizationId } = useParams();
|
||||
|
||||
const [createPeople] =
|
||||
useMutation<CreatePeoplePageCreatePeopleMutation>(createPeopleMutation);
|
||||
@@ -97,7 +96,7 @@ function CreatePeoplePageContent() {
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const peopleConnectionId = ConnectionHandler.getConnectionID(
|
||||
currentOrganization!.id,
|
||||
organizationId!,
|
||||
"PeopleListPage_peoples"
|
||||
);
|
||||
|
||||
@@ -105,7 +104,7 @@ function CreatePeoplePageContent() {
|
||||
variables: {
|
||||
connections: [peopleConnectionId],
|
||||
input: {
|
||||
organizationId: currentOrganization!.id,
|
||||
organizationId: organizationId!,
|
||||
fullName: formData.fullName,
|
||||
primaryEmailAddress: formData.primaryEmailAddress,
|
||||
additionalEmailAddresses: formData.additionalEmailAddresses,
|
||||
|
||||
@@ -6,10 +6,9 @@ import {
|
||||
useQueryLoader,
|
||||
} from "react-relay";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Link } from "react-router";
|
||||
import { Link, useParams } from "react-router";
|
||||
import type { FrameworkListPageQuery as FrameworkListPageQueryType } from "./__generated__/FrameworkListPageQuery.graphql";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useOrganization } from "@/contexts/OrganizationContext";
|
||||
|
||||
const FrameworkListPageQuery = graphql`
|
||||
query FrameworkListPageQuery($organizationId: ID!) {
|
||||
@@ -86,6 +85,7 @@ function FrameworkListPageContent({
|
||||
queryRef: PreloadedQuery<FrameworkListPageQueryType>;
|
||||
}) {
|
||||
const data = usePreloadedQuery(FrameworkListPageQuery, queryRef);
|
||||
const { organizationId } = useParams();
|
||||
const frameworks =
|
||||
data.organization.frameworks?.edges.map((edge) => edge?.node) ?? [];
|
||||
|
||||
@@ -101,12 +101,15 @@ function FrameworkListPageContent({
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||
{frameworks.map((framework) => {
|
||||
const validatedControls = framework.controls.edges.filter(
|
||||
(edge) => edge?.node?.state === "IMPLEMENTED",
|
||||
(edge) => edge?.node?.state === "IMPLEMENTED"
|
||||
).length;
|
||||
const totalControls = framework.controls.edges.length;
|
||||
|
||||
return (
|
||||
<Link key={framework.id} to={`/frameworks/${framework.id}`}>
|
||||
<Link
|
||||
key={framework.id}
|
||||
to={`/organizations/${organizationId}/frameworks/${framework.id}`}
|
||||
>
|
||||
<FrameworkCard
|
||||
title={framework.name}
|
||||
description={framework.description}
|
||||
@@ -161,14 +164,14 @@ function FrameworkListPageFallback() {
|
||||
|
||||
export default function FrameworkListPage() {
|
||||
const [queryRef, loadQuery] = useQueryLoader<FrameworkListPageQueryType>(
|
||||
FrameworkListPageQuery,
|
||||
FrameworkListPageQuery
|
||||
);
|
||||
|
||||
const { currentOrganization } = useOrganization();
|
||||
const { organizationId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
loadQuery({ organizationId: currentOrganization!.id });
|
||||
}, [loadQuery, currentOrganization]);
|
||||
loadQuery({ organizationId: organizationId! });
|
||||
}, [loadQuery, organizationId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -54,19 +54,17 @@ function FrameworkOverviewPageContent({
|
||||
} | null>(null);
|
||||
const [isTooltipHovered, setIsTooltipHovered] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const { organizationId } = useParams();
|
||||
|
||||
// Group controls by their category
|
||||
const controlsByCategory = controls.reduce(
|
||||
(acc, control) => {
|
||||
if (!control?.category) return acc;
|
||||
if (!acc[control.category]) {
|
||||
acc[control.category] = [];
|
||||
}
|
||||
acc[control.category].push(control);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, typeof controls>,
|
||||
);
|
||||
const controlsByCategory = controls.reduce((acc, control) => {
|
||||
if (!control?.category) return acc;
|
||||
if (!acc[control.category]) {
|
||||
acc[control.category] = [];
|
||||
}
|
||||
acc[control.category].push(control);
|
||||
return acc;
|
||||
}, {} as Record<string, typeof controls>);
|
||||
|
||||
const controlCards = Object.entries(controlsByCategory).map(
|
||||
([category, controls]) => ({
|
||||
@@ -74,11 +72,11 @@ function FrameworkOverviewPageContent({
|
||||
controls,
|
||||
completed: controls.filter((c) => c?.state === "IMPLEMENTED").length,
|
||||
total: controls.length,
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const totalImplemented = controls.filter(
|
||||
(c) => c?.state === "IMPLEMENTED",
|
||||
(c) => c?.state === "IMPLEMENTED"
|
||||
).length;
|
||||
|
||||
return (
|
||||
@@ -190,7 +188,7 @@ function FrameworkOverviewPageContent({
|
||||
onClick={() => {
|
||||
if (control?.id) {
|
||||
navigate(
|
||||
`/frameworks/${framework.id}/controls/${control.id}`,
|
||||
`/organizations/${organizationId}/frameworks/${framework.id}/controls/${control.id}`
|
||||
);
|
||||
}
|
||||
}}
|
||||
@@ -274,7 +272,7 @@ function FrameworkOverviewPageContent({
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
document.body
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -308,7 +306,7 @@ function FrameworkOverviewPageFallback() {
|
||||
export default function FrameworkOverviewPage() {
|
||||
const { frameworkId } = useParams();
|
||||
const [queryRef, loadQuery] = useQueryLoader<FrameworkOverviewPageQueryType>(
|
||||
FrameworkOverviewPageQuery,
|
||||
FrameworkOverviewPageQuery
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -5,116 +5,112 @@ import {
|
||||
usePreloadedQuery,
|
||||
useQueryLoader,
|
||||
} from "react-relay";
|
||||
import { Link } from "react-router";
|
||||
import { Link, useParams } from "react-router";
|
||||
import type { HomePageQuery as HomePageQueryType } from "./__generated__/HomePageQuery.graphql";
|
||||
|
||||
export const HomePageQuery = graphql`
|
||||
query HomePageQuery {
|
||||
viewer {
|
||||
id
|
||||
organizations {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
createdAt
|
||||
updatedAt
|
||||
vendors {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
query HomePageQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
... on Organization {
|
||||
id
|
||||
name
|
||||
createdAt
|
||||
updatedAt
|
||||
vendors {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
peoples {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
additionalEmailAddresses
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
peoples {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
fullName
|
||||
primaryEmailAddress
|
||||
additionalEmailAddresses
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
frameworks {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
name
|
||||
description
|
||||
controls {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
stateTransisions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
toState
|
||||
fromState
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
frameworks {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
name
|
||||
description
|
||||
controls {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
stateTransisions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
toState
|
||||
fromState
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
tasks {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
evidences {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
state
|
||||
fileUrl
|
||||
stateTransisions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
fromState
|
||||
toState
|
||||
reason
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
evidences {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
state
|
||||
fileUrl
|
||||
stateTransisions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
fromState
|
||||
toState
|
||||
reason
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
stateTransisions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
toState
|
||||
fromState
|
||||
reason
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
stateTransisions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
toState
|
||||
fromState
|
||||
reason
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,10 +126,15 @@ export const HomePageQuery = graphql`
|
||||
`;
|
||||
|
||||
export default function HomePage() {
|
||||
const { organizationId } = useParams();
|
||||
const [queryRef, loadQuery] =
|
||||
useQueryLoader<HomePageQueryType>(HomePageQuery);
|
||||
|
||||
useEffect(() => loadQuery({}), [loadQuery]);
|
||||
useEffect(() => {
|
||||
if (organizationId) {
|
||||
loadQuery({ organizationId });
|
||||
}
|
||||
}, [organizationId, loadQuery]);
|
||||
|
||||
if (!queryRef) {
|
||||
return <HomePageFallback />;
|
||||
@@ -155,6 +156,7 @@ function HomePageContent({
|
||||
}: {
|
||||
queryRef: PreloadedQuery<HomePageQueryType>;
|
||||
}) {
|
||||
const { organizationId } = useParams();
|
||||
const data = usePreloadedQuery(HomePageQuery, queryRef);
|
||||
|
||||
return (
|
||||
@@ -166,7 +168,9 @@ function HomePageContent({
|
||||
</div>
|
||||
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min">
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
<Link to="/foobar">Go to FooPage</Link>
|
||||
<Link to={`/organizations/${organizationId}/frameworks`}>
|
||||
Go to Frameworks
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -22,16 +22,6 @@ export default function LoginPage() {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!email || !password) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Email and password are required",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
@@ -40,24 +30,30 @@ export default function LoginPage() {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify({ email, password }),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || "Login failed");
|
||||
const error = await response.json();
|
||||
throw new Error(error.message || "Failed to login");
|
||||
}
|
||||
|
||||
// Check authentication status after login
|
||||
await checkAuth();
|
||||
|
||||
// Redirect to the original page or home after successful login
|
||||
navigate(from, { replace: true });
|
||||
} catch (error) {
|
||||
const authenticated = await checkAuth();
|
||||
if (authenticated) {
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Logged in successfully",
|
||||
variant: "default",
|
||||
});
|
||||
navigate("/");
|
||||
} else {
|
||||
throw new Error("Authentication failed");
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error instanceof Error ? error.message : "Login failed",
|
||||
description: error.message || "Failed to login",
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
@@ -66,58 +62,86 @@ export default function LoginPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Log in - Probo</title>
|
||||
</Helmet>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-3xl font-bold">Log in</h1>
|
||||
<p className="text-gray-500 dark:text-gray-400">
|
||||
Enter your credentials to access your account
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="name@example.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<div className="flex flex-col space-y-6">
|
||||
<div className="flex flex-col space-y-2 text-center">
|
||||
<h1 className="text-2xl font-semibold tracking-tight">
|
||||
Login to your account
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enter your email below to login to your account
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-6">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
placeholder="name@example.com"
|
||||
type="email"
|
||||
autoCapitalize="none"
|
||||
autoComplete="email"
|
||||
autoCorrect="off"
|
||||
disabled={isLoading}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Link
|
||||
to="/forgot-password"
|
||||
className="text-sm font-medium text-primary underline-offset-4 hover:underline"
|
||||
>
|
||||
Forgot password?
|
||||
</Link>
|
||||
</div>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
autoCapitalize="none"
|
||||
autoComplete="current-password"
|
||||
autoCorrect="off"
|
||||
disabled={isLoading}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button disabled={isLoading}>
|
||||
{isLoading ? "Logging in..." : "Login"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="••••••••"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
{isLoading ? "Logging in..." : "Log in with email"}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Don't have an account?{" "}
|
||||
<Link to="/register" className="underline">
|
||||
Sign up here
|
||||
</Link>
|
||||
</p>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<span className="w-full border-t" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-xs uppercase">
|
||||
<span className="bg-muted/40 px-2 text-muted-foreground">
|
||||
Or continue with
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<Button variant="outline" type="button" disabled={isLoading}>
|
||||
GitHub
|
||||
</Button>
|
||||
<Button variant="outline" type="button" disabled={isLoading}>
|
||||
Google
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div className="text-center text-sm">
|
||||
Don't have an account?{" "}
|
||||
<Link
|
||||
to="/register"
|
||||
className="font-medium text-primary underline-offset-4 hover:underline"
|
||||
>
|
||||
Register
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
136
apps/console/src/pages/OrganizationSelectionPage.tsx
Normal file
136
apps/console/src/pages/OrganizationSelectionPage.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { graphql, useLazyLoadQuery } from "react-relay";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
} from "@/components/ui/card";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Plus } from "lucide-react";
|
||||
import { OrganizationSelectionPageQuery } from "./__generated__/OrganizationSelectionPageQuery.graphql";
|
||||
|
||||
const organizationSelectionQuery = graphql`
|
||||
query OrganizationSelectionPageQuery {
|
||||
viewer {
|
||||
id
|
||||
organizations(first: 25) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
logoUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function OrganizationSelectionPage() {
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const data = useLazyLoadQuery<OrganizationSelectionPageQuery>(
|
||||
organizationSelectionQuery,
|
||||
{}
|
||||
);
|
||||
|
||||
const organizations = data.viewer.organizations.edges.map(
|
||||
(edge) => edge.node
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// If there's only one organization, redirect to it automatically
|
||||
if (organizations.length === 1) {
|
||||
navigate(`/organizations/${organizations[0].id}`);
|
||||
}
|
||||
}, [organizations, navigate]);
|
||||
|
||||
// If no organizations, show a message to create one
|
||||
if (organizations.length === 0) {
|
||||
return (
|
||||
<div className="container mx-auto py-10">
|
||||
<Helmet>
|
||||
<title>Select Organization - Probo</title>
|
||||
</Helmet>
|
||||
<div className="flex flex-col items-center justify-center h-[70vh]">
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle>Welcome to Probo</CardTitle>
|
||||
<CardDescription>
|
||||
You don't have any organizations yet. Create your first one to
|
||||
get started.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardFooter>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => navigate("/organizations/create")}
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" /> Create Organization
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// If multiple organizations, show a selection screen
|
||||
return (
|
||||
<div className="container mx-auto py-10">
|
||||
<Helmet>
|
||||
<title>Select Organization - Probo</title>
|
||||
</Helmet>
|
||||
<div className="flex flex-col items-center">
|
||||
<h1 className="text-2xl font-bold mb-6">Select an Organization</h1>
|
||||
<div className="grid gap-4 w-full max-w-2xl">
|
||||
{organizations.map((org) => (
|
||||
<Card key={org.id} className="w-full">
|
||||
<CardHeader className="flex flex-row items-center gap-4">
|
||||
{org.logoUrl && (
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg border">
|
||||
<img src={org.logoUrl} alt={org.name} className="h-8 w-8" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<CardTitle>{org.name}</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardFooter>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={() => navigate(`/organizations/${org.id}`)}
|
||||
>
|
||||
Select
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Create a New Organization</CardTitle>
|
||||
<CardDescription>
|
||||
Add a new organization to your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
onClick={() => navigate("/organizations/create")}
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" /> Create Organization
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useMutation,
|
||||
usePaginationFragment,
|
||||
} from "react-relay";
|
||||
import { useSearchParams } from "react-router";
|
||||
import { useSearchParams, useParams } from "react-router";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { UserPlus, Trash2, ChevronRight } from "lucide-react";
|
||||
@@ -18,7 +18,6 @@ import type { PeopleListPageQuery as PeopleListPageQueryType } from "./__generat
|
||||
import type { PeopleListPageDeletePeopleMutation } from "./__generated__/PeopleListPageDeletePeopleMutation.graphql";
|
||||
import { PeopleListPagePaginationQuery } from "./__generated__/PeopleListPagePaginationQuery.graphql";
|
||||
import { PeopleListPage_peoples$key } from "./__generated__/PeopleListPage_peoples.graphql";
|
||||
import { useOrganization } from "@/contexts/OrganizationContext";
|
||||
|
||||
const ITEMS_PER_PAGE = 25;
|
||||
|
||||
@@ -141,6 +140,7 @@ function PeopleListContent({
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [deletePeople] =
|
||||
useMutation<PeopleListPageDeletePeopleMutation>(deletePeopleMutation);
|
||||
const { organizationId } = useParams();
|
||||
|
||||
const {
|
||||
data: peoplesConnection,
|
||||
@@ -177,7 +177,7 @@ function PeopleListContent({
|
||||
style={{ borderRadius: "0.5rem" }}
|
||||
className="gap-2"
|
||||
>
|
||||
<Link to="/peoples/create">
|
||||
<Link to={`/organizations/${organizationId}/peoples/create`}>
|
||||
<UserPlus className="h-4 w-4" />
|
||||
Add a people
|
||||
</Link>
|
||||
@@ -188,7 +188,7 @@ function PeopleListContent({
|
||||
{peoples.map((person) => (
|
||||
<Link
|
||||
key={person?.id}
|
||||
to={`/peoples/${person?.id}`}
|
||||
to={`/organizations/${organizationId}/peoples/${person?.id}`}
|
||||
className="block"
|
||||
>
|
||||
<div className="flex items-center justify-between p-4 rounded-xl border bg-card hover:bg-accent/5 transition-colors">
|
||||
@@ -307,20 +307,20 @@ export default function PeopleListPage() {
|
||||
const [queryRef, loadQuery] =
|
||||
useQueryLoader<PeopleListPageQueryType>(peopleListPageQuery);
|
||||
|
||||
const { currentOrganization } = useOrganization();
|
||||
const { organizationId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
const after = searchParams.get("after");
|
||||
const before = searchParams.get("before");
|
||||
|
||||
loadQuery({
|
||||
organizationId: currentOrganization!.id,
|
||||
organizationId: organizationId!,
|
||||
first: before ? undefined : ITEMS_PER_PAGE,
|
||||
after: after || undefined,
|
||||
last: before ? ITEMS_PER_PAGE : undefined,
|
||||
before: before || undefined,
|
||||
});
|
||||
}, [loadQuery, currentOrganization]);
|
||||
}, [loadQuery, organizationId, searchParams]);
|
||||
|
||||
if (!queryRef) {
|
||||
return <PeopleListPageFallback />;
|
||||
|
||||
@@ -22,8 +22,8 @@ import {
|
||||
usePreloadedQuery,
|
||||
useQueryLoader,
|
||||
} from "react-relay";
|
||||
import { useParams } from "react-router";
|
||||
import type { SettingsPageQuery as SettingsPageQueryType } from "./__generated__/SettingsPageQuery.graphql";
|
||||
import { useOrganization } from "@/contexts/OrganizationContext";
|
||||
|
||||
const settingsPageQuery = graphql`
|
||||
query SettingsPageQuery($organizationID: ID!) {
|
||||
@@ -217,11 +217,11 @@ export default function SettingsPage() {
|
||||
const [queryRef, loadQuery] =
|
||||
useQueryLoader<SettingsPageQueryType>(settingsPageQuery);
|
||||
|
||||
const { currentOrganization } = useOrganization();
|
||||
const { organizationId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
loadQuery({ organizationID: currentOrganization!.id });
|
||||
}, [loadQuery, currentOrganization]);
|
||||
loadQuery({ organizationID: organizationId! });
|
||||
}, [loadQuery, organizationId]);
|
||||
|
||||
if (!queryRef) {
|
||||
return <SettingsPageFallback />;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useMutation,
|
||||
usePaginationFragment,
|
||||
} from "react-relay";
|
||||
import { useSearchParams } from "react-router";
|
||||
import { useSearchParams, useParams } from "react-router";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Store, ChevronRight, Trash2 } from "lucide-react";
|
||||
@@ -22,7 +22,6 @@ import { VendorListPageDeleteVendorMutation } from "./__generated__/VendorListPa
|
||||
import { VendorListPagePaginationQuery } from "./__generated__/VendorListPagePaginationQuery.graphql";
|
||||
import { VendorListPage_vendors$key } from "./__generated__/VendorListPage_vendors.graphql";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useOrganization } from "@/contexts/OrganizationContext";
|
||||
|
||||
const ITEMS_PER_PAGE = 25;
|
||||
|
||||
@@ -199,6 +198,7 @@ function VendorListContent({
|
||||
useMutation<VendorListPageCreateVendorMutation>(createVendorMutation);
|
||||
const [deleteVendor] =
|
||||
useMutation<VendorListPageDeleteVendorMutation>(deleteVendorMutation);
|
||||
const { organizationId } = useParams();
|
||||
|
||||
const {
|
||||
data: vendorsConnection,
|
||||
@@ -300,7 +300,7 @@ function VendorListContent({
|
||||
{vendors.map((vendor) => (
|
||||
<Link
|
||||
key={vendor?.id}
|
||||
to={`/vendors/${vendor?.id}`}
|
||||
to={`/organizations/${organizationId}/vendors/${vendor?.id}`}
|
||||
className="block"
|
||||
>
|
||||
<div className="flex items-center justify-between p-4 rounded-xl border bg-card hover:bg-accent/5 transition-colors">
|
||||
@@ -425,20 +425,20 @@ export default function VendorListPage() {
|
||||
const [queryRef, loadQuery] =
|
||||
useQueryLoader<VendorListPageQueryType>(vendorListPageQuery);
|
||||
|
||||
const { currentOrganization } = useOrganization();
|
||||
const { organizationId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
const after = searchParams.get("after");
|
||||
const before = searchParams.get("before");
|
||||
|
||||
loadQuery({
|
||||
organizationId: currentOrganization!.id,
|
||||
organizationId: organizationId!,
|
||||
first: before ? undefined : ITEMS_PER_PAGE,
|
||||
after: after || undefined,
|
||||
last: before ? ITEMS_PER_PAGE : undefined,
|
||||
before: before || undefined,
|
||||
});
|
||||
}, [loadQuery, currentOrganization]);
|
||||
}, [loadQuery, organizationId, searchParams]);
|
||||
|
||||
if (!queryRef) {
|
||||
return <VendorListFallback />;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<f40d67f614a103849ba940fbd14f71af>>
|
||||
* @generated SignedSource<<72fcf5e9542bdba3b547fa523e55378d>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -12,73 +12,52 @@ import { ConcreteRequest } from 'relay-runtime';
|
||||
export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
||||
export type EvidenceState = "EXPIRED" | "INVALID" | "VALID";
|
||||
export type TaskState = "DONE" | "TODO";
|
||||
export type HomePageQuery$variables = Record<PropertyKey, never>;
|
||||
export type HomePageQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type HomePageQuery$data = {
|
||||
readonly viewer: {
|
||||
readonly id: string;
|
||||
readonly organizations: {
|
||||
readonly organization: {
|
||||
readonly createdAt?: any;
|
||||
readonly frameworks?: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly cursor: any;
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly frameworks: {
|
||||
readonly controls: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly cursor: any;
|
||||
readonly node: {
|
||||
readonly controls: {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly state: ControlState;
|
||||
readonly stateTransisions: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly fromState: ControlState | null | undefined;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly state: ControlState;
|
||||
readonly stateTransisions: {
|
||||
readonly toState: ControlState;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly tasks: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly evidences: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly fromState: ControlState | null | undefined;
|
||||
readonly fileUrl: string;
|
||||
readonly id: string;
|
||||
readonly toState: ControlState;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly tasks: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly evidences: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly fileUrl: string;
|
||||
readonly id: string;
|
||||
readonly state: EvidenceState;
|
||||
readonly stateTransisions: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly fromState: EvidenceState | null | undefined;
|
||||
readonly id: string;
|
||||
readonly reason: string | null | undefined;
|
||||
readonly toState: EvidenceState;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly state: TaskState;
|
||||
readonly state: EvidenceState;
|
||||
readonly stateTransisions: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly fromState: TaskState | null | undefined;
|
||||
readonly fromState: EvidenceState | null | undefined;
|
||||
readonly id: string;
|
||||
readonly reason: string | null | undefined;
|
||||
readonly toState: TaskState;
|
||||
readonly toState: EvidenceState;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
@@ -87,45 +66,62 @@ export type HomePageQuery$data = {
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly state: TaskState;
|
||||
readonly stateTransisions: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly fromState: TaskState | null | undefined;
|
||||
readonly id: string;
|
||||
readonly reason: string | null | undefined;
|
||||
readonly toState: TaskState;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly description: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
}>;
|
||||
readonly pageInfo: {
|
||||
readonly endCursor: any | null | undefined;
|
||||
readonly hasNextPage: boolean;
|
||||
readonly hasPreviousPage: boolean;
|
||||
readonly startCursor: any | null | undefined;
|
||||
};
|
||||
};
|
||||
readonly description: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
}>;
|
||||
readonly pageInfo: {
|
||||
readonly endCursor: any | null | undefined;
|
||||
readonly hasNextPage: boolean;
|
||||
readonly hasPreviousPage: boolean;
|
||||
readonly startCursor: any | null | undefined;
|
||||
};
|
||||
};
|
||||
readonly id?: string;
|
||||
readonly name?: string;
|
||||
readonly peoples?: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly additionalEmailAddresses: ReadonlyArray<string>;
|
||||
readonly createdAt: any;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly primaryEmailAddress: string;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly updatedAt?: any;
|
||||
readonly vendors?: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly peoples: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly additionalEmailAddresses: ReadonlyArray<string>;
|
||||
readonly createdAt: any;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly primaryEmailAddress: string;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly updatedAt: any;
|
||||
readonly vendors: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly updatedAt: any;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
};
|
||||
@@ -137,35 +133,49 @@ export type HomePageQuery = {
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "organizationId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "organizationId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v1 = {
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = {
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
v6 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "VendorConnection",
|
||||
@@ -189,10 +199,10 @@ v4 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -202,7 +212,7 @@ v4 = {
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
v7 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PeopleConnection",
|
||||
@@ -226,7 +236,7 @@ v5 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -248,8 +258,8 @@ v5 = {
|
||||
"name": "additionalEmailAddresses",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -259,35 +269,35 @@ v5 = {
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
v8 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
v7 = {
|
||||
v9 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "toState",
|
||||
"storageKey": null
|
||||
},
|
||||
v8 = {
|
||||
v10 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fromState",
|
||||
"storageKey": null
|
||||
},
|
||||
v9 = {
|
||||
v11 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "reason",
|
||||
"storageKey": null
|
||||
},
|
||||
v10 = {
|
||||
v12 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "FrameworkConnection",
|
||||
@@ -357,8 +367,8 @@ v10 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -390,9 +400,9 @@ v10 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v8/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -417,11 +427,11 @@ v10 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -455,9 +465,9 @@ v10 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v8/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -482,8 +492,8 @@ v10 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v8/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -515,12 +525,12 @@ v10 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v10/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v11/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -530,8 +540,8 @@ v10 = {
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -565,12 +575,12 @@ v10 = {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
(v11/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -580,8 +590,8 @@ v10 = {
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -611,58 +621,32 @@ v10 = {
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [],
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "HomePageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "User",
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "viewer",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "OrganizationConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "organizations",
|
||||
"plural": false,
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "OrganizationEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Organization",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v10/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v12/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -673,58 +657,38 @@ return {
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [],
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "HomePageQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "User",
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "viewer",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "OrganizationConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "organizations",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "OrganizationEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Organization",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v10/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v7/*: any*/),
|
||||
(v12/*: any*/)
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -732,16 +696,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "5a56fefd066703ca0ebac1ef3e9a7e1f",
|
||||
"cacheID": "29440183dcc30ec5e0bcce9c1910b92f",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "HomePageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query HomePageQuery {\n viewer {\n id\n organizations {\n edges {\n node {\n name\n createdAt\n updatedAt\n vendors {\n edges {\n node {\n id\n name\n createdAt\n updatedAt\n }\n }\n }\n peoples {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n createdAt\n updatedAt\n }\n }\n }\n frameworks {\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n edges {\n cursor\n node {\n id\n name\n description\n controls {\n edges {\n node {\n id\n name\n state\n stateTransisions {\n edges {\n node {\n id\n toState\n fromState\n createdAt\n updatedAt\n }\n }\n }\n tasks {\n edges {\n node {\n id\n name\n state\n evidences {\n edges {\n node {\n id\n state\n fileUrl\n stateTransisions {\n edges {\n node {\n id\n fromState\n toState\n reason\n createdAt\n updatedAt\n }\n }\n }\n createdAt\n updatedAt\n }\n }\n }\n stateTransisions {\n edges {\n node {\n id\n toState\n fromState\n reason\n createdAt\n updatedAt\n }\n }\n }\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n }\n }\n }\n }\n id\n }\n }\n }\n }\n}\n"
|
||||
"text": "query HomePageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n name\n createdAt\n updatedAt\n vendors {\n edges {\n node {\n id\n name\n createdAt\n updatedAt\n }\n }\n }\n peoples {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n createdAt\n updatedAt\n }\n }\n }\n frameworks {\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n edges {\n cursor\n node {\n id\n name\n description\n controls {\n edges {\n node {\n id\n name\n state\n stateTransisions {\n edges {\n node {\n id\n toState\n fromState\n createdAt\n updatedAt\n }\n }\n }\n tasks {\n edges {\n node {\n id\n name\n state\n evidences {\n edges {\n node {\n id\n state\n fileUrl\n stateTransisions {\n edges {\n node {\n id\n fromState\n toState\n reason\n createdAt\n updatedAt\n }\n }\n }\n createdAt\n updatedAt\n }\n }\n }\n stateTransisions {\n edges {\n node {\n id\n toState\n fromState\n reason\n createdAt\n updatedAt\n }\n }\n }\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "7a1699535af738f939d954a88c6daa9e";
|
||||
(node as any).hash = "d4f516b436d096fe9c91ce532a75b022";
|
||||
|
||||
export default node;
|
||||
|
||||
138
apps/console/src/pages/__generated__/OrganizationSelectionPageQuery.graphql.ts
generated
Normal file
138
apps/console/src/pages/__generated__/OrganizationSelectionPageQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* @generated SignedSource<<59c4e8be77fdb5ebcc76c2a611f38052>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type OrganizationSelectionPageQuery$variables = Record<PropertyKey, never>;
|
||||
export type OrganizationSelectionPageQuery$data = {
|
||||
readonly viewer: {
|
||||
readonly id: string;
|
||||
readonly organizations: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly logoUrl: string;
|
||||
readonly name: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type OrganizationSelectionPageQuery = {
|
||||
response: OrganizationSelectionPageQuery$data;
|
||||
variables: OrganizationSelectionPageQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "User",
|
||||
"kind": "LinkedField",
|
||||
"name": "viewer",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 25
|
||||
}
|
||||
],
|
||||
"concreteType": "OrganizationConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "organizations",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "OrganizationEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Organization",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "logoUrl",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "organizations(first:25)"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "OrganizationSelectionPageQuery",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Operation",
|
||||
"name": "OrganizationSelectionPageQuery",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "634e8f413a8b1f2685cfc1f4fd3c13cc",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "OrganizationSelectionPageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query OrganizationSelectionPageQuery {\n viewer {\n id\n organizations(first: 25) {\n edges {\n node {\n id\n name\n logoUrl\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "786037ac4c8d61f3b7cf6b4536aadbbb";
|
||||
|
||||
export default node;
|
||||
Reference in New Issue
Block a user