Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NotificationPreference for weekly reminder #1867

Merged
merged 8 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

class InternalApi::V1::TeamMembers::NotificationPreferencesController < InternalApi::V1::ApplicationController
def show
authorize notification_preference, policy_class: TeamMembers::NotificationPreferencePolicy
render json: { notification_enabled: notification_preference.notification_enabled }, status: :ok
end

def update
authorize notification_preference, policy_class: TeamMembers::NotificationPreferencePolicy

notification_preference.update!(notification_preference_params)
render json: {
notification_enabled: notification_preference.notification_enabled,
notice: "Preference updated successfully"
}, status: :ok
end

private

def notification_preference
@notification_preference ||= NotificationPreference.find_by(
user_id: params[:team_id],
company_id: current_company.id)
end

def notification_preference_params
params.require(:notification_preference).permit(:notification_enabled)
end
end
11 changes: 11 additions & 0 deletions app/javascript/src/apis/preferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import axios from "./api";

const get = async userId =>
axios.get(`team/${userId}/notification_preferences`);

const updatePreference = async (userId, payload) =>
axios.patch(`team/${userId}/notification_preferences`, payload);

const preferencesApi = { get, updatePreference };

export default preferencesApi;
11 changes: 11 additions & 0 deletions app/javascript/src/components/Profile/Layout/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CalendarIcon,
CakeIcon,
ClientsIcon,
ReminderIcon,
} from "miruIcons";

import OrgDetails from "components/Profile/Organization/Details";
Expand All @@ -19,6 +20,7 @@ import AllocatedDevicesDetails from "components/Profile/Personal/Devices";
import AllocatedDevicesEdit from "components/Profile/Personal/Devices/Edit";
import EmploymentDetails from "components/Profile/Personal/Employment";
import EmploymentDetailsEdit from "components/Profile/Personal/Employment/Edit";
import NotificationPreferences from "components/Profile/Personal/NotificationPreferences";
import UserDetailsView from "components/Profile/Personal/User";
import UserDetailsEdit from "components/Profile/Personal/User/Edit";
import { Roles } from "constants/index";
Expand Down Expand Up @@ -80,6 +82,15 @@ export const SETTINGS = [
category: "personal",
isTab: false,
},
{
label: "NOTIFICATION SETTINGS",
path: "notifications",
icon: <ReminderIcon className="mr-2" size={20} weight="bold" />,
authorisedRoles: [ADMIN, OWNER, BOOK_KEEPER, EMPLOYEE],
Component: NotificationPreferences,
category: "personal",
isTab: true,
},
// Uncomment when Integrating with API
// {
// label: "COMPENSATION",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-disable no-unused-vars */
import React, { Fragment, useEffect, useState } from "react";

import { useParams } from "react-router-dom";

import preferencesApi from "apis/preferences";
import CustomToggle from "common/CustomToggle";
import Loader from "common/Loader/index";
import { MobileEditHeader } from "common/Mobile/MobileEditHeader";
import DetailsHeader from "components/Profile/Common/DetailsHeader";
import { useProfileContext } from "context/Profile/ProfileContext";
import { useUserContext } from "context/UserContext";

const NotificationPreferences = () => {
const { user, isDesktop } = useUserContext();
const { memberId } = useParams();
const { isCalledFromSettings } = useProfileContext();
const currentUserId = isCalledFromSettings ? user.id : memberId;

const [isLoading, setIsLoading] = useState<boolean>(false);
const [isSelected, setIsSelected] = useState<boolean>(false);

const getPreferences = async () => {
const res = await preferencesApi.get(currentUserId);
setIsSelected(res.data.notification_enabled);
setIsLoading(false);
};

const updatePreferences = async () => {
setIsLoading(true);
const res = await preferencesApi.updatePreference(currentUserId, {
notification_enabled: !isSelected,
});
setIsLoading(false);
};

useEffect(() => {
setIsLoading(true);
getPreferences();
}, []);

return (
<Fragment>
{isDesktop ? (
<DetailsHeader subTitle="" title="Notification Settings" />
) : (
<MobileEditHeader
backHref={isCalledFromSettings ? "/settings/" : `/team/${memberId}`}
href=""
showEdit={false}
title="Notification Settings"
/>
)}
{isLoading ? (
<Loader className="min-h-70v" />
) : (
<div className="mt-10 flex h-full items-center justify-between bg-miru-gray-100 p-10 lg:mt-4">
<span className="w-1/2">Weekly Email Reminder</span>
<CustomToggle
id={currentUserId}
isChecked={isSelected}
setIsChecked={setIsSelected}
toggleCss="mt-5"
onToggle={updatePreferences}
/>
</div>
)}
</Fragment>
);
};

