Skip to content

Added email service - #115

Merged
maximgorbatyuk merged 2 commits into
mainfrom
email
Jan 20, 2026
Merged

maximgorbatyuk merged 2 commits into
mainfrom
email

Conversation

@maximgorbatyuk

@maximgorbatyuk maximgorbatyuk commented Jan 20, 2026 •

Copy link
Copy Markdown
Member

Summary by CodeRabbit

New Features

  • Added an email sending tool in the Admin Tools section, enabling administrators to send custom emails with From, To, CC, Subject, and HTML Body fields.
  • Includes form validation and provides real-time feedback on email transmission success or failure.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 20, 2026 •

Copy link
Copy Markdown

Walkthrough

This PR introduces a new email sending feature to the admin panel. It adds a navigation link, a new route, a component for the email form UI, and a service to handle email requests to the backend API. Integration includes module declarations and routing configuration.

Changes

Cohort / File(s) Summary
Navigation & Routing Configuration
src/app/components/admin-navbar/admin-navbar.component.ts, src/app/modules/admin/admin-routing.module.ts
Added new navbar dropdown link titled "Отправка email" pointing to /admin/tools/send-email. Introduced corresponding route in admin routing module with SendEmailPageComponent.
Module Setup
src/app/modules/admin/admin.module.ts
Registered SendEmailPageComponent in declarations and AdminEmailService in module providers for admin module integration.
Email Sending Feature
src/app/modules/admin/components/tools/send-email-page/send-email-page.component.ts, src/app/modules/admin/components/tools/send-email-page/send-email-page.component.html, src/app/modules/admin/components/tools/send-email-page/send-email-page.component.scss
New component with email form containing From, To, CC, Subject, and HTML Body fields. Implements form validation (isFormValid), email submission (sendEmail) with isSending state toggle, and user feedback via AlertService. Template includes form bindings, placeholders, and conditional button state.
Email Service
src/app/services/admin-email.service.ts
New service providing SendCustomEmailRequest and SendCustomEmailResponse interfaces. Implements sendEmail method that POSTs to /api/admin/emails/send via injected ApiService, returning Observable with email send response.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant SendEmailPageComponent
    participant AdminEmailService
    participant ApiService
    participant Backend as Backend API

    User->>SendEmailPageComponent: Submit email form
    activate SendEmailPageComponent
    SendEmailPageComponent->>SendEmailPageComponent: Validate form (isFormValid)
    alt Form Invalid
        SendEmailPageComponent->>User: Display error alert
    else Form Valid
        SendEmailPageComponent->>SendEmailPageComponent: Set isSending = true
        SendEmailPageComponent->>AdminEmailService: sendEmail(emailRequest)
        activate AdminEmailService
        AdminEmailService->>ApiService: post("/api/admin/emails/send", request)
        activate ApiService
        ApiService->>Backend: POST /api/admin/emails/send
        activate Backend
        Backend-->>ApiService: SendCustomEmailResponse
        deactivate Backend
        ApiService-->>AdminEmailService: Observable<SendCustomEmailResponse>
        deactivate ApiService
        AdminEmailService-->>SendEmailPageComponent: Observable response
        deactivate AdminEmailService
        SendEmailPageComponent->>SendEmailPageComponent: Set isSending = false
        alt Response Success
            SendEmailPageComponent->>User: Display success alert
        else Response Error
            SendEmailPageComponent->>User: Display error message
        end
    end
    deactivate SendEmailPageComponent
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Added email service' is partially related to the changeset. While an email service (AdminEmailService) is added, the PR encompasses much more: a complete email sending feature including UI component, routing, navbar integration, and service implementation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@maximgorbatyuk
maximgorbatyuk merged commit 9f8463e into main Jan 20, 2026
1 check passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@src/app/modules/admin/components/tools/send-email-page/send-email-page.component.ts`:
- Around line 31-55: The sendEmail() method currently sends raw
this.emailRequest which may include trailing spaces or empty cc values; before
calling adminEmailService.sendEmail, normalize address fields by trimming
strings (to, from, subject, body, and cc entries) and if cc is empty after
trimming, remove the cc property or set it to undefined so it is omitted from
the payload; update sendEmail() to build a sanitized payload (or overwrite
this.emailRequest) and then pass that sanitized object to
adminEmailService.sendEmail(...), keeping the existing isFormValid() and error
handling intact.
🧹 Nitpick comments (4)
src/app/services/admin-email.service.ts (1)

18-26: Consider using providedIn or simplify the URL construction.

Two minor observations:

  1. The apiUrl property is defined but only used with string concatenation. Consider simplifying to a constant or using it directly.

  2. Per Angular best practices, consider using @Injectable({ providedIn: 'root' }) for tree-shakable services, though module-level provision in AdminModule is also valid for admin-scoped services.

♻️ Optional: Simplify URL and add error handling
 `@Injectable`()
 export class AdminEmailService {
-  private readonly apiUrl = "/api/admin/emails/";
+  private readonly sendEmailUrl = "/api/admin/emails/send";

   constructor(private readonly api: ApiService) {}

   sendEmail(request: SendCustomEmailRequest): Observable<SendCustomEmailResponse> {
-    return this.api.post<SendCustomEmailResponse>(this.apiUrl + "send", request);
+    return this.api.post<SendCustomEmailResponse>(this.sendEmailUrl, request);
   }
 }
src/app/modules/admin/components/tools/send-email-page/send-email-page.component.html (3)

9-68: Consider using Angular's form validation for better UX.

The form uses HTML5 required attributes but lacks Angular form validation feedback. Users don't see validation errors before submission.

Consider wrapping in a <form> element with template-driven or reactive form validation to display inline error messages.

♻️ Example: Add form validation feedback
-      <div class="mb-3">
-        <label for="fromEmail" class="form-label">From <span class="text-danger">*</span></label>
-        <input
-          type="email"
-          id="fromEmail"
-          [(ngModel)]="emailRequest.from"
-          class="form-control"
-          placeholder="sender@yourdomain.com"
-          required
-        />
+      <div class="mb-3">
+        <label for="fromEmail" class="form-label">From <span class="text-danger">*</span></label>
+        <input
+          type="email"
+          id="fromEmail"
+          [(ngModel)]="emailRequest.from"
+          `#fromEmail`="ngModel"
+          class="form-control"
+          [class.is-invalid]="fromEmail.invalid && fromEmail.touched"
+          placeholder="sender@yourdomain.com"
+          required
+        />
+        <div class="invalid-feedback" *ngIf="fromEmail.invalid && fromEmail.touched">
+          Valid email address is required
+        </div>

