ITOTEAM-523 notification status

This commit is contained in:
Peter Maquiran
2024-06-27 16:53:45 +01:00
parent 78c13d1bfb
commit 11587cc944
23 changed files with 686 additions and 111 deletions
+23 -16
View File
@@ -40,7 +40,7 @@
<div class="d-flex align-center flex-column">
<div *ngIf="profilePicture == ''">
<img
<img
class="profile-pic" src="assets/images/theme/gov/icons-profile.svg">
<!-- <img *ngIf="SessionStore.user.RoleDescription == 'Presidente da República' " class="profile-pic"
@@ -55,7 +55,7 @@
<img class="profile-pic"
src={{profilePicture}}>
</div>
<!-- <ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="profile-pic"
src="assets/images/icons-default-profile.svg"></ion-icon>
@@ -93,25 +93,32 @@
<div class="notifications-content height-100">
<ion-list>
<ion-list *ngIf="(notificationList$ | async) as notificationList">
<div class="item cursor-pointer ion-no-padding ion-no-margin" lines="none"
*ngFor="let item of NotificationHolderService.notificationList; let i = index">
*ngFor="let item of notificationList; let i = index">
<div *ngIf="item.status==false" class="item-conten1 item-conten-{{item.service}}-{{item.typeAgenda}}-{{item.role}}" (click)="notificatinsRoutes(item.index, item)">
<div *ngIf="item.read == false" class="item-conten-{{item.Service}}-{{item.TypeAgenda}}-{{item.Role}}" (click)="notificatinsRoutes(item.index,item.Service,item.Object,item.IdObject,item.FolderId,i)">
<div class="notification-item">
<img class="notification-icon" slot="end" *ngIf = "item.Service == 'agenda'" src="assets/images/icons-default-agenda.svg" >
<img class="notification-icon" slot="end" *ngIf = "item.Service == 'gabinete-digital'" src="assets/images/icons-correspondencias.svg" >
<img class="notification-icon" slot="end" *ngIf = "item.Service == 'accoes'" src="assets/images/icons-nav-accoes-active.svg" >
<img class="notification-icon" slot="end" *ngIf = "item.Service == 'chat'" src="assets/images/icons-nav-agenda-active.svg" >
<img class="notification-icon" slot="end" *ngIf = "item.service == 'agenda'" src="assets/images/icons-default-agenda.svg" >
<img class="notification-icon" slot="end" *ngIf = "item.service == 'gabinete-digital'" src="assets/images/icons-correspondencias.svg" >
<img class="notification-icon" slot="end" *ngIf = "item.service == 'accoes'" src="assets/images/icons-nav-accoes-active.svg" >
<img class="notification-icon" slot="end" *ngIf = "item.service == 'chat'" src="assets/images/icons-nav-agenda-active.svg" >
</div>
<div class="approve-event-time">
<p *ngIf = "item.Service == 'agenda'">{{item.dateInit}}</p>
<p *ngIf = "item.Service == 'agenda'">{{item.dateEnd}}</p>
<div class="approve-event-time" *ngIf = "item.service == 'agenda'">
<div *ngIf="isValidDate(item.dateInit)">
<p *ngIf="toDateString(item.dateInit) == toDateString(item.dateEnd)">{{item.dateInit | date: 'HH:mm'}} </p>
<p *ngIf="toDateString(item.dateInit) == toDateString(item.dateEnd)">{{item.dateEnd | date: 'HH:mm'}}</p>
<p *ngIf="toDateString(item.dateInit) != toDateString(item.dateEnd)">{{item.dateInit | date: 'd/M/yy' }}</p>
<p *ngIf="toDateString(item.dateInit) != toDateString(item.dateEnd)">{{ item.dateEnd | date: 'dd/M/yy'}} </p>
</div>
</div>
<div class="approve-event-detail">
<h3 id="profile-title">{{item.title}}</h3>
<p *ngIf = "item.Service == 'agenda'">{{item.Location}}</p>
<p id="profile-title" *ngIf = "item.Service != 'agenda'">{{item.body}}</p>
<p *ngIf = "item.service == 'agenda'">{{item.location}}</p>
<p id="profile-title" *ngIf = "item.service != 'agenda'">{{item.body}}</p>
</div>
</div>
@@ -119,7 +126,7 @@
<div class="notification-item">
<img class="notification-icon-error" slot="end" src="assets/images/exclamation_mark.svg" >
</div>
<div class="approve-event-detail-error">
<p id="profile-title-error">Possivelmente esta tarefa não foi criada ou já foi executada. Deseja dar a notificação como lida?</p>
</div>
@@ -143,4 +150,4 @@
</div>
</ion-content>
</ion-content>
+12 -9
View File
@@ -136,16 +136,19 @@ ion-list {
.notifications-content {
padding: 0px 20px;
.item {
border-radius: 15px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.07);
border: solid 1px #e9e9e9;
background-color: var(--white);
margin: 0 auto;
padding: 10px;
margin-bottom: 10px;
color: #000;
overflow: hidden;
.item-conten1{
border-radius: 15px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.07);
border: solid 1px #e9e9e9;
background-color: var(--white);
margin: 0 auto;
padding: 10px;
margin-bottom: 10px;
color: #000;
overflow: hidden;
}
.notification-item {
width: fit-content;
float: left;
+79 -62
View File
@@ -15,6 +15,10 @@ import { NotificationHolderService } from 'src/app/store/notification-holder.ser
import { TracingType, XTracerAsync } from 'src/app/services/monitoring/opentelemetry/tracer';
import { AgendaDataRepositoryService } from 'src/app/services/Repositorys/Agenda/agenda-data-repository.service';
import { ToastService } from 'src/app/services/toast.service';
import { NotificationRepositoryService } from 'src/app/module/notification/data/notification-repository.service';
import { Observable } from 'rxjs';
import { NotificationTable } from 'src/app/module/notification/data/infra/db/notification.db';
import { isHttpError } from 'src/app/services/http.service';
@Component({
selector: 'app-profile',
@@ -45,6 +49,9 @@ export class ProfilePage implements OnInit {
SessionStore = SessionStore;
notificationList$: Observable<NotificationTable[]>
constructor(
private modalController: ModalController,
private animationController: AnimationController,
@@ -62,43 +69,43 @@ export class ProfilePage implements OnInit {
private authservice: AuthService,
private agendaDataRepository: AgendaDataRepositoryService,
private toastService: ToastService,
private notificationRepositoryService: NotificationRepositoryService
) {
this.notificationList$ = this.notificationRepositoryService.getNotificationLive()
window['e'] = () => {
console.log(
this.zone.run(() => this.router.navigate(['/home/chat']))
)
}
router.events.subscribe((val) => {
this.isProfileOpen = false
this.logoutOut = true
});
/* this.eventTriger.getObservable().subscribe((event) => {
if (event.notification == "recive") {
this.getNotificationData();
} else if (event.notification == "deleted") {
console.log('header', event.notification)
this.getNotificationData();
}
}); */
/* this.notificationService.notificationReceived.subscribe(() => {
this.getNotificationData();
}); */
setTimeout(() => {
this.hideImage = true
}, 2000)
this.notificationRepositoryService.init()
}
ngOnInit() {
// this.getNotificationData();
this.getProfilpicture();
}
toDateString(e) {
return new Date(e).toDateString()
}
isValidDate(dateString) {
const date = new Date(dateString);
return !isNaN(date.getTime());
}
getProfilpicture() {
@@ -216,29 +223,39 @@ export class ProfilePage implements OnInit {
}
@XTracerAsync({name:'profile/notificationClick', bugPrint: true, module:'notification'})
async notificatinsRoutes (index, Service, Object, IdObject, FolderId, i, tracing?: TracingType) {
async notificatinsRoutes (index, item: NotificationTable, tracing?: TracingType) {
console.log(index, i)
console.log({item})
this.notificationRepositoryService.notificationStatus(item)
try {
if (Service === "agenda") {
if (item.service === "agenda") {
this.isloading = true
if (Service === "agenda") {
if (item.service === "agenda") {
let res = await this.agendaDataRepository.getEventById(IdObject, tracing)
let res = await this.agendaDataRepository.getEventById(item.idObject, tracing)
if(IdObject) {
if(item.idObject) {
if(res.isOk()) {
this.zone.run(() => this.router.navigate(['/home/agenda', IdObject, 'agenda']));
this.zone.run(() => this.router.navigate(['/home/agenda', item.idObject, 'agenda']));
this.deleteNotification(index);
tracing.setAttribute('outcome', 'success')
} else {
tracing.setAttribute('outcome', 'success')
tracing.setAttribute('data.exist', 'false')
this.NotificationHolderService.notificationList[i].read = true;
console.log('error', res.error)
this.NotificationHolderService.notificationList[index].read = true;
if (isHttpError(res.error)) {
if(res.error.status == 404) {
this.toastService._badRequest('Este evento já não existe')
this.notificationRepositoryService.localNotificationStatus(item)
}
}
}
} else {
tracing.setAttribute('data.IdObject', 'false')
@@ -247,23 +264,23 @@ export class ProfilePage implements OnInit {
this.isloading = false
} else if (Service === "agenda" && Object === "event-list") {
} else if (item.service === "agenda" && item.object === "event-list") {
this.isloading = true
if(IdObject) {
if(item.idObject) {
let res = await this.agendaDataRepository.getEventById(IdObject, tracing)
let res = await this.agendaDataRepository.getEventById(item.idObject, tracing)
if(res.isOk()) {
console.log('evento exist')
tracing.setAttribute('outcome', 'success')
this.zone.run(() => this.router.navigate(['/home/agenda/event-list/approve-event', IdObject, 'agenda']));
this.zone.run(() => this.router.navigate(['/home/agenda/event-list/approve-event', item.idObject, 'agenda']));
this.deleteNotification(index);
this.isloading = false
} else {
tracing.setAttribute('outcome', 'success')
console.log('evento não existe')
tracing.setAttribute('data.exist', 'false')
this.NotificationHolderService.notificationList[i].read = true;
// this.NotificationHolderService.notificationList[index].read = true;
this.isloading = false
}
} else {
@@ -276,65 +293,65 @@ export class ProfilePage implements OnInit {
} else {
tracing.setAttribute('notification.route', 'false')
tracing.setAttribute('outcome', 'failed')
tracing.setAttribute('parameters', JSON.stringify({Service, Object, IdObject, FolderId}))
tracing.setAttribute('parameters', JSON.stringify({Service:item.service, Object:item.object, IdObject:item.idObject, FolderId:item.folderId}))
}
} else if (Service === "gabinete-digital") {
} else if (item.service === "gabinete-digital") {
this.isloading = true
this.processesService.GetTask(IdObject).subscribe((task) => {
this.processesService.GetTask(item.idObject).subscribe((task) => {
if (Service === "gabinete-digital" && Object === "expedientes") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/expediente', IdObject, 'gabinete-digital']));
} else if (Service === "gabinete-digital" && Object === "despachos") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/despachos', IdObject, 'gabinete-digital'], { replaceUrl: true }));
} else if (Service === "gabinete-digital" && Object === "parecer") {
if (item.service === "gabinete-digital" && item.object === "expedientes") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/expediente', item.idObject, 'gabinete-digital']));
} else if (item.service === "gabinete-digital" && item.object === "despachos") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/despachos', item.idObject, 'gabinete-digital'], { replaceUrl: true }));
} else if (item.service === "gabinete-digital" && item.object === "parecer") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/pedidos', IdObject, 'gabinete-digital']));
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/pedidos', item.idObject, 'gabinete-digital']));
}
else if (Service === "gabinete-digital" && Object === "deferimento") {
else if (item.service === "gabinete-digital" && item.object === "deferimento") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/pedidos', IdObject, 'gabinete-digital']));
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/pedidos', item.idObject, 'gabinete-digital']));
}
else if (Service === "gabinete-digital" && Object === "despachos-pr") {
else if (item.service === "gabinete-digital" && item.object === "despachos-pr") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/despachos-pr', IdObject, 'gabinete-digital']));
}else if (Service === "gabinete-digital" && Object === "diplomas") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/diplomas', IdObject, 'gabinete-digital']));
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/despachos-pr', item.idObject, 'gabinete-digital']));
}else if (item.service === "gabinete-digital" && item.object === "diplomas") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/diplomas', item.idObject, 'gabinete-digital']));
}
else if (Service === "gabinete-digital" && Object === "diplomas-assinar") {
else if (item.service === "gabinete-digital" && item.object === "diplomas-assinar") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/diplomas-assinar', IdObject, 'gabinete-digital']));
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/diplomas-assinar', item.idObject, 'gabinete-digital']));
}
else if (Service === "gabinete-digital" && Object === "diploma-revisao") {
else if (item.service === "gabinete-digital" && item.object === "diploma-revisao") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/diplomas', IdObject, 'gabinete-digital']));
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/diplomas', item.idObject, 'gabinete-digital']));
}
else if (Service === "gabinete-digital" && Object === "expedientes-pr") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/expedientes-pr', IdObject, 'gabinete-digital']));
else if (item.service === "gabinete-digital" && item.object === "expedientes-pr") {
this.zone.run(() => this.router.navigate(['/home/gabinete-digital/expedientes-pr', item.idObject, 'gabinete-digital']));
}
this.deleteNotification(index);
this.isloading = false
}, (error) => {
this.NotificationHolderService.notificationList[i].read = true;
// this.NotificationHolderService.notificationList[i].read = true;
this.isloading = false
})
} else if (Service === "accoes") {
if (Service === "accoes" && Object === "accao") {
this.zone.run(() => this.router.navigate(['/home/publications', IdObject]));
} else if (item.service === "accoes") {
if (item.service === "accoes" && item.idObject === "accao") {
this.zone.run(() => this.router.navigate(['/home/publications', item.idObject]));
this.deleteNotification(index);
}
else if (Service === "accoes" && Object === "publicacao") {
this.zone.run(() => this.router.navigate(['/home/publications/view-publications', FolderId, IdObject]));
else if (item.service === "accoes" && item.idObject === "publicacao") {
this.zone.run(() => this.router.navigate(['/home/publications/view-publications', item.folderId, item.idObject]));
this.deleteNotification(index);
}
} else if (Service === "chat") {
} else if (item.service === "chat") {
let navigationExtras: NavigationExtras = { queryParams: { "roomId": IdObject, } };
let navigationExtras: NavigationExtras = { queryParams: { "roomId": item.idObject, } };
this.zone.run(() => this.router.navigate(['/home/chat']));
@@ -346,18 +363,18 @@ export class ProfilePage implements OnInit {
this.deleteNotification(index);
} else {
console.log({Service, Object, IdObject, FolderId})
console.log({service:item.service, Object, IdObject:item.idObject, FolderId:item.folderId})
tracing.setAttribute('notification.route', 'false')
tracing.setAttribute('outcome', 'failed')
tracing.setAttribute('parameters', JSON.stringify({Service, Object, IdObject, FolderId}))
tracing.setAttribute('parameters', JSON.stringify({Service:item.service, Object, IdObject:item.idObject, FolderId:item.folderId}))
}
} catch(error) {
console.log({Service, Object, IdObject, FolderId})
console.log({service:item.service, Object, IdObject:item.idObject, FolderId: item.folderId})
tracing.setAttribute('outcome', 'failed')
tracing.setAttribute('parameters', JSON.stringify({Service, Object, IdObject, FolderId}))
tracing.setAttribute('parameters', JSON.stringify({service:item.service, Object, IdObject:item.idObject, FolderId:item.folderId}))
tracing.setAttribute('error', JSON.stringify(error))
}
@@ -0,0 +1,37 @@
import { z } from "zod";
import { NotificationTable } from '../../infra/db/notification.db'
type Changes = {
insert: NotificationTable[];
update: NotificationTable[];
remove: NotificationTable[];
};
export function NotificationListChanges(
localList: NotificationTable[],
serverList: NotificationTable[]
): Changes {
const changes: Changes = { insert: [], update: [], remove: [] };
const localMap = new Map(localList.map(item => [item.notificationId, item]));
const serverMap = new Map(serverList.map(item => [item.notificationId, item]));
// Detect new or updated items
for (const [id, serverItem] of serverMap) {
const localItem = localMap.get(id);
if (!localItem) {
changes.insert.push(serverItem);
} else if (localItem.status !== serverItem.status) {
changes.update.push(serverItem);
}
}
// Detect deleted items
for (const [id, localItem] of localMap) {
if (!serverMap.has(id)) {
changes.remove.push(localItem);
}
}
return changes;
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { FirebasePushNotificationService } from './firebase-push-notification.service';
describe('FirebasePushNotificationService', () => {
let service: FirebasePushNotificationService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(FirebasePushNotificationService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,80 @@
import { Injectable } from '@angular/core';
import { AlertController, Platform } from '@ionic/angular';
import { Capacitor } from '@capacitor/core';
import { ActionPerformed, PushNotifications, PushNotificationSchema } from '@capacitor/push-notifications';
import { AngularFireMessaging } from '@angular/fire/messaging';
@Injectable({
providedIn: 'root'
})
export class FirebasePushNotificationService {
isPushNotificationsAvailable = Capacitor.isPluginAvailable('PushNotifications');
active = false
constructor(
private platform: Platform,
private afMessaging: AngularFireMessaging,
) {}
onReceiveBackground(callback: Function) {
if (this.platform.is('mobile')) {
if (!this.isPushNotificationsAvailable) {
return false
}
PushNotifications.addListener('pushNotificationActionPerformed',
(notification: ActionPerformed) => {
this.active = true
callback(notification)
console.log('NOtification Listener Backgroud', notification)
}
);
} else {
try {
navigator.serviceWorker.onmessage = (event) => {
console.log('Mensagem recebida do Service Worker:', event.data);
let object = {
notification: event.data
}
callback(object)
if (event.data.notificationClicked) {
console.log('Notificação push do Firebase clicada em segundo plano!');
}
};
} catch(e) {
console.log(e)
}
}
}
onReceiveForeground(callback: Function) {
if (this.platform.is('mobile')) {
if (!this.isPushNotificationsAvailable) {
return false
}
PushNotifications.addListener('pushNotificationReceived',
(notification: PushNotificationSchema) => {
this.active = true
console.log('NOtification Listener', notification)
callback(notification)
}
);
} else {
this.afMessaging.messages.subscribe((notification) => {
console.log('NOtification Listener', notification)
callback(notification)
// Handle the received message, e.g., show a notification
});
}
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { LocalNotificationService } from './local-notification.service';
describe('LocalNotificationService', () => {
let service: LocalNotificationService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(LocalNotificationService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,86 @@
import { Injectable } from '@angular/core';
import { NotificationDataSource, NotificationTable, NotificationTableSchema } from '../infra/db/notification.db';
import { err, ok } from 'neverthrow';
import { from } from 'rxjs';
import { liveQuery } from 'Dexie';
@Injectable({
providedIn: 'root'
})
export class LocalNotificationService {
constructor() { }
async addNotification(data: NotificationTable) {
// db.eve
try {
const result = await NotificationDataSource.notification.add(data)
return ok(result)
} catch (e) {
return err(false)
}
}
async updateNotification(data: NotificationTable) {
// db.eve
try {
const result = await NotificationDataSource.notification.update(data.notificationId, data)
return ok(result)
} catch (e) {
return err(false)
}
}
async updateNotifications(data: NotificationTable[]) {
// db.eve
try {
const result = await NotificationDataSource.notification.bulkUpdate(data.map(e => ({
key: e.notificationId,
changes: { status: e.status }
})))
return ok(result)
} catch (e) {
return err(false)
}
}
async addNotifications(notifications: NotificationTable[]) {
// Validate each notification
const failed = []
const validNotifications = notifications.filter(notification => {
const result = NotificationTableSchema.safeParse(notification);
if(!result.success) {
failed.push(notification)
}
return result.success;
});
// Add valid notifications to the database
if (validNotifications.length > 0) {
await NotificationDataSource.notification.bulkAdd(validNotifications);
} else {
console.log('No valid notifications to add.');
}
console.log({failed})
return ok(failed)
}
getNotificationLive() {
return from(liveQuery( () => {
return NotificationDataSource.notification.orderBy('createdAt').reverse().toArray()
}))
}
async getNotification() {
return NotificationDataSource.notification.toArray()
}
clear() {
return NotificationDataSource.notification.clear()
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { RemoteNotificationService } from './remote-notification.service';
describe('RemoteNotificationService', () => {
let service: RemoteNotificationService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(RemoteNotificationService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { HttpService } from 'src/app/services/http.service';
import { NotificationInputDTO } from '../dto/NotificationInputDTO';
import { NotificationOutputDTO, NotificationOutputDTOSchema } from '../dto/NotificationOutputDTO';
import { APIReturn } from 'src/app/services/decorator/api-validate-schema.decorator';
@Injectable({
providedIn: 'root'
})
export class RemoteNotificationService {
private baseUrl = 'https://gdapi-dev.dyndns.info/stage/api/v2';
constructor(
private httpService: HttpService
) { }
@APIReturn(NotificationOutputDTOSchema, 'Get/Notifications')
async getNotification(queryParameter: NotificationInputDTO) {
return await this.httpService.get<NotificationOutputDTO>(`${this.baseUrl}/Notifications`, queryParameter);
}
async notificationStatus(id: string) {
return await this.httpService.patch<NotificationOutputDTO>(`${this.baseUrl}/Notifications/${id}/status`);
}
}
@@ -0,0 +1,9 @@
import { z } from 'zod';
export const NotificationInputDTOSchema = z.object({
userId: z.string(),
PageNumber: z.string(),
PageSize: z.string(),
})
export type NotificationInputDTO = z.infer<typeof NotificationInputDTOSchema>;
@@ -0,0 +1,31 @@
import { z } from 'zod';
export const NotificationOutputDTOSchema = z.object({
success: z.boolean(),
message: z.string(),
data: z.object({
total: z.number(),
result: z.array(
z.object({
id: z.string(),
service: z.string(),
title: z.string(),
body: z.string(),
object: z.string(),
objectId: z.string(),
folderId: z.string().nullable(),
createdAt: z.string(),
viewDate: z.string().nullable(),
status: z.boolean(),
startDate: z.string().nullable(),
endDate: z.string().nullable(),
bodyEvent: z.string().nullable(),
location: z.string().optional().nullable(),
})
)
}),
})
export type NotificationOutputDTO = z.infer<typeof NotificationOutputDTOSchema>;
@@ -0,0 +1,25 @@
import { Dexie, EntityTable, liveQuery } from 'Dexie';
import { z } from 'zod';
export const NotificationTableSchema = z.object({
notificationId: z.string().nullable(),
title: z.string().optional().nullable(),
service: z.string().nullable(),
object: z.string().optional().nullable(),
idObject: z.string().nullable(),
folderId: z.string().optional().nullable(),
dateInit: z.string().optional().nullable(),
dateEnd: z.string().optional().nullable(),
location: z.string().optional().nullable(),
status: z.boolean().optional(),
})
export type NotificationTable = z.infer<typeof NotificationTableSchema>
// Database declaration (move this to its own module also)
export const NotificationDataSource = new Dexie('NotificationDataSource') as Dexie & {
notification: EntityTable<NotificationTable, 'notificationId'>;
};
NotificationDataSource.version(1).stores({
notification: 'notificationId, title, service, object, idObject, folderId, dateInit, dateEnd, location, createdAt'
});
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { NotificationRepositoryService } from './notification-repository.service';
describe('NotificationRepositoryService', () => {
let service: NotificationRepositoryService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(NotificationRepositoryService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,83 @@
import { Injectable } from '@angular/core';
import { NotificationInputDTO } from './dto/NotificationInputDTO';
import { RemoteNotificationService } from './datasource/remote-notification.service'
import { FirebasePushNotificationService } from './datasource/firebase-push-notification.service'
import { LocalNotificationService } from './datasource/local-notification.service'
import { SessionStore } from 'src/app/store/session.service';
import { NotificationListMapper } from '../domain/mapper/notificationListMapper';
import { NotificationListChanges } from './async/changes/notificationListChange';
import { NotificationTable } from './infra/db/notification.db';
@Injectable({
providedIn: 'root'
})
export class NotificationRepositoryService {
constructor(
private RemoteNotificationService: RemoteNotificationService,
private FirebasePushNotificationService: FirebasePushNotificationService,
private LocalNotificationService: LocalNotificationService
) {
this.FirebasePushNotificationService.onReceiveForeground(async (data)=> {
console.log('FirebasePushNotificationService', data)
this.init()
})
this.init()
}
async init() {
const result = await this.getNotification({
PageNumber: "1",
PageSize: "50",
userId: SessionStore.user.UserId.toString()
})
if(result.isOk()) {
console.log('notification-list', result.value.data.result)
if(result.value.data.result.length >= 1) {
const localList = await this.LocalNotificationService.getNotification()
const serverList = NotificationListMapper(result.value)
const { insert, update } = NotificationListChanges(localList, serverList)
console.log({insert, update})
this.LocalNotificationService.addNotifications(insert)
this.LocalNotificationService.updateNotifications(update)
// this.LocalNotificationService.addNotifications.
} else {
this.LocalNotificationService.clear()
}
}
}
async getNotification(queryParameter: NotificationInputDTO) {
return await this.RemoteNotificationService.getNotification(queryParameter)
}
getNotificationLive() {
return this.LocalNotificationService.getNotificationLive()
}
async notificationStatus(item: NotificationTable) {
await this.RemoteNotificationService.notificationStatus(item.notificationId)
item.status = true
this.LocalNotificationService.updateNotification(item)
this
this.init()
return
}
async localNotificationStatus(item: NotificationTable) {
item.status = true
this.LocalNotificationService.updateNotification(item)
this.init()
return
}
}
@@ -0,0 +1,21 @@
import { NotificationTable } from "../../data/infra/db/notification.db";
import { NotificationOutputDTO } from '../../data/dto/NotificationOutputDTO'
export function NotificationListMapper(NotificationOutputDTO: NotificationOutputDTO): NotificationTable[] {
return NotificationOutputDTO.data.result.map( e => (
{
notificationId: e.id,
title: e.title,
service: e.service,
object: e.object,
idObject: e.objectId,
folderId: e.folderId,
dateInit: e.startDate,
dateEnd: e.endDate,
createdAt: e.createdAt,
status: e.status,
location: e.location
}
))
}
+34 -9
View File
@@ -249,14 +249,31 @@
<div class="schedule-time" *ngIf="event.event.IsAllDayEvent != true">
<div *ngIf="event.startMany && !event.middle" class="time-start labelb">Início</div>
<div *ngIf="event.endMany && !event.middle " class="time-end labelb">Fim</div>
<div *ngIf="event.startMany" class="time-start labelb">Início</div>
<div *ngIf="event.endMany " class="time-end labelb">Fim</div>
<div *ngIf="!(event.endMany && !event.middle) && !event.middle" class="time-start">{{event.event.StartDate | date: 'HH:mm'}}</div>
<div *ngIf="!(event.startMany && !event.middle) && !event.middle" class="time-end"> {{event.event.EndDate | date: 'HH:mm'}} </div>
<div *ngIf="event.endMany == false && event.middle == false || event.sameDay" class="time-start">{{event.event.StartDate | date: 'HH:mm'}}</div>
<div *ngIf="event.endMany == false && event.middle == false && event.eventTotalDuration>=1" class="time-start" style="color:red !important;">{{ event.duration }}</div>
<div *ngIf="event.startMany == false && event.middle == false && event.eventTotalDuration==0 || event.sameDay || event.endMany" class="time-end">
<span *ngIf="event.hasMany == true" >
{{event.event.EndDate | date: 'HH:mm'}}
</span>
<span *ngIf="event.manyDays == false">
{{event.event.EndDate | date: 'HH:mm'}}
</span>
</div>
<div *ngIf="event.startMany == false && event.middle == false && event.eventTotalDuration>=1 && event.endMany == false" class="time-end" style="color:red !important;"> {{ event.duration }} </div>
<div *ngIf="event.middle" class="time-start"> Todo </div>
<div *ngIf="event.middle" class="time-end text-center"> o dia </div>
<div *ngIf="event.middle" class="time-start">
<span style="color:red;" *ngIf="event.daysLeft >= 1">
{{ event.duration }}
</span>
</div>
</div>
<div class="schedule-time" *ngIf="event.event.IsAllDayEvent == true">
@@ -264,11 +281,11 @@
<div *ngIf="event.middle" class="time-start">Todo </div>
<div *ngIf="event.middle" class="time-end text-center">o dia</div>
<div *ngIf="!event.middle && !(event.endMany && !event.middle)" class="time-start">Todo </div>
<div *ngIf="!event.middle && !(event.endMany && !event.middle)" class="time-end text-center">o dia </div>
<div *ngIf="!event.middle && !(event.endMany)" class="time-start">Todo </div>
<div *ngIf="!event.middle && !(event.endMany)" class="time-end text-center">o dia </div>
<div *ngIf="event.endMany && !event.middle" class="time-start">{{event.event.StartDate | date: 'HH:mm'}}</div>
<div *ngIf="event.endMany && !event.middle" class="time-end"> {{event.event.EndDate | date: 'HH:mm'}} </div>
<div *ngIf="event.endMany" class="time-start">{{event.event.StartDate | date: 'HH:mm'}}</div>
<div *ngIf="event.endMany" class="time-end"> {{event.event.EndDate | date: 'HH:mm'}} </div>
</div>
<div class="schedule-details">
@@ -276,7 +293,15 @@
<p class="m-0">{{event.event.Subject}}</p>
</div>
<div class="location">{{event.event.Location}} <span style="color:red;" *ngIf="event.daysLeft >= 2">{{ event.daysLeft }}</span> <span style="color:red;" *ngIf="event.event.IsAllDayEvent != true && event.endMany && !event.middle">24h</span> </div>
<div class="location">
{{event.event.Location}}
<!-- <span style="color:red;" *ngIf="event.daysLeft >= 1">
{{ event.duration }}
</span>
<span style="color:red;" *ngIf="event.event.IsAllDayEvent != true && event.endMany">
{{ event.duration }} h
</span> -->
</div>
<div class="font-13 calendar-owner"*ngIf="eventService.getCalendarOwnNameByCalendarId(event.event.CalendarId) == 'Meu calendario'">{{SessionStore.user.FullName}} </div>
<ng-template #other_content>{{eventService.getCalendarOwnNameByCalendarId(event.event.CalendarId)}}</ng-template>
</div>
+18
View File
@@ -1254,6 +1254,24 @@ export class AgendaPage implements OnInit {
}
diffHours(date1Str: string, date2Str: string) {
// Convert string dates to Date objects
const date1: any = new Date(date1Str);
const date2: any = new Date(date2Str);
date1.setHours(0, 0, 0, 0); // Set hours, minutes, seconds, and milliseconds to 0
// Calculate the difference in milliseconds
const timeDifferenceMs = date2 - date1;
// Convert difference to hours
const hoursDifference = timeDifferenceMs / (1000 * 60 * 60);
return hoursDifference
}
shoeEventDay(events: any[]) {
if (this.segment == 'Combinado') {
+31 -5
View File
@@ -135,7 +135,7 @@ export class ListBoxService {
if (diffDays >= 1) {
const StartEvent = this.transForm(event, {startMany: true, endMany: false, middle: false})
const StartEvent = this.transForm(event, {startMany: true, endMany: false, middle: false, hasMany: true})
if(this.CanPush(event, selectedDate) && (new Date(event.start)).getTime() >= cloneSelectedDate.getTime()) {
days[day].push(StartEvent); this.push(StartEvent, year)
@@ -164,14 +164,14 @@ export class ListBoxService {
startDate.getMonth() != endDate.getMonth() ||
startDate.getDate() != endDate.getDate())) {
// last push
const EndEvent = this.transForm(cloneEvent, {startMany: false, endMany: true, middle: false})
const EndEvent = this.transForm(cloneEvent, {startMany: false, endMany: true, middle: false, hasMany: true})
if(this.CanPush(cloneEvent, selectedDate) && cloneEvent.start.getTime() >= cloneSelectedDate.getTime()) {
days[otherDays].push(EndEvent) ; this.push(EndEvent, year)
}
} else {
const EndEvent = this.transForm(cloneEvent, {startMany: false,endMany: true, middle: true})
const EndEvent = this.transForm(cloneEvent, {startMany: false,endMany: false, middle: true, hasMany: true})
if(this.CanPush(cloneEvent, selectedDate) && cloneEvent.start.getTime() >= cloneSelectedDate.getTime()) {
days[otherDays].push(EndEvent) ; this.push(EndEvent, year)
} else {
@@ -191,11 +191,11 @@ export class ListBoxService {
}
} else {
if(this.CanPush(event, selectedDate)) { days[day].push(event) ; this.push(event, year) }
}
} else {
event['sameDay'] = true
if(this.CanPush(event, selectedDate) && diffDays != 2) { days[day].push(event) ; this.push(event, year) }
}
@@ -306,9 +306,10 @@ export class ListBoxService {
return events;
}
transForm(event: CustomCalendarEvent, {startMany, endMany, middle}) {
transForm(event: CustomCalendarEvent, {startMany, endMany, middle, hasMany = false}) {
let daysLeft = this.daysToEndWithJS(event.start, event.end);
let eventTotalDuration = this.daysToEndWithJS(event.event.StartDate, event.event.EndDate);
return Object.assign({}, {
start: event.start,
@@ -325,6 +326,9 @@ export class ListBoxService {
CalendarId: event.event.CalendarId,
daysLeft
},
eventTotalDuration,
hasMany,
duration: this.duration(event.start, event.event.EndDate),
daysLeft,
Subject: event.event.Subject,
startMany: startMany,
@@ -341,6 +345,8 @@ export class ListBoxService {
const startDate: any = new Date(startDateStr);
const endDate: any = new Date(endDateStr);
startDate.setHours(0, 0, 0, 0); // Set hours, minutes, seconds, and milliseconds to 0
endDate.setHours(0, 0, 0, 0); // Set hours, minutes, seconds, and milliseconds to 0
// Calculate the difference in milliseconds between the two dates
const differenceMs = Math.abs(endDate - startDate);
@@ -355,6 +361,26 @@ export class ListBoxService {
}
duration(date1Str, date2Str) {
// Convert string dates to Date objects
const date1: any = new Date(date1Str);
const date2: any = new Date(date2Str);
// Calculate the difference in milliseconds
const timeDifferenceMs = date2 - date1;
// Convert difference to days, hours, and minutes
const totalMinutes = Math.floor(timeDifferenceMs / (1000 * 60));
const days = Math.floor(totalMinutes / (60 * 24));
const hours = Math.floor((totalMinutes % (60 * 24)) / 60);
const minutes = totalMinutes % 60;
return `${days}d`
}
transformObjectKeyOrder(originalObject, keyOrder) {
const transformedObject = {};
+10
View File
@@ -62,6 +62,16 @@ export class HttpService {
return err(e as HttpErrorResponse)
}
}
async patch<T>(url: string, body ={}): Promise<Result<T, HttpErrorResponse>> {
try {
const result = await this.http.patch<T>(url, body).toPromise();
return ok(result as T);
} catch (e) {
return err(e as HttpErrorResponse);
}
}
}
@@ -6,6 +6,8 @@ import { environment } from 'src/environments/environment';
import { NotificationsEndsPointsService } from './notifications-ends-points.service'
import { AngularFireMessaging } from '@angular/fire/messaging';
import { NavigationExtras, Router } from '@angular/router';
import { NotificationRepositoryService } from 'src/app/module/notification/data/notification-repository.service'
@Injectable({
providedIn: 'root'
})
@@ -20,6 +22,7 @@ export class NotificationsService {
private afMessaging: AngularFireMessaging,
private router: Router,
private zone: NgZone,
private NotificationRepositoryService: NotificationRepositoryService
) { }
+5 -4
View File
@@ -20,7 +20,7 @@ import { ChatSystemService } from './chat/chat-system.service';
import {ChatController} from 'src/app/controller/chat'
import { TracingType, XTracer, XTracerAsync } from 'src/app/services/monitoring/opentelemetry/tracer';
import { z } from 'zod';
import { NotificationRepositoryService } from 'src/app/module/notification/data/notification-repository.service'
const notificationDataSchema = z.object({
Service: z.string(),
IdObject: z.string().optional(),
@@ -69,10 +69,11 @@ export class NotificationsService {
private zone: NgZone,
private eventtrigger: EventTrigger,
private afMessaging: AngularFireMessaging,
public NotificationHolderService: NotificationHolderService) {
public NotificationHolderService: NotificationHolderService,
private NotificationRepositoryService: NotificationRepositoryService) {
this.onReciveForeground();
this.onReciveBackground();
// this.onReciveForeground();
// this.onReciveBackground();
}
+6 -6
View File
@@ -1,11 +1,11 @@
export let versionData = {
"shortSHA": "02891dbb9",
"SHA": "02891dbb9a9d1123f7cd0ca2414fd6014599aec1",
"shortSHA": "78c13d1bf",
"SHA": "78c13d1bfb0b5e24a79a101990ba73e50e831107",
"branch": "feature/agenda-api-peter",
"lastCommitAuthor": "'Peter Maquiran'",
"lastCommitTime": "'Wed Jun 26 10:11:02 2024 +0100'",
"lastCommitMessage": "ITOTEAM-530 Inform the user how many days are left until the end of the wind for each selected day",
"lastCommitNumber": "5843",
"changeStatus": "On branch feature/agenda-api-peter\nYour branch is ahead of 'origin/feature/agenda-api-peter' by 6 commits.\n (use \"git push\" to publish your local commits)\n\nChanges to be committed:\n (use \"git restore --staged <file>...\" to unstage)\n\tmodified: src/app/models/entiry/agenda/eventList.ts\n\tmodified: src/app/models/search-document.ts\n\tmodified: src/app/pages/search/search.page.html\n\tmodified: src/app/pages/search/search.page.ts\n\tmodified: src/app/services/Repositorys/Agenda/agenda-data-repository.service.ts\n\tmodified: src/app/services/Repositorys/Agenda/agenda-data.service.ts\n\tmodified: src/app/services/Repositorys/Agenda/mapper/EventListMapper.ts\n\tnew file: src/app/services/Repositorys/Agenda/mapper/EventSearchMapper.ts\n\tmodified: src/app/services/Repositorys/Agenda/mapper/eventToApproveListMapper.ts\n\tmodified: src/app/services/Repositorys/Agenda/model/eventListDTOOutput.ts\n\tnew file: src/app/services/Repositorys/Agenda/model/eventSearchOutputDTO.ts\n\tmodified: src/app/services/http.service.ts\n\tmodified: version/git-version.ts",
"lastCommitTime": "'Wed Jun 26 13:45:25 2024 +0100'",
"lastCommitMessage": "ITOTEAM-609 search event>",
"lastCommitNumber": "5844",
"changeStatus": "On branch feature/agenda-api-peter\nYour branch is ahead of 'origin/feature/agenda-api-peter' by 7 commits.\n (use \"git push\" to publish your local commits)\n\nChanges to be committed:\n (use \"git restore --staged <file>...\" to unstage)\n\tmodified: src/app/modals/profile/profile.page.html\n\tmodified: src/app/modals/profile/profile.page.scss\n\tmodified: src/app/modals/profile/profile.page.ts\n\tnew file: src/app/module/notification/data/async/changes/notificationListChange.ts\n\tnew file: src/app/module/notification/data/datasource/firebase-push-notification.service.spec.ts\n\tnew file: src/app/module/notification/data/datasource/firebase-push-notification.service.ts\n\tnew file: src/app/module/notification/data/datasource/local-notification.service.spec.ts\n\tnew file: src/app/module/notification/data/datasource/local-notification.service.ts\n\tnew file: src/app/module/notification/data/datasource/remote-notification.service.spec.ts\n\tnew file: src/app/module/notification/data/datasource/remote-notification.service.ts\n\tnew file: src/app/module/notification/data/dto/NotificationInputDTO.ts\n\tnew file: src/app/module/notification/data/dto/NotificationOutputDTO.ts\n\tnew file: src/app/module/notification/data/infra/db/notification.db.ts\n\tnew file: src/app/module/notification/data/notification-repository.service.spec.ts\n\tnew file: src/app/module/notification/data/notification-repository.service.ts\n\tnew file: src/app/module/notification/domain/mapper/notificationListMapper.ts\n\tmodified: src/app/pages/agenda/agenda.page.html\n\tmodified: src/app/pages/agenda/agenda.page.ts\n\tmodified: src/app/services/agenda/list-box.service.ts\n\tmodified: src/app/services/http.service.ts\n\tmodified: src/app/services/notification/notifications.service.ts\n\tmodified: src/app/services/notifications.service.ts",
"changeAuthor": "peter.maquiran"
}