CREATE TABLE IF NOT EXISTS calendars ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, name VARCHAR(200) NOT NULL, color VARCHAR(20) DEFAULT '#3B82F6', type VARCHAR(20) DEFAULT 'personal', owner_id UUID NOT NULL, ics_token VARCHAR(64), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_calendars_tenant ON calendars(tenant_id) WHERE deleted_at IS NULL; CREATE TABLE IF NOT EXISTS calendar_entries ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, calendar_id UUID REFERENCES calendars(id) ON DELETE CASCADE, entry_type VARCHAR(15) NOT NULL, subtype VARCHAR(20) DEFAULT 'normal', title VARCHAR(500) NOT NULL, description TEXT, start_at TIMESTAMPTZ, end_at TIMESTAMPTZ, all_day BOOLEAN DEFAULT false, location VARCHAR(500), due_date TIMESTAMPTZ, priority VARCHAR(10) DEFAULT 'medium', status VARCHAR(15) DEFAULT 'open', assigned_to UUID, reminder JSONB, recurrence JSONB, source_mail_id UUID, created_by UUID NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_entries_tenant_cal ON calendar_entries(tenant_id, calendar_id) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_entries_tenant_start ON calendar_entries(tenant_id, start_at) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_entries_tenant_due ON calendar_entries(tenant_id, due_date) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_entries_assigned ON calendar_entries(tenant_id, assigned_to, status) WHERE deleted_at IS NULL; CREATE TABLE IF NOT EXISTS calendar_entry_links ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, entry_id UUID REFERENCES calendar_entries(id) ON DELETE CASCADE, entity_type VARCHAR(50) NOT NULL, entity_id UUID NOT NULL ); CREATE INDEX IF NOT EXISTS idx_entry_links_entry ON calendar_entry_links(entry_id); CREATE TABLE IF NOT EXISTS calendar_shares ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, calendar_id UUID REFERENCES calendars(id) ON DELETE CASCADE, user_id UUID, group_id UUID, permission VARCHAR(10) NOT NULL ); CREATE INDEX IF NOT EXISTS idx_calendar_shares_cal ON calendar_shares(calendar_id); CREATE TABLE IF NOT EXISTS user_calendar_visibility ( user_id UUID NOT NULL, calendar_id UUID NOT NULL, tenant_id UUID NOT NULL, visible BOOLEAN DEFAULT true, PRIMARY KEY (user_id, calendar_id) ); CREATE TABLE IF NOT EXISTS subtasks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, entry_id UUID REFERENCES calendar_entries(id) ON DELETE CASCADE, title VARCHAR(500) NOT NULL, completed BOOLEAN DEFAULT false, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_subtasks_entry ON subtasks(entry_id); CREATE TABLE IF NOT EXISTS resources ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, name VARCHAR(200) NOT NULL, type VARCHAR(50) NOT NULL ); CREATE INDEX IF NOT EXISTS idx_resources_tenant ON resources(tenant_id); CREATE TABLE IF NOT EXISTS resource_bookings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, resource_id UUID REFERENCES resources(id) ON DELETE CASCADE, entry_id UUID REFERENCES calendar_entries(id) ON DELETE CASCADE, start_at TIMESTAMPTZ NOT NULL, end_at TIMESTAMPTZ NOT NULL ); CREATE INDEX IF NOT EXISTS idx_bookings_resource ON resource_bookings(resource_id); CREATE INDEX IF NOT EXISTS idx_bookings_entry ON resource_bookings(entry_id);