This commit is contained in:
2025-12-11 13:50:38 +01:00
parent e4c1be628d
commit c7e5de40d6
212 changed files with 2965 additions and 763 deletions

View File

@@ -4,10 +4,10 @@
* Represents a user's notification preferences for different channels and types.
*/
import type { NotificationType } from '../value-objects/NotificationType';
import type { NotificationChannel } from '../value-objects/NotificationChannel';
import type { IEntity } from '@gridpilot/shared/domain';
import type { NotificationType, NotificationChannel } from '../types/NotificationTypes';
import { NotificationDomainError } from '../errors/NotificationDomainError';
import { DEFAULT_ENABLED_CHANNELS } from '../value-objects/NotificationChannel';
import { QuietHours } from '../value-objects/QuietHours';
export interface ChannelPreference {
/** Whether this channel is enabled */
@@ -24,6 +24,8 @@ export interface TypePreference {
}
export interface NotificationPreferenceProps {
/** Aggregate ID for this preference (usually same as driverId) */
id: string;
/** Driver ID this preference belongs to */
driverId: string;
/** Global channel preferences */
@@ -42,10 +44,13 @@ export interface NotificationPreferenceProps {
updatedAt: Date;
}
export class NotificationPreference {
export class NotificationPreference implements IEntity<string> {
private constructor(private readonly props: NotificationPreferenceProps) {}
static create(props: Omit<NotificationPreferenceProps, 'updatedAt'> & { updatedAt?: Date }): NotificationPreference {
static create(
props: Omit<NotificationPreferenceProps, 'updatedAt'> & { updatedAt?: Date },
): NotificationPreference {
if (!props.id) throw new NotificationDomainError('Preference ID is required');
if (!props.driverId) throw new NotificationDomainError('Driver ID is required');
if (!props.channels) throw new NotificationDomainError('Channel preferences are required');
@@ -60,6 +65,7 @@ export class NotificationPreference {
*/
static createDefault(driverId: string): NotificationPreference {
return new NotificationPreference({
id: driverId,
driverId,
channels: {
in_app: { enabled: true },
@@ -72,6 +78,7 @@ export class NotificationPreference {
});
}
get id(): string { return this.props.id; }
get driverId(): string { return this.props.driverId; }
get channels(): Record<NotificationChannel, ChannelPreference> { return { ...this.props.channels }; }
get typePreferences(): Partial<Record<NotificationType, TypePreference>> | undefined {
@@ -83,6 +90,13 @@ export class NotificationPreference {
get quietHoursEnd(): number | undefined { return this.props.quietHoursEnd; }
get updatedAt(): Date { return this.props.updatedAt; }
get quietHours(): QuietHours | undefined {
if (this.props.quietHoursStart === undefined || this.props.quietHoursEnd === undefined) {
return undefined;
}
return QuietHours.create(this.props.quietHoursStart, this.props.quietHoursEnd);
}
/**
* Check if a specific channel is enabled
*/
@@ -117,20 +131,13 @@ export class NotificationPreference {
* Check if current time is in quiet hours
*/
isInQuietHours(): boolean {
if (this.props.quietHoursStart === undefined || this.props.quietHoursEnd === undefined) {
const quietHours = this.quietHours;
if (!quietHours) {
return false;
}
const now = new Date();
const currentHour = now.getHours();
if (this.props.quietHoursStart < this.props.quietHoursEnd) {
// Normal range (e.g., 22:00 to 07:00 next day is NOT this case)
return currentHour >= this.props.quietHoursStart && currentHour < this.props.quietHoursEnd;
} else {
// Overnight range (e.g., 22:00 to 07:00)
return currentHour >= this.props.quietHoursStart || currentHour < this.props.quietHoursEnd;
}
return quietHours.containsHour(now.getHours());
}
/**
@@ -165,10 +172,12 @@ export class NotificationPreference {
* Update quiet hours
*/
updateQuietHours(start: number | undefined, end: number | undefined): NotificationPreference {
const validated = start === undefined || end === undefined ? undefined : QuietHours.create(start, end);
return new NotificationPreference({
...this.props,
quietHoursStart: start,
quietHoursEnd: end,
quietHoursStart: validated?.props.startHour,
quietHoursEnd: validated?.props.endHour,
updatedAt: new Date(),
});
}