diff --git a/src/angular/hq/src/app/app.routes.ts b/src/angular/hq/src/app/app.routes.ts index 5ca92293..af1425e6 100644 --- a/src/angular/hq/src/app/app.routes.ts +++ b/src/angular/hq/src/app/app.routes.ts @@ -16,6 +16,44 @@ export const routes: Routes = [ loadComponent: () => import('./callback.component').then((m) => m.CallbackComponent), }, + { + path: 'chargecodes', + title: 'Charge Codes', + canActivate: [AutoLoginPartialRoutesGuard, userRoleGuard(HQRole.Administrator)], + loadComponent: () => + import('.//charge-code/charge-code.component').then( + (m) => m.ChargeCodeComponent + ), + children: [ + { + path: '', + title: 'Charge Code List', + canActivate: [userRoleGuard(HQRole.Administrator)], + loadComponent: () => + import('./charge-code/charge-code-list/charge-code-list.component').then( + (m) => m.ChargeCodeListComponent + ), + }, + { + path: 'create', + title: 'Charge Code Create', + canActivate: [userRoleGuard(HQRole.Administrator)], + loadComponent: () => + import('./charge-code/charge-code-create/charge-code-create.component').then( + (m) => m.ChargeCodeCreateComponent + ), + }, + { + path: 'edit/:chargeCodeId', + title: 'Charge Code Edit', + canActivate: [userRoleGuard(HQRole.Administrator)], + loadComponent: () => + import('./charge-code/charge-code-edit/charge-code-edit.component').then( + (m) => m.ChargeCodeEditComponent + ), + } + ] + }, { path: 'clients', title: 'Clients', diff --git a/src/angular/hq/src/app/charge-code/SearchFilter/charge-code-search-filter.component.html b/src/angular/hq/src/app/charge-code/SearchFilter/charge-code-search-filter.component.html new file mode 100644 index 00000000..27b994b7 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/SearchFilter/charge-code-search-filter.component.html @@ -0,0 +1,57 @@ +
+ @if(ChargeCodeListService.showSearch$ | async) { + +
+ + + + +
+ } @if(ChargeCodeListService.staffMembers$ && ChargeCodeListService.showStaffMembers$ | async) { +
+ + + + +
+ } @if(ChargeCodeListService.showIsSubmitted$ | async) { +
+ + + + +
+ } @if (ChargeCodeListService.showRoaster$ | async) { + + } @if ( ChargeCodeListService.showActivityName$ | async ) { + + } +
\ No newline at end of file diff --git a/src/angular/hq/src/app/charge-code/SearchFilter/charge-code-search-filter.component.ts b/src/angular/hq/src/app/charge-code/SearchFilter/charge-code-search-filter.component.ts new file mode 100644 index 00000000..bbb7d914 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/SearchFilter/charge-code-search-filter.component.ts @@ -0,0 +1,18 @@ +import { ChargeCodeListService } from './../services/ChargeCodeListService'; +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; +import { ReactiveFormsModule, FormsModule } from "@angular/forms"; +import { PsrService } from "../../psr/psr-service"; + + +@Component({ + selector: 'hq-psr-search-filter', + standalone: true, + imports: [CommonModule, ReactiveFormsModule, FormsModule], + templateUrl: './charge-code-search-filter.component.html', +}) +export class ChargeCodeSearchFilterComponent { + constructor(public ChargeCodeListService: ChargeCodeListService) { + + } +} \ No newline at end of file diff --git a/src/angular/hq/src/app/charge-code/charge-code-create/charge-code-create.component.html b/src/angular/hq/src/app/charge-code/charge-code-create/charge-code-create.component.html new file mode 100644 index 00000000..55156034 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code-create/charge-code-create.component.html @@ -0,0 +1,243 @@ +
+
+
+

Create Charge Code

+ + +
+ +
+
+
+
+
+
+ Details +
+
+ +
+
+
+
+
+
+ + + +
+ + +
+
Charge Code Activity
+
+ + + + +
+
+ @if (showProjects$ | async) { +
+ + + + + @if(form.get('ProjectId')?.touched && + form.get('ProjectId')?.errors?.['required']) { + Selecting Project is required. + } +
+ } + @if (showQuotes$ | async) { +
+ + + + + @if(form.get('QuoteId')?.touched && + form.get('QuoteId')?.errors?.['required']) { + Selecting Quote is required. + } +
+ } + @if (showServices$ | async) { +
+ + + + + @if(form.get('ServiceAgreementId')?.touched && + form.get('ServiceAgreementId')?.errors?.['required']) { + Selecting Service Agreement is required. + } +
+ } + + +
+ +
+ +
+
+ + + + +
+
+
+
+ + + + +
+
+
+ + + @if(form.get('Description')?.touched && + form.get('Description')?.errors?.['required']) { + Description is required. + } +
+
+ +
+
+
+
+
+
+
+ +
diff --git a/src/angular/hq/src/app/charge-code/charge-code-create/charge-code-create.component.ts b/src/angular/hq/src/app/charge-code/charge-code-create/charge-code-create.component.ts new file mode 100644 index 00000000..9686b6c8 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code-create/charge-code-create.component.ts @@ -0,0 +1,151 @@ +import { ChargeCodeActivity } from './../../models/charge-codes/get-chargecodes-v1'; +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { + FormsModule, + ReactiveFormsModule, + FormGroup, + FormControl, + Validators, + ValidationErrors, +} from '@angular/forms'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Observable, BehaviorSubject, map, firstValueFrom } from 'rxjs'; +import { APIError } from '../../errors/apierror'; +import { + Jurisdiciton, +} from '../../models/staff-members/get-staff-member-v1'; +import { HQService } from '../../services/hq.service'; +import { ErrorDisplayComponent } from '../../errors/error-display/error-display.component'; +import { GetProjectRecordV1 } from '../../models/projects/get-project-v1'; +import { GetServicesRecordV1 } from '../../models/Services/get-services-v1'; +import { GetQuotesRecordV1 } from '../../models/quotes/get-quotes-v1'; + + +interface Form { + Activity: FormControl; + ProjectId: FormControl; + QuoteId: FormControl; + ServiceAgreementId: FormControl; + Billable: FormControl; + Active: FormControl; + Description: FormControl; +} + +@Component({ + selector: 'hq-charge-code-create', + standalone: true, + imports: [CommonModule, FormsModule, ReactiveFormsModule, ErrorDisplayComponent], + templateUrl: './charge-code-create.component.html' +}) +export class ChargeCodeCreateComponent { + apiErrors: string[] = []; + ChargeCodeActivity = ChargeCodeActivity; + + projects$: Observable; + services$: Observable; + quotes$: Observable; + + showProjects$ = new BehaviorSubject(null); + showQuotes$ = new BehaviorSubject(null); + showServices$ = new BehaviorSubject(null); + + + + + form = new FormGroup
({ + Activity: new FormControl(null, { + validators: [Validators.required], + }), + ProjectId: new FormControl(null, { + validators: [], + }), + QuoteId: new FormControl(null, { + validators: [], + }), + ServiceAgreementId: new FormControl(null, { + validators: [], + }), + Billable: new FormControl(null, { + validators: [Validators.required], + }), + Active: new FormControl(null, { + validators: [Validators.required], + }), + Description: new FormControl(null, { + validators: [], + }), + + }); + + constructor( + private hqService: HQService, + private router: Router, + private route: ActivatedRoute + ) { + const quotesResponse$ = this.hqService.getQuotesV1({}); + this.quotes$ = quotesResponse$.pipe( + map((response) => { + return response.records; + }) + ); + const projectsResponse$ = this.hqService.getProjectsV1({}); + this.projects$ = projectsResponse$.pipe( + map((response) => { + return response.records; + })); + + const servicesResponse$ = this.hqService.getServicesV1({}); + this.services$ = servicesResponse$.pipe( + map((response) => { + return response.records; + })); + + this.form.controls.Activity.valueChanges.subscribe((chargeCodeActivity)=>{ + this.form.controls.ProjectId.setValue(null); + this.form.controls.QuoteId.setValue(null); + this.form.controls.ServiceAgreementId.setValue(null); + + this.showProjects$.next(false); + this.showQuotes$.next(false); + this.showServices$.next(false); + + switch(chargeCodeActivity) { + case(ChargeCodeActivity.Project): + this.showProjects$.next(true); + break; + case(ChargeCodeActivity.Quote): + this.showQuotes$.next(true); + break; + case(ChargeCodeActivity.Service): + this.showServices$.next(true); + break; + } + }) + } + + async submit() { + this.form.markAsTouched(); + console.log(this.form.value); + if (this.form.invalid) { + this.apiErrors = []; + this.apiErrors = ['Invlid Form Error'] + return; + } + console.log('Form is valid'); + + try { + const request = this.form.value; + const response = await firstValueFrom( + this.hqService.upsertChargecodesV1(request) + ); + this.router.navigate(['../'], { relativeTo: this.route }); + } catch (err) { + if (err instanceof APIError) { + this.apiErrors = err.errors + } else { + this.apiErrors = ['An unexpected error has occurred.']; + } + } + } +} diff --git a/src/angular/hq/src/app/charge-code/charge-code-edit/charge-code-edit.component.html b/src/angular/hq/src/app/charge-code/charge-code-edit/charge-code-edit.component.html new file mode 100644 index 00000000..55156034 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code-edit/charge-code-edit.component.html @@ -0,0 +1,243 @@ + +
+
+

