plugin-voting/database/schema.sql
2025-11-03 14:01:07 +02:00

170 lines
6.6 KiB
SQL

-- Voting Plugin Database Schema
-- Plugin naming convention: pg_vt_tableName
-- pg_ = plugin, vt_ = voting plugin
-- Campaign Types: 'board_election', 'decision', 'survey'
-- Campaign Status: 'draft', 'scheduled', 'active', 'completed', 'cancelled'
-- Result Sharing: 'detailed', 'anonymous', 'none'
-- Voting Campaigns table
CREATE TABLE IF NOT EXISTS pg_vt_campaigns (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
site_id UUID REFERENCES sites(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
campaign_type VARCHAR(50) NOT NULL, -- board_election, decision, survey
form_id UUID, -- Foreign key to pg_vt_forms table
status VARCHAR(50) DEFAULT 'draft', -- draft, scheduled, active, completed, cancelled
overall_sharing_results VARCHAR(50) DEFAULT 'detailed', -- detailed, anonymous, none
public_access VARCHAR(50) DEFAULT 'restrict', -- restrict, allow
-- Campaign dates
start_date TIMESTAMP WITH TIME ZONE NOT NULL,
end_date TIMESTAMP WITH TIME ZONE NOT NULL,
-- Recipients
recipient_group_id UUID, -- Reference to a group or null for all owners
recipient_type VARCHAR(50) DEFAULT 'all_owners', -- all_owners, board_members, authorized_voters, custom_group
-- Delivery methods
send_email BOOLEAN DEFAULT false,
send_text BOOLEAN DEFAULT false,
send_owner_app BOOLEAN DEFAULT false,
-- Metadata
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create indexes for campaigns
CREATE INDEX IF NOT EXISTS idx_pg_vt_campaigns_site_id ON pg_vt_campaigns(site_id);
CREATE INDEX IF NOT EXISTS idx_pg_vt_campaigns_type ON pg_vt_campaigns(campaign_type);
CREATE INDEX IF NOT EXISTS idx_pg_vt_campaigns_status ON pg_vt_campaigns(status);
CREATE INDEX IF NOT EXISTS idx_pg_vt_campaigns_dates ON pg_vt_campaigns(start_date, end_date);
-- Voting Forms table
-- Placeholder for form builder - will store form structure as JSON
CREATE TABLE IF NOT EXISTS pg_vt_forms (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
site_id UUID REFERENCES sites(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
form_type VARCHAR(50) NOT NULL, -- board_election, decision, survey
-- Form structure stored as JSON
-- This is a placeholder until we build/embed a form builder
-- Example structure:
-- {
-- "questions": [
-- {
-- "id": "q1",
-- "type": "multiple_choice",
-- "label": "Choose 4 Board Members *",
-- "required": true,
-- "multiple": true,
-- "max_selections": 4,
-- "options": [
-- {"id": "opt1", "value": "bod 1"},
-- {"id": "opt2", "value": "bod 2"}
-- ]
-- }
-- ]
-- }
form_structure JSONB DEFAULT '{"questions": []}',
-- Metadata
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
is_active BOOLEAN DEFAULT true
);
CREATE INDEX IF NOT EXISTS idx_pg_vt_forms_site_id ON pg_vt_forms(site_id);
CREATE INDEX IF NOT EXISTS idx_pg_vt_forms_type ON pg_vt_forms(form_type);
-- Campaign Responses table (main response tracking)
CREATE TABLE IF NOT EXISTS pg_vt_responses (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
campaign_id UUID REFERENCES pg_vt_campaigns(id) ON DELETE CASCADE,
person_id UUID REFERENCES people(id),
user_id UUID REFERENCES users(id),
-- Participation tracking
answered BOOLEAN DEFAULT false,
answered_date TIMESTAMP WITH TIME ZONE,
last_modified_date TIMESTAMP WITH TIME ZONE,
-- Communication tracking
email_sent BOOLEAN DEFAULT false,
text_sent BOOLEAN DEFAULT false,
-- Metadata
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(campaign_id, person_id)
);
CREATE INDEX IF NOT EXISTS idx_pg_vt_responses_campaign_id ON pg_vt_responses(campaign_id);
CREATE INDEX IF NOT EXISTS idx_pg_vt_responses_person_id ON pg_vt_responses(person_id);
CREATE INDEX IF NOT EXISTS idx_pg_vt_responses_answered ON pg_vt_responses(answered);
-- Response Answers table (detailed answer data)
CREATE TABLE IF NOT EXISTS pg_vt_response_answers (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
response_id UUID REFERENCES pg_vt_responses(id) ON DELETE CASCADE,
question_id VARCHAR(255) NOT NULL, -- Reference to question in form_structure
answer_value TEXT, -- For text, numeric, date answers
answer_options JSONB, -- For multiple choice answers: ["bod 1", "bod 3", "bod 5", "bod 4"]
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_pg_vt_response_answers_response_id ON pg_vt_response_answers(response_id);
CREATE INDEX IF NOT EXISTS idx_pg_vt_response_answers_question_id ON pg_vt_response_answers(question_id);
-- Change Logs table (audit trail for response changes)
CREATE TABLE IF NOT EXISTS pg_vt_response_change_logs (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
response_id UUID REFERENCES pg_vt_responses(id) ON DELETE CASCADE,
campaign_id UUID REFERENCES pg_vt_campaigns(id) ON DELETE CASCADE,
person_id UUID REFERENCES people(id),
person_name VARCHAR(255),
unit VARCHAR(255),
email VARCHAR(255),
phone VARCHAR(50),
answered_date TIMESTAMP WITH TIME ZONE,
answered_change_date TIMESTAMP WITH TIME ZONE,
who_changed VARCHAR(255), -- Person who made the change
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_pg_vt_change_logs_campaign_id ON pg_vt_response_change_logs(campaign_id);
CREATE INDEX IF NOT EXISTS idx_pg_vt_change_logs_response_id ON pg_vt_response_change_logs(response_id);
-- Groups table (for custom recipient groups)
CREATE TABLE IF NOT EXISTS pg_vt_groups (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
site_id UUID REFERENCES sites(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_pg_vt_groups_site_id ON pg_vt_groups(site_id);
-- Group Members junction table
CREATE TABLE IF NOT EXISTS pg_vt_group_members (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
group_id UUID REFERENCES pg_vt_groups(id) ON DELETE CASCADE,
person_id UUID REFERENCES people(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(group_id, person_id)
);
CREATE INDEX IF NOT EXISTS idx_pg_vt_group_members_group_id ON pg_vt_group_members(group_id);
CREATE INDEX IF NOT EXISTS idx_pg_vt_group_members_person_id ON pg_vt_group_members(person_id);