fix: mail template return type to Promises#458
fix: mail template return type to Promises#458VulpritProoze wants to merge 2 commits intoPSITS-UCMAIN:stagingfrom
Conversation
|
@VulpritProoze is attempting to deploy a commit to the PSITS-UCMAIN's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new/expanded client-side-ts frontend setup (UI primitives, auth route-guards, and Axios auth refresh) along with project documentation and tooling configuration.
Changes:
- Added a large set of reusable UI primitives/components (Radix/shadcn-style) under
src/components/ui. - Implemented frontend auth infrastructure:
AuthProviderwiring, shared Axios instance with refresh-queue logic, and route guard components. - Added/updated project docs and developer tooling/config files (ESLint, workspace docs, structure guide).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| client-side-ts/src/components/ui/empty.tsx | Adds “Empty state” UI building blocks |
| client-side-ts/src/components/ui/dropdown-menu.tsx | Adds dropdown menu primitives based on Radix |
| client-side-ts/src/components/ui/drawer.tsx | Adds drawer primitives (vaul) |
| client-side-ts/src/components/ui/dialog.tsx | Adds dialog primitives based on Radix |
| client-side-ts/src/components/ui/context-menu.tsx | Adds context menu primitives based on Radix |
| client-side-ts/src/components/ui/command.tsx | Adds command palette primitives (cmdk + dialog) |
| client-side-ts/src/components/ui/collapsible.tsx | Adds collapsible primitives (Radix) |
| client-side-ts/src/components/ui/checkbox.tsx | Adds checkbox primitive (Radix) |
| client-side-ts/src/components/ui/chart.tsx | Adds chart helpers/wrappers (Recharts) |
| client-side-ts/src/components/ui/carousel.tsx | Adds carousel wrapper (Embla) |
| client-side-ts/src/components/ui/card.tsx | Adds card primitives |
| client-side-ts/src/components/ui/calendar.tsx | Adds calendar wrapper (react-day-picker) |
| client-side-ts/src/components/ui/button.tsx | Adds button primitive + variants (CVA) |
| client-side-ts/src/components/ui/button-group.tsx | Adds button-group wrapper components |
| client-side-ts/src/components/ui/breadcrumb.tsx | Adds breadcrumb components |
| client-side-ts/src/components/ui/badge.tsx | Adds badge primitive + variants (CVA) |
| client-side-ts/src/components/ui/avatar.tsx | Adds avatar primitives (Radix) |
| client-side-ts/src/components/ui/aspect-ratio.tsx | Adds aspect ratio primitive (Radix) |
| client-side-ts/src/components/ui/alert.tsx | Adds alert primitive + variants (CVA) |
| client-side-ts/src/components/ui/alert-dialog.tsx | Adds alert-dialog primitives (Radix) |
| client-side-ts/src/components/ui/accordion.tsx | Adds accordion primitives (Radix) |
| client-side-ts/src/components/common/StudentCampusRouteGuard.tsx | Adds student + campus constrained route guard |
| client-side-ts/src/components/common/RouteGuards.tsx | Adds simple role-based guard wrappers |
| client-side-ts/src/components/common/ProtectedRoute.tsx | Adds core route guard logic (auth/role/campus) |
| client-side-ts/src/components/common/OptimizedImage.tsx | Adds optimized image component with framer-motion |
| client-side-ts/src/components/common/Header.tsx | Adds app header/navigation component |
| client-side-ts/src/components/common/Footer.tsx | Adds app footer component |
| client-side-ts/src/components/common/CampusView.tsx | Adds role/campus-based conditional rendering wrapper |
| client-side-ts/src/components/common/BackgroundText.tsx | Adds background text presentation component |
| client-side-ts/src/components/common/AdminCampusRouteGuard.tsx | Adds admin + campus constrained route guard |
| client-side-ts/src/api/backendApi.ts | Adds backend base URL helper |
| client-side-ts/src/api/axios.ts | Adds shared Axios instance with token attach/refresh queue |
| client-side-ts/src/App.tsx | Wires router + auth provider |
| client-side-ts/package.json | Adds/updates frontend dependencies and scripts |
| client-side-ts/index.html | Updates base HTML template |
| client-side-ts/eslint.config.js | Adds ESLint configuration (TS + prettier) |
| client-side-ts/docs/AUTH_V2_FRONTEND.md | Documents frontend auth usage patterns |
| client-side-ts/components.json | Adds shadcn/ui config file |
| client-side-ts/README.md | Adds default Vite/React template README |
| client-side-ts/FILE_STRUCTURE_GUIDE.md | Adds repository file-structure conventions |
| client-side-ts/.gitignore | Adds frontend gitignore entries (incl. .env) |
| GEMINI.md | Adds workspace context documentation |
| .vscode/settings.json | Adds editor defaults (prettier + tailwind lint settings) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
disregard broskie's review |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 6 comments.
Files not reviewed (1)
- server-side/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return new Promise((resolve, reject) => { | ||
| transporter.sendMail(mailOptions, (error, info) => { | ||
| if (error) { | ||
| console.error( | ||
| `Error sending membershipRequestReceipt email for ${studentEmail}: ${error.message}` | ||
| ); | ||
| resolve({ | ||
| status: false, | ||
| message: `Error sending membershipRequestReceipt email for ${studentEmail}`, | ||
| }); | ||
| } else { | ||
| resolve({ | ||
| status: true, | ||
| message: `Successfully sent membershipRequestReceipt email for ${studentEmail}`, | ||
| }); | ||
| } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Nodemailer (since v6.0) natively supports Promises when used without a callback. You can simplify this code by removing the manual Promise wrapper and directly awaiting transporter.sendMail(mailOptions), which returns a Promise. This eliminates the need for the callback pattern and the unused reject parameter.
| return new Promise((resolve, reject) => { | |
| transporter.sendMail(mailOptions, (error, info) => { | |
| if (error) { | |
| console.error( | |
| `Error sending membershipRequestReceipt email for ${studentEmail}: ${error.message}` | |
| ); | |
| resolve({ | |
| status: false, | |
| message: `Error sending membershipRequestReceipt email for ${studentEmail}`, | |
| }); | |
| } else { | |
| resolve({ | |
| status: true, | |
| message: `Successfully sent membershipRequestReceipt email for ${studentEmail}`, | |
| }); | |
| } | |
| }); | |
| }); | |
| try { | |
| await transporter.sendMail(mailOptions); | |
| return { | |
| status: true, | |
| message: `Successfully sent membershipRequestReceipt email for ${studentEmail}`, | |
| }; | |
| } catch (error: any) { | |
| console.error( | |
| `Error sending membershipRequestReceipt email for ${studentEmail}: ${error.message}` | |
| ); | |
| return { | |
| status: false, | |
| message: `Error sending membershipRequestReceipt email for ${studentEmail}`, | |
| }; | |
| } |
| return new Promise((resolve, reject) => { | ||
| transporter.sendMail(mailOptions, (error, info) => { | ||
| if (error) { | ||
| console.error( | ||
| `Error sending orderReceipt email for ${studentEmail}: ${error.message}` | ||
| ); | ||
| resolve({ | ||
| status: false, | ||
| message: `Error sending orderReceipt email for ${studentEmail}`, | ||
| }); | ||
| } else { | ||
| resolve({ | ||
| status: true, | ||
| message: `Successfully sent orderReceipt email for ${studentEmail}`, | ||
| }); | ||
| } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Nodemailer (since v6.0) natively supports Promises when used without a callback. You can simplify this code by removing the manual Promise wrapper and directly awaiting transporter.sendMail(mailOptions), which returns a Promise. This eliminates the need for the callback pattern and the unused reject parameter.
| return new Promise((resolve, reject) => { | ||
| transporter.sendMail(mailOptions, (err, info) => { | ||
| if (err) { | ||
| console.error( | ||
| `Error sending forgotPassword email for ${studentMail}: ${err.message}` | ||
| ); | ||
| resolve({ | ||
| status: false, | ||
| message: `Error sending forgotPassword email for ${studentMail}`, | ||
| }); | ||
| } else { | ||
| resolve({ | ||
| status: true, | ||
| message: `Successfully sent forgotPassword email for ${studentMail}`, | ||
| }); | ||
| } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Nodemailer (since v6.0) natively supports Promises when used without a callback. You can simplify this code by removing the manual Promise wrapper and directly awaiting transporter.sendMail(mailOptions), which returns a Promise. This eliminates the need for the callback pattern and the unused reject parameter.
| return new Promise((resolve, reject) => { | |
| transporter.sendMail(mailOptions, (err, info) => { | |
| if (err) { | |
| console.error( | |
| `Error sending forgotPassword email for ${studentMail}: ${err.message}` | |
| ); | |
| resolve({ | |
| status: false, | |
| message: `Error sending forgotPassword email for ${studentMail}`, | |
| }); | |
| } else { | |
| resolve({ | |
| status: true, | |
| message: `Successfully sent forgotPassword email for ${studentMail}`, | |
| }); | |
| } | |
| }); | |
| }); | |
| try { | |
| await transporter.sendMail(mailOptions); | |
| return { | |
| status: true, | |
| message: `Successfully sent forgotPassword email for ${studentMail}`, | |
| }; | |
| } catch (err: any) { | |
| console.error( | |
| `Error sending forgotPassword email for ${studentMail}: ${err.message}` | |
| ); | |
| return { | |
| status: false, | |
| message: `Error sending forgotPassword email for ${studentMail}`, | |
| }; | |
| } |
| return new Promise((resolve, reject) => { | ||
| transporter.sendMail(mailOptions, (error, info) => { | ||
| if (error) { | ||
| console.error( | ||
| `Error sending membershipRequestReceipt email for ${studentEmail}: ${error.message}` | ||
| ); | ||
| resolve({ | ||
| status: false, | ||
| message: `Error sending membershipRequestReceipt email for ${studentEmail}`, | ||
| }); | ||
| } else { | ||
| resolve({ | ||
| status: true, | ||
| message: `Successfully sent membershipRequestReceipt email for ${studentEmail}`, | ||
| }); | ||
| } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
The Promise resolves with status:false on email sending errors instead of rejecting. This makes it impossible for callers to use try/catch blocks to handle failures properly. Additionally, the reject parameter is never used. Consider either rejecting the Promise on errors, or document that callers must check the returned status object.
| return new Promise((resolve, reject) => { | ||
| transporter.sendMail(mailOptions, (error, info) => { | ||
| if (error) { | ||
| console.error( | ||
| `Error sending orderReceipt email for ${studentEmail}: ${error.message}` | ||
| ); | ||
| resolve({ | ||
| status: false, | ||
| message: `Error sending orderReceipt email for ${studentEmail}`, | ||
| }); | ||
| } else { | ||
| resolve({ | ||
| status: true, | ||
| message: `Successfully sent orderReceipt email for ${studentEmail}`, | ||
| }); | ||
| } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
The Promise resolves with status:false on email sending errors instead of rejecting. This makes it impossible for callers to use try/catch blocks to handle failures properly. Additionally, the reject parameter is never used. Consider either rejecting the Promise on errors, or document that callers must check the returned status object.
| return new Promise((resolve, reject) => { | ||
| transporter.sendMail(mailOptions, (err, info) => { | ||
| if (err) { | ||
| console.error( | ||
| `Error sending forgotPassword email for ${studentMail}: ${err.message}` | ||
| ); | ||
| resolve({ | ||
| status: false, | ||
| message: `Error sending forgotPassword email for ${studentMail}`, | ||
| }); | ||
| } else { | ||
| resolve({ | ||
| status: true, | ||
| message: `Successfully sent forgotPassword email for ${studentMail}`, | ||
| }); | ||
| } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
The Promise resolves with status:false on email sending errors instead of rejecting. This makes it impossible for callers to use try/catch blocks to handle failures properly. Additionally, the reject parameter is never used. Consider either rejecting the Promise on errors, or document that callers must check the returned status object.
This pull request fixes the issue of not being able to synchronously track when email templates resolve. For example, when forgot password is sent, there is no way to track when that was resolved, resulting in a weird issue where success message seems to appear first before the email was actually sent.
The configuration edited in this PR is only in mail.template. Core logic for changing the handling of email template resolution will need to be changed on controllers that uses these mail templates.