@bigbinary/neeto-email-notifications-frontend
v2.2.19
Published
A repo acts as the source of truth for the new nano's structure, configs, data etc.
Downloads
881
Readme
neeto-email-notifications-nano
The neeto-email-notifications-nano is a comprehensive email notification management solution designed for the Neeto ecosystem. Implemented as a Ruby on Rails engine with associated React frontend components (@bigbinary/neeto-email-notifications-frontend), it provides a unified interface for managing email notifications across neeto applications.
Table of Contents
- Installation (Backend Engine)
- Configuration (Backend Engine)
- Frontend Integration
- Core Concepts
- Usage Examples
- API Endpoints
- Liquid Template Variables
- Email Validation
- Incineration Concern
- Development Environment Setup
- Testing & Debugging
- Publishing
Installation (Backend Engine)
Follow these steps to integrate the neeto-email-notifications-engine into your host Rails application:
1. Add the Gem
Add the gem to your application's Gemfile:
# Gemfile
source "NEETO_GEM_SERVER_URL" do
gem "neeto-email-notifications-engine"
end2. Install Gems
Run bundler to install the gem and its dependencies:
bundle install3. Install Migrations
Add required migrations in the db/migrate folder. Run the following
commands to generate the migrations.
bundle exec rails g neeto_email_notifications_engine:installThis will generate the migration to create the
neeto_email_notifications_engine_email_notifications table, which holds the
data for the email notifications, and have custom_fields column to include
custom attributes.
4. Run Migrations
Apply the migrations to your database:
bundle exec rails db:migrate5. Mount the Engine
Add the engine's routes to your application's config/routes.rb:
# config/routes.rb
mount NeetoEmailNotificationsEngine::Engine, at: "/neeto_email_notifications"NOTE: The mount point & base URL must be /neeto_email_notifications and cannot be changed to any other path.
Note: The engine uses the
liquidgem for template rendering. This will be installed automatically as a dependency.
Configuration (Backend Engine)
1. Initializer Setup
Create an initializer file config/initializers/neeto_email_notifications_engine.rb to configure the engine:
# config/initializers/neeto_email_notifications_engine.rb
NeetoEmailNotificationsEngine.configure do |config|
# IMPORTANT: Configure which model types can hold email notifications.
# This configuration is MANDATORY for the engine to work properly.
config.allowed_notification_holder_types = ["PaymentPlan", "SomeOtherModel"]
endThe allowed_notification_holder_types configuration is mandatory. Failing to configure this will result in validation errors when creating email notifications.
2. Models
NeetoEmailNotificationsEngine::EmailNotification (source code)
Key Associations:
belongs_to :notification_holder, polymorphic: true
has_rich_text :messageKey Validations:
notification_holder_typemust be in the configuredallowed_notification_holder_types.subject,messageare required whenis_enabledis true.send_frommust be a valid email format when required.- All email fields (
notify_emails,notify_cc_emails,notify_bcc_emails) are validated for proper email format.
Available Methods:
message_body_content: Returns HTML content of the rich text message.custom_field_key: Abstract method that subclasses must implement.notification_type: Returns the notification type from custom fields.disabled_by_user?: Checks if the notification was disabled by user action.
Setting Up Host Application Models
Ensure the models specified in allowed_notification_holder_types have the correct associations defined:
Single Notification Per Model (Most Common)
# app/models/form.rb
class Form < ApplicationRecord
# Standard single notification association
has_one :email_notification, as: :notification_holder,
class_name: "::NeetoEmailNotificationsEngine::EmailNotification", dependent: :destroy
# Optional: Create default notification after model creation
after_create :seed_email_notification
private
def seed_email_notification
self.create_email_notification!(
send_from: Rails.application.secrets.mailer[:default_from_email],
notify_emails: [],
is_enabled: false,
subject: "New {{form-name}} submission",
message: "A new submission has been received for {{form-name}}."
)
end
endMultiple Notification Types Per Model
# app/models/meeting.rb
class Meeting < ApplicationRecord
# General association for all notifications
has_many :email_notifications, as: :notification_holder,
class_name: "::NeetoEmailNotificationsEngine::EmailNotification", dependent: :destroy
# Specific notification types using custom fields
has_one :reminder_notification,
-> { where("custom_fields -> 'notification_type' ? :value", value: "reminder") },
as: :notification_holder, class_name: "::NeetoEmailNotificationsEngine::EmailNotification"
has_one :confirmation_notification,
-> { where("custom_fields -> 'notification_type' ? :value", value: "confirmation") },
as: :notification_holder, class_name: "::NeetoEmailNotificationsEngine::EmailNotification"
has_one :cancellation_notification,
-> { where("custom_fields -> 'notification_type' ? :value", value: "cancellation") },
as: :notification_holder, class_name: "::NeetoEmailNotificationsEngine::EmailNotification"
after_create :setup_default_notifications
private
def setup_default_notifications
create_reminder_notification!(
send_from: "[email protected]",
subject: "Reminder: {{meeting-title}} starts in 1 hour",
message: "Your meeting {{meeting-title}} is scheduled to start in 1 hour.",
custom_fields: { notification_type: "reminder" },
is_enabled: false
)
create_confirmation_notification!(
send_from: "[email protected]",
subject: "Meeting Confirmed: {{meeting-title}}",
message: "Your meeting {{meeting-title}} has been confirmed for {{meeting-date}}.",
custom_fields: { notification_type: "confirmation" },
is_enabled: true
)
end
endCreating Custom Notification Models
# app/models/submission_email_notification.rb
class SubmissionEmailNotification < ::NeetoEmailNotificationsEngine::EmailNotification
# Scope to only submission notifications
default_scope { where("custom_fields -> 'notification_type' ? :value", value: "submission") }
after_initialize :set_notification_type
def custom_field_key
"notification_type"
end
private
def set_notification_type
self.custom_fields ||= {}
self.custom_fields[custom_field_key] = "submission"
end
end
# Usage in host model
class Form < ApplicationRecord
has_one :submission_email_notification, as: :notification_holder, dependent: :destroy
# You can still have the general association too
has_one :email_notification, as: :notification_holder,
class_name: "::NeetoEmailNotificationsEngine::EmailNotification", dependent: :destroy
endDatabase Schema Information
The engine creates a table neeto_email_notifications_engine_email_notifications with these key columns:
notification_holder_type¬ification_holder_id: Polymorphic association.notify_emails: Array of recipient email addresses.notify_cc_emails¬ify_bcc_emails: Arrays for CC and BCC recipients.send_from: Sender email address.subject: Email subject (supports Liquid templates).message: Rich text message content (supports Liquid templates).is_enabled: Boolean flag to enable/disable notifications.custom_fields: JSONB column for additional metadata.reply_to_id: Optional reference for reply-to configuration.type: String column for Single Table Inheritance (STI) support.
Default Attributes Class Method
Important: Host applications must define the default_attributes class method in their custom notification models that inherit from NeetoEmailNotificationsEngine::EmailNotification. This method is required and will receive the notification_holder as a parameter. This method should return a hash with the default attributes for the email notification.
Variables in the subject and message must always be wrapped like this:
<span data-variable data-label="label" data-id="key">{{key}}</span>Example Implementation
def self.default_attributes(quiz)
{
send_from: Rails.application.secrets.mailer[:default_from_email],
notify_emails: [quiz.user.email],
is_enabled: true,
subject: "A new submission has arrived for {{quiz-name}}",
message: <<~HTML.squish
<b><span data-variable data-label="Quiz name" data-id="quiz-name">{{quiz-name}}</span></b>
has a new submission.<br/><br/>
<span data-variable data-label="All answers" data-id="all-answers">{{all-answers}}</span>
HTML
}
endFrontend Integration
1. Install Frontend Package
yarn add @bigbinary/neeto-email-notifications-frontendThis package will provide a single component NeetoEmailNotification, which
uses components from neeto-molecules.
2. Install Peer Dependencies
If the host app doesn't already include these peer dependencies, install them:
# DO NOT INSTALL THE EXACT VERSIONS MENTIONED BELOW AS THEY MIGHT BE OUTDATED.
# ALWAYS PREFER INSTALLING THE LATEST COMPATIBLE VERSIONS.
yarn add @babel/runtime@^7.26.10 @bigbinary/neeto-cist@^1.0.17 @bigbinary/neeto-commons-frontend@^4.13.43 @bigbinary/neeto-editor@^1.47.16 @bigbinary/neeto-filters-frontend@^4.3.21 @bigbinary/neeto-icons@^1.20.49 @bigbinary/neeto-molecules@^3.16.63 @bigbinary/neetoui@^8.3.9 @honeybadger-io/js@^6.10.1 @honeybadger-io/react@^6.1.25 @tailwindcss/container-queries@^0.1.1 @tanstack/react-query@^5.59.20 @tanstack/react-query-devtools@^5.59.20 antd@^5.22.0 axios@^1.8.2 buffer@^6.0.3 classnames@^2.5.1 crypto-browserify@^3.12.1 dompurify@^3.2.4 formik@^2.4.6 https-browserify@^1.0.0 i18next@^22.5.1 js-logger@^1.6.1 mixpanel-browser@^2.47.0 os-browserify@^0.3.0 path-browserify@^1.0.1 qs@^6.11.2 ramda@^0.29.0 react@^18.3.1 react-dom@^18.3.1 react-helmet@^6.1.0 react-i18next@^12.3.1 react-router-dom@^5.3.3 react-toastify@^8.0.2 source-map-loader@^4.0.1 stream-browserify@^3.0.0 stream-http@^3.2.0 tailwindcss@^3.4.14 tty-browserify@^0.0.1 url@^0.11.0 util@^0.12.5 vm-browserify@^1.1.2 yup@^0.32.11 zustand@^4.4.2Note: Carefully manage potential version conflicts with your host application.
3. Components
NeetoEmailNotificationForm (source code)
Props
| Prop | Type | Default | Description |
| ------------------------- | --------- | -------------------------------------------------------- | ---------------------------------------------------------------- |
| emailNotificationParams | Object | {} | Required. Parameters for fetching email notification data |
| title | String | "Email notification" | Title displayed above the notification toggle |
| notificationToggleLabel | String | "Send an email notification when a new event occurred" | The label displayed beside the notification toggle switch |
| disabled | Boolean | false | Disables the entire component |
| onSuccess | Function | noop | Callback function triggered after successful notification update |
| breadcrumbs | Array | [] | Breadcrumb navigation data |
| children | ReactNode | undefined | Additional content rendered when notifications are enabled |
| blockNavigation | Boolean | false | Shows block navigation alert for unsaved changes |
| helpPopoverProps | Object | {} | Props for the help popover component |
| tooltipProps | Object | {} | Props for tooltip shown when component is disabled |
| emailFormProps | Object | {} | Props passed to the underlying EmailForm component |
| emailFormikProps | Object | {} | Props passed to the EmailFormProvider component |
| emailPreviewProps | Object | {} | Props passed to the EmailPreview component |
| fieldsVisibility | Object | {} | Controls which fields are visible in the email form |
FieldsVisibility
Default visibility for each field is as follows:
{
showSendToField: true,
showReplyToField: false,
showSendToAsRadio: false,
showCcField: false,
showBccField: false,
}You can override any of these by passing a fieldsVisibility prop with your
desired values.
emailNotificationParams Object Structure
{
notificationHolderId: "uuid-string", // ID of the notification holder
notificationHolderType: "Form", // Type of the notification holder
customFields: { // Optional custom fields for filtering
notificationType: "submission" // Example: different notification types
}
}Usage Example
import React from "react";
import { NeetoEmailNotificationForm } from "@bigbinary/neeto-email-notifications-frontend";
const FormSettingsPage = ({ formId }) => {
const handleNotificationUpdate = () => {
console.log("Email notification updated successfully!");
};
return (
<NeetoEmailNotificationForm
title="Form Submission Notification"
notificationToggleLabel="Get notified when someone submits your form"
emailNotificationParams={{
notificationHolderId: formId,
notificationHolderType: "Form",
customFields: { notificationType: "submission" }
}}
emailPreviewProps={{
formatBody: message => replaceVariablesWithDummyValues(message, values),
}}
breadcrumbs={[
{ text: "Forms", link: "/forms" },
{ text: "Settings", link: `/forms/${formId}/settings` }
]}
onSuccess={handleNotificationUpdate}
/>
);
};
export default FormSettingsPage;4. Hooks
useFetchEmailNotifications (source code)
Fetches email notification data for a specific notification holder.
import { useFetchEmailNotifications } from "@bigbinary/neeto-email-notifications-frontend";
const { data, isLoading, error } = useFetchEmailNotifications({
notificationHolderId: "form-uuid",
notificationHolderType: "Form",
customFields: { notificationType: "submission" }
});useUpdateEmailNotification (source code)
Updates email notification settings.
import { useUpdateEmailNotification } from "@bigbinary/neeto-email-notifications-frontend";
const { mutate: updateEmailNotification, isPending } = useUpdateEmailNotification();
const handleUpdate = (notificationData) => {
updateEmailNotification({
...notificationData,
notificationHolderId: "form-uuid",
notificationHolderType: "Form"
});
};Usage Examples
Creating Custom Notification Models
You can create specialized notification models that inherit from the base EmailNotification class:
class SubmissionEmailNotification < ::NeetoEmailNotificationsEngine::EmailNotification
default_scope { where("custom_fields -> 'notification_type' ? :value", value: "submission") }
after_initialize :set_notification_type
def custom_field_key
"notification_type"
end
private
def set_notification_type
self.custom_fields ||= {}
self.custom_fields[custom_field_key] = "submission"
end
endSetting Up Notification Holders
Configure your models to support email notifications:
class Meeting < ApplicationRecord
has_one :reminder_notification, -> { where("custom_fields -> 'notification_type' ? :value", value: "reminder") },
as: :notification_holder, class_name: "::NeetoEmailNotificationsEngine::EmailNotification"
has_one :confirmation_notification, -> { where("custom_fields -> 'notification_type' ? :value", value: "confirmation") },
as: :notification_holder, class_name: "::NeetoEmailNotificationsEngine::EmailNotification"
after_create :setup_notifications
private
def setup_notifications
create_reminder_notification!(
send_from: "[email protected]",
subject: "Reminder: {{meeting-title}} in 1 hour",
message: "Your meeting {{meeting-title}} is scheduled to start in 1 hour.",
custom_fields: { notification_type: "reminder" }
)
create_confirmation_notification!(
send_from: "[email protected]",
subject: "Meeting Confirmed: {{meeting-title}}",
message: "Your meeting {{meeting-title}} has been confirmed for {{meeting-date}}.",
custom_fields: { notification_type: "confirmation" }
)
end
endSeeding Default Email Notification Content
It is recommended to automatically create a default email notification record for each instance of your model. This ensures the notification UI and API always have a record to fetch and update, preventing errors and missing functionality. You can use an after_create callback in your model to seed this record.
Example:
class Quiz < ApplicationRecord
has_many :email_notifications, as: :notification_holder,
class_name: "NeetoEmailNotificationsEngine::EmailNotification", dependent: :destroy
has_one :push_email_notification, as: :notification_holder
after_create :create_default_email_notification
private
def create_default_email_notification
self.create_push_email_notification!(PushEmailNotification.default_attributes(self))
end
endUsing the Email Notification Service
Create service classes that include the email notification functionality:
class MeetingReminderService
include ::NeetoEmailNotificationsEngine::EmailNotificationService
def initialize(meeting)
@meeting = meeting
@email_notification = meeting.reminder_notification
end
private
def send_emails
return unless @email_notification.is_enabled?
MeetingMailer.reminder_email(
recipients: @email_notification.notify_emails,
cc: @email_notification.notify_cc_emails,
bcc: @email_notification.notify_bcc_emails,
subject: @subject,
message: @message,
meeting: @meeting
).deliver_now
end
def load_liquid_parameters
@liquid_parameters[:text] = {
"meeting-title" => @meeting.title,
"meeting-date" => @meeting.scheduled_at.strftime("%B %d, %Y"),
"meeting-time" => @meeting.scheduled_at.strftime("%I:%M %p")
}
@liquid_parameters[:html] = @liquid_parameters[:text]
end
end
# Usage
meeting = Meeting.find(params[:id])
MeetingReminderService.new(meeting).processAPI Endpoints
The engine exposes API endpoints under the configured mount path (default /neeto_email_notifications):
| Method | Path | Description | Parameters |
|--------|------|-------------|------------|
| GET | /email_notification | Fetch email notification settings | notification_holder_id, notification_holder_type, custom_fields |
| PUT | /email_notification | Update email notification settings | notification_holder_id, notification_holder_type, email_notification params |
Example API Usage:
// Fetch notification settings
axios.get('/neeto_email_notifications/email_notification', {
params: {
notification_holder_id: 'form-uuid',
notification_holder_type: 'Form',
custom_fields: { notification_type: 'submission' }
}
})
// Update notification settings
axios.put('/neeto_email_notifications/email_notification', {
notification_holder_id: 'form-uuid',
notification_holder_type: 'Form',
email_notification: {
is_enabled: true,
subject: 'New form submission',
message: 'A new submission was received.',
notify_emails: ['[email protected]'],
send_from: '[email protected]'
}
})Liquid Template Variables
Email subjects and messages support Liquid templating for dynamic content:
# In your notification setup
email_notification.update!(
subject: "New {{form-name}} submission from {{user-name}}",
message: "Hello {{admin-name}}, a new submission was received for {{form-name}} on {{submission-date}}."
)
# In your service class
def load_liquid_parameters
@liquid_parameters[:text] = {
"form-name" => @form.name,
"user-name" => @submission.user.name,
"admin-name" => @form.owner.name,
"submission-date" => @submission.created_at.strftime("%B %d, %Y")
}
@liquid_parameters[:html] = @liquid_parameters[:text]
endDevelopment Environment Setup
Instructions for Development
Check the Frontend package development guide for step-by-step instructions to develop the frontend package.
Backend Development
Rails Server: Start the main application
bundle exec rails serverDatabase Setup: Ensure migrations are run
bundle exec rails db:migrateTesting: Run the test suite
bundle exec rails test
Testing & Debugging
Test Helpers
The engine provides factories for testing:
# Use in your tests
FactoryBot.create(:neeto_email_notification_engine_email_notification,
notification_holder: your_model_instance
)Use the test/dummy app within the engine's repository for isolated testing.
Publishing
For instructions on building and releasing the @bigbinary/neeto-email-notifications-frontend NPM package and the neeto-email-notifications-engine Ruby gem, please refer to the internal guide: Building and Releasing Packages.
