-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsupabase-schema.sql
More file actions
282 lines (254 loc) Β· 11.6 KB
/
Copy pathsupabase-schema.sql
File metadata and controls
282 lines (254 loc) Β· 11.6 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
-- ============================================================
-- TaskCloud Database Schema
-- Run this in your Supabase SQL editor
-- ============================================================
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- ============================================================
-- PROFILES (extends Supabase auth.users)
-- ============================================================
CREATE TABLE profiles (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
full_name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
country TEXT DEFAULT 'Kenya',
avatar_url TEXT,
bio TEXT,
skills TEXT[], -- array of skill tags
rating DECIMAL(3,2) DEFAULT 0.00,
total_reviews INTEGER DEFAULT 0,
tasks_completed INTEGER DEFAULT 0,
tasks_posted INTEGER DEFAULT 0,
wallet_balance DECIMAL(12,2) DEFAULT 0.00,
wallet_currency TEXT DEFAULT 'KES',
is_verified BOOLEAN DEFAULT false,
role TEXT DEFAULT 'both', -- 'tasker', 'client', 'both'
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- TASKS
-- ============================================================
CREATE TABLE tasks (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL,
category TEXT NOT NULL,
budget DECIMAL(12,2) NOT NULL,
currency TEXT DEFAULT 'KES',
deadline DATE NOT NULL,
status TEXT DEFAULT 'open', -- 'open', 'assigned', 'in_progress', 'submitted', 'completed', 'cancelled', 'disputed'
poster_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
assignee_id UUID REFERENCES profiles(id),
required_skills TEXT[],
attachments TEXT[], -- array of file URLs
max_applications INTEGER DEFAULT 10,
application_count INTEGER DEFAULT 0,
is_featured BOOLEAN DEFAULT false,
views INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- TASK APPLICATIONS (Bids)
-- ============================================================
CREATE TABLE applications (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE NOT NULL,
applicant_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
cover_letter TEXT NOT NULL,
proposed_amount DECIMAL(12,2),
proposed_deadline DATE,
status TEXT DEFAULT 'pending', -- 'pending', 'accepted', 'rejected', 'withdrawn'
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(task_id, applicant_id)
);
-- ============================================================
-- SUBMISSIONS
-- ============================================================
CREATE TABLE submissions (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE NOT NULL,
tasker_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
content TEXT NOT NULL,
attachments TEXT[],
status TEXT DEFAULT 'pending', -- 'pending', 'approved', 'rejected', 'revision_requested'
feedback TEXT,
revision_count INTEGER DEFAULT 0,
submitted_at TIMESTAMPTZ DEFAULT NOW(),
reviewed_at TIMESTAMPTZ
);
-- ============================================================
-- MESSAGES
-- ============================================================
CREATE TABLE messages (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
sender_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
receiver_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
content TEXT NOT NULL,
is_read BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- REVIEWS
-- ============================================================
CREATE TABLE reviews (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE NOT NULL,
reviewer_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
reviewee_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
rating INTEGER CHECK (rating >= 1 AND rating <= 5) NOT NULL,
comment TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(task_id, reviewer_id)
);
-- ============================================================
-- WALLET TRANSACTIONS
-- ============================================================
CREATE TABLE transactions (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
type TEXT NOT NULL, -- 'deposit', 'withdrawal', 'payment_sent', 'payment_received', 'refund', 'fee'
amount DECIMAL(12,2) NOT NULL,
currency TEXT DEFAULT 'KES',
status TEXT DEFAULT 'pending', -- 'pending', 'completed', 'failed'
reference TEXT UNIQUE, -- Paystack reference
task_id UUID REFERENCES tasks(id),
description TEXT,
paystack_data JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- NOTIFICATIONS
-- ============================================================
CREATE TABLE notifications (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
type TEXT NOT NULL, -- 'new_application', 'application_accepted', 'task_assigned', 'submission_received', 'payment_received', 'message', 'review'
title TEXT NOT NULL,
body TEXT NOT NULL,
data JSONB,
is_read BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ============================================================
-- CATEGORIES (for reference)
-- ============================================================
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
slug TEXT UNIQUE NOT NULL,
icon TEXT,
description TEXT,
task_count INTEGER DEFAULT 0
);
INSERT INTO categories (name, slug, icon, description) VALUES
('Research', 'research', 'π¬', 'Academic and market research tasks'),
('Writing', 'writing', 'βοΈ', 'Content writing, copywriting, and editing'),
('Design', 'design', 'π¨', 'Graphic design, UI/UX, and creative work'),
('Data Entry', 'data-entry', 'π', 'Data collection, cleaning, and entry tasks'),
('Surveys', 'surveys', 'π', 'Survey creation and participation'),
('Digital Marketing', 'digital-marketing', 'π’', 'Social media, SEO, and marketing tasks'),
('Business Support', 'business-support', 'πΌ', 'Administrative and business tasks'),
('Academic Assistance', 'academic', 'π', 'Tutoring and academic task support'),
('Translation', 'translation', 'π', 'Language translation and interpretation'),
('Tech Support', 'tech-support', 'π»', 'Technical assistance and IT tasks'),
('Video & Audio', 'video-audio', 'π₯', 'Video editing, voiceovers, and audio tasks'),
('General', 'general', 'β‘', 'Miscellaneous tasks');
-- ============================================================
-- ROW LEVEL SECURITY
-- ============================================================
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;
ALTER TABLE applications ENABLE ROW LEVEL SECURITY;
ALTER TABLE submissions ENABLE ROW LEVEL SECURITY;
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE reviews ENABLE ROW LEVEL SECURITY;
ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;
ALTER TABLE notifications ENABLE ROW LEVEL SECURITY;
-- Profiles: public read, own write
CREATE POLICY "Profiles are viewable by everyone" ON profiles FOR SELECT USING (true);
CREATE POLICY "Users can update own profile" ON profiles FOR UPDATE USING (auth.uid() = id);
CREATE POLICY "Users can insert own profile" ON profiles FOR INSERT WITH CHECK (auth.uid() = id);
-- Tasks: public read, authenticated write
CREATE POLICY "Tasks are viewable by everyone" ON tasks FOR SELECT USING (true);
CREATE POLICY "Authenticated users can create tasks" ON tasks FOR INSERT WITH CHECK (auth.uid() = poster_id);
CREATE POLICY "Poster can update own tasks" ON tasks FOR UPDATE USING (auth.uid() = poster_id);
-- Applications: own read, authenticated write
CREATE POLICY "Users can view own applications" ON applications FOR SELECT USING (auth.uid() = applicant_id OR auth.uid() IN (SELECT poster_id FROM tasks WHERE id = task_id));
CREATE POLICY "Authenticated users can apply" ON applications FOR INSERT WITH CHECK (auth.uid() = applicant_id);
CREATE POLICY "Users can update own applications" ON applications FOR UPDATE USING (auth.uid() = applicant_id OR auth.uid() IN (SELECT poster_id FROM tasks WHERE id = task_id));
-- Messages: participants can read
CREATE POLICY "Message participants can view" ON messages FOR SELECT USING (auth.uid() = sender_id OR auth.uid() = receiver_id);
CREATE POLICY "Authenticated users can send messages" ON messages FOR INSERT WITH CHECK (auth.uid() = sender_id);
CREATE POLICY "Receivers can mark as read" ON messages FOR UPDATE USING (auth.uid() = receiver_id);
-- Reviews: public read, authenticated write
CREATE POLICY "Reviews are public" ON reviews FOR SELECT USING (true);
CREATE POLICY "Task participants can review" ON reviews FOR INSERT WITH CHECK (auth.uid() = reviewer_id);
-- Transactions: own only
CREATE POLICY "Users can view own transactions" ON transactions FOR SELECT USING (auth.uid() = user_id);
-- Notifications: own only
CREATE POLICY "Users can view own notifications" ON notifications FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "Users can update own notifications" ON notifications FOR UPDATE USING (auth.uid() = user_id);
-- ============================================================
-- FUNCTIONS & TRIGGERS
-- ============================================================
-- Auto-create profile on signup
CREATE OR REPLACE FUNCTION handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO profiles (id, email, full_name, phone, country)
VALUES (
NEW.id,
NEW.email,
COALESCE(NEW.raw_user_meta_data->>'full_name', 'User'),
NEW.raw_user_meta_data->>'phone',
COALESCE(NEW.raw_user_meta_data->>'country', 'Kenya')
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION handle_new_user();
-- Update task application count
CREATE OR REPLACE FUNCTION update_application_count()
RETURNS TRIGGER AS $$
BEGIN
UPDATE tasks SET application_count = (
SELECT COUNT(*) FROM applications WHERE task_id = NEW.task_id AND status != 'withdrawn'
) WHERE id = NEW.task_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER on_application_change
AFTER INSERT OR UPDATE ON applications
FOR EACH ROW EXECUTE FUNCTION update_application_count();
-- Update profile rating after review
CREATE OR REPLACE FUNCTION update_profile_rating()
RETURNS TRIGGER AS $$
BEGIN
UPDATE profiles SET
rating = (SELECT AVG(rating) FROM reviews WHERE reviewee_id = NEW.reviewee_id),
total_reviews = (SELECT COUNT(*) FROM reviews WHERE reviewee_id = NEW.reviewee_id)
WHERE id = NEW.reviewee_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER on_review_created
AFTER INSERT ON reviews
FOR EACH ROW EXECUTE FUNCTION update_profile_rating();
-- Updated at timestamp
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER update_tasks_updated_at BEFORE UPDATE ON tasks FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_profiles_updated_at BEFORE UPDATE ON profiles FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_applications_updated_at BEFORE UPDATE ON applications FOR EACH ROW EXECUTE FUNCTION update_updated_at();