70-83: Method call in template binding may impact performance.

isFormValid() is called on every change detection cycle. For simple validation this is acceptable, but consider caching the result in a getter or using Angular's built-in form validation state.

Based on coding guidelines for Angular templates, you might also consider using the newer @if control flow syntax available in Angular 17+:

-          <span *ngIf="isSending">
+          `@if` (isSending) {
+            <span>
             <span class="spinner-border spinner-border-sm me-2" role="status"></span>
             Sending...
-          </span>
-          <span *ngIf="!isSending">Send Email</span>
+            </span>
+          } `@else` {
+            <span>Send Email</span>
+          }

77-79: Consider adding aria-label to the spinner for screen readers.

The spinner has role="status" which is good, but adding an aria-label would provide better context for screen reader users.

-            <span class="spinner-border spinner-border-sm me-2" role="status"></span>
+            <span class="spinner-border spinner-border-sm me-2" role="status" aria-label="Sending email"></span>

Comment on lines +31 to +55
sendEmail(): void {
if (!this.isFormValid()) {
this.alertService.error("Please fill in all required fields");
return;
}

this.isSending = true;

this.adminEmailService
.sendEmail(this.emailRequest)
.pipe(untilDestroyed(this))
.subscribe({
next: (response) => {
this.isSending = false;
if (response.success) {
this.alertService.success(response.message);
} else {
this.alertService.error(response.message);
}
},
error: (err) => {
this.isSending = false;
this.alertService.error("Failed to send email: " + (err.message || "Unknown error"));
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize address fields before sending (validation/payload mismatch).

isFormValid() trims inputs, but sendEmail() submits raw strings. Trailing spaces (or an empty cc) can pass validation yet be rejected by the backend. Consider trimming address fields and omitting cc when blank before sending.

✅ Suggested fix
   sendEmail(): void {
     if (!this.isFormValid()) {
       this.alertService.error("Please fill in all required fields");
       return;
     }

     this.isSending = true;
+    const cc = this.emailRequest.cc?.trim();
+    const request: SendCustomEmailRequest = {
+      to: this.emailRequest.to.trim(),
+      from: this.emailRequest.from.trim(),
+      subject: this.emailRequest.subject.trim(),
+      htmlBody: this.emailRequest.htmlBody,
+      ...(cc ? { cc } : {}),
+    };

     this.adminEmailService
-      .sendEmail(this.emailRequest)
+      .sendEmail(request)
       .pipe(untilDestroyed(this))
       .subscribe({
🤖 Prompt for AI Agents
In
`@src/app/modules/admin/components/tools/send-email-page/send-email-page.component.ts`
around lines 31 - 55, The sendEmail() method currently sends raw
this.emailRequest which may include trailing spaces or empty cc values; before
calling adminEmailService.sendEmail, normalize address fields by trimming
strings (to, from, subject, body, and cc entries) and if cc is empty after
trimming, remove the cc property or set it to undefined so it is omitted from
the payload; update sendEmail() to build a sanitized payload (or overwrite
this.emailRequest) and then pass that sanitized object to
adminEmailService.sendEmail(...), keeping the existing isFormValid() and error
handling intact.

@maximgorbatyuk
maximgorbatyuk deleted the email branch January 20, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant