From 1ee105368d17ba40c06b0818b8d24afa232f8cef Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 17:31:47 +0530 Subject: [PATCH 01/10] feat: setup email workflows across admins env --- handlers/admin_post.go | 4 +++- handlers/admin_status.go | 6 +++--- middleware/status.go | 11 +++++++++++ models/centrehead_auth.go | 1 + models/faculty_auth.go | 1 + models/warden_auth.go | 1 + services/email.go | 29 +++++++++++++++++++++++++++++ 7 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 middleware/status.go diff --git a/handlers/admin_post.go b/handlers/admin_post.go index 3d0f266..c70b5cf 100644 --- a/handlers/admin_post.go +++ b/handlers/admin_post.go @@ -233,7 +233,9 @@ func (h* AdminHandler) GetJEPosts (c *gin.Context) { }) } -// for now we treat 404 and 500 during fetching post the same + +// AdminGetPost fetches the whole post page for any admin +// (for now we treat 404 and 500 during fetching post the same) func (h *AdminHandler) AdminGetPost (c *gin.Context) { // get email from gin context diff --git a/handlers/admin_status.go b/handlers/admin_status.go index 361e6bc..98bd6db 100644 --- a/handlers/admin_status.go +++ b/handlers/admin_status.go @@ -16,7 +16,7 @@ type AdminReview struct { } // AdminFacultyPostStatus sets the stage of the faculty posts -func (h *AdminHandler) AdminFacultyPostStatus (c *gin.Context) { +func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) if !exists { c.JSON(401, gin.H{"error": "permission denied"}) @@ -135,7 +135,7 @@ func (h *AdminHandler) AdminFacultyPostStatus (c *gin.Context) { } // AdminWardenPostStatus sets the stage of the warden posts -func (h *AdminHandler) AdminWardenPostStatus (c *gin.Context) { +func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) if !exists { c.JSON(401, gin.H{"error": "permission denied"}) @@ -251,7 +251,7 @@ func (h *AdminHandler) AdminWardenPostStatus (c *gin.Context) { } // AdminCentreHeadPostStatus sets the stage of the centre_head posts -func (h *AdminHandler) AdminCentreHeadPostStatus (c *gin.Context) { +func (h *AdminHandler) AdminCentreHeadPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) if !exists { c.JSON(401, gin.H{"error": "permission denied"}) diff --git a/middleware/status.go b/middleware/status.go new file mode 100644 index 0000000..64f24b8 --- /dev/null +++ b/middleware/status.go @@ -0,0 +1,11 @@ +package middleware + +import ( + "github.com/gin-gonic/gin" +) + +func PatchPostStatus() (gin.HandlerFunc) { + return func(c *gin.Context) { + // from authenticated middleware we can get ? + } +} diff --git a/models/centrehead_auth.go b/models/centrehead_auth.go index 61598c9..2f1bb84 100644 --- a/models/centrehead_auth.go +++ b/models/centrehead_auth.go @@ -33,6 +33,7 @@ type CentreHead struct { Password string `gorm:"not null" json:"password"` Building BuildingName `gorm:"type:varchar(100);not null" json:"building"` PhoneNumber string `gorm:"type:char(10);not null" json:"phone_number"` + Role string `gorm:"default:centrehead" json:"role"` IsVerified bool `gorm:"default:false" json:"is_verified"` CreatedAt time.Time `json:"created_at"` } diff --git a/models/faculty_auth.go b/models/faculty_auth.go index f964d6c..0d3fb4e 100644 --- a/models/faculty_auth.go +++ b/models/faculty_auth.go @@ -51,6 +51,7 @@ type Faculty struct { Block BlockLabel `gorm:"type:char(1);not null" json:"block"` Type BlockType `gorm:"type:char(1);not null" json:"type"` PhoneNumber string `gorm:"type:char(10);not null" json:"phone_number"` + Role string `gorm:"default:faculty" json:"role"` IsVerified bool `gorm:"default:false" json:"is_verified"` CreatedAt time.Time `json:"created_at"` } diff --git a/models/warden_auth.go b/models/warden_auth.go index dfdff4e..5240f5a 100644 --- a/models/warden_auth.go +++ b/models/warden_auth.go @@ -27,6 +27,7 @@ type Warden struct { Password string `gorm:"not null" json:"password"` Hostel HostelName `gorm:"type:varchar(30);not null" json:"hostel"` PhoneNumber string `gorm:"type:char(10);not null" json:"phone_number"` + Role string `gorm:"default:warden" json:"role"` IsVerified bool `gorm:"default:false" json:"is_verified"` CreatedAt time.Time `json:"created_at"` } diff --git a/services/email.go b/services/email.go index 761f63a..c28ad95 100644 --- a/services/email.go +++ b/services/email.go @@ -108,3 +108,32 @@ func SendPasswordResetMail(userID uint, email, role string) error { log.Printf("Password reset link sent to %s", email) return nil } + +func SendPostMail(email, postURL string) error { + // send the email + mail := fmt.Sprintf(` + + + + + + + +

