parent
1534e1f0af
commit
9af0ef5307
17 changed files with 1053 additions and 186 deletions
@ -0,0 +1,14 @@ |
||||
<script lang="ts"> |
||||
import { toasts } from '$lib/stores/toast'; |
||||
import Toast from './Toast.svelte'; |
||||
</script> |
||||
|
||||
<div class="fixed bottom-4 right-4 z-50 flex flex-col gap-2 max-w-sm"> |
||||
{#each $toasts as toast (toast.id)} |
||||
<Toast |
||||
variant={toast.variant} |
||||
message={toast.message} |
||||
onClose={() => toasts.remove(toast.id)} |
||||
/> |
||||
{/each} |
||||
</div> |
||||
@ -0,0 +1,48 @@ |
||||
import { writable } from 'svelte/store'; |
||||
|
||||
export type ToastVariant = 'success' | 'error' | 'warning' | 'info'; |
||||
|
||||
export interface Toast { |
||||
id: string; |
||||
message: string; |
||||
variant: ToastVariant; |
||||
duration?: number; |
||||
} |
||||
|
||||
function createToastStore() { |
||||
const { subscribe, update } = writable<Toast[]>([]); |
||||
|
||||
function add(message: string, variant: ToastVariant = 'info', duration = 5000) { |
||||
const id = crypto.randomUUID(); |
||||
const toast: Toast = { id, message, variant, duration }; |
||||
|
||||
update((toasts) => [...toasts, toast]); |
||||
|
||||
if (duration > 0) { |
||||
setTimeout(() => remove(id), duration); |
||||
} |
||||
|
||||
return id; |
||||
} |
||||
|
||||
function remove(id: string) { |
||||
update((toasts) => toasts.filter((t) => t.id !== id)); |
||||
} |
||||
|
||||
function clear() { |
||||
update(() => []); |
||||
} |
||||
|
||||
return { |
||||
subscribe, |
||||
add, |
||||
remove, |
||||
clear, |
||||
success: (message: string, duration?: number) => add(message, 'success', duration), |
||||
error: (message: string, duration?: number) => add(message, 'error', duration), |
||||
warning: (message: string, duration?: number) => add(message, 'warning', duration), |
||||
info: (message: string, duration?: number) => add(message, 'info', duration) |
||||
}; |
||||
} |
||||
|
||||
export const toasts = createToastStore(); |
||||
@ -0,0 +1,69 @@ |
||||
-- Tags system for kanban tasks |
||||
-- Allows categorizing tasks by team, type, priority, etc. |
||||
|
||||
-- Create tags table |
||||
CREATE TABLE IF NOT EXISTS tags ( |
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), |
||||
org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, |
||||
name TEXT NOT NULL, |
||||
color TEXT DEFAULT '#00A3E0', |
||||
created_at TIMESTAMPTZ DEFAULT NOW(), |
||||
UNIQUE(org_id, name) |
||||
); |
||||
|
||||
-- Create junction table for card tags (many-to-many) |
||||
CREATE TABLE IF NOT EXISTS card_tags ( |
||||
card_id UUID NOT NULL REFERENCES kanban_cards(id) ON DELETE CASCADE, |
||||
tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE, |
||||
created_at TIMESTAMPTZ DEFAULT NOW(), |
||||
PRIMARY KEY (card_id, tag_id) |
||||
); |
||||
|
||||
-- Create indexes |
||||
CREATE INDEX IF NOT EXISTS idx_tags_org ON tags(org_id); |
||||
CREATE INDEX IF NOT EXISTS idx_card_tags_card ON card_tags(card_id); |
||||
CREATE INDEX IF NOT EXISTS idx_card_tags_tag ON card_tags(tag_id); |
||||
|
||||
-- Enable RLS |
||||
ALTER TABLE tags ENABLE ROW LEVEL SECURITY; |
||||
ALTER TABLE card_tags ENABLE ROW LEVEL SECURITY; |
||||
|
||||
-- RLS policies for tags |
||||
CREATE POLICY "Users can view tags in their orgs" |
||||
ON tags FOR SELECT |
||||
USING ( |
||||
org_id IN ( |
||||
SELECT org_id FROM org_members WHERE user_id = auth.uid() |
||||
) |
||||
); |
||||
|
||||
CREATE POLICY "Admins can manage tags" |
||||
ON tags FOR ALL |
||||
USING ( |
||||
org_id IN ( |
||||
SELECT org_id FROM org_members |
||||
WHERE user_id = auth.uid() |
||||
AND role IN ('owner', 'admin') |
||||
) |
||||
); |
||||
|
||||
-- RLS policies for card_tags |
||||
CREATE POLICY "Users can view card tags in their orgs" |
||||
ON card_tags FOR SELECT |
||||
USING ( |
||||
tag_id IN ( |
||||
SELECT id FROM tags WHERE org_id IN ( |
||||
SELECT org_id FROM org_members WHERE user_id = auth.uid() |
||||
) |
||||
) |
||||
); |
||||
|
||||
CREATE POLICY "Members can manage card tags" |
||||
ON card_tags FOR ALL |
||||
USING ( |
||||
tag_id IN ( |
||||
SELECT id FROM tags WHERE org_id IN ( |
||||
SELECT org_id FROM org_members WHERE user_id = auth.uid() |
||||
) |
||||
) |
||||
); |
||||
@ -0,0 +1,83 @@ |
||||
-- Teams/Roles system for organization structure |
||||
-- Like TipiLAN: infra team, sponsors, etc. |
||||
|
||||
-- Create teams table |
||||
CREATE TABLE IF NOT EXISTS teams ( |
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), |
||||
org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, |
||||
name TEXT NOT NULL, |
||||
description TEXT, |
||||
color TEXT DEFAULT '#00A3E0', |
||||
created_at TIMESTAMPTZ DEFAULT NOW(), |
||||
updated_at TIMESTAMPTZ DEFAULT NOW(), |
||||
UNIQUE(org_id, name) |
||||
); |
||||
|
||||
-- Create team members junction table |
||||
CREATE TABLE IF NOT EXISTS team_members ( |
||||
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, |
||||
user_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, |
||||
role TEXT DEFAULT 'member' CHECK (role IN ('lead', 'member')), |
||||
joined_at TIMESTAMPTZ DEFAULT NOW(), |
||||
PRIMARY KEY (team_id, user_id) |
||||
); |
||||
|
||||
-- Create indexes |
||||
CREATE INDEX IF NOT EXISTS idx_teams_org ON teams(org_id); |
||||
CREATE INDEX IF NOT EXISTS idx_team_members_team ON team_members(team_id); |
||||
CREATE INDEX IF NOT EXISTS idx_team_members_user ON team_members(user_id); |
||||
|
||||
-- Enable RLS |
||||
ALTER TABLE teams ENABLE ROW LEVEL SECURITY; |
||||
ALTER TABLE team_members ENABLE ROW LEVEL SECURITY; |
||||
|
||||
-- RLS policies for teams |
||||
CREATE POLICY "Users can view teams in their orgs" |
||||
ON teams FOR SELECT |
||||
USING ( |
||||
org_id IN ( |
||||
SELECT org_id FROM org_members WHERE user_id = auth.uid() |
||||
) |
||||
); |
||||
|
||||
CREATE POLICY "Admins can manage teams" |
||||
ON teams FOR ALL |
||||
USING ( |
||||
org_id IN ( |
||||
SELECT org_id FROM org_members |
||||
WHERE user_id = auth.uid() |
||||
AND role IN ('owner', 'admin') |
||||
) |
||||
); |
||||
|
||||
-- RLS policies for team_members |
||||
CREATE POLICY "Users can view team members in their orgs" |
||||
ON team_members FOR SELECT |
||||
USING ( |
||||
team_id IN ( |
||||
SELECT id FROM teams WHERE org_id IN ( |
||||
SELECT org_id FROM org_members WHERE user_id = auth.uid() |
||||
) |
||||
) |
||||
); |
||||
|
||||
CREATE POLICY "Admins can manage team members" |
||||
ON team_members FOR ALL |
||||
USING ( |
||||
team_id IN ( |
||||
SELECT id FROM teams WHERE org_id IN ( |
||||
SELECT org_id FROM org_members |
||||
WHERE user_id = auth.uid() |
||||
AND role IN ('owner', 'admin') |
||||
) |
||||
) |
||||
); |
||||
|
||||
-- Add team_id to kanban_boards for team-specific boards |
||||
ALTER TABLE kanban_boards ADD COLUMN IF NOT EXISTS team_id UUID REFERENCES teams(id) ON DELETE SET NULL; |
||||
ALTER TABLE kanban_boards ADD COLUMN IF NOT EXISTS is_personal BOOLEAN DEFAULT FALSE; |
||||
ALTER TABLE kanban_boards ADD COLUMN IF NOT EXISTS created_by UUID REFERENCES profiles(id); |
||||
|
||||
-- Create index for team boards |
||||
CREATE INDEX IF NOT EXISTS idx_kanban_boards_team ON kanban_boards(team_id); |
||||
CREATE INDEX IF NOT EXISTS idx_kanban_boards_personal ON kanban_boards(is_personal, created_by); |
||||
Loading…
Reference in new issue