mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Use reminders form component for the admin view as well and remove now outdated angular components
This commit is contained in:
@@ -48,12 +48,12 @@ module My
|
||||
{
|
||||
name: "notifications",
|
||||
path: helpers.my_notifications_path(tab: "notifications"),
|
||||
label: I18n.t("js.notifications.settings.title")
|
||||
label: t("my_account.notifications_and_email.tabs.notifications")
|
||||
},
|
||||
{
|
||||
name: "reminders",
|
||||
path: helpers.my_notifications_path(tab: "reminders"),
|
||||
label: I18n.t("js.reminders.settings.title")
|
||||
label: t("my_account.notifications_and_email.tabs.email_reminders")
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
@@ -31,7 +31,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
settings_primer_form_with(
|
||||
model: @user.pref,
|
||||
scope: "pref[immediate_reminders]",
|
||||
url: { action: "update_settings" },
|
||||
url: update_url,
|
||||
method: :patch,
|
||||
data: { turbo: false }
|
||||
) do |form|
|
||||
@@ -43,7 +43,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
settings_primer_form_with(
|
||||
model: daily_reminders_form_model,
|
||||
scope: "pref[daily_reminders]",
|
||||
url: { action: "update_settings" },
|
||||
url: update_url,
|
||||
method: :patch,
|
||||
data: {
|
||||
turbo: false,
|
||||
@@ -59,7 +59,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
settings_primer_form_with(
|
||||
model: @user.pref,
|
||||
scope: :pref,
|
||||
url: { action: "update_settings" },
|
||||
url: update_url,
|
||||
method: :patch,
|
||||
data: { turbo: false }
|
||||
) do |form|
|
||||
@@ -71,7 +71,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
settings_primer_form_with(
|
||||
model: pause_reminders_form_model,
|
||||
scope: "pref[pause_reminders]",
|
||||
url: { action: "update_settings" },
|
||||
url: update_url,
|
||||
method: :patch,
|
||||
data: {
|
||||
turbo: false,
|
||||
@@ -87,7 +87,7 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
settings_primer_form_with(
|
||||
model: global_notification_setting,
|
||||
scope: :notification_setting,
|
||||
url: { action: "update_email_alerts" },
|
||||
url: update_email_alerts_url,
|
||||
method: :patch,
|
||||
data: { turbo: false }
|
||||
) do |form|
|
||||
|
||||
@@ -33,13 +33,15 @@ module My
|
||||
class ShowPageComponent < ApplicationComponent
|
||||
include OpPrimer::FormHelpers
|
||||
|
||||
attr_reader :global_notification_setting
|
||||
attr_reader :global_notification_setting, :update_url, :update_email_alerts_url
|
||||
|
||||
def initialize(user:, global_notification_setting:)
|
||||
def initialize(user:, global_notification_setting:, update_url:, update_email_alerts_url:)
|
||||
super
|
||||
|
||||
@user = user
|
||||
@global_notification_setting = global_notification_setting
|
||||
@update_url = update_url
|
||||
@update_email_alerts_url = update_email_alerts_url
|
||||
end
|
||||
|
||||
def daily_reminders_form_model
|
||||
|
||||
@@ -39,6 +39,7 @@ class UsersController < ApplicationController
|
||||
before_action :find_user, only: %i[show
|
||||
edit
|
||||
update
|
||||
update_email_alerts
|
||||
change_status_info
|
||||
change_status
|
||||
destroy
|
||||
@@ -109,6 +110,17 @@ class UsersController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def update_email_alerts
|
||||
global_setting = @user.notification_settings.find_or_initialize_by(project: nil)
|
||||
if global_setting.update(permitted_params.notification_setting_email_alerts)
|
||||
flash[:notice] = I18n.t(:notice_successful_update)
|
||||
else
|
||||
flash[:error] = I18n.t(:notice_failed_to_save_messages, count: global_setting.errors.count,
|
||||
object: global_setting.class.model_name.human)
|
||||
end
|
||||
redirect_back_or_to edit_user_path(@user, tab: "reminders")
|
||||
end
|
||||
|
||||
def update # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
||||
update_params = build_user_update_params
|
||||
call = ::Users::UpdateService.new(model: @user, user: current_user).call(update_params)
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
class My::Reminders::ImmediateRemindersForm < ApplicationForm
|
||||
form do |f|
|
||||
f.fieldset_group(title: helpers.t("my_account.email_reminders.immediate_reminders.title")) do |fg|
|
||||
f.fieldset_group(title: helpers.t("my_account.email_reminders.immediate_reminders.title"), mt: 2) do |fg|
|
||||
fg.check_box(
|
||||
name: :mentioned,
|
||||
label: helpers.t("my_account.email_reminders.immediate_reminders.mentioned")
|
||||
|
||||
@@ -32,7 +32,14 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
<%= render(My::Notifications::ShowPageHeaderComponent.new) %>
|
||||
|
||||
<% if params[:tab] == "reminders" %>
|
||||
<%= render(My::Reminders::ShowPageComponent.new(user: @user, global_notification_setting: @global_notification_setting)) %>
|
||||
<%= render(
|
||||
My::Reminders::ShowPageComponent.new(
|
||||
user: @user,
|
||||
global_notification_setting: @global_notification_setting,
|
||||
update_url: { action: "update_settings" },
|
||||
update_email_alerts_url: { action: "update_email_alerts" }
|
||||
)
|
||||
) %>
|
||||
<% else %>
|
||||
<%= angular_component_tag "opce-notification-settings" %>
|
||||
<% end %>
|
||||
|
||||
@@ -26,4 +26,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
++#%>
|
||||
<%= angular_component_tag "opce-reminder-settings", inputs: { userId: @user.id } %>
|
||||
<%= render(My::Reminders::ShowPageComponent.new(
|
||||
user: @user,
|
||||
global_notification_setting: @user.notification_settings.find_or_initialize_by(project: nil),
|
||||
update_url: user_path(@user),
|
||||
update_email_alerts_url: update_email_alerts_user_path(@user)
|
||||
)) %>
|
||||
|
||||
@@ -3528,6 +3528,9 @@ en:
|
||||
my_account:
|
||||
notifications_and_email:
|
||||
title: "Notification and email"
|
||||
tabs:
|
||||
notifications: "Notification settings"
|
||||
email_reminders: "Email reminders"
|
||||
access_tokens:
|
||||
description: "Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them."
|
||||
no_results:
|
||||
|
||||
@@ -701,40 +701,6 @@ en:
|
||||
Please choose a project to create the work package in to see all attributes.
|
||||
You can only select projects which have the type above activated.
|
||||
|
||||
reminders:
|
||||
settings:
|
||||
daily:
|
||||
add_time: "Add time"
|
||||
enable: "Enable daily email reminders"
|
||||
explanation: "You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone}"
|
||||
no_time_zone: "Until you configure a time zone for your account, the times will be interpreted to be in UTC."
|
||||
time_label: "Time %{counter}:"
|
||||
title: "Send me daily email reminders for unread notifications"
|
||||
workdays:
|
||||
title: "Receive email reminders on these days"
|
||||
immediate:
|
||||
title: "Send me an email reminder"
|
||||
mentioned: "Immediately when someone @mentions me"
|
||||
personal_reminder: "Immediately when I receive a personal reminder"
|
||||
alerts:
|
||||
title: "Email alerts for other items (that are not work packages)"
|
||||
explanation: >
|
||||
Notifications today are limited to work packages.
|
||||
You can choose to continue receiving email alerts for these events until they are included in notifications:
|
||||
news_added: "News added"
|
||||
news_commented: "Comment on a news item"
|
||||
document_added: "Documents added"
|
||||
forum_messages: "New forum messages"
|
||||
wiki_page_added: "Wiki page added"
|
||||
wiki_page_updated: "Wiki page updated"
|
||||
membership_added: "Membership added"
|
||||
membership_updated: "Membership updated"
|
||||
title: "Email reminders"
|
||||
pause:
|
||||
label: "Temporarily pause daily email reminders"
|
||||
first_day: "First day"
|
||||
last_day: "Last day"
|
||||
|
||||
text_are_you_sure: "Are you sure?"
|
||||
text_are_you_sure_to_cancel: "You have unsaved changes on this page. Are you sure you want to discard them?"
|
||||
breadcrumb: "Breadcrumb"
|
||||
|
||||
@@ -950,6 +950,7 @@ Rails.application.routes.draw do
|
||||
get "/change_status/:change_action" => "users#change_status_info", as: "change_status_info"
|
||||
post :change_status
|
||||
post :resend_invitation
|
||||
patch :update_email_alerts
|
||||
get :deletion_info
|
||||
end
|
||||
end
|
||||
|
||||
@@ -138,9 +138,7 @@ import { OpenProjectJobStatusModule } from 'core-app/features/job-status/openpro
|
||||
import {
|
||||
NotificationsSettingsPageComponent,
|
||||
} from 'core-app/features/user-preferences/notifications-settings/page/notifications-settings-page.component';
|
||||
import {
|
||||
ReminderSettingsPageComponent,
|
||||
} from 'core-app/features/user-preferences/reminder-settings/page/reminder-settings-page.component';
|
||||
|
||||
import { OpenProjectMyAccountModule } from 'core-app/features/user-preferences/user-preferences.module';
|
||||
import { OpAttachmentsComponent } from 'core-app/shared/components/attachments/attachments.component';
|
||||
import {
|
||||
@@ -391,7 +389,7 @@ export class OpenProjectModule implements DoBootstrap {
|
||||
|
||||
// TODO: These elements are now registered custom elements, but are actually single-use components. They should be removed when we move these pages to Rails.
|
||||
registerCustomElement('opce-notification-settings', NotificationsSettingsPageComponent, { injector });
|
||||
registerCustomElement('opce-reminder-settings', ReminderSettingsPageComponent, { injector });
|
||||
|
||||
registerCustomElement('opce-notification-center', InAppNotificationCenterComponent, { injector });
|
||||
registerCustomElement('opce-wp-split-view', WorkPackageSplitViewEntryComponent, { injector });
|
||||
registerCustomElement('opce-wp-full-view', WorkPackageFullViewEntryComponent, { injector });
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
<ng-container [formGroup]="form">
|
||||
<div class="op-form--section-header">
|
||||
<h3 [textContent]="text.title" class="op-form--section-header-title"></h3>
|
||||
<p [textContent]="text.explanation"></p>
|
||||
</div>
|
||||
|
||||
@for (setting of alerts; track setting) {
|
||||
<spot-selector-field
|
||||
[label]="text[setting]"
|
||||
[control]="form.get(setting)"
|
||||
>
|
||||
<input
|
||||
slot="input"
|
||||
type="checkbox"
|
||||
[formControlName]="setting"
|
||||
/>
|
||||
</spot-selector-field>
|
||||
}
|
||||
</ng-container>
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service';
|
||||
import {
|
||||
UntypedFormGroup,
|
||||
FormGroupDirective,
|
||||
} from '@angular/forms';
|
||||
|
||||
export type EmailAlertType =
|
||||
'newsAdded'|'newsCommented'|'documentAdded'|'forumMessages'|'wikiPageAdded'|
|
||||
'wikiPageUpdated'|'membershipAdded'|'membershipUpdated';
|
||||
|
||||
export const emailAlerts:EmailAlertType[] = [
|
||||
'newsAdded',
|
||||
'newsCommented',
|
||||
'documentAdded',
|
||||
'forumMessages',
|
||||
'wikiPageAdded',
|
||||
'wikiPageUpdated',
|
||||
'membershipAdded',
|
||||
'membershipUpdated',
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'op-email-alerts-settings',
|
||||
templateUrl: './email-alerts-settings.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class EmailAlertsSettingsComponent implements OnInit {
|
||||
form:UntypedFormGroup;
|
||||
|
||||
alerts:EmailAlertType[] = emailAlerts;
|
||||
|
||||
text = {
|
||||
title: this.I18n.t('js.reminders.settings.alerts.title'),
|
||||
explanation: this.I18n.t('js.reminders.settings.alerts.explanation'),
|
||||
newsAdded: this.I18n.t('js.reminders.settings.alerts.news_added'),
|
||||
newsCommented: this.I18n.t('js.reminders.settings.alerts.news_commented'),
|
||||
documentAdded: this.I18n.t('js.reminders.settings.alerts.document_added'),
|
||||
forumMessages: this.I18n.t('js.reminders.settings.alerts.forum_messages'),
|
||||
wikiPageAdded: this.I18n.t('js.reminders.settings.alerts.wiki_page_added'),
|
||||
wikiPageUpdated: this.I18n.t('js.reminders.settings.alerts.wiki_page_updated'),
|
||||
membershipAdded: this.I18n.t('js.reminders.settings.alerts.membership_added'),
|
||||
membershipUpdated: this.I18n.t('js.reminders.settings.alerts.membership_updated'),
|
||||
};
|
||||
|
||||
constructor(
|
||||
private I18n:I18nService,
|
||||
private storeService:UserPreferencesService,
|
||||
private rootFormGroup:FormGroupDirective,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.form = this.rootFormGroup.control.get('emailAlerts') as UntypedFormGroup;
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
<ng-container [formGroup]="form">
|
||||
<div class="op-form--section-header">
|
||||
<h3 [textContent]="text.title" class="op-form--section-header-title"></h3>
|
||||
</div>
|
||||
|
||||
<spot-selector-field
|
||||
[label]="text.mentioned"
|
||||
[control]="form.get('mentioned')"
|
||||
>
|
||||
<input
|
||||
slot="input"
|
||||
type="checkbox"
|
||||
formControlName="mentioned"
|
||||
data-qa-immediate-reminder="mentioned"
|
||||
/>
|
||||
</spot-selector-field>
|
||||
|
||||
<spot-selector-field
|
||||
[label]="text.personalReminder"
|
||||
[control]="form.get('personalReminder')"
|
||||
>
|
||||
<input
|
||||
slot="input"
|
||||
type="checkbox"
|
||||
formControlName="personalReminder"
|
||||
data-qa-immediate-reminder="personalReminder"
|
||||
/>
|
||||
</spot-selector-field>
|
||||
</ng-container>
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service';
|
||||
import {
|
||||
UntypedFormGroup,
|
||||
FormGroupDirective,
|
||||
} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'op-immediate-reminder-settings',
|
||||
templateUrl: './immediate-reminder-settings.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class ImmediateReminderSettingsComponent implements OnInit {
|
||||
form:UntypedFormGroup;
|
||||
|
||||
text = {
|
||||
title: this.I18n.t('js.reminders.settings.immediate.title'),
|
||||
explanation: this.I18n.t('js.reminders.settings.immediate.explanation'),
|
||||
mentioned: this.I18n.t('js.reminders.settings.immediate.mentioned'),
|
||||
personalReminder: this.I18n.t('js.reminders.settings.immediate.personal_reminder'),
|
||||
};
|
||||
|
||||
constructor(
|
||||
private I18n:I18nService,
|
||||
private storeService:UserPreferencesService,
|
||||
private rootFormGroup:FormGroupDirective,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.form = this.rootFormGroup.control.get('immediateReminders') as UntypedFormGroup;
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
@if (formInitialized) {
|
||||
<form
|
||||
[formGroup]="form"
|
||||
(ngSubmit)="saveChanges()"
|
||||
class="op-form"
|
||||
>
|
||||
<op-immediate-reminder-settings class="op-form--fieldset" />
|
||||
<op-reminder-settings-daily-time class="op-form--fieldset" />
|
||||
<op-workdays-settings class="op-form--fieldset" />
|
||||
<op-email-alerts-settings class="op-form--fieldset" />
|
||||
<div class="op-form--submit">
|
||||
<button
|
||||
class="button -primary"
|
||||
[textContent]="text.save"
|
||||
type="submit"
|
||||
></button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
// TODO: remove once we have a standard
|
||||
// styling for these section headings
|
||||
.form--section-title
|
||||
text-transform: initial
|
||||
border-bottom: initial
|
||||
margin-bottom: initial !important
|
||||
-193
@@ -1,193 +0,0 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit } from '@angular/core';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import { CurrentUserService } from 'core-app/core/current-user/current-user.service';
|
||||
import { take } from 'rxjs/internal/operators/take';
|
||||
import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service';
|
||||
import { UntypedFormArray, UntypedFormBuilder } from '@angular/forms';
|
||||
import {
|
||||
DailyRemindersSettings,
|
||||
ImmediateRemindersSettings,
|
||||
IUserPreference,
|
||||
PauseRemindersSettings,
|
||||
} from 'core-app/features/user-preferences/state/user-preferences.model';
|
||||
import {
|
||||
emailAlerts,
|
||||
EmailAlertType,
|
||||
} from 'core-app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component';
|
||||
import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin';
|
||||
import { filter, withLatestFrom } from 'rxjs/operators';
|
||||
import { filterObservable } from 'core-app/shared/helpers/rxjs/filterWith';
|
||||
import { INotificationSetting } from 'core-app/features/user-preferences/state/notification-setting.model';
|
||||
import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs';
|
||||
|
||||
interface IReminderSettingsFormValue {
|
||||
immediateReminders:ImmediateRemindersSettings,
|
||||
dailyReminders:DailyRemindersSettings,
|
||||
pauseReminders:Partial<PauseRemindersSettings>,
|
||||
emailAlerts:Record<EmailAlertType, boolean>;
|
||||
workdays:boolean[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: './reminder-settings-page.component.html',
|
||||
styleUrls: ['./reminder-settings-page.component.sass'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class ReminderSettingsPageComponent extends UntilDestroyedMixin implements OnInit {
|
||||
@Input() userId:string;
|
||||
|
||||
public form = this.fb.group({
|
||||
immediateReminders: this.fb.group({
|
||||
mentioned: this.fb.control(false),
|
||||
personalReminder: this.fb.control(false),
|
||||
}),
|
||||
dailyReminders: this.fb.group({
|
||||
enabled: this.fb.control(false),
|
||||
times: this.fb.array([]),
|
||||
}),
|
||||
pauseReminders: this.fb.group({
|
||||
enabled: this.fb.control(false),
|
||||
firstDay: this.fb.control(''),
|
||||
lastDay: this.fb.control(''),
|
||||
}),
|
||||
workdays: this.fb.array([
|
||||
this.fb.control(false),
|
||||
this.fb.control(true),
|
||||
this.fb.control(true),
|
||||
this.fb.control(true),
|
||||
this.fb.control(true),
|
||||
this.fb.control(true),
|
||||
this.fb.control(false),
|
||||
]),
|
||||
emailAlerts: this.fb.group({
|
||||
newsAdded: this.fb.control(false),
|
||||
newsCommented: this.fb.control(false),
|
||||
documentAdded: this.fb.control(false),
|
||||
forumMessages: this.fb.control(false),
|
||||
wikiPageAdded: this.fb.control(false),
|
||||
wikiPageUpdated: this.fb.control(false),
|
||||
membershipAdded: this.fb.control(false),
|
||||
membershipUpdated: this.fb.control(false),
|
||||
}),
|
||||
});
|
||||
|
||||
text = {
|
||||
title: this.I18n.t('js.reminders.settings.title'),
|
||||
save: this.I18n.t('js.button_save'),
|
||||
};
|
||||
|
||||
formInitialized = false;
|
||||
|
||||
constructor(
|
||||
readonly elementRef:ElementRef,
|
||||
readonly I18n:I18nService,
|
||||
readonly storeService:UserPreferencesService,
|
||||
readonly currentUserService:CurrentUserService,
|
||||
readonly fb:UntypedFormBuilder,
|
||||
readonly cdRef:ChangeDetectorRef,
|
||||
) {
|
||||
super();
|
||||
populateInputsFromDataset(this);
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this
|
||||
.currentUserService
|
||||
.user$
|
||||
.pipe(take(1))
|
||||
.subscribe((user) => {
|
||||
this.userId = this.userId || user?.id!;
|
||||
this.storeService.get(this.userId);
|
||||
});
|
||||
|
||||
this.storeService.query.select()
|
||||
.pipe(
|
||||
filter((settings) => !!settings),
|
||||
withLatestFrom(this.storeService.query.globalNotification$),
|
||||
filterObservable(this.storeService.query.selectLoading(), (val) => !val),
|
||||
)
|
||||
.subscribe(([settings, globalSetting]) => {
|
||||
this.buildForm(settings, globalSetting);
|
||||
});
|
||||
}
|
||||
|
||||
private buildForm(settings:IUserPreference, globalSetting:INotificationSetting) {
|
||||
this.form.get('immediateReminders.mentioned')?.setValue(settings.immediateReminders.mentioned);
|
||||
this.form.get('immediateReminders.personalReminder')?.setValue(settings.immediateReminders.personalReminder);
|
||||
|
||||
this.form.get('dailyReminders.enabled')?.setValue(settings.dailyReminders.enabled);
|
||||
|
||||
this.form.get('pauseReminders')?.patchValue(settings.pauseReminders);
|
||||
|
||||
const dailyReminderTimes = this.form.get('dailyReminders.times') as UntypedFormArray;
|
||||
dailyReminderTimes.clear({ emitEvent: false });
|
||||
[...settings.dailyReminders.times].sort().forEach((time) => {
|
||||
dailyReminderTimes.push(this.fb.control(time), { emitEvent: false });
|
||||
});
|
||||
|
||||
dailyReminderTimes.enable({ emitEvent: true });
|
||||
|
||||
const workdays = this.form.get('workdays') as UntypedFormArray;
|
||||
for (let i = 0; i <= 6; i++) {
|
||||
const control = workdays.at(i);
|
||||
control.setValue(settings.workdays.includes(i + 1));
|
||||
}
|
||||
|
||||
emailAlerts.forEach((alert) => {
|
||||
this.form.get(`emailAlerts.${alert}`)?.setValue(globalSetting[alert]);
|
||||
});
|
||||
|
||||
this.formInitialized = true;
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
public saveChanges():void {
|
||||
const prefs = this.storeService.query.getValue();
|
||||
const globalNotifications = prefs.notifications.filter((notification) => !notification._links.project.href);
|
||||
const projectNotifications = prefs.notifications.filter((notification) => !!notification._links.project.href);
|
||||
const reminderSettings = (this.form.value as IReminderSettingsFormValue);
|
||||
const workdays = ReminderSettingsPageComponent.buildWorkdays(reminderSettings.workdays);
|
||||
const pauseReminders = ReminderSettingsPageComponent.buildPauses(reminderSettings.pauseReminders);
|
||||
const { dailyReminders, immediateReminders } = reminderSettings;
|
||||
|
||||
this.storeService.update(this.userId, {
|
||||
...prefs,
|
||||
workdays,
|
||||
dailyReminders,
|
||||
immediateReminders,
|
||||
pauseReminders,
|
||||
notifications: [
|
||||
...globalNotifications.map((notification) => (
|
||||
{
|
||||
...notification,
|
||||
...reminderSettings.emailAlerts,
|
||||
}
|
||||
)),
|
||||
...projectNotifications,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
private static buildWorkdays(formValues:boolean[]):number[] {
|
||||
return formValues
|
||||
.reduce(
|
||||
(result, val, index) => {
|
||||
if (val) {
|
||||
return result.concat([index + 1]);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
[] as number[],
|
||||
);
|
||||
}
|
||||
|
||||
private static buildPauses(formValues:Partial<PauseRemindersSettings>):Partial<PauseRemindersSettings> {
|
||||
if (formValues.enabled) {
|
||||
return formValues;
|
||||
}
|
||||
|
||||
return { enabled: false };
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
<div
|
||||
class="op-pause-reminders"
|
||||
[formGroup]="form"
|
||||
>
|
||||
<spot-selector-field
|
||||
class="op-pause-reminders--checkbox"
|
||||
[label]="text.label"
|
||||
[control]="form.get('enabled')"
|
||||
>
|
||||
<input
|
||||
slot="input"
|
||||
type="checkbox"
|
||||
formControlName="enabled"
|
||||
/>
|
||||
</spot-selector-field>
|
||||
|
||||
@if ((enabled$ | async)) {
|
||||
<op-basic-range-date-picker [required]="enabled$ | async"
|
||||
[value]="selectedDates$ | async"
|
||||
(valueChange)="setDates($event)"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
.op-pause-reminders
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
&--checkbox
|
||||
margin-right: 2rem
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
UntypedFormGroup,
|
||||
FormGroupDirective,
|
||||
} from '@angular/forms';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import {
|
||||
map,
|
||||
startWith,
|
||||
} from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'op-pause-reminders',
|
||||
templateUrl: './pause-reminders.component.html',
|
||||
styleUrls: ['./pause-reminders.component.sass'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class PauseRemindersComponent implements OnInit {
|
||||
form:UntypedFormGroup;
|
||||
|
||||
selectedDates$:Observable<[string, string]>;
|
||||
|
||||
enabled$:Observable<boolean>;
|
||||
|
||||
text = {
|
||||
label: this.I18n.t('js.reminders.settings.pause.label'),
|
||||
date_placeholder: this.I18n.t('js.placeholders.date'),
|
||||
first_day: this.I18n.t('js.reminders.settings.pause.first_day'),
|
||||
last_day: this.I18n.t('js.reminders.settings.pause.first_day'),
|
||||
};
|
||||
|
||||
constructor(
|
||||
private I18n:I18nService,
|
||||
private rootFormGroup:FormGroupDirective,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.form = this.rootFormGroup.control.get('pauseReminders') as UntypedFormGroup;
|
||||
this.selectedDates$ = this
|
||||
.form
|
||||
.valueChanges
|
||||
.pipe(
|
||||
startWith(this.form.value),
|
||||
map((form:{ firstDay:string, lastDay:string }) => [form.firstDay, form.lastDay]),
|
||||
);
|
||||
|
||||
this.enabled$ = this
|
||||
.form
|
||||
.valueChanges
|
||||
.pipe(
|
||||
startWith(this.form.value),
|
||||
map((form:{ enabled:boolean }) => form.enabled),
|
||||
);
|
||||
}
|
||||
|
||||
setDates($event:[string, string]):void {
|
||||
const [firstDay, lastDay] = $event;
|
||||
this.form.patchValue({
|
||||
firstDay,
|
||||
lastDay,
|
||||
});
|
||||
}
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
@if ((selectedTimes$ | async); as selectedTimes) {
|
||||
<ng-container
|
||||
[formGroup]="form"
|
||||
>
|
||||
<div class="op-form--section-header">
|
||||
<h3 [textContent]="text.title" class="op-form--section-header-title"></h3>
|
||||
<p [textContent]="text.explanation"></p>
|
||||
</div>
|
||||
<spot-selector-field
|
||||
[label]="text.enable"
|
||||
[control]="form.get('enabled')">
|
||||
<input
|
||||
slot="input"
|
||||
type="checkbox"
|
||||
formControlName="enabled"
|
||||
/>
|
||||
</spot-selector-field>
|
||||
@for (time of selectedTimes; track i; let i = $index) {
|
||||
<div
|
||||
class="op-reminder-settings-daily-time--row"
|
||||
>
|
||||
@if ((activeTimes$ | async); as activeTimes) {
|
||||
<input
|
||||
type="checkbox"
|
||||
[ngModel]="isActive(time)"
|
||||
(ngModelChange)="toggleActive($event, i, selectedTimes)"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[disabled]="isDisabled(time, activeTimes)"
|
||||
class="op-reminder-settings-daily-time--active"
|
||||
attr.data-test-selector="op-settings-daily-time--active-{{i + 1}}">
|
||||
}
|
||||
<label
|
||||
class="op-reminder-settings-daily-time--label"
|
||||
[textContent]="text.timeLabel(i + 1)"
|
||||
attr.for="op-reminder-settings-daily-time-{{i + 1}}--time">
|
||||
</label>
|
||||
<select
|
||||
[ngModel]="time"
|
||||
(ngModelChange)="changeTime($event, selectedTimes, i)"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
[disabled]="(enabled$ | async) === false"
|
||||
class="op-reminder-settings-daily-time--time form--select -narrow"
|
||||
attr.id="op-reminder-settings-daily-time-{{i + 1}}--time"
|
||||
required="true">
|
||||
@for (availableTime of availableTimes; track availableTime) {
|
||||
<option
|
||||
[value]="availableTime"
|
||||
[disabled]="time !== availableTime && selectedTimes.includes(availableTime)">
|
||||
{{timeLabel(availableTime)}}
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
@if (timeRemovable$ | async) {
|
||||
<button
|
||||
class="spot-link op-reminder-settings-daily-time--remove"
|
||||
type="button"
|
||||
(click)="removeTime(selectedTimes, i)"
|
||||
attr.data-test-selector="op-settings-daily-time--remove-{{i + 1}}">
|
||||
<op-icon icon-classes="icon-small icon-remove icon4" />
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<button
|
||||
class="button op-reminder-settings-daily-time--add"
|
||||
type="button"
|
||||
[disabled]="nonAddable$ | async"
|
||||
(click)="addTime(selectedTimes)">
|
||||
<i class="button--icon icon-add"></i>
|
||||
<span class="button--text">{{text.addTime}}</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
@import "helpers"
|
||||
|
||||
.op-reminder-settings-daily-time
|
||||
&--enable
|
||||
display: flex
|
||||
align-items: center
|
||||
margin-top: 30px
|
||||
margin-bottom: 10px
|
||||
|
||||
&--row
|
||||
margin-left: 40px
|
||||
line-height: 45px
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
&:nth-of-type(1)
|
||||
margin-top: 10px
|
||||
|
||||
&--active
|
||||
flex: 0 0 25px
|
||||
|
||||
&--label
|
||||
flex: 0 0 80px
|
||||
margin-bottom: 0
|
||||
@include text-shortener
|
||||
|
||||
&--time
|
||||
height: 32px
|
||||
width: 150px
|
||||
|
||||
&--remove
|
||||
padding-left: 10px
|
||||
|
||||
&--add
|
||||
margin-top: 20px
|
||||
margin-left: 40px
|
||||
width: auto
|
||||
-251
@@ -1,251 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
import {
|
||||
map,
|
||||
shareReplay,
|
||||
startWith,
|
||||
} from 'rxjs/operators';
|
||||
import {
|
||||
combineLatest,
|
||||
NEVER,
|
||||
Observable,
|
||||
} from 'rxjs';
|
||||
import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service';
|
||||
import {
|
||||
UntypedFormArray,
|
||||
UntypedFormControl,
|
||||
UntypedFormGroup,
|
||||
FormGroupDirective,
|
||||
} from '@angular/forms';
|
||||
import { ConfigurationService } from 'core-app/core/config/configuration.service';
|
||||
import moment from 'moment';
|
||||
|
||||
@Component({
|
||||
selector: 'op-reminder-settings-daily-time',
|
||||
templateUrl: './reminder-settings-daily-time.component.html',
|
||||
styleUrls: ['./reminder-settings-daily-time.component.sass'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class ReminderSettingsDailyTimeComponent implements OnInit {
|
||||
// All times that are available in a day with a 1 hour gap between each.
|
||||
// ['00:00', '01:00', ..., '24:00']
|
||||
public availableTimes:string[] = ReminderSettingsDailyTimeComponent.setupAvailableTimes();
|
||||
|
||||
// The times (hours) that the user deactivated. Those are only stored within the component
|
||||
// as the inactive hours are not persisted. This list is then interleaved with the list
|
||||
// of times stored in the backend. As the order of the times should be kept,
|
||||
// the position needs to be maintained.
|
||||
// Upon a reload of the page, it is accepted to loose this information.
|
||||
public inactiveTimes:{ position:number, time:string }[] = [];
|
||||
|
||||
public form:UntypedFormGroup;
|
||||
|
||||
// Hours suggested if a new time is added by a user.
|
||||
public suggestedTimes = ['08:00', '12:00', '15:00', '18:00'];
|
||||
|
||||
// Whether the reminder are active at all.
|
||||
public enabled$:Observable<boolean>;
|
||||
|
||||
// The active times as present in the store interleaved with the inactive
|
||||
// times.
|
||||
public selectedTimes$:Observable<string[]> = NEVER;
|
||||
|
||||
// Times that are truly active:
|
||||
// * the reminders are not disabled completely
|
||||
// * the times are not inactive individually.
|
||||
public activeTimes$:Observable<string[]> = NEVER;
|
||||
|
||||
// Times can only be removed if the element is active and if there is more than one time present.
|
||||
public timeRemovable$:Observable<boolean> = NEVER;
|
||||
|
||||
// Times can not be added if the element is disabled or if all the possible times have already been added (active or not).
|
||||
public nonAddable$:Observable<boolean> = NEVER;
|
||||
|
||||
text = {
|
||||
title: this.I18n.t('js.reminders.settings.daily.title'),
|
||||
explanation: this.I18n.t('js.reminders.settings.daily.explanation',
|
||||
{ no_time_zone: this.configurationService.isTimezoneSet() ? '' : this.I18n.t('js.reminders.settings.daily.no_time_zone') }),
|
||||
timeLabel: (counter:number):string => this.I18n.t('js.reminders.settings.daily.time_label', { counter }),
|
||||
addTime: this.I18n.t('js.reminders.settings.daily.add_time'),
|
||||
enable: this.I18n.t('js.reminders.settings.daily.enable'),
|
||||
};
|
||||
|
||||
constructor(
|
||||
private I18n:I18nService,
|
||||
private storeService:UserPreferencesService,
|
||||
private rootFormGroup:FormGroupDirective,
|
||||
private configurationService:ConfigurationService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.form = this.rootFormGroup.control.get('dailyReminders') as UntypedFormGroup;
|
||||
|
||||
this.enabled$ = this
|
||||
.form
|
||||
.valueChanges
|
||||
.pipe(
|
||||
startWith(() => this.form.get('enabled')?.value as boolean),
|
||||
map(() => this.form.get('enabled')?.value as boolean),
|
||||
shareReplay(1),
|
||||
);
|
||||
|
||||
this.selectedTimes$ = (this
|
||||
.form
|
||||
.get('times') as UntypedFormArray)
|
||||
.valueChanges
|
||||
.pipe(
|
||||
startWith(() => this.form.get('times')?.value as UntypedFormArray),
|
||||
map(() => {
|
||||
const timesArray = this.form.get('times') as UntypedFormArray;
|
||||
const activeTimes = timesArray.controls.map((c) => c.value as string);
|
||||
|
||||
this
|
||||
.inactiveTimes
|
||||
.sort((a, b) => a.position - b.position)
|
||||
.forEach((inactiveTime) => {
|
||||
activeTimes.splice(inactiveTime.position, 0, inactiveTime.time);
|
||||
});
|
||||
|
||||
return activeTimes;
|
||||
}),
|
||||
shareReplay(1),
|
||||
);
|
||||
|
||||
this.timeRemovable$ = combineLatest([
|
||||
this.enabled$,
|
||||
this.selectedTimes$,
|
||||
]).pipe(map(([enabled, selectedTimes]) => enabled && selectedTimes.length > 1));
|
||||
|
||||
this.nonAddable$ = combineLatest([
|
||||
this.enabled$,
|
||||
this.selectedTimes$,
|
||||
]).pipe(map(([enabled, selectedTimes]) => !enabled || selectedTimes.length === this.availableTimes.length));
|
||||
|
||||
this.activeTimes$ = combineLatest([
|
||||
this.enabled$,
|
||||
this.selectedTimes$,
|
||||
]).pipe(
|
||||
map(([enabled, times]) => (enabled ? times : [])),
|
||||
);
|
||||
}
|
||||
|
||||
addTime(selectedTimes:string[]):void {
|
||||
const time = this.firstAvailableSuggested(selectedTimes) || this.firstAfterSelected(selectedTimes);
|
||||
|
||||
if (time) {
|
||||
this.storeTimes(selectedTimes.concat(time));
|
||||
}
|
||||
}
|
||||
|
||||
changeTime(newTime:string, selectedTimes:string[], index:number):void {
|
||||
selectedTimes.splice(index, 1, newTime);
|
||||
|
||||
this.storeTimes(selectedTimes);
|
||||
}
|
||||
|
||||
isActive(time:string):boolean {
|
||||
return !this.inactiveTimes.find((inactive) => inactive.time === time);
|
||||
}
|
||||
|
||||
removeTime(selectedTimes:string[], index:number):void {
|
||||
this.inactiveTimes = this
|
||||
.inactiveTimes
|
||||
.filter((inactiveTime) => inactiveTime.time !== selectedTimes[index]);
|
||||
|
||||
this.inactiveTimes
|
||||
.forEach((inactiveTime) => {
|
||||
if (inactiveTime.position > index) {
|
||||
inactiveTime.position -= 1;
|
||||
}
|
||||
});
|
||||
|
||||
selectedTimes.splice(index, 1);
|
||||
|
||||
if (selectedTimes.length === 1) {
|
||||
this.inactiveTimes = [];
|
||||
}
|
||||
|
||||
// Activate the first time if none is active.
|
||||
if (selectedTimes.length === this.inactiveTimes.length) {
|
||||
this.inactiveTimes.shift();
|
||||
}
|
||||
|
||||
this.storeTimes(selectedTimes);
|
||||
}
|
||||
|
||||
toggleActive(active:boolean, index:number, selectedTimes:string[]):void {
|
||||
if (!active) {
|
||||
this.inactiveTimes.push({ position: index, time: selectedTimes[index] });
|
||||
} else {
|
||||
this.inactiveTimes = this.inactiveTimes.filter((inactiveTime) => inactiveTime.time !== selectedTimes[index]);
|
||||
}
|
||||
|
||||
this.storeTimes(selectedTimes);
|
||||
}
|
||||
|
||||
timeLabel(time:string):string {
|
||||
return this
|
||||
.I18n
|
||||
.toTime(
|
||||
'time.formats.time',
|
||||
ReminderSettingsDailyTimeComponent.dateForHour(parseInt(time.split(':')[0], 10)),
|
||||
);
|
||||
}
|
||||
|
||||
isDisabled(time:string, activeTimes:string[]):boolean {
|
||||
return activeTimes.length === 0 || (activeTimes.length === 1 && activeTimes[0] === time);
|
||||
}
|
||||
|
||||
private storeTimes(selectedTimes:string[]) {
|
||||
const times = selectedTimes
|
||||
.filter(
|
||||
(selected) => !this.inactiveTimes
|
||||
.map((inactive) => inactive.time)
|
||||
.includes(selected),
|
||||
);
|
||||
|
||||
const timesForm = this.form.get('times') as UntypedFormArray;
|
||||
timesForm.clear({ emitEvent: false });
|
||||
times.forEach((time) => {
|
||||
timesForm.push(new UntypedFormControl(time), { emitEvent: false });
|
||||
});
|
||||
|
||||
timesForm.enable({ emitEvent: true });
|
||||
}
|
||||
|
||||
private firstAvailableSuggested(selectedTimes:string[]) {
|
||||
return this.availableTimes.find((v) => this.suggestedTimes.includes(v) && !selectedTimes.includes(v));
|
||||
}
|
||||
|
||||
private firstAfterSelected(selectedTimes:string[]) {
|
||||
const indexLastSelected = this.availableTimes.indexOf(selectedTimes[selectedTimes.length - 1]);
|
||||
|
||||
for (let i = indexLastSelected; i < 24 + indexLastSelected; i++) {
|
||||
if (!selectedTimes.includes(this.availableTimes[i % 24])) {
|
||||
return this.availableTimes[i % 24];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static setupAvailableTimes() {
|
||||
return Array.from({ length: 24 }, (v, i) => ReminderSettingsDailyTimeComponent
|
||||
.dateForHour(i)
|
||||
.toLocaleTimeString('en-US', { hour12: false, hour: 'numeric', minute: 'numeric' }));
|
||||
}
|
||||
|
||||
private static dateForHour(hour:number) {
|
||||
const currentTime = new Date();
|
||||
currentTime.setTime(1000 * 60 * 60 * (hour - 1));
|
||||
const convertTimeObject = new Date(moment(currentTime).utc().hours(hour).format('YYYY-MM-DDTHH:mm:ss'));
|
||||
|
||||
return convertTimeObject;
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
<ng-container [formGroup]="formGroup.control">
|
||||
<div class="op-form--section-header">
|
||||
<h3 [textContent]="text.title" class="op-form--section-header-title"></h3>
|
||||
</div>
|
||||
|
||||
@for (workday of localeWorkdays; track workday; let i = $index) {
|
||||
<spot-selector-field
|
||||
formArrayName="workdays"
|
||||
[label]="workday"
|
||||
[control]="controlForLocalWorkday(workday)"
|
||||
>
|
||||
<input
|
||||
slot="input"
|
||||
type="checkbox"
|
||||
[formControlName]="indexOfLocalWorkday(workday)"
|
||||
/>
|
||||
</spot-selector-field>
|
||||
}
|
||||
|
||||
<op-pause-reminders />
|
||||
</ng-container>
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
UntypedFormArray,
|
||||
UntypedFormControl,
|
||||
FormGroupDirective,
|
||||
} from '@angular/forms';
|
||||
import moment from 'moment';
|
||||
import { I18nService } from 'core-app/core/i18n/i18n.service';
|
||||
|
||||
@Component({
|
||||
selector: 'op-workdays-settings',
|
||||
templateUrl: './workdays-settings.component.html',
|
||||
styleUrls: ['./workdays-settings.component.sass'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class WorkdaysSettingsComponent implements OnInit {
|
||||
control:UntypedFormArray;
|
||||
|
||||
/**
|
||||
* The locale might render workdays in a different order, which is what moment return with localeSorted
|
||||
* and used for rendering the component.
|
||||
*/
|
||||
localeWorkdays:string[] = moment.weekdays(true);
|
||||
|
||||
/**
|
||||
* Almost* ISO workdays with localized strings.
|
||||
* ISO workdays are 1=Monday, ... 7=Sunday which is what we persist
|
||||
*
|
||||
* Working with the FormArray however, we use 0=Monday, 6=Sunday and add one before saving
|
||||
* @private
|
||||
*/
|
||||
private isoWorkdays:string[] = WorkdaysSettingsComponent.buildISOWeekdays();
|
||||
|
||||
text = {
|
||||
title: this.I18n.t('js.reminders.settings.workdays.title'),
|
||||
};
|
||||
|
||||
constructor(
|
||||
private I18n:I18nService,
|
||||
readonly formGroup:FormGroupDirective,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.control = this.formGroup.control.get('workdays') as UntypedFormArray;
|
||||
}
|
||||
|
||||
indexOfLocalWorkday(day:string):number {
|
||||
return this.isoWorkdays.indexOf(day);
|
||||
}
|
||||
|
||||
controlForLocalWorkday(day:string):UntypedFormControl {
|
||||
const index = this.indexOfLocalWorkday(day);
|
||||
return this.control.at(index) as UntypedFormControl;
|
||||
}
|
||||
|
||||
/** Workdays from moment.js are in non-ISO order, that means Sunday=0, Saturday=6 */
|
||||
static buildISOWeekdays():string[] {
|
||||
const days = moment.weekdays(false);
|
||||
|
||||
days.push(days.shift()!);
|
||||
|
||||
return days;
|
||||
}
|
||||
}
|
||||
@@ -15,18 +15,6 @@ import {
|
||||
import {
|
||||
NotificationSettingsTableComponent,
|
||||
} from './notifications-settings/table/notification-settings-table.component';
|
||||
import { ReminderSettingsPageComponent } from './reminder-settings/page/reminder-settings-page.component';
|
||||
import {
|
||||
ReminderSettingsDailyTimeComponent,
|
||||
} from 'core-app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component';
|
||||
import {
|
||||
ImmediateReminderSettingsComponent,
|
||||
} from 'core-app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component';
|
||||
import {
|
||||
EmailAlertsSettingsComponent,
|
||||
} from 'core-app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component';
|
||||
import { WorkdaysSettingsComponent } from './reminder-settings/workdays/workdays-settings.component';
|
||||
import { PauseRemindersComponent } from './reminder-settings/pause-reminders/pause-reminders.component';
|
||||
import { OpenprojectEnterpriseModule } from 'core-app/features/enterprise/openproject-enterprise.module';
|
||||
|
||||
@NgModule({
|
||||
@@ -37,12 +25,6 @@ import { OpenprojectEnterpriseModule } from 'core-app/features/enterprise/openpr
|
||||
NotificationsSettingsPageComponent,
|
||||
NotificationSettingInlineCreateComponent,
|
||||
NotificationSettingsTableComponent,
|
||||
ReminderSettingsPageComponent,
|
||||
ReminderSettingsDailyTimeComponent,
|
||||
ImmediateReminderSettingsComponent,
|
||||
EmailAlertsSettingsComponent,
|
||||
WorkdaysSettingsComponent,
|
||||
PauseRemindersComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
@@ -105,13 +107,13 @@ module OpenProject
|
||||
name: "notifications",
|
||||
partial: "users/notifications",
|
||||
path: ->(params) { edit_user_path(params[:user], tab: :notifications) },
|
||||
label: :"js.notifications.settings.title"
|
||||
label: :"my_account.notifications_and_email.tabs.notifications"
|
||||
},
|
||||
{
|
||||
name: "reminders",
|
||||
partial: "users/reminders",
|
||||
path: ->(params) { edit_user_path(params[:user], tab: :reminders) },
|
||||
label: :"js.reminders.settings.title"
|
||||
label: :"my_account.notifications_and_email.tabs.email_reminders"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user