fixed shared to viewing in settings and added workspace navigation in mobile view

This commit is contained in:
2026-02-11 13:21:23 -06:00
parent 25403ecaf0
commit 9761c8c5fc
3 changed files with 105 additions and 39 deletions
+6 -1
View File
@@ -1,5 +1,5 @@
import { type Component, type JSX, createSignal, Show, lazy, Suspense, onMount } from "solid-js";
import { Sidebar, BottomNav } from "./Navigation";
import { Sidebar, BottomNav, ContextSwitcher } from "./Navigation";
import { store, activeTaskId, setActiveTaskId } from "@/store";
import { PanelLeftOpen } from "lucide-solid";
@@ -62,6 +62,11 @@ export const Layout: Component<LayoutProps> = (props) => {
</div>
)}
{/* Mobile Context Switcher */}
<div class="fixed top-4 left-4 z-[60] md:hidden">
<ContextSwitcher isMobile={true} />
</div>
{/* Main Content Area */}
<main class="flex-1 min-w-0 overflow-y-auto overflow-x-hidden pb-32 sm:pb-6 relative scroll-smooth">
<div class="w-full max-w-screen-2xl mx-auto px-4 sm:px-8 lg:px-12 pt-16 sm:pt-20 pb-8 space-y-6 sm:space-y-8">
+46 -27
View File
@@ -1,5 +1,5 @@
import { type Component, For, Show } from "solid-js";
import { LayoutDashboard, ListTodo, Settings, Clock, ArrowUpCircle, PanelLeftClose, Snowflake, Pickaxe, ChevronDown, Gauge, TrendingUp } from "lucide-solid";
import { LayoutDashboard, ListTodo, Settings, Clock, ArrowUpCircle, PanelLeftClose, Snowflake, Pickaxe, ChevronDown, Gauge, TrendingUp, Users } from "lucide-solid";
import { cn } from "@/lib/utils";
import { Button } from "./ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
@@ -112,11 +112,12 @@ export const BottomNav: Component<{ currentView: string; setView: (v: string) =>
);
};
const ContextSwitcher: Component<{
isLocked: boolean;
isPeeking: boolean;
setIsPeeking: (v: boolean) => void;
onOpenChange?: (open: boolean) => void
export const ContextSwitcher: Component<{
isLocked?: boolean;
isPeeking?: boolean;
setIsPeeking?: (v: boolean) => void;
onOpenChange?: (open: boolean) => void;
isMobile?: boolean;
}> = (props) => {
const currentUserId = pb.authStore.model?.id;
const [open, setOpen] = createSignal(false);
@@ -126,23 +127,28 @@ const ContextSwitcher: Component<{
props.onOpenChange?.(open());
});
// Close popover when sidebar disappears
// Close popover when sidebar disappears (Desktop only)
createEffect(() => {
if (!props.isLocked && !props.isPeeking) {
if (!props.isMobile && !props.isLocked && !props.isPeeking) {
setOpen(false);
}
});
const [oversightUsers] = createResource(
() => store.shareRules.filter(r => r.targetUserId === currentUserId && r.type === 'all').map(r => r.ownerId),
() => [...new Set(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' })));
// Fetch user names for these IDs in a single query
const filter = ownerIds.map(id => `id="${id}"`).join(' || ');
const users = await pb.collection('users').getFullList({
filter,
fields: 'id,name,email',
requestKey: null // Disable auto-cancellation
});
return users.map(u => ({ id: u.id, name: u.name || u.email }));
} catch (e) {
} catch (e: any) {
if (e.isAbort) return []; // Ignore explicit aborts
console.error("Failed to load oversight users", e);
return [];
}
@@ -166,8 +172,8 @@ const ContextSwitcher: Component<{
// Close popover
setOpen(false);
// Collapse sidebar if peaking
if (!props.isLocked && props.isPeeking) {
// Collapse sidebar if peaking (Desktop only)
if (!props.isMobile && props.setIsPeeking && !props.isLocked && props.isPeeking) {
props.setIsPeeking(false);
}
};
@@ -175,21 +181,34 @@ const ContextSwitcher: Component<{
return (
<Show when={oversightUsers() && oversightUsers()!.length > 0}>
<Popover open={open()} onOpenChange={setOpen}>
<PopoverTrigger class="w-full">
<PopoverTrigger class={cn(props.isMobile ? "w-auto" : "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"
"flex items-center justify-between transition-all duration-200 group",
props.isMobile
? "h-10 w-10 flex items-center justify-center bg-background/80 backdrop-blur-sm border border-border hover:bg-muted shadow-sm rounded-xl"
: "px-3 py-2.5 rounded-lg text-muted-foreground hover:bg-muted hover:text-foreground border border-transparent hover:border-border/50 w-full text-sm",
)}>
<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" />
<Show when={props.isMobile} fallback={
<>
<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 ml-2" />
</>
}>
<div class="relative">
<Users size={18} class={cn("transition-transform duration-300", open() && "scale-110", ctx() !== 'mine' && "text-orange-500")} />
{ctx() !== 'mine' && (
<div class="absolute -top-1 -right-1 w-2 h-2 rounded-full bg-orange-500 border border-background" />
)}
</div>
<span class="font-medium truncate">{label()}</span>
</div>
<ChevronDown size={14} class="opacity-50 group-hover:opacity-100 transition-opacity" />
</Show>
</div>
</PopoverTrigger>
<PopoverContent class="w-[200px] p-1 bg-card border-border shadow-xl">