-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSQL.sql
568 lines (518 loc) · 17.7 KB
/
SQL.sql
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
-- Enable necessary extensions
create extension if not exists "uuid-ossp";
-- Create enum types
create type subscription_tier as enum ('free', 'starter', 'professional', 'enterprise');
create type menu_layout as enum ('grid', 'list', 'compact');
-- Create organizations table
create table organizations (
id uuid primary key default uuid_generate_v4(),
name text not null,
subdomain text unique not null,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
business_phone text,
address text,
city text,
state text,
zipcode text,
subscription_tier subscription_tier default 'free',
subscription_status text default 'active',
stripe_customer_id text unique,
stripe_subscription_id text unique,
max_menus integer default 1,
max_qr_codes integer default 5,
max_views integer default 1000,
max_storage_gb integer default 1
);
-- Create organization_members table to link users to organizations
create table organization_members (
id uuid primary key default uuid_generate_v4(),
organization_id uuid references organizations(id) on delete cascade,
user_id uuid references auth.users(id) on delete cascade,
role text not null default 'member',
created_at timestamp with time zone default now(),
unique(organization_id, user_id)
);
-- Create business_hours table
create table business_hours (
id uuid primary key default uuid_generate_v4(),
organization_id uuid references organizations(id) on delete cascade,
day_of_week integer not null check (day_of_week between 0 and 6),
open_time time,
close_time time,
is_closed boolean default false,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
unique(organization_id, day_of_week)
);
-- Create menus table
create table menus (
id uuid primary key default uuid_generate_v4(),
organization_id uuid references organizations(id) on delete cascade,
name text not null,
description text,
image_url text,
is_listed boolean default true,
start_time time,
end_time time,
available_days integer[] default array[0,1,2,3,4,5,6],
layout menu_layout default 'grid',
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
seo_title text,
seo_description text,
custom_css text,
custom_js text,
custom_html text
);
-- Create categories table
create table categories (
id uuid primary key default uuid_generate_v4(),
menu_id uuid references menus(id) on delete cascade,
name text not null,
description text,
display_order integer default 0,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now()
);
-- Create items table
create table items (
id uuid primary key default uuid_generate_v4(),
category_id uuid references categories(id) on delete cascade,
name text not null,
description text,
price decimal(10,2) not null,
image_url text,
ingredients text[],
allergens text[],
calories integer,
display_order integer default 0,
is_available boolean default true,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
preparation_time interval,
spiciness_level integer check (spiciness_level between 0 and 5),
dietary_flags text[] default array[]::text[],
custom_fields jsonb default '{}'::jsonb
);
-- Create qr_codes table
create table qr_codes (
id uuid primary key default uuid_generate_v4(),
menu_id uuid references menus(id) on delete cascade,
created_at timestamp with time zone default now(),
last_regenerated_at timestamp with time zone default now()
);
-- Create menu_views table for analytics
create table menu_views (
id uuid primary key default uuid_generate_v4(),
menu_id uuid references menus(id) on delete cascade,
item_id uuid references items(id) on delete set null,
device_type text,
view_duration integer, -- in seconds
created_at timestamp with time zone default now()
);
-- Create branding_settings table
create table branding_settings (
id uuid primary key default uuid_generate_v4(),
organization_id uuid references organizations(id) on delete cascade,
primary_color text default '#adfa1d',
logo_url text,
favicon_url text,
font_family text default 'Inter',
custom_css text,
business_page_settings jsonb default '{
"heroImages": [],
"backgroundColor": "#ffffff",
"textColor": "#000000",
"showSocialLinks": true
}'::jsonb,
menu_selection_settings jsonb default '{
"layout": "grid",
"showDescriptions": true,
"showImages": true,
"backgroundColor": "#ffffff"
}'::jsonb,
menu_page_settings jsonb default '{
"layout": "grid",
"showImages": true,
"showDescriptions": true,
"showAllergies": true,
"showNutrition": true,
"backgroundColor": "#ffffff"
}'::jsonb,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
unique(organization_id)
);
-- Create dashboard_widgets table
create table dashboard_widgets (
id uuid primary key default uuid_generate_v4(),
organization_id uuid references organizations(id) on delete cascade,
enabled_widgets text[] default array[
'total-menus',
'active-qr-codes',
'total-views',
'revenue',
'avg-time',
'menu-item-views',
'return-visitors',
'peak-hours',
'language-preferences',
'menu-categories',
'dietary-filters',
'search-usage',
'views-chart',
'device-usage',
'menu-views-distribution'
],
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
unique(organization_id)
);
-- Create RLS policies
alter table organizations enable row level security;
alter table organization_members enable row level security;
alter table business_hours enable row level security;
alter table menus enable row level security;
alter table categories enable row level security;
alter table items enable row level security;
alter table qr_codes enable row level security;
alter table menu_views enable row level security;
alter table branding_settings enable row level security;
alter table dashboard_widgets enable row level security;
-- Create policies for organizations
create policy "Users can view their organizations"
on organizations for select
using (
id in (
select organization_id
from organization_members
where user_id = auth.uid()
)
);
create policy "Users can create organizations"
on organizations for insert
with check (true);
create policy "Organization members can update their organization"
on organizations for update
using (
id in (
select organization_id
from organization_members
where user_id = auth.uid()
)
);
-- Add similar policies for other tables...
-- Create functions
create or replace function public.handle_new_user()
returns trigger as $$
begin
insert into public.organizations (name, subdomain)
values (
new.raw_user_meta_data->>'business_name',
lower(regexp_replace(new.raw_user_meta_data->>'business_name', '[^a-zA-Z0-9]', '-', 'g'))
)
returning id into new.organization_id;
insert into public.organization_members (organization_id, user_id, role)
values (new.organization_id, new.id, 'owner');
return new;
end;
$$ language plpgsql security definer;
-- Create trigger for new user registration
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();
-- Create indexes for better performance
create index idx_organization_members_user_id on organization_members(user_id);
create index idx_organization_members_org_id on organization_members(organization_id);
create index idx_menus_organization_id on menus(organization_id);
create index idx_items_category_id on items(category_id);
create index idx_menu_views_menu_id on menu_views(menu_id);
create index idx_menu_views_created_at on menu_views(created_at);
-- Add these after the existing tables but before the RLS policies
-- Create a function to check menu count limits
create or replace function check_menu_limit(org_id uuid)
returns boolean as $$
declare
menu_count integer;
tier subscription_tier;
begin
-- Get organization's subscription tier
select subscription_tier into tier
from organizations
where id = org_id;
-- Get current menu count
select count(*) into menu_count
from menus
where organization_id = org_id;
-- Check limits based on tier
case tier
when 'starter' then
return menu_count < 1;
when 'professional' then
return menu_count < 5;
when 'enterprise' then
return true;
else
return menu_count < 1;
end case;
end;
$$ language plpgsql security definer;
-- Create a function to get allowed widgets based on tier
create or replace function get_allowed_widgets(org_id uuid)
returns text[] as $$
declare
tier subscription_tier;
begin
-- Get organization's subscription tier
select subscription_tier into tier
from organizations
where id = org_id;
-- Return allowed widgets based on tier
case tier
when 'starter' then
return array[
'views-chart',
'device-usage',
'menu-views-distribution'
];
when 'professional', 'enterprise' then
return array[
'total-menus',
'active-qr-codes',
'total-views',
'revenue',
'avg-time',
'menu-item-views',
'return-visitors',
'peak-hours',
'language-preferences',
'menu-categories',
'dietary-filters',
'search-usage',
'views-chart',
'device-usage',
'menu-views-distribution'
];
else
return array[
'views-chart',
'device-usage',
'menu-views-distribution'
];
end case;
end;
$$ language plpgsql security definer;
-- Modify the dashboard_widgets table default to use the function
alter table dashboard_widgets
alter column enabled_widgets
set default array[
'views-chart',
'device-usage',
'menu-views-distribution'
];
-- Add policy for menu creation with tier limits
create policy "Check menu limits on insert"
on menus
for insert
with check (
check_menu_limit(organization_id)
);
-- Add trigger to enforce widget restrictions
create or replace function enforce_widget_restrictions()
returns trigger as $$
declare
allowed_widgets text[];
begin
-- Get allowed widgets for the organization
allowed_widgets := get_allowed_widgets(new.organization_id);
-- Filter out any widgets that aren't allowed for the tier
new.enabled_widgets := array(
select unnest(new.enabled_widgets)
intersect
select unnest(allowed_widgets)
);
return new;
end;
$$ language plpgsql security definer;
-- Create trigger for widget restrictions
create trigger enforce_widget_restrictions_trigger
before insert or update on dashboard_widgets
for each row execute procedure enforce_widget_restrictions();
-- Add function to handle subscription tier changes
create or replace function handle_subscription_tier_change()
returns trigger as $$
begin
-- Update organization limits based on new tier
case new.subscription_tier
when 'starter' then
new.max_menus := 1;
new.max_qr_codes := 5;
new.max_views := 1000;
new.max_storage_gb := 1;
when 'professional' then
new.max_menus := 5;
new.max_qr_codes := 25;
new.max_views := 5000;
new.max_storage_gb := 5;
when 'enterprise' then
new.max_menus := null; -- unlimited
new.max_qr_codes := null; -- unlimited
new.max_views := null; -- unlimited
new.max_storage_gb := 25;
end case;
return new;
end;
$$ language plpgsql security definer;
-- Create trigger for subscription tier changes
drop trigger if exists on_subscription_tier_change on organizations;
create trigger on_subscription_tier_change
before update of subscription_tier on organizations
for each row
when (old.subscription_tier is distinct from new.subscription_tier)
execute procedure handle_subscription_tier_change();
-- Create new table for menu translations
create table menu_translations (
id uuid primary key default uuid_generate_v4(),
menu_id uuid references menus(id) on delete cascade,
language_code text not null,
name text not null,
description text,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
unique(menu_id, language_code)
);
-- Create new table for item translations
create table item_translations (
id uuid primary key default uuid_generate_v4(),
item_id uuid references items(id) on delete cascade,
language_code text not null,
name text not null,
description text,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
unique(item_id, language_code)
);
-- Create new table for menu access logs
create table menu_access_logs (
id uuid primary key default uuid_generate_v4(),
menu_id uuid references menus(id) on delete cascade,
ip_address text,
user_agent text,
referer text,
language_code text,
device_type text,
created_at timestamp with time zone default now()
);
-- Create new table for menu feedback
create table menu_feedback (
id uuid primary key default uuid_generate_v4(),
menu_id uuid references menus(id) on delete cascade,
rating integer check (rating between 1 and 5),
comment text,
created_at timestamp with time zone default now()
);
-- Create new table for menu revisions
create table menu_revisions (
id uuid primary key default uuid_generate_v4(),
menu_id uuid references menus(id) on delete cascade,
data jsonb not null,
created_by uuid references auth.users(id) on delete set null,
created_at timestamp with time zone default now()
);
-- Add RLS policies for new tables
alter table menu_translations enable row level security;
alter table item_translations enable row level security;
alter table menu_access_logs enable row level security;
alter table menu_feedback enable row level security;
alter table menu_revisions enable row level security;
-- Create policies for menu translations
create policy "Users can view translations for their organizations' menus"
on menu_translations for select
using (
menu_id in (
select m.id
from menus m
join organizations o on m.organization_id = o.id
join organization_members om on o.id = om.organization_id
where om.user_id = auth.uid()
)
);
create policy "Users can create translations for their organizations' menus"
on menu_translations for insert
with check (
menu_id in (
select m.id
from menus m
join organizations o on m.organization_id = o.id
join organization_members om on o.id = om.organization_id
where om.user_id = auth.uid()
)
);
-- Create function to check translation limits
create or replace function check_translation_limit(org_id uuid, menu_id uuid)
returns boolean as $$
declare
translation_count integer;
tier subscription_tier;
begin
-- Get organization's subscription tier
select subscription_tier into tier
from organizations
where id = org_id;
-- Get current translation count for the menu
select count(*) into translation_count
from menu_translations mt
join menus m on mt.menu_id = m.id
where m.id = menu_id;
-- Check limits based on tier
case tier
when 'starter' then
return translation_count < 1;
when 'professional' then
return translation_count < 5;
when 'enterprise' then
return true;
else
return translation_count < 1;
end case;
end;
$$ language plpgsql security definer;
-- Create function to track menu views
create or replace function track_menu_view(
p_menu_id uuid,
p_ip_address text,
p_user_agent text,
p_referer text,
p_language_code text,
p_device_type text
)
returns void as $$
begin
insert into menu_access_logs (
menu_id,
ip_address,
user_agent,
referer,
language_code,
device_type
) values (
p_menu_id,
p_ip_address,
p_user_agent,
p_referer,
p_language_code,
p_device_type
);
-- Update menu views count
insert into menu_views (menu_id, device_type)
values (p_menu_id, p_device_type);
end;
$$ language plpgsql security definer;
-- Create indexes for better performance
create index idx_menu_translations_menu_id on menu_translations(menu_id);
create index idx_item_translations_item_id on item_translations(item_id);
create index idx_menu_access_logs_menu_id on menu_access_logs(menu_id);
create index idx_menu_access_logs_created_at on menu_access_logs(created_at);
create index idx_menu_feedback_menu_id on menu_feedback(menu_id);
create index idx_menu_revisions_menu_id on menu_revisions(menu_id);