Reset your password

+

+ Reset! +

+ + + `, postURL) + + err := SendMail(email, "New complaint recieved", mail) + if err != nil { + return err + } + log.Printf("complaint mail was sent to %s", email) + return nil +} From 03a1e289b779c8328172487cccb7806c058c7a0a Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 18:54:57 +0530 Subject: [PATCH 02/10] middlewares deleted --- middleware/status.go | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 middleware/status.go diff --git a/middleware/status.go b/middleware/status.go deleted file mode 100644 index 64f24b8..0000000 --- a/middleware/status.go +++ /dev/null @@ -1,11 +0,0 @@ -package middleware - -import ( - "github.com/gin-gonic/gin" -) - -func PatchPostStatus() (gin.HandlerFunc) { - return func(c *gin.Context) { - // from authenticated middleware we can get ? - } -} From d03729641af2cb260d75b378c84240a1ae7048e5 Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 18:56:01 +0530 Subject: [PATCH 03/10] fix: moved adminType to session storage --- app/src/App.tsx | 2 +- app/src/pages/admin/AEPostView.tsx | 2 +- app/src/pages/admin/AdminPostView.tsx | 4 +++- app/src/pages/admin/JEPostView.tsx | 2 +- app/src/pages/admin/XENPostView.tsx | 2 +- app/src/pages/auth/StaffLogin.tsx | 1 + 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/App.tsx b/app/src/App.tsx index eaa91bc..bdf35b4 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -45,7 +45,7 @@ function App() { } /> } /> } /> - } /> + } /> ); diff --git a/app/src/pages/admin/AEPostView.tsx b/app/src/pages/admin/AEPostView.tsx index 24d39c0..98f319c 100644 --- a/app/src/pages/admin/AEPostView.tsx +++ b/app/src/pages/admin/AEPostView.tsx @@ -71,7 +71,7 @@ function PostTile({ label, icon, role, posts }: PostTileProps) { {posts.map((post) => (
  • {/* ID chip */} diff --git a/app/src/pages/admin/AdminPostView.tsx b/app/src/pages/admin/AdminPostView.tsx index 459a116..44aeace 100644 --- a/app/src/pages/admin/AdminPostView.tsx +++ b/app/src/pages/admin/AdminPostView.tsx @@ -214,7 +214,9 @@ function MetaRow({ icon, label, value }: { icon: React.ReactNode; label: string; // ── Page ─────────────────────────────────────────────────────────────────────── export function AdminPostView() { - const { adminType, role, post_id } = useParams<{ adminType: string; role: string; post_id: string }>(); + const { role, post_id } = useParams<{ role: string; post_id: string }>(); + const adminPosition = sessionStorage.getItem('adminPosition') ?? ''; + const adminType = adminPosition.startsWith('XEN') ? 'xen' : adminPosition.startsWith('AE') ? 'ae' : adminPosition.startsWith('JE') ? 'je' : ''; const navigate = useNavigate(); const [post, setPost] = useState(null); diff --git a/app/src/pages/admin/JEPostView.tsx b/app/src/pages/admin/JEPostView.tsx index 61c8f51..31bf217 100644 --- a/app/src/pages/admin/JEPostView.tsx +++ b/app/src/pages/admin/JEPostView.tsx @@ -71,7 +71,7 @@ function PostTile({ label, icon, role, posts }: PostTileProps) { {posts.map((post) => (
  • {/* ID chip */} diff --git a/app/src/pages/admin/XENPostView.tsx b/app/src/pages/admin/XENPostView.tsx index 52af183..961f7b5 100644 --- a/app/src/pages/admin/XENPostView.tsx +++ b/app/src/pages/admin/XENPostView.tsx @@ -76,7 +76,7 @@ function PostTile({ label, icon, role, posts }: PostTileProps) { {posts.map((post) => (
  • {/* ID chip */} diff --git a/app/src/pages/auth/StaffLogin.tsx b/app/src/pages/auth/StaffLogin.tsx index 8ec75f5..73ed41a 100644 --- a/app/src/pages/auth/StaffLogin.tsx +++ b/app/src/pages/auth/StaffLogin.tsx @@ -43,6 +43,7 @@ export function StaffLogin() { setStatus('error'); setMessage(`Unknown position "${data.position}" — contact admin.`); } else { + sessionStorage.setItem('adminPosition', data.position ?? ''); navigate(dest); } } else { From 215c15a719c0d921de4964991e52a9e5be72e247 Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 18:56:29 +0530 Subject: [PATCH 04/10] feat: mail xen with postURL on post creation --- handlers/admin_auth.go | 8 ++++---- handlers/admin_post.go | 2 +- handlers/centrehead_post.go | 34 +++++++++++++++++++++++++++++---- handlers/faculty_post.go | 38 +++++++++++++++++++++++++++++++------ handlers/warden_post.go | 34 +++++++++++++++++++++++++++++---- 5 files changed, 97 insertions(+), 19 deletions(-) diff --git a/handlers/admin_auth.go b/handlers/admin_auth.go index 9a2af8b..2f47b2c 100644 --- a/handlers/admin_auth.go +++ b/handlers/admin_auth.go @@ -55,10 +55,10 @@ func (h *AdminHandler) AdminLogin (c *gin.Context) { return } - // if admin.IsVerified == false { - // c.JSON(401, gin.H{"error": "unverified user"}) - // return - // } + if admin.IsVerified == false { + c.JSON(401, gin.H{"error": "unverified user"}) + return + } err := bcrypt.CompareHashAndPassword([]byte(admin.Password), []byte(inputs.Password)) if err != nil { diff --git a/handlers/admin_post.go b/handlers/admin_post.go index bbacad6..0a143a9 100644 --- a/handlers/admin_post.go +++ b/handlers/admin_post.go @@ -236,7 +236,7 @@ func (h* AdminHandler) GetJEPosts (c *gin.Context) { // AdminGetPost fetches the whole post page for any admin // (for now we treat 404 and 500 during fetching post the same) -func (h *AdminHandler) AdminGetPost (c *gin.Context) { +func (h *AdminHandler) AdminGetPost(c *gin.Context) { // get email from gin context email, exists := c.Get(middleware.EmailKey) diff --git a/handlers/centrehead_post.go b/handlers/centrehead_post.go index d8a66b3..9e31e12 100644 --- a/handlers/centrehead_post.go +++ b/handlers/centrehead_post.go @@ -1,11 +1,14 @@ package handlers import ( + "fmt" + "log" "time" "errors" "github.com/ayush00git/cms-web/middleware" "github.com/ayush00git/cms-web/models" + "github.com/ayush00git/cms-web/services" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -27,7 +30,7 @@ type CentreheadPostType struct { // CentreheadPost registers the post of centre-head members. // forwards the post to the associated XEN. -func (h *PostHandler) CentreheadPost (c *gin.Context) { +func (h *PostHandler) CentreheadPost(c *gin.Context) { var inputs CentreheadPostType if err := c.ShouldBindJSON(&inputs); err != nil { @@ -70,13 +73,36 @@ func (h *PostHandler) CentreheadPost (c *gin.Context) { return } + // send the mail to the corresponding xen + // } /> + postURL := fmt.Sprintf(`http://localhost:5173/admin/posts/%s/%d`, head.Role, post.ID) + go func() { + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + // through type of post send the mail to the corresponding civil/electrical XEN + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send XEN mail for post %d", post.ID) + return + } + // send mail to that user + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send XEN mail for post %d: %v", post.ID, err) + } + }() + c.JSON(201, gin.H{"success": "post submitted successfully", "post": post}) } // CentreheadPostEdit let's the author of the post edit it. // Match is the author trying to edit. -func (h *PostHandler) CentreheadPostEdit (c *gin.Context) { +func (h *PostHandler) CentreheadPostEdit(c *gin.Context) { // get the id of the user from gin context userID, exists := c.Get(middleware.UserIDKey) if !exists { @@ -121,7 +147,7 @@ func (h *PostHandler) CentreheadPostEdit (c *gin.Context) { // CentreheadPostDelete lets the author delete his post. // Matches is the author trying to delete. -func (h *PostHandler) CentreheadPostDelete (c *gin.Context) { +func (h *PostHandler) CentreheadPostDelete(c *gin.Context) { // get userID from gin context userID, exists := c.Get(middleware.UserIDKey); if !exists { @@ -157,7 +183,7 @@ func (h *PostHandler) CentreheadPostDelete (c *gin.Context) { // GetCentreheadPosts fetch the posts of the centre head member along with their status and comments -func (h *PostHandler) GetCentreheadPosts (c *gin.Context) { +func (h *PostHandler) GetCentreheadPosts(c *gin.Context) { email, exists := c.Get(middleware.EmailKey) if !exists { c.JSON(401, gin.H{"error": "unauthenticated user"}) diff --git a/handlers/faculty_post.go b/handlers/faculty_post.go index d5b2886..8883a1d 100644 --- a/handlers/faculty_post.go +++ b/handlers/faculty_post.go @@ -1,11 +1,14 @@ package handlers import ( - "time" "errors" + "fmt" + "log" + "time" "github.com/ayush00git/cms-web/middleware" "github.com/ayush00git/cms-web/models" + "github.com/ayush00git/cms-web/services" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -33,7 +36,7 @@ type FacultyPostType struct { // FacultyPost registers the post of faculty members. // forwards the post to the associated XEN. -func (h *PostHandler) FacultyPost (c *gin.Context) { +func (h *PostHandler) FacultyPost(c *gin.Context) { var inputs FacultyPostType if err := c.ShouldBindJSON(&inputs); err != nil { @@ -76,14 +79,37 @@ func (h *PostHandler) FacultyPost (c *gin.Context) { c.JSON(500, gin.H{"error": "failed inserting to table"}) return } - + + // send the mail to the corresponding xen + // } /> + postURL := fmt.Sprintf(`http://localhost:5173/admin/posts/%s/%d`, faculty.Role, post.ID) + go func() { + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + // through type of post send the mail to the corresponding civil/electrical XEN + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send XEN mail for post %d", post.ID) + return + } + // send mail to that user + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send XEN mail for post %d: %v", post.ID, err) + } + }() + c.JSON(201, gin.H{"success": "post submitted successfully", "post": post}) } // FacultyPostEdit let's the author of the post edit it. // Match is the author trying to edit. -func (h *PostHandler) FacultyPostEdit (c *gin.Context) { +func (h *PostHandler) FacultyPostEdit(c *gin.Context) { // who is trying to edit the post userID, exists := c.Get(middleware.UserIDKey) if !exists { @@ -128,7 +154,7 @@ func (h *PostHandler) FacultyPostEdit (c *gin.Context) { // FacultyPostDelete lets the author delete his post. // Matches is the author trying to delete. -func (h *PostHandler) FacultyPostDelete (c *gin.Context) { +func (h *PostHandler) FacultyPostDelete(c *gin.Context) { // get userID from gin context userID, exists := c.Get(middleware.UserIDKey); if !exists { @@ -165,7 +191,7 @@ func (h *PostHandler) FacultyPostDelete (c *gin.Context) { // GetFacultyPosts fetch the posts of the faculty member along with their status and comments // This API returns all the posts collectively -func (h *PostHandler) GetFacultyPosts (c *gin.Context) { +func (h *PostHandler) GetFacultyPosts(c *gin.Context) { // get email of the logged in user email, exists := c.Get(middleware.EmailKey) if !exists { diff --git a/handlers/warden_post.go b/handlers/warden_post.go index 57e1db6..f7289a4 100644 --- a/handlers/warden_post.go +++ b/handlers/warden_post.go @@ -1,11 +1,14 @@ package handlers import ( + "fmt" + "log" "time" "errors" "github.com/ayush00git/cms-web/middleware" "github.com/ayush00git/cms-web/models" + "github.com/ayush00git/cms-web/services" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -30,7 +33,7 @@ type WardenPostType struct { // WardenPost registers the post of warden members. // forwards the post to the associated XEN. -func (h *PostHandler) WardenPost (c *gin.Context) { +func (h *PostHandler) WardenPost(c *gin.Context) { var inputs WardenPostType if err := c.ShouldBindJSON(&inputs); err != nil { @@ -74,13 +77,36 @@ func (h *PostHandler) WardenPost (c *gin.Context) { return } + // send the mail to the corresponding xen + // } /> + postURL := fmt.Sprintf(`http://localhost:5173/admin/posts/%s/%d`, warden.Role, post.ID) + go func() { + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + // through type of post send the mail to the corresponding civil/electrical XEN + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send XEN mail for post %d", post.ID) + return + } + // send mail to that user + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send XEN mail for post %d: %v", post.ID, err) + } + }() + c.JSON(201, gin.H{"success": "post submitted successfully", "post": post}) } // WardenPostEdit let's the author of the post edit it. // Match is the author trying to edit. -func (h *PostHandler) WardenPostEdit (c *gin.Context) { +func (h *PostHandler) WardenPostEdit(c *gin.Context) { // get the id of the user from gin context userID, exists := c.Get(middleware.UserIDKey) if !exists { @@ -125,7 +151,7 @@ func (h *PostHandler) WardenPostEdit (c *gin.Context) { // WardenPostDelete lets the author delete his post. // Matches is the author trying to delete. -func (h *PostHandler) WardenPostDelete (c *gin.Context) { +func (h *PostHandler) WardenPostDelete(c *gin.Context) { // get userID from gin context userID, exists := c.Get(middleware.UserIDKey); if !exists { @@ -161,7 +187,7 @@ func (h *PostHandler) WardenPostDelete (c *gin.Context) { // GetWardenPosts fetch the posts of the warden member along with their status and comments -func (h *PostHandler) GetWardenPosts (c *gin.Context) { +func (h *PostHandler) GetWardenPosts(c *gin.Context) { email, exists := c.Get(middleware.EmailKey) if !exists { c.JSON(401, gin.H{"error": "unauthenticated user"}) From 071ee8f05679cf559b30b76b0525d18a760986c9 Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 18:56:57 +0530 Subject: [PATCH 05/10] fix: append email to the helper --- services/email.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/email.go b/services/email.go index c28ad95..7a45f09 100644 --- a/services/email.go +++ b/services/email.go @@ -109,7 +109,7 @@ func SendPasswordResetMail(userID uint, email, role string) error { return nil } -func SendPostMail(email, postURL string) error { +func SendPostMailToAdmins(email, postURL string) error { // send the email mail := fmt.Sprintf(` From 6413306e62c057a3ca757505d6a7f927b332d715 Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 20:23:19 +0530 Subject: [PATCH 06/10] feat: add email workflows on faculty type posts --- handlers/admin_status.go | 107 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/handlers/admin_status.go b/handlers/admin_status.go index 37cb1cb..b920ea1 100644 --- a/handlers/admin_status.go +++ b/handlers/admin_status.go @@ -1,12 +1,15 @@ package handlers import ( + "fmt" "errors" + "log" "strconv" "strings" "github.com/ayush00git/cms-web/middleware" "github.com/ayush00git/cms-web/models" + "github.com/ayush00git/cms-web/services" "github.com/gin-gonic/gin" "gorm.io/gorm" ) @@ -65,6 +68,8 @@ func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { return } + // create the postURL + postURL := fmt.Sprintf(`http://localhost:5173/admin/posts/%s/%d`, "faculty", post.ID) switch post.Status { case "Pending_XEN" : @@ -74,8 +79,26 @@ func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { } if review.Review == "to_ae" { post.Status = "Pending_AE" - } else if review.Review == "open" { - post.Status = "Pending_XEN" + // send mail to ae + go func() { + // search for email of ae + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeAECivil + } else { + position = models.TypeAEElectrical + } + var ae models.Admin + result := h.DB.Where("position = ?", position).Take(&ae) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(ae.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "close" { post.Status = "Closed" } else { @@ -90,8 +113,48 @@ func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { } if review.Review == "to_je" { post.Status = "Pending_JE" + // send mail to je + go func() { + // search for email of je + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeJECivil + } else { + position = models.TypeJEElectrical + } + var je models.Admin + result := h.DB.Where("position = ?", position).Take(&je) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(je.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "require_review" { post.Status = "Pending_XEN" + // send mail to xen + go func() { + // search for email of xen + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else { c.JSON(400, gin.H{"error": "invalid review type"}) return @@ -104,8 +167,48 @@ func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { } if review.Review == "resolved" { post.Status = "Resolved_JE" + // send mail to xen + go func() { + // search for email of xen + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "require_review" { post.Status = "Pending_AE" + // send mail to ae + go func() { + // search for email of ae + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeAECivil + } else { + position = models.TypeAEElectrical + } + var ae models.Admin + result := h.DB.Where("position = ?", position).Take(&ae) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(ae.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else { c.JSON(400, gin.H{"error": "invalid review type"}) return From 40c74b7d57505668e0f7ac7a5c522d5df2714087 Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 22:55:04 +0530 Subject: [PATCH 07/10] fix: added resolved_je state --- handlers/admin_status.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/handlers/admin_status.go b/handlers/admin_status.go index b920ea1..c1fc22d 100644 --- a/handlers/admin_status.go +++ b/handlers/admin_status.go @@ -72,6 +72,14 @@ func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { postURL := fmt.Sprintf(`http://localhost:5173/admin/posts/%s/%d`, "faculty", post.ID) switch post.Status { + case "Resolved_JE": + if !strings.Contains(string(admin.Position), "XEN") { + c.JSON(403, gin.H{"error": "permissions denied"}) + return + } + if review.Review == "close" { + post.Status = "Closed" + } case "Pending_XEN" : if !strings.Contains(string(admin.Position), "XEN") { c.JSON(403, gin.H{"error": "permissions denied"}) @@ -286,6 +294,14 @@ func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { switch post.Status { + case "Resolved_JE": + if !strings.Contains(string(admin.Position), "XEN") { + c.JSON(403, gin.H{"error": "permissions denied"}) + return + } + if review.Review == "close" { + post.Status = "Closed" + } case "Pending_XEN" : if !strings.Contains(string(admin.Position), "XEN") { c.JSON(403, gin.H{"error": "permissions denied"}) @@ -402,6 +418,14 @@ func (h *AdminHandler) AdminCentreheadPostStatus(c *gin.Context) { switch post.Status { + case "Resolved_JE": + if !strings.Contains(string(admin.Position), "XEN") { + c.JSON(403, gin.H{"error": "permissions denied"}) + return + } + if review.Review == "close" { + post.Status = "Closed" + } case "Pending_XEN" : if !strings.Contains(string(admin.Position), "XEN") { c.JSON(403, gin.H{"error": "permissions denied"}) From 50c2d609e162dc111b8178075fe3e97ddbc37b1c Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 23:01:19 +0530 Subject: [PATCH 08/10] fix: added emails to warden type --- handlers/admin_status.go | 105 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/handlers/admin_status.go b/handlers/admin_status.go index c1fc22d..ae53b09 100644 --- a/handlers/admin_status.go +++ b/handlers/admin_status.go @@ -245,6 +245,7 @@ func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { c.JSON(200, gin.H{"success": "status updated"}) } + // AdminWardenPostStatus sets the stage of the warden posts func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) @@ -292,6 +293,8 @@ func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { return } + // create the postURL + postURL := fmt.Sprintf(`http://localhost:5173/admin/posts/%s/%d`, "warden", post.ID) switch post.Status { case "Resolved_JE": @@ -309,8 +312,26 @@ func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { } if review.Review == "to_ae" { post.Status = "Pending_AE" - } else if review.Review == "open" { - post.Status = "Pending_XEN" + // send mail to ae + go func() { + // search for email of ae + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeAECivil + } else { + position = models.TypeAEElectrical + } + var ae models.Admin + result := h.DB.Where("position = ?", position).Take(&ae) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(ae.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "close" { post.Status = "Closed" } else { @@ -325,8 +346,48 @@ func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { } if review.Review == "to_je" { post.Status = "Pending_JE" + // send mail to je + go func() { + // search for email of je + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeJECivil + } else { + position = models.TypeJEElectrical + } + var je models.Admin + result := h.DB.Where("position = ?", position).Take(&je) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(je.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "require_review" { post.Status = "Pending_XEN" + // send mail to xen + go func() { + // search for email of xen + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else { c.JSON(400, gin.H{"error": "invalid review type"}) return @@ -339,8 +400,48 @@ func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { } if review.Review == "resolved" { post.Status = "Resolved_JE" + // send mail to xen + go func() { + // search for email of xen + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "require_review" { post.Status = "Pending_AE" + // send mail to ae + go func() { + // search for email of ae + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeAECivil + } else { + position = models.TypeAEElectrical + } + var ae models.Admin + result := h.DB.Where("position = ?", position).Take(&ae) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(ae.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else { c.JSON(400, gin.H{"error": "invalid review type"}) return From a6251d25e0082509d7cfe5326e3d31beadae7925 Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 23:06:50 +0530 Subject: [PATCH 09/10] feat: applied the same to centrehead --- handlers/admin_status.go | 105 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/handlers/admin_status.go b/handlers/admin_status.go index ae53b09..1fb3d36 100644 --- a/handlers/admin_status.go +++ b/handlers/admin_status.go @@ -470,6 +470,7 @@ func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { c.JSON(200, gin.H{"success": "status updated"}) } + // AdminCentreheadPostStatus sets the stage of the centrehead posts func (h *AdminHandler) AdminCentreheadPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) @@ -517,6 +518,8 @@ func (h *AdminHandler) AdminCentreheadPostStatus(c *gin.Context) { return } + // create the postURL + postURL := fmt.Sprintf(`http://localhost:5173/admin/posts/%s/%d`, "centrehead", post.ID) switch post.Status { case "Resolved_JE": @@ -534,8 +537,26 @@ func (h *AdminHandler) AdminCentreheadPostStatus(c *gin.Context) { } if review.Review == "to_ae" { post.Status = "Pending_AE" - } else if review.Review == "open" { - post.Status = "Pending_XEN" + // send mail to ae + go func() { + // search for email of ae + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeAECivil + } else { + position = models.TypeAEElectrical + } + var ae models.Admin + result := h.DB.Where("position = ?", position).Take(&ae) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(ae.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "close" { post.Status = "Closed" } else { @@ -550,8 +571,48 @@ func (h *AdminHandler) AdminCentreheadPostStatus(c *gin.Context) { } if review.Review == "to_je" { post.Status = "Pending_JE" + // send mail to je + go func() { + // search for email of je + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeJECivil + } else { + position = models.TypeJEElectrical + } + var je models.Admin + result := h.DB.Where("position = ?", position).Take(&je) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(je.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "require_review" { post.Status = "Pending_XEN" + // send mail to xen + go func() { + // search for email of xen + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else { c.JSON(400, gin.H{"error": "invalid review type"}) return @@ -564,8 +625,48 @@ func (h *AdminHandler) AdminCentreheadPostStatus(c *gin.Context) { } if review.Review == "resolved" { post.Status = "Resolved_JE" + // send mail to xen + go func() { + // search for email of xen + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeXENCivil + } else { + position = models.TypeXENElectrical + } + var xen models.Admin + result := h.DB.Where("position = ?", position).Take(&xen) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(xen.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else if review.Review == "require_review" { post.Status = "Pending_AE" + // send mail to ae + go func() { + // search for email of ae + var position models.PositionType + if post.TypeOfPost == "Civil" { + position = models.TypeAECivil + } else { + position = models.TypeAEElectrical + } + var ae models.Admin + result := h.DB.Where("position = ?", position).Take(&ae) + if result.Error != nil { + log.Printf("failed to send AE mail for post %d", post.ID) + return + } + if err := services.SendPostMailToAdmins(ae.Email, postURL); err != nil { + log.Printf("failed to send AE mail for post %d: %s", post.ID, err) + return + } + } () } else { c.JSON(400, gin.H{"error": "invalid review type"}) return From e3f61056f6a9d3ecf6a50d0eeacc1fb54596945f Mon Sep 17 00:00:00 2001 From: ayush00git Date: Sat, 30 May 2026 23:08:46 +0530 Subject: [PATCH 10/10] add comments --- handlers/admin_status.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/handlers/admin_status.go b/handlers/admin_status.go index 1fb3d36..b60b4cc 100644 --- a/handlers/admin_status.go +++ b/handlers/admin_status.go @@ -19,6 +19,7 @@ type AdminReview struct { } // AdminFacultyPostStatus sets the stage of the faculty posts +// Sends email to the corresponding post using goroutines func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) if !exists { @@ -247,6 +248,7 @@ func (h *AdminHandler) AdminFacultyPostStatus(c *gin.Context) { // AdminWardenPostStatus sets the stage of the warden posts +// Sends email to the corresponding post using goroutines func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) if !exists { @@ -472,6 +474,7 @@ func (h *AdminHandler) AdminWardenPostStatus(c *gin.Context) { // AdminCentreheadPostStatus sets the stage of the centrehead posts +// Sends email to the corresponding post using goroutines func (h *AdminHandler) AdminCentreheadPostStatus(c *gin.Context) { adminEmail, exists := c.Get(middleware.EmailKey) if !exists {