MED-88: add doctor email notifications (#65)
* MED-88: add doctor email notifications * add logging, send open jobs notification on partial analysis response * update permissions * fix import, permissions * casing, let email be null * unused import
This commit is contained in:
@@ -41,3 +41,43 @@ export async function getAccountAdmin({
|
||||
|
||||
return data as unknown as AccountWithMemberships;
|
||||
}
|
||||
|
||||
export async function getDoctorAccounts() {
|
||||
const { data } = await getSupabaseServerAdminClient()
|
||||
.schema('medreport')
|
||||
.from('accounts')
|
||||
.select('id, email, name, last_name, preferred_locale')
|
||||
.eq('is_personal_account', true)
|
||||
.eq('application_role', 'doctor')
|
||||
.throwOnError();
|
||||
|
||||
return data?.map(({ id, email, name, last_name, preferred_locale }) => ({
|
||||
id,
|
||||
email,
|
||||
name,
|
||||
lastName: last_name,
|
||||
preferredLocale: preferred_locale,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function getAssignedDoctorAccount(analysisOrderId: number) {
|
||||
const { data: doctorUser } = await getSupabaseServerAdminClient()
|
||||
.schema('medreport')
|
||||
.from('doctor_analysis_feedback')
|
||||
.select('doctor_user_id')
|
||||
.eq('analysis_order_id', analysisOrderId)
|
||||
.throwOnError();
|
||||
|
||||
const doctorData = doctorUser[0];
|
||||
if (!doctorData || !doctorData.doctor_user_id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data } = await getSupabaseServerAdminClient()
|
||||
.schema('medreport')
|
||||
.from('accounts')
|
||||
.select('email')
|
||||
.eq('primary_owner_user_id', doctorData.doctor_user_id);
|
||||
|
||||
return { email: data?.[0]?.email };
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Database } from '@kit/supabase/database';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
||||
|
||||
export enum NotificationAction {
|
||||
DOCTOR_FEEDBACK_RECEIVED = 'DOCTOR_FEEDBACK_RECEIVED',
|
||||
NEW_JOBS_ALERT = 'NEW_JOBS_ALERT',
|
||||
PATIENT_RESULTS_RECEIVED_ALERT = 'PATIENT_RESULTS_RECEIVED_ALERT',
|
||||
}
|
||||
|
||||
export const createNotificationLog = async ({
|
||||
@@ -17,7 +19,7 @@ export const createNotificationLog = async ({
|
||||
relatedRecordId?: string | number;
|
||||
}) => {
|
||||
try {
|
||||
const supabase = getSupabaseServerClient();
|
||||
const supabase = getSupabaseServerAdminClient();
|
||||
|
||||
await supabase
|
||||
.schema('audit')
|
||||
@@ -30,6 +32,6 @@ export const createNotificationLog = async ({
|
||||
})
|
||||
.throwOnError();
|
||||
} catch (error) {
|
||||
console.error('Failed to insert doctor page view log', error);
|
||||
console.error('Failed to insert doctor notification log', error);
|
||||
}
|
||||
};
|
||||
|
||||
32
lib/services/doctor-jobs.service.ts
Normal file
32
lib/services/doctor-jobs.service.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
||||
|
||||
async function getAssignedOrderIds() {
|
||||
const supabase = getSupabaseServerAdminClient();
|
||||
|
||||
const { data: assignedOrderIds } = await supabase
|
||||
.schema('medreport')
|
||||
.from('doctor_analysis_feedback')
|
||||
.select('analysis_order_id')
|
||||
.not('doctor_user_id', 'is', null)
|
||||
.throwOnError();
|
||||
|
||||
return assignedOrderIds?.map((f) => f.analysis_order_id) || [];
|
||||
}
|
||||
|
||||
export async function getOpenJobAnalysisResponseIds() {
|
||||
const supabase = getSupabaseServerAdminClient();
|
||||
const assignedIds = await getAssignedOrderIds();
|
||||
|
||||
let query = supabase
|
||||
.schema('medreport')
|
||||
.from('analysis_responses')
|
||||
.select('id, analysis_order_id')
|
||||
.order('created_at', { ascending: false });
|
||||
|
||||
if (assignedIds.length > 0) {
|
||||
query = query.not('analysis_order_id', 'in', `(${assignedIds.join(',')})`);
|
||||
}
|
||||
|
||||
const { data: analysisResponses } = await query.throwOnError();
|
||||
return analysisResponses?.map(({ id }) => id) || [];
|
||||
}
|
||||
@@ -1,50 +1,41 @@
|
||||
'use server';
|
||||
|
||||
import { CompanySubmitData } from '@/lib/types/company';
|
||||
import { emailSchema } from '@/lib/validations/email.schema';
|
||||
import { toArray } from '@/lib/utils';
|
||||
|
||||
import { renderDoctorSummaryReceivedEmail } from '@kit/email-templates';
|
||||
import { getMailer } from '@kit/mailers';
|
||||
import { enhanceAction } from '@kit/next/actions';
|
||||
import { getLogger } from '@kit/shared/logger';
|
||||
|
||||
export const sendDoctorSummaryCompletedEmail = async (
|
||||
language: string,
|
||||
recipientName: string,
|
||||
recipientEmail: string,
|
||||
orderNr: string,
|
||||
orderId: number,
|
||||
) => {
|
||||
const { html, subject } = await renderDoctorSummaryReceivedEmail({
|
||||
language,
|
||||
recipientName,
|
||||
recipientEmail,
|
||||
orderNr,
|
||||
orderId,
|
||||
});
|
||||
import { emailSchema } from '~/lib/validations/email.schema';
|
||||
|
||||
await sendEmail({
|
||||
subject,
|
||||
html,
|
||||
to: recipientEmail,
|
||||
});
|
||||
type EmailTemplate = {
|
||||
html: string;
|
||||
subject: string;
|
||||
};
|
||||
|
||||
export const sendCompanyOfferEmail = async (
|
||||
data: CompanySubmitData,
|
||||
language: string,
|
||||
) => {
|
||||
const { renderCompanyOfferEmail } = await import('@kit/email-templates');
|
||||
const { html, subject } = await renderCompanyOfferEmail({
|
||||
language,
|
||||
companyData: data,
|
||||
});
|
||||
type EmailRenderer<T = any> = (params: T) => Promise<EmailTemplate>;
|
||||
|
||||
await sendEmail({
|
||||
subject,
|
||||
html,
|
||||
to: process.env.CONTACT_EMAIL || '',
|
||||
});
|
||||
export const sendEmailFromTemplate = async <T>(
|
||||
renderer: EmailRenderer<T>,
|
||||
templateParams: T,
|
||||
recipients: string | string[],
|
||||
) => {
|
||||
const { html, subject } = await renderer(templateParams);
|
||||
|
||||
const recipientsArray = toArray(recipients);
|
||||
if (!recipientsArray.length) {
|
||||
throw new Error('No valid email recipients provided');
|
||||
}
|
||||
|
||||
const emailPromises = recipientsArray.map((email) =>
|
||||
sendEmail({
|
||||
subject,
|
||||
html,
|
||||
to: email,
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(emailPromises);
|
||||
};
|
||||
|
||||
export const sendEmail = enhanceAction(
|
||||
@@ -53,7 +44,7 @@ export const sendEmail = enhanceAction(
|
||||
const log = await getLogger();
|
||||
|
||||
if (!process.env.EMAIL_USER) {
|
||||
log.error('Sending email failed, as no sender found in env.')
|
||||
log.error('Sending email failed, as no sender was found in env.');
|
||||
throw new Error('No email user configured');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user