11'use server' ;
22
33import { db , PolicyStatus } from '@db' ;
4+ import { sendPolicyNotificationEmail } from '@trycompai/email' ;
45import { revalidatePath , revalidateTag } from 'next/cache' ;
56import { z } from 'zod' ;
67import { authActionClient } from '../safe-action' ;
@@ -40,6 +41,13 @@ export const acceptRequestedPolicyChangesAction = authActionClient
4041 id,
4142 organizationId : session . activeOrganizationId ,
4243 } ,
44+ include : {
45+ organization : {
46+ select : {
47+ name : true ,
48+ } ,
49+ } ,
50+ } ,
4351 } ) ;
4452
4553 if ( ! policy ) {
@@ -50,7 +58,10 @@ export const acceptRequestedPolicyChangesAction = authActionClient
5058 throw new Error ( 'Approver is not the same' ) ;
5159 }
5260
53- // Update policy status
61+ // Check if there were previous signers to determine notification type
62+ const isNewPolicy = policy . lastPublishedAt === null ;
63+
64+ // Update policy status and clear signedBy field
5465 await db . policy . update ( {
5566 where : {
5667 id,
@@ -59,9 +70,79 @@ export const acceptRequestedPolicyChangesAction = authActionClient
5970 data : {
6071 status : PolicyStatus . published ,
6172 approverId : null ,
73+ signedBy : [ ] , // Clear the signedBy field
74+ lastPublishedAt : new Date ( ) , // Update last published date
6275 } ,
6376 } ) ;
6477
78+ // Get all employees in the organization to send notifications
79+ const employees = await db . member . findMany ( {
80+ where : {
81+ organizationId : session . activeOrganizationId ,
82+ isActive : true ,
83+ } ,
84+ include : {
85+ user : true ,
86+ } ,
87+ } ) ;
88+
89+ // Filter to get only employees
90+ const employeeMembers = employees . filter ( ( member ) => {
91+ const roles = member . role . includes ( ',' ) ? member . role . split ( ',' ) : [ member . role ] ;
92+ return roles . includes ( 'employee' ) ;
93+ } ) ;
94+
95+ // Send notification emails to all employees
96+ // Send emails in batches of 2 per second to respect rate limit
97+ const BATCH_SIZE = 2 ;
98+ const delay = ( ms : number ) => new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
99+
100+ const sendEmailsInBatches = async ( ) => {
101+ for ( let i = 0 ; i < employeeMembers . length ; i += BATCH_SIZE ) {
102+ const batch = employeeMembers . slice ( i , i + BATCH_SIZE ) ;
103+
104+ await Promise . all (
105+ batch . map ( async ( employee ) => {
106+ if ( ! employee . user . email ) return ;
107+
108+ let notificationType : 'new' | 're-acceptance' | 'updated' ;
109+ const wasAlreadySigned = policy . signedBy . includes ( employee . id ) ;
110+ if ( isNewPolicy ) {
111+ notificationType = 'new' ;
112+ } else if ( wasAlreadySigned ) {
113+ notificationType = 're-acceptance' ;
114+ } else {
115+ notificationType = 'updated' ;
116+ }
117+
118+ try {
119+ await sendPolicyNotificationEmail ( {
120+ email : employee . user . email ,
121+ userName : employee . user . name || employee . user . email || 'Employee' ,
122+ policyName : policy . name ,
123+ organizationName : policy . organization . name ,
124+ organizationId : session . activeOrganizationId ,
125+ notificationType,
126+ } ) ;
127+ } catch ( emailError ) {
128+ console . error ( `Failed to send email to ${ employee . user . email } :` , emailError ) ;
129+ // Don't fail the whole operation if email fails
130+ }
131+ } ) ,
132+ ) ;
133+
134+ // Only delay if there are more emails to send
135+ if ( i + BATCH_SIZE < employeeMembers . length ) {
136+ await delay ( 1000 ) ; // wait 1 second between batches
137+ }
138+ }
139+ } ;
140+
141+ // Fire and forget, but log errors if any
142+ sendEmailsInBatches ( ) . catch ( ( error ) => {
143+ console . error ( 'Some emails failed to send:' , error ) ;
144+ } ) ;
145+
65146 // If a comment was provided, create a comment
66147 if ( comment && comment . trim ( ) !== '' ) {
67148 const member = await db . member . findFirst ( {
0 commit comments