Add create organization

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-02-26 10:05:56 +01:00
parent 3ee3b37dac
commit b8eb0198db
19 changed files with 2213 additions and 389 deletions

View File

@@ -4,6 +4,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 {
DropdownMenu,
@@ -27,18 +28,24 @@ import {
export const teamSwitcherFragment = graphql`
fragment TeamSwitcher_organizations on User {
organizations {
id
name
logoUrl
organizations(first: 25) @connection(key: "TeamSwitcher_organizations") {
__id
edges {
node {
id
name
logoUrl
}
}
}
}
`;
// Extended type to include plan field until Relay compiler generates the types
type Organization = TeamSwitcher_organizations$data["organizations"][0] & {
plan?: string;
};
type Organization =
TeamSwitcher_organizations$data["organizations"]["edges"][0]["node"] & {
plan?: string;
};
export function TeamSwitcher({
organizations,
@@ -50,13 +57,15 @@ export function TeamSwitcher({
const { currentOrganization, setCurrentOrganization } = useOrganization();
useEffect(() => {
console.log(data.organizations);
if (
data.organizations &&
data.organizations.length > 0 &&
data.organizations.edges.length > 0 &&
!currentOrganization
) {
// Type assertion to include plan field
setCurrentOrganization(data.organizations[0] as Organization);
setCurrentOrganization(data.organizations.edges[0].node);
}
}, [data.organizations, currentOrganization, setCurrentOrganization]);
@@ -109,27 +118,32 @@ export function TeamSwitcher({
<DropdownMenuLabel className="text-xs text-muted-foreground">
Organizations
</DropdownMenuLabel>
{data.organizations.map((org: Organization, index: number) => (
{data.organizations.edges.map((edge, index) => (
<DropdownMenuItem
key={org.id}
onClick={() => setCurrentOrganization(org)}
key={edge.node.id}
onClick={() => setCurrentOrganization(edge.node)}
className="gap-2 p-2"
>
<div className="flex size-6 items-center justify-center rounded-sm border">
<LogoComponent org={org} className="size-4 shrink-0" />
<LogoComponent org={edge.node} className="size-4 shrink-0" />
</div>
{org.name}
{edge.node.name}
<DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>
</DropdownMenuItem>
))}
<DropdownMenuSeparator />
<DropdownMenuItem className="gap-2 p-2">
<div className="flex size-6 items-center justify-center rounded-md border bg-background">
<Plus className="size-4" />
</div>
<div className="font-medium text-muted-foreground">
Add organization
</div>
<DropdownMenuItem asChild>
<Link
to="/organizations/create"
className="gap-2 p-2 cursor-pointer"
>
<div className="flex size-6 items-center justify-center rounded-md border bg-background">
<Plus className="size-4" />
</div>
<div className="font-medium text-muted-foreground">
Add organization
</div>
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<d060c85c907876f442133c643b917eee>>
* @generated SignedSource<<a1cc304a7eb154a19a1ec9a68c2cda98>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -29,7 +29,14 @@ var v0 = {
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
},
v1 = [
{
"kind": "Literal",
"name": "first",
"value": 25
}
];
return {
"fragment": {
"argumentDefinitions": [],
@@ -80,29 +87,111 @@ return {
(v0/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "Organization",
"args": (v1/*: any*/),
"concreteType": "OrganizationConnection",
"kind": "LinkedField",
"name": "organizations",
"plural": true,
"plural": false,
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"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
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "logoUrl",
"concreteType": "PageInfo",
"kind": "LinkedField",
"name": "pageInfo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
"storageKey": "organizations(first:25)"
},
{
"alias": null,
"args": (v1/*: any*/),
"filters": null,
"handle": "connection",
"key": "TeamSwitcher_organizations",
"kind": "LinkedHandle",
"name": "organizations"
},
{
"alias": null,
@@ -124,12 +213,12 @@ return {
]
},
"params": {
"cacheID": "48137f101a20a5ce116cbf46c2a8cd63",
"cacheID": "52b7d912665a4da10a79b82987a7e58a",
"id": null,
"metadata": {},
"name": "AppSidebarQuery",
"operationKind": "query",
"text": "query AppSidebarQuery {\n viewer {\n id\n ...TeamSwitcher_organizations\n ...NavUser_viewer\n }\n}\n\nfragment NavUser_viewer on User {\n id\n fullName\n email\n}\n\nfragment TeamSwitcher_organizations on User {\n organizations {\n id\n name\n logoUrl\n }\n}\n"
"text": "query AppSidebarQuery {\n viewer {\n id\n ...TeamSwitcher_organizations\n ...NavUser_viewer\n }\n}\n\nfragment NavUser_viewer on User {\n id\n fullName\n email\n}\n\nfragment TeamSwitcher_organizations on User {\n organizations(first: 25) {\n edges {\n node {\n id\n name\n logoUrl\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<8ecb9304d0a498d483d1a8c251f8c49a>>
* @generated SignedSource<<a4a57a018956ccc09931f2b2ce87c933>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -11,11 +11,16 @@
import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type TeamSwitcher_organizations$data = {
readonly organizations: ReadonlyArray<{
readonly id: string;
readonly logoUrl: string;
readonly name: string;
}>;
readonly organizations: {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
readonly logoUrl: string;
readonly name: string;
};
}>;
};
readonly " $fragmentType": "TeamSwitcher_organizations";
};
export type TeamSwitcher_organizations$key = {
@@ -26,37 +31,121 @@ export type TeamSwitcher_organizations$key = {
const node: ReaderFragment = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "forward",
"path": [
"organizations"
]
}
]
},
"name": "TeamSwitcher_organizations",
"selections": [
{
"alias": null,
"alias": "organizations",
"args": null,
"concreteType": "Organization",
"concreteType": "OrganizationConnection",
"kind": "LinkedField",
"name": "organizations",
"plural": true,
"name": "__TeamSwitcher_organizations_connection",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"concreteType": "OrganizationEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Organization",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "logoUrl",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"concreteType": "PageInfo",
"kind": "LinkedField",
"name": "pageInfo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "logoUrl",
"storageKey": null
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
@@ -66,6 +155,6 @@ const node: ReaderFragment = {
"abstractKey": null
};
(node as any).hash = "318f68b82b5b87500ec99ffcdd2929ab";
(node as any).hash = "22731866cafda226a19f8c64fda51ba5";
export default node;