Files
backend/database/migrations/064_workshops.sql
T
Alireza HassaniandCursor c7d5204b49 Add workshops CMS API with schedule, registrations, and public storefront routes.
Includes SQL migrations, Prisma models, dashboard CRUD, registration status workflow, and website OpenAPI updates for workshop listing and signup.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 12:22:24 +03:30

89 lines
3.7 KiB
SQL

-- Workshops module (rich text + image gallery + customer registration)
ALTER TYPE media_entity_type ADD VALUE IF NOT EXISTS 'workshop';
CREATE TABLE workshops (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
business_id BIGINT NOT NULL,
author_id BIGINT,
title VARCHAR(255) NOT NULL,
title_fa VARCHAR(255),
title_en VARCHAR(255),
slug VARCHAR(255) NOT NULL,
excerpt TEXT,
content JSONB NOT NULL DEFAULT '{}',
status content_status NOT NULL DEFAULT 'draft',
featured_media_id BIGINT,
published_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT workshops_business_slug_unique UNIQUE (business_id, slug),
CONSTRAINT workshops_business_id_fkey
FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE,
CONSTRAINT workshops_author_id_fkey
FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE SET NULL,
CONSTRAINT workshops_featured_media_id_fkey
FOREIGN KEY (featured_media_id) REFERENCES media (id) ON DELETE SET NULL
);
CREATE INDEX idx_workshops_business_id ON workshops (business_id);
CREATE INDEX idx_workshops_business_status ON workshops (business_id, status);
CREATE INDEX idx_workshops_business_published ON workshops (business_id, published_at DESC);
CREATE INDEX idx_workshops_author_id ON workshops (author_id);
CREATE TRIGGER workshops_set_updated_at
BEFORE UPDATE ON workshops
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
CREATE TABLE workshop_registrations (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
business_id BIGINT NOT NULL,
workshop_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT workshop_registrations_workshop_user_unique UNIQUE (workshop_id, user_id),
CONSTRAINT workshop_registrations_business_id_fkey
FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE,
CONSTRAINT workshop_registrations_workshop_id_fkey
FOREIGN KEY (workshop_id) REFERENCES workshops (id) ON DELETE CASCADE,
CONSTRAINT workshop_registrations_user_id_fkey
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);
CREATE INDEX idx_workshop_registrations_business_id ON workshop_registrations (business_id);
CREATE INDEX idx_workshop_registrations_workshop_id ON workshop_registrations (workshop_id);
CREATE INDEX idx_workshop_registrations_user_id ON workshop_registrations (user_id);
INSERT INTO permissions (name, slug, group_name, description) VALUES
('View workshops', 'workshops.read', 'workshops', 'View workshops'),
('Create workshops', 'workshops.create', 'workshops', 'Create workshops'),
('Update workshops', 'workshops.update', 'workshops', 'Edit workshops'),
('Delete workshops', 'workshops.delete', 'workshops', 'Delete workshops'),
('Publish workshops', 'workshops.publish', 'workshops', 'Publish and unpublish workshops')
ON CONFLICT (slug) DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug LIKE 'workshops.%'
WHERE r.slug IN ('business_owner', 'owner', 'admin')
ON CONFLICT DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug IN ('workshops.read', 'workshops.create', 'workshops.update', 'workshops.publish')
WHERE r.slug = 'editor'
ON CONFLICT DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug = 'workshops.read'
WHERE r.slug = 'viewer'
ON CONFLICT DO NOTHING;