export default NotificationPreferences;
1 change: 1 addition & 0 deletions app/models/company.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Company < ApplicationRecord
has_many :holidays, dependent: :destroy
has_many :holiday_infos, through: :holidays, dependent: :destroy
has_many :carryovers
has_many :notification_preferences, dependent: :destroy

resourcify

Expand Down
30 changes: 30 additions & 0 deletions app/models/notification_preference.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

# == Schema Information
#
# Table name: notification_preferences
#
# id :bigint not null, primary key
# notification_enabled :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# company_id :bigint not null
# user_id :bigint not null
#
# Indexes
#
# index_notification_preferences_on_company_id (company_id)
# index_notification_preferences_on_user_id (user_id)
# index_notification_preferences_on_user_id_and_company_id (user_id,company_id) UNIQUE
#
# Foreign Keys
#
# fk_rails_... (company_id => companies.id)
# fk_rails_... (user_id => users.id)
#
class NotificationPreference < ApplicationRecord
belongs_to :user
belongs_to :company

validates :notification_enabled, inclusion: { in: [true, false] }
end
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def initialize(msg = "Spam User Login")
has_many :custom_leave_users
has_many :custom_leaves, through: :custom_leave_users, source: :custom_leave
has_many :carryovers
has_many :notification_preferences, dependent: :destroy

rolify strict: true

Expand Down
26 changes: 26 additions & 0 deletions app/policies/team_members/notification_preference_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

class TeamMembers::NotificationPreferencePolicy < ApplicationPolicy
def show?
return false unless record.present?

authorize_current_user
end

def update?
return false unless record.present?

authorize_current_user
end

private

def authorize_current_user
unless user.current_workspace_id == record.company_id
@error_message_key = :different_workspace
return false
end

has_owner_or_admin_role? || record_belongs_to_user?
end
end
7 changes: 7 additions & 0 deletions app/services/create_company_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def initialize(current_user, params: nil, company: nil)
def process
company.save!
add_current_user_to_company
create_notification_preference
company
end

Expand All @@ -24,4 +25,10 @@ def add_current_user_to_company
current_user.add_role(:owner, company)
current_user.save!
end

def create_notification_preference
NotificationPreference.find_or_create_by(
user_id: current_user.id,
company_id: company.id)
end
end
7 changes: 7 additions & 0 deletions app/services/create_invited_user_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def process
find_or_create_user!
add_role_to_invited_user
create_client_member
create_notification_preference
end
rescue StandardError => e
service_failed(e.message)
Expand Down Expand Up @@ -107,6 +108,12 @@ def create_client_member
invitation.company.client_members.create!(client: invitation.client, user:)
end

def create_notification_preference
NotificationPreference.find_or_create_by(
user_id: user.id,
company_id: invitation.company.id)
end

def create_reset_password_token
@reset_password_token = user.create_reset_password_token
end
Expand Down
6 changes: 5 additions & 1 deletion app/services/weekly_reminder_for_missed_entries_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ def process

Company.find_each do |company|
company.users.kept.find_each do |user|
check_entries_and_send_mail(user, company)
notification_preference = NotificationPreference.find_by(user_id: user.id, company_id: company.id)

if notification_preference.present? && notification_preference.notification_enabled
check_entries_and_send_mail(user, company)
end
end
Comment on lines +11 to 16
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: I think it would be better if we have this check in SendWeeklyReminderToUserMailer, so that even when we trigger mailer manually, it won't send any email if the user's notification preference is not satisfied.

In future, we can even have this as a before action check in the application mailer, If we plan to use this notification preference for all the mail notifications.

end
end
Expand Down
1 change: 1 addition & 0 deletions config/routes/internal_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
resource :details, only: [:show, :update], controller: "team_members/details"
resource :avatar, only: [:update, :destroy], controller: "team_members/avatar"
collection { put "update_team_members" }
resource :notification_preferences, only: [:show, :update], controller: "team_members/notification_preferences"
end

resources :invitations, only: [:create, :update, :destroy] do
Expand Down
14 changes: 14 additions & 0 deletions db/migrate/20240617135248_create_notification_preferences.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

class CreateNotificationPreferences < ActiveRecord::Migration[7.1]
def change
create_table :notification_preferences do |t|
t.references :user, null: false, foreign_key: true
t.references :company, null: false, foreign_key: true
t.boolean :notification_enabled, default: false, null: false
t.index [:user_id, :company_id], unique: true

t.timestamps
end
end
end
15 changes: 14 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading