added system user tags/sharing and workspaces for recieving share-all rules
This commit is contained in:
@@ -3,6 +3,9 @@ import { LayoutDashboard, ListTodo, Settings, Clock, ArrowUpCircle, PanelLeftClo
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "./ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { store, currentTaskContext, setCurrentTaskContext } from "@/store";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { createResource, createSignal, createEffect } from "solid-js";
|
||||
|
||||
interface NavItem {
|
||||
icon: any;
|
||||
@@ -109,6 +112,130 @@ export const BottomNav: Component<{ currentView: string; setView: (v: string) =>
|
||||
);
|
||||
};
|
||||
|
||||
const ContextSwitcher: Component<{
|
||||
isLocked: boolean;
|
||||
isPeeking: boolean;
|
||||
setIsPeeking: (v: boolean) => void;
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}> = (props) => {
|
||||
const currentUserId = pb.authStore.model?.id;
|
||||
const [open, setOpen] = createSignal(false);
|
||||
|
||||
// Sync with parent if needed
|
||||
createEffect(() => {
|
||||
props.onOpenChange?.(open());
|
||||
});
|
||||
|
||||
// Close popover when sidebar disappears
|
||||
createEffect(() => {
|
||||
if (!props.isLocked && !props.isPeeking) {
|
||||
setOpen(false);
|
||||
}
|
||||
});
|
||||
|
||||
const [oversightUsers] = createResource(
|
||||
() => store.shareRules.filter(r => r.targetUserId === currentUserId && r.type === 'all').map(r => r.ownerId),
|
||||
async (ownerIds) => {
|
||||
if (ownerIds.length === 0) return [];
|
||||
try {
|
||||
// Fetch user names for these IDs
|
||||
// Optimization: Could check if we already have them or just fetch
|
||||
const users = await Promise.all(ownerIds.map(id => pb.collection('users').getOne(id, { fields: 'id,name,email' })));
|
||||
return users.map(u => ({ id: u.id, name: u.name || u.email }));
|
||||
} catch (e) {
|
||||
console.error("Failed to load oversight users", e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const ctx = currentTaskContext; // Accessor
|
||||
|
||||
const label = () => {
|
||||
const c = ctx();
|
||||
return c === 'mine' ? "My Workspace" : c.name;
|
||||
};
|
||||
|
||||
const handleSwitch = (userId: string, name: string) => {
|
||||
if (userId === 'mine') {
|
||||
setCurrentTaskContext('mine');
|
||||
} else {
|
||||
setCurrentTaskContext({ userId, name });
|
||||
}
|
||||
|
||||
// Close popover
|
||||
setOpen(false);
|
||||
|
||||
// Collapse sidebar if peaking
|
||||
if (!props.isLocked && props.isPeeking) {
|
||||
props.setIsPeeking(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Show when={oversightUsers() && oversightUsers()!.length > 0}>
|
||||
<Popover open={open()} onOpenChange={setOpen}>
|
||||
<PopoverTrigger class="w-full">
|
||||
<div class={cn(
|
||||
"flex items-center justify-between w-full px-3 py-2.5 rounded-lg transition-all duration-200 group text-sm",
|
||||
"text-muted-foreground hover:bg-muted hover:text-foreground border border-transparent hover:border-border/50"
|
||||
)}>
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
<div class={cn(
|
||||
"w-4 h-4 rounded-full flex items-center justify-center shrink-0",
|
||||
ctx() === 'mine' ? "bg-primary/20 text-primary" : "bg-orange-500/20 text-orange-500"
|
||||
)}>
|
||||
<div class="w-2 h-2 rounded-full bg-current" />
|
||||
</div>
|
||||
<span class="font-medium truncate">{label()}</span>
|
||||
</div>
|
||||
<ChevronDown size={14} class="opacity-50 group-hover:opacity-100 transition-opacity" />
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-[200px] p-1 bg-card border-border shadow-xl">
|
||||
<div class="space-y-0.5">
|
||||
<button
|
||||
onClick={() => handleSwitch('mine', '')}
|
||||
class={cn(
|
||||
"w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors",
|
||||
ctx() === 'mine' ? "bg-primary/10 text-primary font-semibold" : "hover:bg-muted text-foreground"
|
||||
)}
|
||||
>
|
||||
<div class="w-4 h-4 rounded-full bg-primary/20 flex items-center justify-center">
|
||||
<div class="w-2 h-2 rounded-full bg-primary" />
|
||||
</div>
|
||||
My Workspace
|
||||
</button>
|
||||
<div class="py-1">
|
||||
<div class="h-px bg-border/50" />
|
||||
</div>
|
||||
<div class="px-2 py-1 text-[10px] font-bold uppercase text-muted-foreground tracking-wider flex items-center gap-2">
|
||||
<span>Oversight</span>
|
||||
<span class="ml-auto bg-muted px-1.5 rounded text-[9px]">{oversightUsers()?.length}</span>
|
||||
</div>
|
||||
<For each={oversightUsers()}>
|
||||
{(user) => (
|
||||
<button
|
||||
onClick={() => handleSwitch(user.id, user.name)}
|
||||
class={cn(
|
||||
"w-full flex items-center gap-2 p-2 rounded-md text-sm transition-colors",
|
||||
(ctx() as any).userId === user.id ? "bg-orange-500/10 text-orange-600 font-semibold" : "hover:bg-muted text-foreground"
|
||||
)}
|
||||
>
|
||||
<div class="w-4 h-4 rounded-full bg-orange-500/20 flex items-center justify-center">
|
||||
<div class="w-2 h-2 rounded-full bg-orange-500" />
|
||||
</div>
|
||||
<span class="truncate">{user.name}</span>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sidebar: Component<{
|
||||
currentView: string;
|
||||
setView: (v: string) => void;
|
||||
@@ -117,10 +244,16 @@ export const Sidebar: Component<{
|
||||
isPeeking: boolean;
|
||||
setIsPeeking: (v: boolean) => void;
|
||||
}> = (props) => {
|
||||
const [switcherOpen, setSwitcherOpen] = createSignal(false);
|
||||
|
||||
return (
|
||||
<aside
|
||||
onMouseEnter={() => !props.isLocked && props.setIsPeeking(true)}
|
||||
onMouseLeave={() => !props.isLocked && props.setIsPeeking(false)}
|
||||
onMouseLeave={() => {
|
||||
if (!props.isLocked && !switcherOpen()) {
|
||||
props.setIsPeeking(false);
|
||||
}
|
||||
}}
|
||||
class={cn(
|
||||
"hidden md:flex flex-col border-r border-border bg-card h-screen fixed left-0 top-0 transition-all duration-300 ease-in-out z-[50]",
|
||||
props.isLocked ? "w-48 translate-x-0" : "w-48 -translate-x-full",
|
||||
@@ -151,6 +284,17 @@ export const Sidebar: Component<{
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
{/* Context Switcher - Only visible if there are oversight contexts */}
|
||||
<div class={cn("px-3 mb-2", !props.isLocked && !props.isPeeking && "hidden")}>
|
||||
<ContextSwitcher
|
||||
isLocked={props.isLocked}
|
||||
isPeeking={props.isPeeking}
|
||||
setIsPeeking={props.setIsPeeking}
|
||||
onOpenChange={setSwitcherOpen}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 px-3 space-y-1 mt-2">
|
||||
<For each={desktopNavItems}>
|
||||
{(item) => (
|
||||
|
||||
Reference in New Issue
Block a user