9 Commits

Author SHA1 Message Date
Anatolii Grynchuk b0996833bc feat: add RabbitMQ worker, contracts, usage UI in channels screen
- Add HrynCo.NotificationService.Contracts project with SendEmailMessage and NotificationResultMessage
- Add SendEmailConsumer (RabbitMQ worker) with reply-to pattern via CorrelationContext.ReplyTo
- Add SendEmailHandler owning SMTP send + usage increment as business logic
- Add GetChannelUsageSummaryHandler with single DB query via navigation property
- Merge usage stats inline into channels list (daily/monthly with progress bars)
- Refactor AdminChannelsController.Index to use GetChannelUsageSummaryQuery
- Add RabbitMQ service to docker-compose files
- Remove dead AdminChannelUsageController, ChannelUsageViewModel, ChannelUsageSummary

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 14:00:58 +03:00
Anatolii Grynchuk 349a5ac560 fix: serialize smtp settings using runtime type not base class
JsonSerializer.Serialize(p.Settings) uses the compile-time type
EmailChannelSettings, producing only {EmailChannelType:1} in the DB.
Use p.Settings.GetType() to force runtime type so all SmtpChannelSettings
properties (Host, Port, Username, Password, etc.) are actually persisted.

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 03:32:50 +03:00
Anatolii Grynchuk 215285d3c0 fix: channel save tracking conflict and test modal rendering
- Use AsNoTracking() on all EmailChannelRepository read methods to prevent
  EF identity conflict when Update() attaches a new entity with same key
- Move test modal to @section Scripts rendered at end of <body> so
  bootstrap.Modal is available and modal is not nested inside card DOM
- Add @RenderSection('Scripts') forwarding in _EditorLayout to bubble
  child scripts sections up to _Layout
- Switch Test button to programmatic bootstrap.Modal() open instead of
  data-bs-toggle (more reliable across layout section boundaries)

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 03:29:54 +03:00
Anatolii Grynchuk c90b07386d feat: polished admin UI styles + email channels admin CRUD
- Extract inline styles to wwwroot/css/admin.css
- Bootstrap Icons for nav and buttons
- Styled page headers, table, empty state, readonly fields
- Email Channels admin: list, create, edit, delete
- GetAllEmailChannelsQuery + handler
- AdminChannelsController with full CRUD
- form id + form= attribute pattern for EditorLayout footer buttons

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 02:43:59 +03:00
Anatolii Grynchuk 2a0a5f737d feat: add MVC Razor admin UI for email templates
- GetAllEmailTemplatesQuery + handler (new GetAll use case)
- IEmailTemplateRepository.GetAllAsync + EF implementation
- AdminTemplatesController: Index, Create, Edit, Save, Delete
- EmailTemplateEditViewModel with IsNew/PageTitle helpers
- Views/_ViewStart, _ViewImports, Shared/_Layout (Bootstrap 5)
- Shared/_EditorLayout (chained layout for all edit screens)
- Views/AdminTemplates/Index (table with edit/delete actions)
- Views/AdminTemplates/Edit (card form, readonly composite key on edit)
- Program.cs: AddControllersWithViews, UseStaticFiles, MapDefaultControllerRoute

Ref: IT-634

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 02:06:08 +03:00
Anatolii Grynchuk 5cf5f888eb feat: wire up Program.cs, DI extensions, Serilog, and appsettings
- ServiceCollectionExtensions in DAL.EF: AddNotificationDataAccess (DbContext, UoW, repositories)
- ServiceCollectionExtensions in Services: AddNotificationServices (MediatR + TransactionBehavior)
- Api/Program.cs: Serilog, OpenAPI, controllers, DI wiring
- Worker/Program.cs: Serilog, DI wiring
- appsettings.json: Serilog config with Console + Seq sinks, connection string
- appsettings.Development.json: Debug log level overrides

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 00:24:54 +03:00
Anatolii Grynchuk 6dcc911fc2 refactor: rename domain types and introduce TransactionBehavior pattern
- Rename Template -> EmailTemplate, Provider -> EmailChannel,
  ProviderSettings -> EmailChannelSettings, ProviderType -> EmailChannelType,
  ProviderUsage -> EmailChannelUsage throughout all layers
- Add Undefined = 0 to EmailChannelType enum for safe default handling
- Remove SaveChangesAsync from EfRepository methods — repositories now only stage changes
- Add SaveChangesAsync to IUnitOfWork and EfUnitOfWork
- Add TransactionBehavior MediatR pipeline: wraps every handler in a transaction,
  saves and commits on success, rolls back on exception
- Add MediatR package reference to Services project

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-02 00:16:47 +03:00
Anatolii Grynchuk ce1ef1fea6 refactor: rename NotificationEfRepository to EfRepository
Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-01 23:34:11 +03:00
Anatolii Grynchuk 4f573da374 feat: add repository layer with IUnitOfWork and fixed EF base
- ITransaction, IUnitOfWork in DAL.Abstract
- EfTransactionAdapter, EfUnitOfWork<TDbContext>, NotificationUnitOfWork in DAL.EF
- NotificationEfRepository<TEntity>: async-only base, fixed Exists (AnyAsync),
  fixed batch Add (AddRangeAsync), single SaveChangesAsync per operation
- TemplateRepository, ProviderRepository, ProviderUsageRepository
- ProviderUsageRepository.IncrementAsync uses atomic PostgreSQL upsert
- ProviderRepository deserializes settings polymorphically via ProviderType discriminator

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-01 23:18:41 +03:00