feat: add shared notification, communication, and export services for bookings, courses, and events; introduce btree_gist extension and new booking atomic function
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import 'server-only';
|
||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import type { Database } from '@kit/supabase/database';
|
||||
|
||||
interface CommunicationListOptions {
|
||||
type?: string;
|
||||
direction?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
interface CreateCommunicationInput {
|
||||
accountId: string;
|
||||
entityId: string;
|
||||
type: string;
|
||||
direction?: string;
|
||||
subject?: string;
|
||||
body?: string;
|
||||
emailTo?: string;
|
||||
emailCc?: string;
|
||||
attachmentPaths?: string[];
|
||||
}
|
||||
|
||||
export function createEventCommunicationService(
|
||||
client: SupabaseClient<Database>,
|
||||
) {
|
||||
return new EventCommunicationService(client);
|
||||
}
|
||||
|
||||
class EventCommunicationService {
|
||||
constructor(private readonly client: SupabaseClient<Database>) {}
|
||||
|
||||
async list(
|
||||
eventId: string,
|
||||
accountId: string,
|
||||
opts?: CommunicationListOptions,
|
||||
) {
|
||||
let query = (this.client.from as CallableFunction)('module_communications')
|
||||
.select('*', { count: 'exact' })
|
||||
.eq('module', 'events')
|
||||
.eq('entity_id', eventId)
|
||||
.eq('account_id', accountId)
|
||||
.order('created_at', { ascending: false });
|
||||
|
||||
if (opts?.type) query = query.eq('type', opts.type);
|
||||
if (opts?.direction) query = query.eq('direction', opts.direction);
|
||||
if (opts?.search) {
|
||||
const escaped = opts.search.replace(/[%_\\]/g, '\\$&');
|
||||
query = query.or(`subject.ilike.%${escaped}%,body.ilike.%${escaped}%`);
|
||||
}
|
||||
|
||||
const page = opts?.page ?? 1;
|
||||
const pageSize = opts?.pageSize ?? 25;
|
||||
query = query.range((page - 1) * pageSize, page * pageSize - 1);
|
||||
|
||||
const { data, error, count } = await query;
|
||||
if (error) throw error;
|
||||
return { data: data ?? [], total: count ?? 0, page, pageSize };
|
||||
}
|
||||
|
||||
async create(input: CreateCommunicationInput, userId: string) {
|
||||
const { data, error } = await (this.client.from as CallableFunction)(
|
||||
'module_communications',
|
||||
)
|
||||
.insert({
|
||||
account_id: input.accountId,
|
||||
module: 'events',
|
||||
entity_id: input.entityId,
|
||||
type: input.type,
|
||||
direction: input.direction ?? 'internal',
|
||||
subject: input.subject ?? null,
|
||||
body: input.body ?? null,
|
||||
email_to: input.emailTo ?? null,
|
||||
email_cc: input.emailCc ?? null,
|
||||
attachment_paths: input.attachmentPaths ?? null,
|
||||
created_by: userId,
|
||||
})
|
||||
.select(
|
||||
'id, account_id, module, entity_id, type, direction, subject, email_to, created_at, created_by',
|
||||
)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
return data;
|
||||
}
|
||||
|
||||
async delete(communicationId: string) {
|
||||
const { error } = await (this.client.from as CallableFunction)(
|
||||
'module_communications',
|
||||
)
|
||||
.delete()
|
||||
.eq('id', communicationId)
|
||||
.eq('module', 'events');
|
||||
|
||||
if (error) throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user