1 Commits

Author SHA1 Message Date
peter.maquiran 010583cd43 list task 2025-06-03 15:09:19 +01:00
15 changed files with 368 additions and 19 deletions
+7 -7
View File
@@ -147,7 +147,7 @@
"date-fns": "^2.17.0",
"deep-object-diff": "^1.1.9",
"depd": "^2.0.0",
"dexie": "^4.0.7",
"dexie": "^4.0.11",
"dompurify": "^3.0.6",
"dotenv": "^10.0.0",
"duration": "^0.2.2",
@@ -15180,9 +15180,9 @@
"integrity": "sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ=="
},
"node_modules/dexie": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.7.tgz",
"integrity": "sha512-M+Lo6rk4pekIfrc2T0o2tvVJwL6EAAM/B78DNfb8aaxFVoI1f8/rz5KTxuAnApkwqTSuxx7T5t0RKH7qprapGg=="
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.11.tgz",
"integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A=="
},
"node_modules/di": {
"version": "0.0.1",
@@ -55940,9 +55940,9 @@
"integrity": "sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ=="
},
"dexie": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.7.tgz",
"integrity": "sha512-M+Lo6rk4pekIfrc2T0o2tvVJwL6EAAM/B78DNfb8aaxFVoI1f8/rz5KTxuAnApkwqTSuxx7T5t0RKH7qprapGg=="
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.11.tgz",
"integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A=="
},
"di": {
"version": "0.0.1",
+1 -1
View File
@@ -166,7 +166,7 @@
"date-fns": "^2.17.0",
"deep-object-diff": "^1.1.9",
"depd": "^2.0.0",
"dexie": "^4.0.7",
"dexie": "^4.0.11",
"dompurify": "^3.0.6",
"dotenv": "^10.0.0",
"duration": "^0.2.2",
+3 -1
View File
@@ -104,6 +104,7 @@ import { registerLocaleData } from '@angular/common';
import localePt from '@angular/common/locales/pt';
import { LogsDatabase } from './infra/database/dexie/instance/logs/service';
import { UserModule } from './module/user/user.module';
import { GabineteModule } from './module/gabinete/gabinete.module';
// Register the locale data
registerLocaleData(localePt, 'pt');
@@ -210,7 +211,8 @@ registerLocaleData(localePt, 'pt');
MatIconModule,
// module
ChatModule,
UserModule
UserModule,
GabineteModule
],
entryComponents: [
DiplomaOptionsPage,
+1 -1
View File
@@ -72,7 +72,7 @@ export const MessageEntitySchema = z.object({
export type IMessage = z.infer<typeof MessageEntitySchema>;
export class MessageEntity {
export class MessageEntity implements IMessage {
$id?: string
id?: string
@@ -0,0 +1,50 @@
import { z } from "zod";
export const TaskListOutputItemDTO = z.object({
id: z.number(),
instanceID: z.number(),
folderId: z.number(),
folio: z.string(),
elementID: z.number(),
elementName: z.string(),
workflowId: z.number(),
workflowName: z.string(),
dispatchNumber: z.string(),
sender: z.string().optional(),
status: z.number(),
createdOn: z.coerce.date(),
createdByName: z.string(),
lastUpdate: z.coerce.date(),
assignedToUserID: z.number(),
assignedOpened: z.boolean(),
assignedUser: z.string(),
delegatedUserId: z.number().optional(),
delegatedOpened: z.boolean(),
delegatedUser: z.string().optional(),
delegatedOn: z.coerce.date().optional(),
openedBy: z.number().optional(),
openedUser: z.string().optional(),
openedDate: z.coerce.date().optional(),
completedBy: z.number().optional(),
completedUser: z.string().optional(),
completedDate: z.coerce.date().optional(),
dueDate: z.coerce.date().optional(),
sequencial: z.boolean(),
order: z.number(),
acknowledge: z.boolean(),
instruction: z.string().optional(),
note: z.string().optional(),
processCreatedBy: z.number(),
processCreatedByName: z.string(),
activityDataFields: z.any().optional(),
assigneeStandBy: z.boolean(),
delegateStandBy: z.boolean(),
pending: z.boolean().default(false),
});
export const TaskListOutputDTOSchema = z.object({
total: z.number(),
result: z.array(TaskListOutputItemDTO),
});
export type ITaskListOutputDTO = z.infer<typeof TaskListOutputDTOSchema>;
@@ -0,0 +1,58 @@
import { z } from 'zod';
import { TaskListOutputItemDTO } from '../dto/task-list-output-dto';
export const TaskEntitySchema = z.object({
}).merge(TaskListOutputItemDTO);
export type ITaskEntity = z.infer<typeof TaskEntitySchema>;
export class TaskEntity implements ITaskEntity {
id: typeof TaskEntitySchema._type.id;
instanceID: typeof TaskEntitySchema._type.instanceID;
folderId: typeof TaskEntitySchema._type.folderId;
folio: typeof TaskEntitySchema._type.folio;
elementID: typeof TaskEntitySchema._type.elementID;
elementName: typeof TaskEntitySchema._type.elementName;
workflowId: typeof TaskEntitySchema._type.workflowId;
workflowName: typeof TaskEntitySchema._type.workflowName;
dispatchNumber: typeof TaskEntitySchema._type.dispatchNumber;
sender?: typeof TaskEntitySchema._type.sender;
status: typeof TaskEntitySchema._type.status;
createdOn: typeof TaskEntitySchema._type.createdOn;
createdByName: typeof TaskEntitySchema._type.createdByName;
lastUpdate: typeof TaskEntitySchema._type.lastUpdate;
assignedToUserID: typeof TaskEntitySchema._type.assignedToUserID;
assignedOpened: typeof TaskEntitySchema._type.assignedOpened;
assignedUser: typeof TaskEntitySchema._type.assignedUser;
delegatedUserId?: typeof TaskEntitySchema._type.delegatedUserId;
delegatedOpened: typeof TaskEntitySchema._type.delegatedOpened;
delegatedUser?: typeof TaskEntitySchema._type.delegatedUser;
delegatedOn?: typeof TaskEntitySchema._type.delegatedOn;
openedBy?: typeof TaskEntitySchema._type.openedBy;
openedUser?: typeof TaskEntitySchema._type.openedUser;
openedDate?: typeof TaskEntitySchema._type.openedDate;
completedBy?: typeof TaskEntitySchema._type.completedBy;
completedUser?: typeof TaskEntitySchema._type.completedUser;
completedDate?: typeof TaskEntitySchema._type.completedDate;
dueDate?: typeof TaskEntitySchema._type.dueDate;
sequencial: typeof TaskEntitySchema._type.sequencial;
order: typeof TaskEntitySchema._type.order;
acknowledge: typeof TaskEntitySchema._type.acknowledge;
instruction?: typeof TaskEntitySchema._type.instruction;
note?: typeof TaskEntitySchema._type.note;
processCreatedBy: typeof TaskEntitySchema._type.processCreatedBy;
processCreatedByName: typeof TaskEntitySchema._type.processCreatedByName;
activityDataFields?: typeof TaskEntitySchema._type.activityDataFields;
assigneeStandBy: typeof TaskEntitySchema._type.assigneeStandBy;
delegateStandBy: typeof TaskEntitySchema._type.delegateStandBy;
pending: typeof TaskEntitySchema._type.pending;
constructor(data: ITaskEntity) {
Object.assign(this, data);
}
setData(data: Partial<ITaskEntity>): void {
Object.assign(this, data);
}
}
@@ -0,0 +1,61 @@
import { TracingType } from "src/app/services/monitoring/opentelemetry/tracer";
import { TaskEntity } from "../entity/task.entity";
import { TaskRepository } from "src/app/module/gabinete/data/repository/task.repository";
import { Injectable } from "@angular/core";
import { TaskLocalRepository } from "src/app/module/gabinete/data/repository/task-local.repository";
@Injectable({
providedIn: 'root'
})
export class TaskGetAllByUserIdUseCase {
constructor(
private taskRepository: TaskRepository,
private taskLocalRepository: TaskLocalRepository
) {}
async execute(tracing?: TracingType): Promise<TaskEntity[]> {
const finalList: Record<string, TaskEntity> = {};
await Promise.all([
(async () => {
const response = await this.taskRepository.getAllTasksPaginated({
pendingtasks: true,
tracing
});
if (response.isOk()) {
const list = response.value
for (const task of list) {
task.pending = true;
}
for (const task of list) {
finalList[task.id.toString()] = task;
}
}
})(),
(async () => {
const response = await this.taskRepository.getAllTasksPaginated({
pendingtasks: false,
tracing
});
if (response.isOk()) {
const list = response.value;
for (const task of list) {
finalList[task.id.toString()] = task;
}
}
})()
]);
tracing?.finish();
//await this._localRepo.clear();
//await this._localRepo.updateMany(Object.values(finalList));
//return this._localRepo.getAll();
return [];
}
}
@@ -0,0 +1,11 @@
import { EntityTable } from 'Dexie';
import { TaskEntitySchema } from 'src/app/core/gabinete/entity/task.entity';
import { z } from 'zod';
export const taskTableSchema = z.object({
//$id: z.string().optional(),
}).merge(TaskEntitySchema);
export type TaskTable = z.infer<typeof taskTableSchema>
export type DexieTaskTable = EntityTable<TaskTable, 'id'>;
export const taskTableColumn = 'id, instanceID, folderId, folio, elementID, elementName, workflowId, workflowName, dispatchNumber, sender, status, createdOn, createdByName, lastUpdate, assignedToUserID, assignedOpened, assignedUser, delegatedUserId, delegatedOpened, delegatedUser, delegatedOn, openedBy, openedUser, openedDate, completedBy, completedUser, completedDate, dueDate, sequencial, order, acknowledge, instruction, note, processCreatedBy, processCreatedByName, activityDataFields, assigneeStandBy, delegateStandBy, pending';
@@ -0,0 +1,16 @@
import { Dexie } from 'Dexie';
import { DexieTaskTable, taskTableColumn } from './schema/task';
// Database declaration (move this to its own module also)
export const gabineteDatabase = new Dexie('gabinete-database-v1',{
//indexedDB: new FDBFactory,
//IDBKeyRange: FDBKeyRange, // Mocking IDBKeyRange
}) as Dexie & {
task: DexieTaskTable,
};
gabineteDatabase.version(1).stores({
message: taskTableColumn,
});
@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { DexieRepository } from 'src/app/infra/repository/dexie/dexie-repository.service';
import { chatDatabase } from 'src/app/infra/database/dexie/instance/chat/service';
import { TaskEntity } from 'src/app/core/gabinete/entity/task.entity';
import { TaskTable, DexieTaskTable, taskTableSchema } from 'src/app/infra/database/dexie/instance/gabinete/schema/task';
import { gabineteDatabase } from 'src/app/infra/database/dexie/instance/gabinete/service';
@Injectable({
providedIn: 'root'
})
export class TaskLocalRepository extends DexieRepository<TaskTable, TaskEntity, DexieTaskTable> {
constructor() {
super(gabineteDatabase.task, taskTableSchema, gabineteDatabase)
}
}
@@ -0,0 +1,78 @@
import { Injectable } from '@angular/core';
import { APINODReturn } from 'src/app/services/decorator/api-validate-schema.decorator';
import { TracingType } from 'src/app/services/monitoring/opentelemetry/tracer';
import { HttpService, isHttpError } from 'src/app/services/http.service';
import { err, ok, Result } from 'neverthrow';
import { HttpErrorResponse } from '@angular/common/http';
import { ITaskEntity, TaskEntity } from 'src/app/core/gabinete/entity/task.entity';
import { SessionStore } from 'src/app/store/session.service';
import { z } from 'zod';
import { ITaskListOutputDTO, TaskListOutputDTOSchema } from 'src/app/core/gabinete/dto/task-list-output-dto';
@Injectable({
providedIn: 'root'
})
export class TaskRepository {
private baseUrl = 'https://dev-api.doneit.co.ao/api'; // Your base URL
constructor(
private httpService: HttpService
) { }
async getAllTasksPaginated(options?: {
pendingtasks?: boolean;
tracing?: TracingType;
}): Promise<Result<TaskEntity[], HttpErrorResponse>> {
let currentPage = 1;
let totalTasks = 0;
const allTasks: TaskEntity[] = [];
const tracing = options?.tracing;
do {
const queryParams: Record<string, string> = {
PageNumber: currentPage.toString(),
PageSize: '500'
};
if (options?.pendingtasks !== undefined) {
queryParams['pendingtasks'] = options.pendingtasks.toString();
}
const result = await this.httpService.get<ITaskListOutputDTO>(
`${this.baseUrl}/Processes/${SessionStore.user.UserId}`,
queryParams,
tracing
);
if (result.isErr()) {
return err(result.error);
}
try {
const dto = result.value;
const rawList = dto.result;
totalTasks = dto.total;
allTasks.push(...rawList.map(item => new TaskEntity(item)));
} catch (e) {
tracing?.setAttribute('outcome', 'mapping_failed');
tracing?.log('Failed to map task entities', {
context: (e as Error).message
});
return err({
name: 'MappingError',
message: 'Failed to map response to TaskEntity',
} as unknown as HttpErrorResponse);
}
currentPage++;
} while (allTasks.length < totalTasks);
return ok(allTasks);
}
}
@@ -0,0 +1,17 @@
import { Injectable } from "@angular/core";
import { TaskGetAllByUserIdUseCase } from "src/app/core/gabinete/use-case/task-get-all-by-user-id";
@Injectable({
providedIn: 'root'
})
export class GabineteService {
constructor(
private taskGetAllByUserIdUseCase: TaskGetAllByUserIdUseCase
) {}
taskGetAllByUserId() {
this.taskGetAllByUserIdUseCase.execute();
}
}
@@ -0,0 +1,34 @@
import { NgModule } from '@angular/core';
import { SignalRService } from 'src/app/infra/socket/signalR/signal-r.service';
import { HttpModule } from 'src/app/infra/http/http.module';
import { TaskGetAllByUserIdUseCase } from 'src/app/core/gabinete/use-case/task-get-all-by-user-id';
import { ISignalRService } from 'src/app/infra/socket/adapter';
import { TaskLocalRepository } from './data/repository/task-local.repository';
@NgModule({
imports: [HttpModule],
providers: [
{
provide: ISignalRService,
useClass: SignalRService, // or MockDataService
},
{
provide: TaskGetAllByUserIdUseCase,
useClass: TaskGetAllByUserIdUseCase, // or MockDataService
},
{
provide: TaskLocalRepository,
useClass: TaskLocalRepository
}
],
declarations: [],
schemas: [],
entryComponents: []
})
export class GabineteModule {
constructor() {}
async listenToTyping() {}
async syncMessage() {}
}
@@ -16,6 +16,7 @@ import { SessionStore } from 'src/app/store/session.service';
import { NotificationsService } from 'src/app/services/notifications.service'
import { environment } from 'src/environments/environment';
import { TaskService } from 'src/app/services/task.service'
import { GabineteService } from 'src/app/module/gabinete/domain/gabinete.service'
@Component({
selector: 'app-gabinete-digital',
@@ -103,9 +104,12 @@ export class GabineteDigitalPage implements OnInit {
public ThemeService: ThemeService,
public p: PermissionService,
public NotificationsService: NotificationsService,
public TaskService: TaskService
public TaskService: TaskService,
private gabineteService: GabineteService,
) {
this.gabineteService.taskGetAllByUserId();
window.onresize = (event) => {
if (window.innerWidth < 701) {
this.hideRefreshBtn = false;
+8 -8
View File
@@ -1,11 +1,11 @@
export let versionData = {
"shortSHA": "470b1e7f0",
"SHA": "470b1e7f0e0e7a4ae7155d7b88c322e770e21f26",
"branch": "feature/login-v2",
"lastCommitAuthor": "'peter.maquiran'",
"lastCommitTime": "'Tue Jun 3 09:42:08 2025 +0100'",
"lastCommitMessage": "fix",
"lastCommitNumber": "6141",
"changeStatus": "On branch feature/login-v2\nYour branch is ahead of 'origin/feature/login-v2' 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: version/git-version.ts",
"shortSHA": "cfc7330e7",
"SHA": "cfc7330e729bb815925e687ffa74c20fe4431eb3",
"branch": "api-doneit",
"lastCommitAuthor": "'Peter Maquiran'",
"lastCommitTime": "'Tue Jun 3 08:46:54 2025 +0000'",
"lastCommitMessage": "Merged in feature/login-v2 (pull request #36)\n\nFeature/login v2",
"lastCommitNumber": "6143",
"changeStatus": "On branch api-doneit\nYour branch is up to date with 'origin/api-doneit'.\n\nChanges to be committed:\n (use \"git restore --staged <file>...\" to unstage)\n\tmodified: package-lock.json\n\tmodified: package.json\n\tmodified: src/app/app.module.ts\n\tmodified: src/app/core/chat/entity/message.ts\n\tnew file: src/app/core/gabinete/dto/task-list-output-dto.ts\n\tnew file: src/app/core/gabinete/entity/task.entity.ts\n\tnew file: src/app/core/gabinete/use-case/task-get-all-by-user-id.ts\n\tnew file: src/app/infra/database/dexie/instance/gabinete/schema/task.ts\n\tnew file: src/app/infra/database/dexie/instance/gabinete/service.ts\n\tnew file: src/app/module/gabinete/data/repository/task-local.repository.ts\n\tnew file: src/app/module/gabinete/data/repository/task.repository.ts\n\tnew file: src/app/module/gabinete/domain/gabinete.service.ts\n\tnew file: src/app/module/gabinete/gabinete.module.ts\n\tmodified: src/app/pages/gabinete-digital/gabinete-digital.page.ts",
"changeAuthor": "peter.maquiran"
}