Create Charge Code

+ + +
+ +
+
+
+
+
+
+ Details +
+
+ +
+
+
+
+
+
+ + + +
+ + +
+
Charge Code Activity
+
+ + + + +
+
+ @if (showProjects$ | async) { +
+ + + + + @if(form.get('ProjectId')?.touched && + form.get('ProjectId')?.errors?.['required']) { + Selecting Project is required. + } +
+ } + @if (showQuotes$ | async) { +
+ + + + + @if(form.get('QuoteId')?.touched && + form.get('QuoteId')?.errors?.['required']) { + Selecting Quote is required. + } +
+ } + @if (showServices$ | async) { +
+ + + + + @if(form.get('ServiceAgreementId')?.touched && + form.get('ServiceAgreementId')?.errors?.['required']) { + Selecting Service Agreement is required. + } +
+ } + + +
+ +
+ +
+
+ + + + +
+
+
+
+ + + + +
+
+
+ + + @if(form.get('Description')?.touched && + form.get('Description')?.errors?.['required']) { + Description is required. + } +
+
+ +
+
+
+
+
+
+
+ +
diff --git a/src/angular/hq/src/app/charge-code/charge-code-edit/charge-code-edit.component.ts b/src/angular/hq/src/app/charge-code/charge-code-edit/charge-code-edit.component.ts new file mode 100644 index 00000000..f5975cf5 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code-edit/charge-code-edit.component.ts @@ -0,0 +1,168 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Observable, BehaviorSubject, map, firstValueFrom } from 'rxjs'; +import { APIError } from '../../errors/apierror'; +import { ChargeCodeActivity } from '../../models/charge-codes/get-chargecodes-v1'; +import { GetProjectRecordV1 } from '../../models/projects/get-project-v1'; +import { GetQuotesRecordV1 } from '../../models/quotes/get-quotes-v1'; +import { GetServicesRecordV1 } from '../../models/Services/get-services-v1'; +import { HQService } from '../../services/hq.service'; +import { CommonModule } from '@angular/common'; +import { ErrorDisplayComponent } from '../../errors/error-display/error-display.component'; + + +interface Form { + Activity: FormControl; + ProjectId: FormControl; + QuoteId: FormControl; + ServiceAgreementId: FormControl; + Billable: FormControl; + Active: FormControl; + Description: FormControl; +} +@Component({ + selector: 'hq-charge-code-edit', + standalone: true, + imports: [CommonModule, FormsModule, ReactiveFormsModule, ErrorDisplayComponent], + + templateUrl: './charge-code-edit.component.html' +}) +export class ChargeCodeEditComponent implements OnInit { + apiErrors: string[] = []; + ChargeCodeActivity = ChargeCodeActivity; + + projects$: Observable; + services$: Observable; + quotes$: Observable; + chargeCodeId?: string; + + showProjects$ = new BehaviorSubject(null); + showQuotes$ = new BehaviorSubject(null); + showServices$ = new BehaviorSubject(null); + + form = new FormGroup
({ + Activity: new FormControl(null, { + validators: [Validators.required], + }), + ProjectId: new FormControl(null, { + validators: [], + }), + QuoteId: new FormControl(null, { + validators: [], + }), + ServiceAgreementId: new FormControl(null, { + validators: [], + }), + Billable: new FormControl(null, { + validators: [Validators.required], + }), + Active: new FormControl(null, { + validators: [Validators.required], + }), + Description: new FormControl(null, { + validators: [], + }), + + }); + async ngOnInit() { + this.chargeCodeId = await (await firstValueFrom(this.route.paramMap.pipe())).get('chargeCodeId') ?? undefined + this.getChargeCode(); + } + + constructor( + private hqService: HQService, + private router: Router, + private route: ActivatedRoute + ) { + const quotesResponse$ = this.hqService.getQuotesV1({}); + this.quotes$ = quotesResponse$.pipe( + map((response) => { + return response.records; + }) + ); + const projectsResponse$ = this.hqService.getProjectsV1({}); + this.projects$ = projectsResponse$.pipe( + map((response) => { + return response.records; + })); + + const servicesResponse$ = this.hqService.getServicesV1({}); + this.services$ = servicesResponse$.pipe( + map((response) => { + return response.records; + })); + + this.form.controls.Activity.valueChanges.subscribe((chargeCodeActivity)=>{ + this.form.controls.ProjectId.setValue(null); + this.form.controls.QuoteId.setValue(null); + this.form.controls.ServiceAgreementId.setValue(null); + + this.showProjects$.next(false); + this.showQuotes$.next(false); + this.showServices$.next(false); + + switch(chargeCodeActivity) { + case(ChargeCodeActivity.Project): + this.showProjects$.next(true); + break; + case(ChargeCodeActivity.Quote): + this.showQuotes$.next(true); + break; + case(ChargeCodeActivity.Service): + this.showServices$.next(true); + break; + } + }) + } + + + async submit() { + this.form.markAsTouched(); + console.log(this.form.value); + if (this.form.invalid) { + this.apiErrors = []; + this.apiErrors = ['Invlid Form Error'] + return; + } + console.log('Form is valid'); + + try { + const request = this.form.value; + const response = await firstValueFrom( + this.hqService.upsertChargecodesV1({...request, id: this.chargeCodeId}) + ); + this.router.navigate(['../../'], { relativeTo: this.route }); + } catch (err) { + if (err instanceof APIError) { + this.apiErrors = err.errors + } else { + this.apiErrors = ['An unexpected error has occurred.']; + } + } + } + private async getChargeCode() { + try { + const request = { "id": this.chargeCodeId } + const response = await firstValueFrom(this.hqService.getChargeCodeseV1(request)); + const chargeCode = response.records[0] + this.form.setValue({ + Activity: chargeCode.activity, + Billable: chargeCode.billable, + Active: chargeCode.active, + ProjectId: chargeCode.projectId ?? null, + QuoteId: chargeCode.quoteId ?? null, + ServiceAgreementId: chargeCode.serviceAgreementId ?? null, + Description: chargeCode.description ?? null + }) + } + catch (err) { + if (err instanceof APIError) { + this.apiErrors = err.errors; + } + else { + this.apiErrors = ['An unexpected error has occurred.']; + } + } + } +} \ No newline at end of file diff --git a/src/angular/hq/src/app/charge-code/charge-code-list/charge-code-list.component.html b/src/angular/hq/src/app/charge-code/charge-code-list/charge-code-list.component.html new file mode 100644 index 00000000..35e822ae --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code-list/charge-code-list.component.html @@ -0,0 +1,102 @@ +
+

