xtablo-source/sql/05_create_tablos_schema.sql
2025-06-10 22:07:11 +02:00

294 lines
No EOL
11 KiB
PL/PgSQL

-- =====================================================
-- TABLOS SYSTEM DATABASE SCHEMA
-- =====================================================
-- 1. TABLOS TABLE
-- Main table for storing tablo/workspace information
CREATE TABLE tablos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
color VARCHAR(50) NOT NULL DEFAULT 'bg-blue-500',
owner_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
is_archived BOOLEAN DEFAULT FALSE,
is_public BOOLEAN DEFAULT FALSE,
settings JSONB DEFAULT '{}',
-- Indexes for performance
CONSTRAINT tablos_name_not_empty CHECK (LENGTH(TRIM(name)) > 0),
CONSTRAINT tablos_color_format CHECK (color ~ '^bg-[a-z]+-[0-9]+$')
);
-- 2. TABLO MEMBERS TABLE
-- Junction table for tablo membership and permissions
CREATE TABLE tablo_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tablo_id UUID NOT NULL REFERENCES tablos(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
role VARCHAR(50) NOT NULL DEFAULT 'member',
permissions JSONB DEFAULT '{"read": true, "write": false, "admin": false}',
joined_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
invited_by UUID REFERENCES auth.users(id),
-- Ensure unique membership per tablo
UNIQUE(tablo_id, user_id),
-- Valid roles constraint
CONSTRAINT valid_member_role CHECK (role IN ('owner', 'admin', 'member', 'viewer'))
);
-- 3. TABLO BOARDS TABLE
-- Different boards within a tablo (like Kanban boards, calendars, etc.)
CREATE TABLE tablo_boards (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tablo_id UUID NOT NULL REFERENCES tablos(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL DEFAULT 'kanban',
description TEXT,
position INTEGER NOT NULL DEFAULT 0,
settings JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
created_by UUID NOT NULL REFERENCES auth.users(id),
-- Valid board types
CONSTRAINT valid_board_type CHECK (type IN ('kanban', 'calendar', 'table', 'timeline', 'chat'))
);
-- 4. TABLO LISTS TABLE
-- Lists/columns within boards (for Kanban-style organization)
CREATE TABLE tablo_lists (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
board_id UUID NOT NULL REFERENCES tablo_boards(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
position INTEGER NOT NULL DEFAULT 0,
color VARCHAR(50),
settings JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 5. TABLO CARDS TABLE
-- Individual cards/items within lists
CREATE TABLE tablo_cards (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
list_id UUID NOT NULL REFERENCES tablo_lists(id) ON DELETE CASCADE,
title VARCHAR(500) NOT NULL,
description TEXT,
position INTEGER NOT NULL DEFAULT 0,
due_date TIMESTAMP WITH TIME ZONE,
priority VARCHAR(20) DEFAULT 'medium',
labels JSONB DEFAULT '[]',
assignees JSONB DEFAULT '[]',
attachments JSONB DEFAULT '[]',
checklist JSONB DEFAULT '[]',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
created_by UUID NOT NULL REFERENCES auth.users(id),
-- Valid priority levels
CONSTRAINT valid_priority CHECK (priority IN ('low', 'medium', 'high', 'urgent'))
);
-- 6. TABLO ACTIVITIES TABLE
-- Activity log for tracking changes and actions
CREATE TABLE tablo_activities (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tablo_id UUID NOT NULL REFERENCES tablos(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
action VARCHAR(100) NOT NULL,
entity_type VARCHAR(50) NOT NULL,
entity_id UUID,
details JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Valid entity types
CONSTRAINT valid_entity_type CHECK (entity_type IN ('tablo', 'board', 'list', 'card', 'member'))
);
-- 7. TABLO CHAT CHANNELS TABLE
-- Chat channels within tablos
CREATE TABLE tablo_chat_channels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tablo_id UUID NOT NULL REFERENCES tablos(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
type VARCHAR(20) NOT NULL DEFAULT 'public',
description TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
created_by UUID NOT NULL REFERENCES auth.users(id),
-- Valid channel types
CONSTRAINT valid_channel_type CHECK (type IN ('public', 'private', 'direct'))
);
-- 8. TABLO CHAT MESSAGES TABLE
-- Messages within chat channels
CREATE TABLE tablo_chat_messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
channel_id UUID NOT NULL REFERENCES tablo_chat_channels(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
message_type VARCHAR(20) DEFAULT 'text',
attachments JSONB DEFAULT '[]',
reply_to UUID REFERENCES tablo_chat_messages(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
is_edited BOOLEAN DEFAULT FALSE,
-- Valid message types
CONSTRAINT valid_message_type CHECK (message_type IN ('text', 'image', 'file', 'system'))
);
-- =====================================================
-- INDEXES FOR PERFORMANCE
-- =====================================================
-- Tablos indexes
CREATE INDEX idx_tablos_owner_id ON tablos(owner_id);
CREATE INDEX idx_tablos_created_at ON tablos(created_at DESC);
CREATE INDEX idx_tablos_name_search ON tablos USING gin(to_tsvector('french', name));
-- Tablo members indexes
CREATE INDEX idx_tablo_members_tablo_id ON tablo_members(tablo_id);
CREATE INDEX idx_tablo_members_user_id ON tablo_members(user_id);
-- Boards indexes
CREATE INDEX idx_tablo_boards_tablo_id ON tablo_boards(tablo_id);
CREATE INDEX idx_tablo_boards_position ON tablo_boards(tablo_id, position);
-- Lists indexes
CREATE INDEX idx_tablo_lists_board_id ON tablo_lists(board_id);
CREATE INDEX idx_tablo_lists_position ON tablo_lists(board_id, position);
-- Cards indexes
CREATE INDEX idx_tablo_cards_list_id ON tablo_cards(list_id);
CREATE INDEX idx_tablo_cards_position ON tablo_cards(list_id, position);
CREATE INDEX idx_tablo_cards_assignees ON tablo_cards USING gin(assignees);
CREATE INDEX idx_tablo_cards_due_date ON tablo_cards(due_date) WHERE due_date IS NOT NULL;
-- Activities indexes
CREATE INDEX idx_tablo_activities_tablo_id ON tablo_activities(tablo_id);
CREATE INDEX idx_tablo_activities_created_at ON tablo_activities(created_at DESC);
CREATE INDEX idx_tablo_activities_user_id ON tablo_activities(user_id);
-- Chat indexes
CREATE INDEX idx_tablo_chat_channels_tablo_id ON tablo_chat_channels(tablo_id);
CREATE INDEX idx_tablo_chat_messages_channel_id ON tablo_chat_messages(channel_id);
CREATE INDEX idx_tablo_chat_messages_created_at ON tablo_chat_messages(created_at DESC);
-- =====================================================
-- ROW LEVEL SECURITY (RLS) POLICIES
-- =====================================================
-- Enable RLS on all tables
ALTER TABLE tablos ENABLE ROW LEVEL SECURITY;
ALTER TABLE tablo_members ENABLE ROW LEVEL SECURITY;
ALTER TABLE tablo_boards ENABLE ROW LEVEL SECURITY;
ALTER TABLE tablo_lists ENABLE ROW LEVEL SECURITY;
ALTER TABLE tablo_cards ENABLE ROW LEVEL SECURITY;
ALTER TABLE tablo_activities ENABLE ROW LEVEL SECURITY;
ALTER TABLE tablo_chat_channels ENABLE ROW LEVEL SECURITY;
ALTER TABLE tablo_chat_messages ENABLE ROW LEVEL SECURITY;
-- Tablos policies
CREATE POLICY "Users can view tablos they are members of" ON tablos
FOR SELECT USING (
id IN (
SELECT tablo_id FROM tablo_members
WHERE user_id = auth.uid()
) OR owner_id = auth.uid() OR is_public = true
);
CREATE POLICY "Users can create their own tablos" ON tablos
FOR INSERT WITH CHECK (owner_id = auth.uid());
CREATE POLICY "Owners and admins can update tablos" ON tablos
FOR UPDATE USING (
owner_id = auth.uid() OR
id IN (
SELECT tablo_id FROM tablo_members
WHERE user_id = auth.uid() AND role IN ('admin', 'owner')
)
);
-- Tablo members policies
CREATE POLICY "Users can view members of tablos they belong to" ON tablo_members
FOR SELECT USING (
tablo_id IN (
SELECT tablo_id FROM tablo_members
WHERE user_id = auth.uid()
)
);
CREATE POLICY "Owners and admins can manage members" ON tablo_members
FOR ALL USING (
tablo_id IN (
SELECT id FROM tablos WHERE owner_id = auth.uid()
) OR
tablo_id IN (
SELECT tablo_id FROM tablo_members
WHERE user_id = auth.uid() AND role IN ('admin', 'owner')
)
);
-- =====================================================
-- TRIGGERS FOR AUTOMATIC UPDATES
-- =====================================================
-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Apply triggers to relevant tables
CREATE TRIGGER update_tablos_updated_at BEFORE UPDATE ON tablos
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_tablo_boards_updated_at BEFORE UPDATE ON tablo_boards
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_tablo_lists_updated_at BEFORE UPDATE ON tablo_lists
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_tablo_cards_updated_at BEFORE UPDATE ON tablo_cards
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Function to automatically add owner as member
CREATE OR REPLACE FUNCTION add_owner_as_member()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO tablo_members (tablo_id, user_id, role, permissions)
VALUES (
NEW.id,
NEW.owner_id,
'owner',
'{"read": true, "write": true, "admin": true}'::jsonb
);
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER add_owner_as_member_trigger AFTER INSERT ON tablos
FOR EACH ROW EXECUTE FUNCTION add_owner_as_member();
-- =====================================================
-- SAMPLE DATA
-- =====================================================
-- Insert sample tablos (assuming user IDs exist)
-- Note: Replace with actual user UUIDs from your auth.users table
/*
INSERT INTO tablos (name, description, color, owner_id) VALUES
('Projet Alpha', 'Développement de la nouvelle application', 'bg-blue-500', 'user-uuid-1'),
('Marketing Q4', 'Campagnes marketing pour le quatrième trimestre', 'bg-green-500', 'user-uuid-2'),
('Équipe Dev', 'Coordination de l''équipe de développement', 'bg-purple-500', 'user-uuid-1'),
('Budget 2024', 'Planification budgétaire pour 2024', 'bg-red-500', 'user-uuid-3'),
('Roadmap', 'Feuille de route produit', 'bg-yellow-500', 'user-uuid-1'),
('Support Client', 'Gestion du support client', 'bg-indigo-500', 'user-uuid-2');
*/