Migrate current organization from in memory to url state
Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user