From 85528ee63f8c2231838a63d1fbd1e66fa5e579b7 Mon Sep 17 00:00:00 2001 From: Micha de Vries Date: Wed, 27 Dec 2023 11:44:09 +0100 Subject: [PATCH] Finish first implementation of actor selection --- src/components/cards/avatar.tsx | 87 ++++++++++++------ src/components/cards/profile.tsx | 35 +++++-- src/components/layout/navbar.tsx | 151 ++++++++++++++++++++++++------- src/schema/resources/actor.ts | 20 +++- 4 files changed, 222 insertions(+), 71 deletions(-) diff --git a/src/components/cards/avatar.tsx b/src/components/cards/avatar.tsx index 8508fa5..be0ff6a 100644 --- a/src/components/cards/avatar.tsx +++ b/src/components/cards/avatar.tsx @@ -1,7 +1,8 @@ import { cn } from '@/lib/utils'; import { Profile, unknownProfile } from '@/schema/resources/profile'; +import { Building, Users } from 'lucide-react'; import Image from 'next/image'; -import React from 'react'; +import React, { ReactNode } from 'react'; import { AvatarFallback, AvatarImage, @@ -24,13 +25,14 @@ export function Avatar({ }: { profile?: Profile | null; loading?: boolean; - size?: 'tiny' | 'small' | 'normal' | 'big' | 'huge'; + size?: 'extra-tiny' | 'tiny' | 'small' | 'normal' | 'big' | 'huge'; renderBadge?: boolean; className?: string; }) { profile = profile ?? unknownProfile; const avatarFallback = avatarFallbackByName(profile.name); const avatarSize = { + 'extra-tiny': 'h-6 w-6 text-xs', tiny: 'h-8 w-8 text-md', small: 'h-10 w-10 text-lg', normal: 'h-12 w-12 text-xl', @@ -38,6 +40,34 @@ export function Avatar({ huge: 'h-20 w-20 text-4xl', }[size ?? 'normal']; + function Badge({ icon, text }: { icon: ReactNode; text: string }) { + return ( + + + +
+ {icon} +
+
+ +

{text}

+
+
+
+ ); + } + return loading ? ( - {renderBadge && 'type' in profile && profile.type == 'admin' && ( - - - -
- Playrbase Logo -
-
- -

Platform admin

-
-
-
- )} + {renderBadge && 'type' in profile ? ( + profile.type == 'admin' ? ( + + } + /> + ) : profile.type == 'organisation' ? ( + } + /> + ) : profile.type == 'team' ? ( + } + /> + ) : undefined + ) : undefined} ); } diff --git a/src/components/cards/profile.tsx b/src/components/cards/profile.tsx index 6b0d303..a5df444 100644 --- a/src/components/cards/profile.tsx +++ b/src/components/cards/profile.tsx @@ -16,7 +16,7 @@ export function Profile({ }: { profile?: TProfile | null; loading?: boolean; - size?: 'tiny' | 'small' | 'normal' | 'big'; + size?: 'extra-tiny' | 'tiny' | 'small' | 'normal' | 'big'; noSub?: boolean; customSub?: ReactNode | string; }) { @@ -27,20 +27,43 @@ export function Profile({
{loading ? ( -
- - +
+ {size == 'tiny' || size == 'extra-tiny' ? ( + <> + + {!noSub && } + + ) : ( + <> + + {!noSub && } + + )}
) : ( -
+

diff --git a/src/components/layout/navbar.tsx b/src/components/layout/navbar.tsx index 38a02d2..9d8d52c 100644 --- a/src/components/layout/navbar.tsx +++ b/src/components/layout/navbar.tsx @@ -9,19 +9,23 @@ import { NavigationMenuTrigger, navigationMenuTriggerStyle, } from '@/components/ui/navigation-menu.tsx'; +import { useSurreal } from '@/lib/Surreal.tsx'; import { useAuth } from '@/lib/auth'; import { useFeatureFlags } from '@/lib/featureFlags.tsx'; import { useScrolledContext, useScrolledState } from '@/lib/scrolled.tsx'; import { cn } from '@/lib/utils.ts'; import { Language, languageEntries } from '@/locales/languages.ts'; import { Link, usePathname } from '@/locales/navigation.ts'; -import { Actor } from '@/schema/resources/actor.ts'; +import { Actor, linkToActorOverview } from '@/schema/resources/actor.ts'; +import { Organisation } from '@/schema/resources/organisation.ts'; +import { Team } from '@/schema/resources/team.ts'; import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu'; +import { useQuery } from '@tanstack/react-query'; import { ChevronRightSquare, ChevronsUpDown, Languages, - LogOut, + // LogOut, Menu, } from 'lucide-react'; import { useLocale, useTranslations } from 'next-intl'; @@ -37,8 +41,8 @@ import React, { } from 'react'; import ReactCountryFlag from 'react-country-flag'; import * as portals from 'react-reverse-portal'; +import { z } from 'zod'; import LogoFull from '../../assets/LogoFull.svg'; -import { Avatar } from '../cards/avatar.tsx'; import { Profile } from '../cards/profile.tsx'; import { Button } from '../ui/button.tsx'; import { Skeleton } from '../ui/skeleton.tsx'; @@ -215,20 +219,22 @@ const Links = ({ onValueChange={setState} > - + {loading ? ( - +
+ + +
) : user ? ( <> -
-
- + + - {actor?.name ?? user.name} -
+
- + ) : ( @@ -278,35 +284,110 @@ const Links = ({ ); }; -const AccountOptions = () => { - const { user, loading, signout } = useAuth(); - const t = useTranslations('components.layout.navbar.account-options'); +const AccountOptions = ({ actor }: { actor?: Actor }) => { + // const t = useTranslations('components.layout.navbar.account-options'); + const surreal = useSurreal(); + const { user, loading: userLoading } = useAuth(); + const { data, isLoading: dataLoading } = useQuery({ + queryKey: ['navbar-actor-switcher'], + queryFn: async function () { + const [orgs, teams] = await surreal.query< + [Organisation[], Team[]] + >(/* surrealql */ ` + SELECT * FROM organisation WHERE part_of = NONE LIMIT 3; + SELECT * FROM team LIMIT 3; + `); + + const data = { + organisations: z.array(Organisation).parse(orgs), + teams: z.array(Team).parse(teams), + }; + + if (actor) { + if (actor.type == 'organisation') { + data.organisations = data.organisations.filter( + ({ id }) => id != actor.id + ); + data.organisations.unshift(actor as Organisation); + console.log(data.organisations); + if (data.organisations.length > 3) data.organisations.pop(); + } + + if (actor.type == 'team') { + data.teams = data.teams.filter(({ id }) => id != actor.id); + data.teams.unshift(actor as Team); + if (data.teams.length > 3) data.teams.pop(); + } + } + + return data; + }, + }); + + const loading = userLoading || dataLoading; + if (!loading && (!data || !user)) return

An error occurred

; + const organisations = data?.organisations ?? []; + const teams = data?.teams ?? []; + + function LinkActor({ actor: thisActor }: { actor: Actor }) { + return ( + + + + + + + + ); + } return ( user && ( -
    - - - - - - - - +
      +
      + + {organisations.length > 0 && ( +
      + + Organisations + {' '} + {organisations.map((org) => ( + + ))} +
      + )} + {teams.length > 0 && ( +
      + + Teams + {' '} + {teams.map((team) => ( + + ))} +
      + )} +
      + {/* - + */}
    ) ); diff --git a/src/schema/resources/actor.ts b/src/schema/resources/actor.ts index 31de106..5f2375f 100644 --- a/src/schema/resources/actor.ts +++ b/src/schema/resources/actor.ts @@ -1,8 +1,26 @@ import { z } from 'zod'; +import { Admin } from './admin'; import { Organisation, OrganisationSafeParse } from './organisation'; import { Team } from './team'; import { User } from './user'; -export const Actor = z.union([User, Organisation, OrganisationSafeParse, Team]); +export const Actor = z.union([ + Admin, + User, + Organisation, + OrganisationSafeParse, + Team, +]); export type Actor = z.infer; + +export function linkToActorOverview(actor: Actor) { + switch (actor.type) { + case 'user': + return `/account`; + case 'organisation': + return `/organisation/${actor.slug}/overview`; + default: + return '/'; + } +}