Skip to content

Commit

Permalink
setup org switcher
Browse files Browse the repository at this point in the history
  • Loading branch information
bbtgnn committed Dec 23, 2024
1 parent 4c40f60 commit e9cb846
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 1 deletion.
152 changes: 152 additions & 0 deletions webapp/src/lib/layout/organizationSwitcher.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<script lang="ts">
import ChevronsUpDown from 'lucide-svelte/icons/chevrons-up-down';
import Check from 'lucide-svelte/icons/check';
import { tick } from 'svelte';
import { cn } from '@/components/ui/utils.js';
import * as Avatar from '@/components/ui/avatar/index.js';
import { buttonVariants } from '@/components/ui/button/index.js';
import * as Command from '@/components/ui/command/index.js';
import * as Dialog from '@/components/ui/dialog/index.js';
import * as Popover from '@/components/ui/popover/index.js';
import type { OrganizationsResponse } from '@/pocketbase/types';
import { goto, m } from '@/i18n';
import OrganizationAvatar from '@/organizations/components/organizationAvatar.svelte';
//
type Props = {
class?: string;
organizations: OrganizationsResponse[];
};
let { class: className = '', organizations = [] }: Props = $props();
//
const groups = [
{
label: 'Personal Account',
teams: [
{
label: 'Alicia Koch',
value: 'personal'
}
]
},
{
label: 'Teams',
teams: [
{
label: 'Acme Inc.',
value: 'acme-inc'
},
{
label: 'Monsters Inc.',
value: 'monsters'
}
]
}
];
type Team = (typeof groups)[number]['teams'][number];
let open = $state(false);
let showTeamDialog = $state(false);
let selectedTeam: Team = $state(groups[0].teams[0]);
let triggerId = 'team-switcher-trigger';
function closeAndRefocusTrigger(triggerId: string) {
open = false;
tick().then(() => document.getElementById(triggerId)?.focus());
}
</script>

<Dialog.Root bind:open={showTeamDialog}>
<Popover.Root bind:open>
<!-- -->

<Popover.Trigger
id={triggerId}
role="combobox"
aria-expanded={open}
aria-label="Select a team"
class={cn(
buttonVariants({ variant: 'outline', class: 'w-[200px] justify-between' }),
className
)}
>
<Avatar.Root class="mr-2 size-5">
<Avatar.Image
src="https://avatar.vercel.sh/${selectedTeam.value}.png"
alt={selectedTeam.label}
class="grayscale"
/>
<Avatar.Fallback>SC</Avatar.Fallback>
</Avatar.Root>
{selectedTeam.label}
<ChevronsUpDown class="ml-auto size-4 shrink-0 opacity-50" />
</Popover.Trigger>

<Popover.Content class="w-[200px] p-0">
<Command.Root>
<Command.Input placeholder="Search team..." />

<Command.List>
<Command.Empty>No team found.</Command.Empty>

<Command.Group heading={m.organizations()}>
{#each organizations as organization}
<Command.Item
onSelect={() => {
goto(`/my/organizations/${organization.id}`);
closeAndRefocusTrigger(triggerId);
}}
value={organization.name}
class="text-sm"
>
<OrganizationAvatar {organization} />
{organization.name}
<Check
class={cn(
'ml-auto size-4',
selectedTeam.value !== organization.id && 'text-transparent'
)}
/>
</Command.Item>
{/each}
</Command.Group>
<!-- {#each groups as group}
<Command.Group heading={group.label}>
{#each group.teams as team}
<Command.Item
onSelect={() => {
selectedTeam = team;
closeAndRefocusTrigger(triggerId);
}}
value={team.label}
class="text-sm"
>
<Avatar.Root class="mr-2 size-5">
<Avatar.Image
src="https://avatar.vercel.sh/${team.value}.png"
alt={team.label}
class="grayscale"
/>
<Avatar.Fallback>SC</Avatar.Fallback>
</Avatar.Root>
{team.label}
<Check
class={cn(
'ml-auto size-4',
selectedTeam.value !== team.value && 'text-transparent'
)}
/>
</Command.Item>
{/each}
</Command.Group>
{/each} -->
</Command.List>
</Command.Root>
</Popover.Content>
</Popover.Root>
</Dialog.Root>
21 changes: 20 additions & 1 deletion webapp/src/routes/my/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
<script lang="ts">
import BaseLayout from '$lib/layout/baseLayout.svelte';
import OrganizationSwitcher from '$lib/layout/organizationSwitcher.svelte';
import { Button } from '@/components/ui/button';
import { m } from '@/i18n';
import { currentUser } from '@/pocketbase';
import { PocketbaseQuery } from '@/pocketbase/query';
import type { Snippet } from 'svelte';
//
interface Props {
children?: Snippet;
}
let { children }: Props = $props();
//
const authorizationsQuery = new PocketbaseQuery('orgAuthorizations', {
filter: `user = "${$currentUser!.id}"`,
expand: ['organization', 'role']
});
const organizationsPromise = authorizationsQuery.getFullList().then((authorizations) => {
return authorizations.map((authorization) => authorization.expand?.organization!);
});
</script>

<BaseLayout>
<div class="border-y">
<div class="mx-auto max-w-screen-xl px-2 py-2">
<div class="mx-auto flex max-w-screen-xl items-center px-2 py-2">
{#await organizationsPromise then organizations}
<OrganizationSwitcher {organizations} />
{/await}
<Button variant="link" href="/my/profile">{m.My_profile()}</Button>
<Button variant="link" href="/my/organizations">{m.organizations()}</Button>
</div>
Expand Down

0 comments on commit e9cb846

Please sign in to comment.