@@ -34,13 +34,15 @@ import {
3434 getUserGithubOAuthToken as dbGetUserGithubOAuthToken ,
3535 deleteUserGithubOAuthToken as dbDeleteUserGithubOAuthToken ,
3636 getTaskByUuid as dbGetTaskByUuid ,
37+ updateProjectDiscordSettings as dbUpdateProjectDiscordSettings ,
3738} from '@/lib/db' ;
3839import { z } from 'zod' ;
3940import { auth } from '@/lib/authEdge' ;
4041import { Octokit } from 'octokit' ;
4142import { Buffer } from 'buffer' ;
4243import { generateProjectScaffold , type GenerateProjectScaffoldInput , type GenerateProjectScaffoldOutput } from '@/ai/flows/generate-project-scaffold' ;
4344import { editFileContentWithAI , type EditFileContentAIInput , type EditFileContentAIOutput } from '@/ai/flows/edit-file-content-ai' ;
45+ import { sendDiscordNotification } from '@/lib/discord' ;
4446
4547
4648export async function fetchProjectAction ( uuid : string | undefined ) : Promise < Project | null > {
@@ -134,12 +136,12 @@ export async function inviteUserToProjectAction(
134136 formData : FormData
135137) : Promise < InviteUserFormState > {
136138 const session = await auth ( ) ;
137- if ( ! session ?. user ?. uuid ) {
138- console . error ( "[inviteUserToProjectAction] Authentication required. No session user UUID." ) ;
139+ if ( ! session ?. user ?. uuid || ! session . user . name ) {
140+ console . error ( "[inviteUserToProjectAction] Authentication required. No session user UUID or name ." ) ;
139141 return { error : "Authentication required." } ;
140142 }
141143 const inviterUserUuid = session . user . uuid ;
142- console . log ( "[inviteUserToProjectAction] Authenticated user for permission check:" , inviterUserUuid ) ;
144+ const inviterName = session . user . name ;
143145
144146 const validatedFields = InviteUserSchema . safeParse ( {
145147 projectUuid : formData . get ( 'projectUuid' ) ,
@@ -160,7 +162,6 @@ export async function inviteUserToProjectAction(
160162 if ( ! project ) return { error : "Project not found." } ;
161163
162164 const inviterRole = await dbGetProjectMemberRole ( projectUuid , inviterUserUuid ) ;
163- console . log ( `[inviteUserToProjectAction] Inviter role check for project ${ projectUuid } (user ${ inviterUserUuid } ): ${ inviterRole } ` ) ;
164165 if ( ! inviterRole || ! [ 'owner' , 'co-owner' ] . includes ( inviterRole ) ) {
165166 return { error : `You do not have permission to invite users to this project. Your role: ${ inviterRole || 'not a member' } . UUID: ${ inviterUserUuid } ` } ;
166167 }
@@ -200,7 +201,7 @@ export async function inviteUserToProjectAction(
200201 invitedUserGithubLogin = invitedUserOAuthDetails . login ;
201202 console . log ( `[inviteUserToProjectAction] Fetched GitHub login for invited user: ${ invitedUserGithubLogin } ` ) ;
202203 } else {
203- console . log ( `[inviteUserToProjectAction] Invited user ${ userToInvite . email } has not connected their GitHub account via OAuth or login not found.` ) ;
204+ console . log ( `[inviteUserToProjectAction] Invited user ${ userToInvite . email } has not connected their GitHub account or login not found.` ) ;
204205 }
205206
206207
@@ -238,6 +239,18 @@ export async function inviteUserToProjectAction(
238239 }
239240 }
240241
242+ if ( project . discordWebhookUrl && project . discordNotificationsEnabled ) {
243+ await sendDiscordNotification ( project . discordWebhookUrl , {
244+ embeds : [ {
245+ title : "👥 New Member Invited" ,
246+ description : `**${ userToInvite . name } ** was invited to the project as a **${ role } ** by **${ inviterName } **.` ,
247+ color : 3447003 , // Blue
248+ timestamp : new Date ( ) . toISOString ( ) ,
249+ footer : { text : `Project: ${ project . name } ` }
250+ } ]
251+ } ) ;
252+ }
253+
241254 return { message : `${ userToInvite . name } has been successfully ${ existingMembership ? 'updated to role' : 'invited as' } ${ role } .${ githubMessage } ` , invitedMember : newOrUpdatedMember } ;
242255 } catch ( error : any ) {
243256 console . error ( "Error inviting user:" , error ) ;
@@ -314,11 +327,11 @@ export interface CreateTaskFormState {
314327
315328export async function createTaskAction ( prevState : CreateTaskFormState , formData : FormData ) : Promise < CreateTaskFormState > {
316329 const session = await auth ( ) ;
317- if ( ! session ?. user ?. uuid ) {
318- console . error ( "[createTaskAction] Authentication required. No session user UUID." ) ;
330+ if ( ! session ?. user ?. uuid || ! session . user . name ) {
331+ console . error ( "[createTaskAction] Authentication required. No session user UUID or name ." ) ;
319332 return { error : "Authentication required." } ;
320333 }
321- console . log ( "[createTaskAction] Authenticated user:" , session . user . uuid ) ;
334+ const creatorName = session . user . name ;
322335
323336 const validatedFields = CreateTaskSchema . safeParse ( {
324337 projectUuid : formData . get ( 'projectUuid' ) ,
@@ -343,8 +356,10 @@ export async function createTaskAction(prevState: CreateTaskFormState, formData:
343356 }
344357
345358 try {
359+ const project = await dbGetProjectByUuid ( projectUuid ) ;
360+ if ( ! project ) return { error : "Project not found." } ;
361+
346362 const userRole = await dbGetProjectMemberRole ( projectUuid , session . user . uuid ) ;
347- console . log ( `[createTaskAction] User role for project ${ projectUuid } (user ${ session . user . uuid } ): ${ userRole } ` ) ;
348363 if ( ! userRole || ! [ 'owner' , 'co-owner' , 'editor' ] . includes ( userRole ) ) {
349364 return { error : `You do not have permission to create tasks in this project. Your role: ${ userRole || 'not a member' } . UUID: ${ session . user . uuid } ` } ;
350365 }
@@ -359,6 +374,23 @@ export async function createTaskAction(prevState: CreateTaskFormState, formData:
359374 tagsString : tagsString || undefined ,
360375 } ;
361376 const createdTask = await dbCreateTask ( taskData ) ;
377+
378+ if ( project . discordWebhookUrl && project . discordNotificationsEnabled ) {
379+ await sendDiscordNotification ( project . discordWebhookUrl , {
380+ embeds : [ {
381+ title : `✅ New Task Created: ${ createdTask . title } ` ,
382+ description : `A new task has been added by **${ creatorName } **.` ,
383+ color : 5763719 , // Green
384+ fields : [
385+ { name : "Status" , value : createdTask . status , inline : true } ,
386+ { name : "Assigned To" , value : createdTask . assigneeName || "Everyone" , inline : true } ,
387+ ] ,
388+ timestamp : new Date ( ) . toISOString ( ) ,
389+ footer : { text : `Project: ${ project . name } ` }
390+ } ]
391+ } ) ;
392+ }
393+
362394 return { message : "Task created successfully!" , createdTask } ;
363395 } catch ( error : any ) {
364396 console . error ( "Error creating task:" , error ) ;
@@ -1793,3 +1825,56 @@ export async function editFileWithAIAction(
17931825 return { error : `AI file editing failed: ${ error . message || "An unexpected error occurred." } ` } ;
17941826 }
17951827}
1828+
1829+
1830+ export interface UpdateProjectDiscordSettingsFormState {
1831+ message ?: string ;
1832+ error ?: string ;
1833+ project ?: Project ;
1834+ }
1835+
1836+ const updateDiscordSettingsSchema = z . object ( {
1837+ projectUuid : z . string ( ) . uuid ( ) ,
1838+ discordWebhookUrl : z . string ( ) . url ( "Please enter a valid Discord webhook URL." ) . or ( z . literal ( '' ) ) ,
1839+ discordNotificationsEnabled : z . enum ( [ 'true' , 'false' ] ) . transform ( v => v === 'true' ) ,
1840+ } ) ;
1841+
1842+ export async function updateProjectDiscordSettingsAction (
1843+ prevState : UpdateProjectDiscordSettingsFormState ,
1844+ formData : FormData
1845+ ) : Promise < UpdateProjectDiscordSettingsFormState > {
1846+ const session = await auth ( ) ;
1847+ if ( ! session ?. user ?. uuid ) {
1848+ return { error : "Authentication required." } ;
1849+ }
1850+
1851+ const validatedFields = updateDiscordSettingsSchema . safeParse ( {
1852+ projectUuid : formData . get ( 'projectUuid' ) ,
1853+ discordWebhookUrl : formData . get ( 'discordWebhookUrl' ) ,
1854+ discordNotificationsEnabled : formData . get ( 'discordNotificationsEnabled' ) ,
1855+ } ) ;
1856+
1857+ if ( ! validatedFields . success ) {
1858+ return { error : "Invalid input: " + validatedFields . error . flatten ( ) . fieldErrors . discordWebhookUrl ?. join ( ', ' ) } ;
1859+ }
1860+
1861+ const { projectUuid, discordWebhookUrl, discordNotificationsEnabled } = validatedFields . data ;
1862+
1863+ try {
1864+ const userRole = await dbGetProjectMemberRole ( projectUuid , session . user . uuid ) ;
1865+ if ( ! userRole || ! [ 'owner' , 'co-owner' ] . includes ( userRole ) ) {
1866+ return { error : "You do not have permission to change Discord settings for this project." } ;
1867+ }
1868+
1869+ const updatedProject = await dbUpdateProjectDiscordSettings ( projectUuid , discordWebhookUrl , discordNotificationsEnabled ) ;
1870+
1871+ if ( ! updatedProject ) {
1872+ return { error : "Failed to update project settings in the database." } ;
1873+ }
1874+
1875+ return { message : "Discord settings updated successfully." , project : updatedProject } ;
1876+
1877+ } catch ( error : any ) {
1878+ return { error : error . message || "An unexpected error occurred." } ;
1879+ }
1880+ }
0 commit comments