Charge Codes

+ + +
+ +
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + @for((chargeCode of chargeCodes$ | async); track $index) { + + + + + + + + + + } + +
+ Charge Code + + + Billable + + + Active + + + Project Name + + + Quote Name + + + Service Agreement Name + + + +
{{ chargeCode.code }}{{ chargeCode.billable ? 'Yes' : 'No' }} + {{ chargeCode.active ? 'Yes' : 'No' }} + {{ chargeCode.projectName }}{{ chargeCode.quoteName }} + {{ chargeCode.serviceAgreementName }} + + EDIT +
+
+
+ + items per page +
+ +
+ {{ skipDisplay$ | async }}-{{ takeToDisplay$ | async }} / + {{ totalRecords$ | async }} items +
+
diff --git a/src/angular/hq/src/app/charge-code/charge-code-list/charge-code-list.component.ts b/src/angular/hq/src/app/charge-code/charge-code-list/charge-code-list.component.ts new file mode 100644 index 00000000..a2127646 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code-list/charge-code-list.component.ts @@ -0,0 +1,131 @@ +import { ChargeCodeListService } from './../services/ChargeCodeListService'; +import { Component, OnInit } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, RouterLink } from '@angular/router'; +import { Observable, BehaviorSubject, startWith, combineLatest, map, tap, debounceTime, switchMap, shareReplay } from 'rxjs'; +import { ProjectStatus } from '../../clients/client-details.service'; +import { GetChargeCodeRecordV1, SortColumn } from '../../models/charge-codes/get-chargecodes-v1'; +import { SortDirection } from '../../models/common/sort-direction'; +import { GetProjectRecordV1 } from '../../models/projects/get-project-v1'; +import { PsrService } from '../../psr/psr-service'; +import { HQService } from '../../services/hq.service'; +import { CommonModule } from '@angular/common'; +import { PaginatorComponent } from '../../common/paginator/paginator.component'; +import { SortIconComponent } from '../../common/sort-icon/sort-icon.component'; +import { PsrSearchFilterComponent } from '../../psr/psr-search-filter/psr-search-filter.component'; +import { ChargeCodeSearchFilterComponent } from '../SearchFilter/charge-code-search-filter.component'; + +@Component({ + selector: 'hq-charge-code-list', + standalone: true, + imports: [ + RouterLink, + CommonModule, + ReactiveFormsModule, + PaginatorComponent, + SortIconComponent, + ChargeCodeSearchFilterComponent, + ], + templateUrl: './charge-code-list.component.html' +}) +export class ChargeCodeListComponent implements OnInit { + ngOnInit(): void { + this.chargeCodeListService.showSearch(); + } + apiErrors: string[] = []; + + skipDisplay$: Observable; + takeToDisplay$: Observable; + totalRecords$: Observable; + chargeCodes$: Observable; + sortOption$: BehaviorSubject; + sortDirection$: BehaviorSubject; + + itemsPerPage = new FormControl(20, { nonNullable: true }); + page = new FormControl(1, { nonNullable: true }); + + sortColumn = SortColumn; + sortDirection = SortDirection; + + constructor( + private hqService: HQService, + private route: ActivatedRoute, + private chargeCodeListService: ChargeCodeListService + ) { + this.sortOption$ = new BehaviorSubject(SortColumn.ProjectName); + this.sortDirection$ = new BehaviorSubject(SortDirection.Asc); + + const itemsPerPage$ = this.itemsPerPage.valueChanges.pipe( + startWith(this.itemsPerPage.value) + ); + const page$ = this.page.valueChanges.pipe(startWith(this.page.value)); + + const skip$ = combineLatest([itemsPerPage$, page$]).pipe( + map(([itemsPerPage, page]) => (page - 1) * itemsPerPage), + startWith(0) + ); + const search$ = chargeCodeListService.search.valueChanges.pipe( + tap((t) => this.goToPage(1)), + startWith(chargeCodeListService.search.value) + ); + + this.skipDisplay$ = skip$.pipe(map((skip) => skip + 1)); + + const request$ = combineLatest({ + search: search$, + skip: skip$, + take: itemsPerPage$, + sortBy: this.sortOption$, + sortDirection: this.sortDirection$, + }); + + const response$ = request$.pipe( + debounceTime(500), + switchMap((request) => this.hqService.getChargeCodeseV1(request)), + shareReplay(1) + ); + + this.chargeCodes$ = response$.pipe( + map((response) => { + return response.records; + }) + ); + + this.totalRecords$ = response$.pipe(map((t) => t.total!)); + this.chargeCodes$.subscribe((records) => { + console.log(records); + }); + + this.takeToDisplay$ = combineLatest([ + skip$, + itemsPerPage$, + this.totalRecords$, + ]).pipe( + map(([skip, itemsPerPage, totalRecords]) => + Math.min(skip + itemsPerPage, totalRecords) + ) + ); + + } + + goToPage(page: number) { + this.page.setValue(page); + } + + onSortClick(sortColumn: SortColumn) { + if (this.sortOption$.value == sortColumn) { + this.sortDirection$.next( + this.sortDirection$.value == SortDirection.Asc + ? SortDirection.Desc + : SortDirection.Asc + ); + } else { + this.sortOption$.next(sortColumn); + this.sortDirection$.next(SortDirection.Asc); + } + this.page.setValue(1); + } + getProjectSatusString(status: ProjectStatus): string { + return ProjectStatus[status]; + } +} diff --git a/src/angular/hq/src/app/charge-code/charge-code.component.html b/src/angular/hq/src/app/charge-code/charge-code.component.html new file mode 100644 index 00000000..0680b43f --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code.component.html @@ -0,0 +1 @@ + diff --git a/src/angular/hq/src/app/charge-code/charge-code.component.ts b/src/angular/hq/src/app/charge-code/charge-code.component.ts new file mode 100644 index 00000000..deb50a2e --- /dev/null +++ b/src/angular/hq/src/app/charge-code/charge-code.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'hq-charge-code', + standalone: true, + imports: [RouterOutlet], + templateUrl: './charge-code.component.html' +}) +export class ChargeCodeComponent { + +} diff --git a/src/angular/hq/src/app/charge-code/services/ChargeCodeListService.ts b/src/angular/hq/src/app/charge-code/services/ChargeCodeListService.ts new file mode 100644 index 00000000..03909676 --- /dev/null +++ b/src/angular/hq/src/app/charge-code/services/ChargeCodeListService.ts @@ -0,0 +1,86 @@ +import { Injectable } from "@angular/core"; +import { FormControl } from "@angular/forms"; +import { BehaviorSubject } from "rxjs"; +import { ProjectStatus } from "../../clients/client-details.service"; +import { GetPSRTimeRecordStaffV1 } from "../../models/PSR/get-psr-time-v1"; + + +export enum ActivityName { + Support = 0, + Development = 1, + Todo = 2, +} + +@Injectable({ + providedIn: 'root', +}) +export class ChargeCodeListService { + staffMembers$ = new BehaviorSubject([]); + search = new FormControl(''); + roaster = new FormControl(''); + + projectStatus = new FormControl(ProjectStatus.InProduction); + activityName = new FormControl(ActivityName.Development); + staffMember = new FormControl(null); + isSubmitted = new FormControl(false); + + ProjectStatus = ProjectStatus; + ActivityName = ActivityName; + + showProjectStatus$ = new BehaviorSubject(false); + showSearch$ = new BehaviorSubject(false); + showStaffMembers$ = new BehaviorSubject(false); + showIsSubmitted$ = new BehaviorSubject(false); + + + showActivityName$ = new BehaviorSubject(false); + showRoaster$ = new BehaviorSubject(false); + + constructor() {} + + resetFilter() { + this.search.setValue(''); + this.projectStatus.setValue(ProjectStatus.InProduction); + this.activityName.setValue(ActivityName.Development); + this.staffMember.setValue(null); + this.roaster.setValue(''); + } + showProjectStatus() { + this.showProjectStatus$.next(true); + } + showSearch() { + this.showSearch$.next(true); + } + + hideSearch() { + this.showSearch$.next(false); + } + showStaffMembers() { + this.showStaffMembers$.next(true); + } + hideStaffMembers() { + this.showStaffMembers$.next(false); + } + hideProjectStatus() { + this.showProjectStatus$.next(false); + } + + showActivityName() { + this.showActivityName$.next(true); + } + hideActivityName() { + this.showActivityName$.next(false); + } + showRoaster() { + this.showRoaster$.next(true); + } + hideRoaster() { + this.showRoaster$.next(false); + } + showIsSubmitted() { + this.showIsSubmitted$.next(true); + } + hideIsSubmitted() { + this.showIsSubmitted$.next(false); + } +} diff --git a/src/angular/hq/src/app/layout.component.html b/src/angular/hq/src/app/layout.component.html index 5ba1fea1..dbf5af6d 100644 --- a/src/angular/hq/src/app/layout.component.html +++ b/src/angular/hq/src/app/layout.component.html @@ -14,6 +14,8 @@ @if(HQRole.Administrator | inRole | async) { + + } diff --git a/src/angular/hq/src/app/models/charge-codes/get-chargecodes-v1.ts b/src/angular/hq/src/app/models/charge-codes/get-chargecodes-v1.ts index 2c67b541..970958a4 100644 --- a/src/angular/hq/src/app/models/charge-codes/get-chargecodes-v1.ts +++ b/src/angular/hq/src/app/models/charge-codes/get-chargecodes-v1.ts @@ -4,7 +4,7 @@ import { SortDirection } from "../common/sort-direction"; // Interface for the paged request with sorting and optional filters specific to charge codes export interface GetChargeCodesRequestV1 extends PagedRequestV1 { - search?: string; + search?: string | null; id?: string; sortBy: SortColumn; sortDirection: SortDirection; @@ -13,6 +13,13 @@ export interface GetChargeCodesRequestV1 extends PagedRequestV1 { billable?: boolean; active?: boolean; } +export enum ChargeCodeActivity +{ + General = 1, + Project = 2, + Quote = 3, + Service = 4 +} // Enum for specifying the sortable columns in charge code requests export enum SortColumn { @@ -26,6 +33,7 @@ export enum SortColumn { // Interface representing a single charge code record export interface GetChargeCodeRecordV1 { + activity: ChargeCodeActivity; id: string; code: string; billable: boolean; @@ -34,6 +42,9 @@ export interface GetChargeCodeRecordV1 { quoteName?: string; serviceAgreementName?: string; description?: string; + quoteId?: string; + projectId?: string; + serviceAgreementId?: string; } // Interface for the response containing a list of charge code records diff --git a/src/angular/hq/src/app/models/charge-codes/upsert-chargecodes.ts b/src/angular/hq/src/app/models/charge-codes/upsert-chargecodes.ts new file mode 100644 index 00000000..a5a8ba9e --- /dev/null +++ b/src/angular/hq/src/app/models/charge-codes/upsert-chargecodes.ts @@ -0,0 +1,17 @@ + +export interface UpsertChargeCodesRequestV1 { + id?: string | null; + activity: number | null; + projectId?: string | null; + QuoteId?: string | null; + serviceAgreementId?: string | null; + billable: boolean | null; + active: boolean | null; + description?: string | null; + +} + +export interface UpsertChargeCodesResponseV1 { + id: string; + chargeCode: string; +} \ No newline at end of file diff --git a/src/angular/hq/src/app/projects/project-list/project-list.component.html b/src/angular/hq/src/app/projects/project-list/project-list.component.html index 3099c46a..76ac6013 100644 --- a/src/angular/hq/src/app/projects/project-list/project-list.component.html +++ b/src/angular/hq/src/app/projects/project-list/project-list.component.html @@ -98,7 +98,7 @@

