fixed sharing
This commit is contained in:
+158
-3
@@ -1,15 +1,16 @@
|
||||
import { type Component, For, Show, createSignal, lazy, Suspense } from "solid-js";
|
||||
import { ThemeToggle } from "../components/ThemeToggle";
|
||||
import { store, restoreTask, deleteTaskPermanently, setMatrixScaleDays, addTemplate, removeTemplate, updateTemplate, upsertTagDefinition, removeTagDefinition, renameTagDefinition } from "@/store";
|
||||
import { store, restoreTask, deleteTaskPermanently, setMatrixScaleDays, addTemplate, removeTemplate, updateTemplate, upsertTagDefinition, removeTagDefinition, renameTagDefinition, addShareRule, removeShareRule } from "@/store";
|
||||
import { useTheme } from "@/components/ThemeProvider";
|
||||
import { Trash2, Undo2, ArrowLeftRight, Hash, Copy, Plus, ChevronDown, ChevronRight, Type } from "lucide-solid";
|
||||
import { Trash2, Undo2, ArrowLeftRight, Hash, Copy, Plus, ChevronDown, ChevronRight, Type, Share2, Users, Tag } from "lucide-solid";
|
||||
import { TagPicker } from "@/components/TagPicker";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { toast } from "solid-sonner";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { PRIORITY_OPTIONS, URGENCY_OPTIONS } from "@/lib/constants";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { createEffect } from "solid-js";
|
||||
|
||||
const TaskEditor = lazy(() => import("../components/TaskEditor").then(m => ({ default: m.TaskEditor })));
|
||||
|
||||
@@ -18,7 +19,35 @@ export const SettingsView: Component = () => {
|
||||
const [isTagsOpen, setIsTagsOpen] = createSignal(false);
|
||||
const [isTemplatesOpen, setIsTemplatesOpen] = createSignal(false);
|
||||
const [isTrashOpen, setIsTrashOpen] = createSignal(false);
|
||||
const [isSharingOpen, setIsSharingOpen] = createSignal(false);
|
||||
const [expandedTemplateId, setExpandedTemplateId] = createSignal<string | null>(null);
|
||||
const [shareUserId, setShareUserId] = createSignal("");
|
||||
const [shareType, setShareType] = createSignal<'all' | 'tag'>('all');
|
||||
const [shareTag, setShareTag] = createSignal<string>("");
|
||||
const [allUsers, setAllUsers] = createSignal<Array<{ id: string; name: string }>>([]);
|
||||
const [usersLoading, setUsersLoading] = createSignal(false);
|
||||
|
||||
// Fetch users when sharing section opens
|
||||
createEffect(() => {
|
||||
if (isSharingOpen() && allUsers().length === 0 && !usersLoading()) {
|
||||
setUsersLoading(true);
|
||||
pb.collection('users').getFullList({ fields: 'id,name,email' })
|
||||
.then(records => {
|
||||
setAllUsers(records.map((r: any) => ({ id: r.id, name: r.name || r.email || r.id })));
|
||||
})
|
||||
.catch(err => console.error("Failed to load users:", err))
|
||||
.finally(() => setUsersLoading(false));
|
||||
}
|
||||
});
|
||||
|
||||
const getUserName = (userId: string) => {
|
||||
return allUsers().find(u => u.id === userId)?.name || userId;
|
||||
};
|
||||
|
||||
const availableUsers = () => {
|
||||
const currentUserId = pb.authStore.model?.id;
|
||||
return allUsers().filter(u => u.id !== currentUserId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="w-full max-w-2xl mx-auto py-4 sm:py-10 px-4 sm:px-0 space-y-8 sm:space-y-12 animate-in fade-in slide-in-from-bottom-2 duration-500 min-w-0 overflow-hidden text-balance">
|
||||
@@ -53,6 +82,132 @@ export const SettingsView: Component = () => {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Sharing */}
|
||||
<section class="p-4 sm:p-5 rounded-2xl border border-border bg-card shadow-sm space-y-4 overflow-hidden">
|
||||
<div
|
||||
class="flex items-center justify-between cursor-pointer group"
|
||||
onClick={() => setIsSharingOpen(!isSharingOpen())}
|
||||
>
|
||||
<div class="space-y-0.5">
|
||||
<h3 class="text-base font-semibold flex items-center gap-2">
|
||||
<Share2 size={16} />
|
||||
Task Sharing
|
||||
</h3>
|
||||
<p class="text-sm text-muted-foreground">Share all tasks or tasks with specific tags with others.</p>
|
||||
</div>
|
||||
<div class="w-8 h-8 rounded-full flex items-center justify-center bg-muted/50 group-hover:bg-muted transition-colors">
|
||||
{isSharingOpen() ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={isSharingOpen()}>
|
||||
<div class="space-y-4 pt-2 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
{/* Add new share rule */}
|
||||
<div class="p-3 rounded-xl bg-muted/30 border border-border/50 space-y-3">
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
variant={shareType() === 'all' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
class="h-8 text-xs gap-1"
|
||||
onClick={() => setShareType('all')}
|
||||
>
|
||||
<Users size={12} />
|
||||
All Tasks
|
||||
</Button>
|
||||
<Button
|
||||
variant={shareType() === 'tag' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
class="h-8 text-xs gap-1"
|
||||
onClick={() => setShareType('tag')}
|
||||
>
|
||||
<Tag size={12} />
|
||||
By Tag
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Show when={shareType() === 'tag'}>
|
||||
<Select
|
||||
value={shareTag()}
|
||||
onChange={(val) => val && setShareTag(val)}
|
||||
options={store.tagDefinitions.map(t => t.name)}
|
||||
placeholder="Select a tag"
|
||||
itemComponent={(props: any) => <SelectItem item={props.item}>{props.item.rawValue}</SelectItem>}
|
||||
>
|
||||
<SelectTrigger class="h-9 text-sm">
|
||||
<span>{shareTag() || "Select a tag..."}</span>
|
||||
</SelectTrigger>
|
||||
<SelectContent />
|
||||
</Select>
|
||||
</Show>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Select<{ id: string; name: string }>
|
||||
value={allUsers().find(u => u.id === shareUserId()) || null}
|
||||
onChange={(user) => setShareUserId(user?.id || "")}
|
||||
options={availableUsers()}
|
||||
optionValue="id"
|
||||
optionTextValue="name"
|
||||
placeholder={usersLoading() ? "Loading..." : "Select user..."}
|
||||
itemComponent={(itemProps: any) => (
|
||||
<SelectItem item={itemProps.item}>{itemProps.item.rawValue.name}</SelectItem>
|
||||
)}
|
||||
>
|
||||
<SelectTrigger class="flex-1 h-9 text-sm">
|
||||
<SelectValue<{ id: string; name: string }>>
|
||||
{(state) => state.selectedOption()?.name || "Select user..."}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent />
|
||||
</Select>
|
||||
<Button
|
||||
size="sm"
|
||||
class="h-9 gap-2"
|
||||
disabled={!shareUserId()}
|
||||
onClick={async () => {
|
||||
if (shareUserId()) {
|
||||
await addShareRule(shareType(), shareUserId(), shareType() === 'tag' ? shareTag() : undefined);
|
||||
setShareUserId("");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Plus size={14} />
|
||||
Share
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Active share rules */}
|
||||
<For each={store.shareRules} fallback={
|
||||
<div class="text-center py-6 text-muted-foreground text-sm italic border border-dashed border-border rounded-xl">
|
||||
No active share rules.
|
||||
</div>
|
||||
}>
|
||||
{(rule) => (
|
||||
<div class="flex items-center justify-between p-3 rounded-xl bg-muted/20 border border-border/40 gap-3">
|
||||
<div class="flex items-center gap-3 min-w-0 flex-1">
|
||||
{rule.type === 'all' ? <Users size={14} class="text-primary shrink-0" /> : <Tag size={14} class="text-primary shrink-0" />}
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm font-medium truncate">{getUserName(rule.targetUserId)}</p>
|
||||
<p class="text-[10px] text-muted-foreground uppercase tracking-wider">
|
||||
{rule.type === 'all' ? 'All Tasks' : `Tag: ${rule.tagName}`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8 hover:bg-destructive/10 hover:text-destructive"
|
||||
onClick={() => removeShareRule(rule.id)}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</section>
|
||||
|
||||
{/* Matrix Configuration */}
|
||||
<section class="p-4 sm:p-5 rounded-2xl border border-border bg-card shadow-sm space-y-4">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
|
||||
Reference in New Issue
Block a user