Skip to content

Commit

Permalink
Finish first implementation of actor selection
Browse files Browse the repository at this point in the history
  • Loading branch information
kearfy committed Dec 27, 2023
1 parent 4527fc9 commit 85528ee
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 71 deletions.
87 changes: 58 additions & 29 deletions src/components/cards/avatar.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -24,20 +25,49 @@ 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',
big: 'h-14 w-14 text-2xl',
huge: 'h-20 w-20 text-4xl',
}[size ?? 'normal'];

function Badge({ icon, text }: { icon: ReactNode; text: string }) {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
'absolute -bottom-1 -right-1 aspect-square rounded-full bg-background transition-colors group-hover:bg-accent',
['big', 'huge'].includes(size as string)
? 'w-[45%] p-1.5'
: size == 'extra-tiny'
? 'w-2/3 p-1'
: size == 'tiny'
? 'w-3/5 p-1'
: 'w-1/2 p-1'
)}
>
{icon}
</div>
</TooltipTrigger>
<TooltipContent>
<p>{text}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}

return loading ? (
<Skeleton
className={cn(
Expand Down Expand Up @@ -66,33 +96,32 @@ export function Avatar({
{avatarFallback}
</AvatarFallback>
</RenderAvatar>
{renderBadge && 'type' in profile && profile.type == 'admin' && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
'absolute -bottom-1 -right-1 aspect-square rounded-full bg-background transition-colors group-hover:bg-accent',
['big', 'huge'].includes(size as string)
? 'w-[45%] p-1.5'
: 'w-1/2 p-1'
)}
>
<Image
src="/favicon.ico"
alt="Playrbase Logo"
width="50"
height="50"
className="h-full w-full text-white"
/>
</div>
</TooltipTrigger>
<TooltipContent>
<p>Platform admin</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{renderBadge && 'type' in profile ? (
profile.type == 'admin' ? (
<Badge
text="Platform admin"
icon={
<Image
src="/favicon.ico"
alt="Playrbase Logo"
width="50"
height="50"
className="h-full w-full text-white"
/>
}
/>
) : profile.type == 'organisation' ? (
<Badge
text="Organisation"
icon={<Building className="h-full w-full" />}
/>
) : profile.type == 'team' ? (
<Badge
text="Team"
icon={<Users className="h-full w-full" />}
/>
) : undefined
) : undefined}
</div>
);
}
Expand Down
35 changes: 29 additions & 6 deletions src/components/cards/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}) {
Expand All @@ -27,20 +27,43 @@ export function Profile({
<div
className={cn(
'flex items-center',
size == 'tiny' ? 'space-x-3' : 'space-x-4'
size == 'tiny' || size == 'extra-tiny'
? 'space-x-3'
: 'space-x-4'
)}
>
<Avatar profile={profile} loading={loading} size={size} />
{loading ? (
<div className="min-w-[150px] space-y-2 pr-4">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-[80%]" />
<div
className={cn(
'min-w-[100px] space-y-2',
size == 'tiny' || size == 'extra-tiny' ? 'pr-3' : 'pr-4'
)}
>
{size == 'tiny' || size == 'extra-tiny' ? (
<>
<Skeleton
className={cn('w-full', noSub ? 'h-4' : 'h-3')}
/>
{!noSub && <Skeleton className="h-2 w-[80%]" />}
</>
) : (
<>
<Skeleton className="h-4 w-full" />
{!noSub && <Skeleton className="h-3 w-[80%]" />}
</>
)}
</div>
) : (
<div className="min-w-[150px] pr-4">
<div
className={cn(
size == 'tiny' || size == 'extra-tiny' ? 'pr-3' : 'pr-4'
)}
>
<h2
className={cn(
'text-foreground',
size == 'extra-tiny' && 'text-sm',
noSub ? 'font-semibold' : 'font-bold'
)}
>
Expand Down
151 changes: 116 additions & 35 deletions src/components/layout/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -215,20 +219,22 @@ const Links = ({
onValueChange={setState}
>
<NavigationMenuList className="flex flex-col items-start gap-4 space-x-0 max-md:py-4 md:flex-row md:items-center">
<NavigationMenuItem>
<NavigationMenuItem className="mr-2">
{loading ? (
<Skeleton className="h-10 w-24" />
<div className="flex">
<Profile loading size="extra-tiny" noSub />
<Skeleton className="h-8 w-6" />
</div>
) : user ? (
<>
<div className="flex gap-2">
<div className="flex items-center gap-1 text-sm">
<Avatar
<div className="flex items-center">
<Link href={linkToActorOverview(actor ?? user)}>
<Profile
profile={actor ?? user}
size="tiny"
className="mr-2"
size="extra-tiny"
noSub
/>
<span>{actor?.name ?? user.name}</span>
</div>
</Link>
<NavigationMenuPrimitive.Trigger asChild>
<Button
className="data-[state:open]:bg-muted m-0 bg-transparent px-2 py-1 text-foreground hover:bg-accent focus:bg-transparent focus:hover:bg-accent active:bg-muted"
Expand All @@ -239,7 +245,7 @@ const Links = ({
</NavigationMenuPrimitive.Trigger>
</div>
<NavigationMenuContent {...hoverOptions}>
<AccountOptions />
<AccountOptions actor={actor} />
</NavigationMenuContent>
</>
) : (
Expand Down Expand Up @@ -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 <p>An error occurred</p>;
const organisations = data?.organisations ?? [];
const teams = data?.teams ?? [];

function LinkActor({ actor: thisActor }: { actor: Actor }) {
return (
<NavigationMenuItem className="w-full">
<Link
href={linkToActorOverview(thisActor)}
className="w-full"
legacyBehavior
passHref
>
<NavigationMenuLink
className={cn(
navigationMenuTriggerStyle(),
'w-full justify-start px-2 py-6 max-sm:bg-muted',
thisActor.id == actor?.id && 'ring-2 ring-accent'
)}

Check failure on line 346 in src/components/layout/navbar.tsx

View workflow job for this annotation

GitHub Actions / Linting

Delete `····`
>
<Profile
profile={thisActor}
loading={loading}
size="tiny"
/>
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
);
}

return (
user && (
<ul className="grid gap-6 p-6">
<NavigationMenuItem>
<Link href="/account" legacyBehavior passHref>
<NavigationMenuLink
className={cn(
navigationMenuTriggerStyle(),
'h-18 max-sm:bg-muted'
)}
>
<Profile
profile={user}
loading={loading}
size="small"
/>
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem asChild>
<ul className="space-y-6 p-2">
<div className="space-y-2">
<LinkActor actor={user} />
{organisations.length > 0 && (
<div className="space-y-1">
<span className="ml-2 text-xs text-muted-foreground">
Organisations
</span>{' '}
{organisations.map((org) => (
<LinkActor key={org.id} actor={org} />
))}
</div>
)}
{teams.length > 0 && (
<div className="space-y-1">
<span className="ml-2 text-xs text-muted-foreground">
Teams
</span>{' '}
{teams.map((team) => (
<LinkActor key={team.id} actor={team} />
))}
</div>
)}
</div>
{/* <NavigationMenuItem asChild>
<Button variant="destructive" onClick={signout}>
<LogOut className="mr-2" size={18} />
{t('signout')}
</Button>
</NavigationMenuItem>
</NavigationMenuItem> */}
</ul>
)
);
Expand Down
Loading

0 comments on commit 85528ee

Please sign in to comment.