No matching records found

- VIEWVIEW diff --git a/src/angular/hq/src/app/services/hq.service.ts b/src/angular/hq/src/app/services/hq.service.ts index 5e528307..8f90e39f 100644 --- a/src/angular/hq/src/app/services/hq.service.ts +++ b/src/angular/hq/src/app/services/hq.service.ts @@ -86,6 +86,7 @@ import { UnapprovePSRTimeResponseV1, } from '../models/PSR/unapprove-psrtime-v1'; import { UpsertStaffMemberRequestV1 } from '../models/staff-members/upsert-staff-member-v1'; +import { UpsertChargeCodesRequestV1, UpsertChargeCodesResponseV1 } from '../models/charge-codes/upsert-chargecodes'; @Injectable({ providedIn: 'root', @@ -340,6 +341,23 @@ export class HQService { }) ); } + + upsertChargecodesV1(request: Partial) { + return this.appSettings.apiUrl$.pipe( + switchMap((apiUrl) => + this.http.post( + `${apiUrl}/v1/ChargeCodes/UpsertChargeCodesV1 + `, + request + ) + ), catchError((err: HttpErrorResponse) => { + if (err.status == 400) { + return throwError(() => new APIError(err.error)); + } + throw err; + })) + } + submitProjectStatusReportV1(request: Partial) { return this.appSettings.apiUrl$.pipe( switchMap((apiUrl) => diff --git a/src/angular/hq/src/app/staff/staff-create/staff-create.component.html b/src/angular/hq/src/app/staff/staff-create/staff-create.component.html index e3ec429b..dae3d49a 100644 --- a/src/angular/hq/src/app/staff/staff-create/staff-create.component.html +++ b/src/angular/hq/src/app/staff/staff-create/staff-create.component.html @@ -179,7 +179,7 @@

Create Staff Member

>
-
Jurisdiction\?
+
Jurisdiction?