-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsupabase-schema-support.sql
More file actions
345 lines (303 loc) · 14 KB
/
supabase-schema-support.sql
File metadata and controls
345 lines (303 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
-- Support Portal Database Schema
-- This schema handles support tickets, messages, and related functionality
-- Enable UUID extension if not already enabled
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Support Tickets Table
CREATE TABLE IF NOT EXISTS support_tickets (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'in-progress', 'resolved', 'closed')),
priority VARCHAR(50) NOT NULL DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'urgent')),
category VARCHAR(50) NOT NULL DEFAULT 'general' CHECK (category IN ('migration', 'technical', 'billing', 'general')),
user_email VARCHAR(255) NOT NULL,
assigned_to VARCHAR(255),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
resolved_at TIMESTAMP WITH TIME ZONE,
estimated_resolution TIMESTAMP WITH TIME ZONE,
tags TEXT[] DEFAULT '{}',
metadata JSONB DEFAULT '{}'
);
-- Support Messages Table
CREATE TABLE IF NOT EXISTS support_messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
ticket_id UUID NOT NULL REFERENCES support_tickets(id) ON DELETE CASCADE,
sender VARCHAR(50) NOT NULL CHECK (sender IN ('user', 'support')),
message TEXT NOT NULL,
timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
attachments TEXT[] DEFAULT '{}',
is_internal BOOLEAN DEFAULT FALSE,
metadata JSONB DEFAULT '{}'
);
-- Support Ticket Attachments Table
CREATE TABLE IF NOT EXISTS support_attachments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
ticket_id UUID NOT NULL REFERENCES support_tickets(id) ON DELETE CASCADE,
message_id UUID REFERENCES support_messages(id) ON DELETE CASCADE,
file_name VARCHAR(255) NOT NULL,
file_path VARCHAR(500) NOT NULL,
file_size BIGINT NOT NULL,
mime_type VARCHAR(100) NOT NULL,
uploaded_by VARCHAR(255) NOT NULL,
uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
metadata JSONB DEFAULT '{}'
);
-- Support Knowledge Base Table
CREATE TABLE IF NOT EXISTS support_knowledge_base (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
category VARCHAR(100) NOT NULL,
tags TEXT[] DEFAULT '{}',
is_published BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
created_by VARCHAR(255) NOT NULL,
view_count INTEGER DEFAULT 0,
helpful_count INTEGER DEFAULT 0,
not_helpful_count INTEGER DEFAULT 0
);
-- Support FAQ Table
CREATE TABLE IF NOT EXISTS support_faq (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
question TEXT NOT NULL,
answer TEXT NOT NULL,
category VARCHAR(100) NOT NULL,
priority INTEGER DEFAULT 0,
is_published BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
helpful_count INTEGER DEFAULT 0,
not_helpful_count INTEGER DEFAULT 0
);
-- Support Ticket Categories Table
CREATE TABLE IF NOT EXISTS support_categories (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(100) NOT NULL UNIQUE,
description TEXT,
color VARCHAR(7) DEFAULT '#3B82F6',
icon VARCHAR(50),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Support Ticket Priorities Table
CREATE TABLE IF NOT EXISTS support_priorities (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(50) NOT NULL UNIQUE,
description TEXT,
color VARCHAR(7) DEFAULT '#6B7280',
sla_hours INTEGER,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Support Team Members Table
CREATE TABLE IF NOT EXISTS support_team (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
role VARCHAR(100) NOT NULL DEFAULT 'support_agent',
specialties TEXT[] DEFAULT '{}',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Support Ticket Assignments Table
CREATE TABLE IF NOT EXISTS support_assignments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
ticket_id UUID NOT NULL REFERENCES support_tickets(id) ON DELETE CASCADE,
team_member_id UUID NOT NULL REFERENCES support_team(id) ON DELETE CASCADE,
assigned_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
assigned_by VARCHAR(255) NOT NULL,
notes TEXT,
UNIQUE(ticket_id, team_member_id)
);
-- Support Ticket History Table
CREATE TABLE IF NOT EXISTS support_ticket_history (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
ticket_id UUID NOT NULL REFERENCES support_tickets(id) ON DELETE CASCADE,
action VARCHAR(100) NOT NULL,
field_name VARCHAR(100),
old_value TEXT,
new_value TEXT,
changed_by VARCHAR(255) NOT NULL,
changed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
metadata JSONB DEFAULT '{}'
);
-- Support Ticket Metrics Table
CREATE TABLE IF NOT EXISTS support_metrics (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
date DATE NOT NULL,
total_tickets INTEGER DEFAULT 0,
open_tickets INTEGER DEFAULT 0,
resolved_tickets INTEGER DEFAULT 0,
avg_resolution_time_hours DECIMAL(10,2) DEFAULT 0,
avg_response_time_hours DECIMAL(10,2) DEFAULT 0,
customer_satisfaction_score DECIMAL(3,2) DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(date)
);
-- Indexes for better performance
CREATE INDEX IF NOT EXISTS idx_support_tickets_user_email ON support_tickets(user_email);
CREATE INDEX IF NOT EXISTS idx_support_tickets_status ON support_tickets(status);
CREATE INDEX IF NOT EXISTS idx_support_tickets_priority ON support_tickets(priority);
CREATE INDEX IF NOT EXISTS idx_support_tickets_category ON support_tickets(category);
CREATE INDEX IF NOT EXISTS idx_support_tickets_created_at ON support_tickets(created_at);
CREATE INDEX IF NOT EXISTS idx_support_tickets_assigned_to ON support_tickets(assigned_to);
CREATE INDEX IF NOT EXISTS idx_support_messages_ticket_id ON support_messages(ticket_id);
CREATE INDEX IF NOT EXISTS idx_support_messages_timestamp ON support_messages(timestamp);
CREATE INDEX IF NOT EXISTS idx_support_messages_sender ON support_messages(sender);
CREATE INDEX IF NOT EXISTS idx_support_attachments_ticket_id ON support_attachments(ticket_id);
CREATE INDEX IF NOT EXISTS idx_support_attachments_message_id ON support_attachments(message_id);
CREATE INDEX IF NOT EXISTS idx_support_knowledge_base_category ON support_knowledge_base(category);
CREATE INDEX IF NOT EXISTS idx_support_knowledge_base_tags ON support_knowledge_base USING GIN(tags);
CREATE INDEX IF NOT EXISTS idx_support_knowledge_base_published ON support_knowledge_base(is_published);
CREATE INDEX IF NOT EXISTS idx_support_faq_category ON support_faq(category);
CREATE INDEX IF NOT EXISTS idx_support_faq_published ON support_faq(is_published);
CREATE INDEX IF NOT EXISTS idx_support_assignments_ticket_id ON support_assignments(ticket_id);
CREATE INDEX IF NOT EXISTS idx_support_assignments_team_member_id ON support_assignments(team_member_id);
CREATE INDEX IF NOT EXISTS idx_support_ticket_history_ticket_id ON support_ticket_history(ticket_id);
CREATE INDEX IF NOT EXISTS idx_support_ticket_history_changed_at ON support_ticket_history(changed_at);
CREATE INDEX IF NOT EXISTS idx_support_metrics_date ON support_metrics(date);
-- Triggers for automatic timestamp updates
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_support_tickets_updated_at
BEFORE UPDATE ON support_tickets
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_support_knowledge_base_updated_at
BEFORE UPDATE ON support_knowledge_base
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_support_faq_updated_at
BEFORE UPDATE ON support_faq
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_support_team_updated_at
BEFORE UPDATE ON support_team
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Trigger to update ticket history when ticket is modified
CREATE OR REPLACE FUNCTION log_ticket_changes()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'UPDATE' THEN
IF OLD.status != NEW.status THEN
INSERT INTO support_ticket_history (ticket_id, action, field_name, old_value, new_value, changed_by)
VALUES (NEW.id, 'status_change', 'status', OLD.status, NEW.status, COALESCE(NEW.assigned_to, 'system'));
END IF;
IF OLD.priority != NEW.priority THEN
INSERT INTO support_ticket_history (ticket_id, action, field_name, old_value, new_value, changed_by)
VALUES (NEW.id, 'priority_change', 'priority', OLD.priority, NEW.priority, COALESCE(NEW.assigned_to, 'system'));
END IF;
IF OLD.assigned_to != NEW.assigned_to THEN
INSERT INTO support_ticket_history (ticket_id, action, field_name, old_value, new_value, changed_by)
VALUES (NEW.id, 'assignment_change', 'assigned_to', OLD.assigned_to, NEW.assigned_to, COALESCE(NEW.assigned_to, 'system'));
END IF;
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER log_support_ticket_changes
AFTER UPDATE ON support_tickets
FOR EACH ROW EXECUTE FUNCTION log_ticket_changes();
-- Function to calculate support metrics
CREATE OR REPLACE FUNCTION calculate_daily_support_metrics(target_date DATE DEFAULT CURRENT_DATE)
RETURNS VOID AS $$
BEGIN
INSERT INTO support_metrics (
date,
total_tickets,
open_tickets,
resolved_tickets,
avg_resolution_time_hours,
avg_response_time_hours
)
SELECT
target_date,
COUNT(*) as total_tickets,
COUNT(*) FILTER (WHERE status IN ('open', 'in-progress')) as open_tickets,
COUNT(*) FILTER (WHERE status = 'resolved') as resolved_tickets,
AVG(EXTRACT(EPOCH FROM (resolved_at - created_at)) / 3600) FILTER (WHERE status = 'resolved') as avg_resolution_time_hours,
AVG(EXTRACT(EPOCH FROM (
SELECT MIN(timestamp) FROM support_messages
WHERE ticket_id = t.id AND sender = 'support'
) - created_at) / 3600) as avg_response_time_hours
FROM support_tickets t
WHERE DATE(created_at) = target_date
ON CONFLICT (date) DO UPDATE SET
total_tickets = EXCLUDED.total_tickets,
open_tickets = EXCLUDED.open_tickets,
resolved_tickets = EXCLUDED.resolved_tickets,
avg_resolution_time_hours = EXCLUDED.avg_resolution_time_hours,
avg_response_time_hours = EXCLUDED.avg_response_time_hours;
END;
$$ language 'plpgsql';
-- Insert default categories
INSERT INTO support_categories (name, description, color, icon) VALUES
('migration', 'QuickBooks migration related issues', '#10B981', 'migration'),
('technical', 'Technical problems and bugs', '#EF4444', 'bug'),
('billing', 'Billing and payment questions', '#F59E0B', 'credit-card'),
('general', 'General questions and support', '#3B82F6', 'help-circle')
ON CONFLICT (name) DO NOTHING;
-- Insert default priorities
INSERT INTO support_priorities (name, description, color, sla_hours) VALUES
('low', 'Low priority issues', '#10B981', 72),
('medium', 'Medium priority issues', '#F59E0B', 24),
('high', 'High priority issues', '#EF4444', 4),
('urgent', 'Urgent issues requiring immediate attention', '#DC2626', 1)
ON CONFLICT (name) DO NOTHING;
-- Insert default team members
INSERT INTO support_team (name, email, role, specialties) VALUES
('Sarah Mitchell', 'sarah@escaperamp.com', 'senior_support', ARRAY['migration', 'technical']),
('Thomas Chen', 'thomas@escaperamp.com', 'support_agent', ARRAY['technical', 'billing']),
('Dante Rodriguez', 'dante@escaperamp.com', 'support_agent', ARRAY['migration', 'general'])
ON CONFLICT (email) DO NOTHING;
-- Row Level Security (RLS) policies
ALTER TABLE support_tickets ENABLE ROW LEVEL SECURITY;
ALTER TABLE support_messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE support_attachments ENABLE ROW LEVEL SECURITY;
-- Users can only see their own tickets
CREATE POLICY "Users can view own tickets" ON support_tickets
FOR SELECT USING (user_email = current_setting('request.jwt.claims', true)::json->>'email');
-- Users can create tickets
CREATE POLICY "Users can create tickets" ON support_tickets
FOR INSERT WITH CHECK (user_email = current_setting('request.jwt.claims', true)::json->>'email');
-- Users can update their own tickets
CREATE POLICY "Users can update own tickets" ON support_tickets
FOR UPDATE USING (user_email = current_setting('request.jwt.claims', true)::json->>'email');
-- Users can only see messages for their tickets
CREATE POLICY "Users can view own ticket messages" ON support_messages
FOR SELECT USING (
ticket_id IN (
SELECT id FROM support_tickets
WHERE user_email = current_setting('request.jwt.claims', true)::json->>'email'
)
);
-- Users can create messages for their tickets
CREATE POLICY "Users can create messages for own tickets" ON support_messages
FOR INSERT WITH CHECK (
ticket_id IN (
SELECT id FROM support_tickets
WHERE user_email = current_setting('request.jwt.claims', true)::json->>'email'
)
);
-- Users can only see attachments for their tickets
CREATE POLICY "Users can view own ticket attachments" ON support_attachments
FOR SELECT USING (
ticket_id IN (
SELECT id FROM support_tickets
WHERE user_email = current_setting('request.jwt.claims', true)::json->>'email'
)
);
-- Users can upload attachments for their tickets
CREATE POLICY "Users can upload attachments for own tickets" ON support_attachments
FOR INSERT WITH CHECK (
ticket_id IN (
SELECT id FROM support_tickets
WHERE user_email = current_setting('request.jwt.claims', true)::json->>'email'
)
);