294 lines
11 KiB
MySQL
294 lines
11 KiB
MySQL
|
|
-- =====================================================
|
||
|
|
-- 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');
|
||
|
|
*/
|