Skip to content

Commit

Permalink
done for vacation list
Browse files Browse the repository at this point in the history
  • Loading branch information
Moaed Alloula authored and Mouayad-97 committed Dec 12, 2024
1 parent 909292b commit 182a513
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
<div class="flex flex-col">
<div class="bg-primary text-white p-4">{{ lang.locals.vacation_request }}</div>
<form class="p-4 min-h-0 flex-auto flex flex-col" [formGroup]="vacationRequestForm" (ngSubmit)="onSubmit()">
<div class="flex flex-col" cdkDrag cdkDragRootElement=".cdk-overlay-pane">
<div class="bg-primary text-white p-4">
<span class="text-lg">{{ lang.locals.vacation_request }}</span>
<button type="button" cdkDragHandle>
<svg width="24px" class="absolute cursor-move top-1 left-1 mt-4 ml-4" fill="currentColor" viewBox="0 0 24 24">
<path
d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</button>
</div>
<form class="p-4 min-h-0 flex-auto flex flex-col" [formGroup]="vacationRequestForm">
<!-- Type -->
<mat-form-field class="w-full" appearance="outline">
<mat-label>Type</mat-label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { OnDestroyMixin } from '@/mixins/on-destroy-mixin'
import { takeUntil, tap } from 'rxjs'
import { AsyncPipe } from '@angular/common'
import { FunctionArguments } from '@/contracts/tool-call-contract'
import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop'

@Component({
selector: 'app-request-vacation-popup',
Expand All @@ -31,6 +32,8 @@ import { FunctionArguments } from '@/contracts/tool-call-contract'
MatDatepickerModule,
MatNativeDateModule,
AsyncPipe,
CdkDrag,
CdkDragHandle,
],
providers: [provideNativeDateAdapter(), { provide: MAT_DATE_FORMATS, useValue: MAT_NATIVE_DATE_FORMATS }],
templateUrl: './request-vacation-popup.component.html',
Expand Down Expand Up @@ -78,8 +81,4 @@ export class RequestVacationPopupComponent extends OnDestroyMixin(class {}) impl
console.log(this.data)
this.vacationRequestForm.patchValue(this.data)
}

onSubmit() {
console.log(this.vacationRequestForm.value)
}
}
137 changes: 137 additions & 0 deletions src/components/vacation-list-popup/vacation-list-popup.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<div class="flex flex-col" cdkDrag cdkDragRootElement=".cdk-overlay-pane">
<!-- Dialog Header -->
<div class="bg-primary text-white p-4">
<span class="text-lg">{{ lang.locals.vacations_list }}</span>
<button type="button" cdkDragHandle>
<svg width="24px" class="absolute cursor-move top-1 left-1 mt-4 ml-4" fill="currentColor" viewBox="0 0 24 24">
<path
d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</button>
</div>

<div #vacationWrapper class="relative max-h-96">
<table class="min-w-full border-collapse">
<!-- Sticky Header -->
<thead class="bg-primary text-white text-center sticky top-0 z-10">
<tr>
<th class="px-4 py-2">{{ lang.locals.employee_name }}</th>
<th class="px-4 py-2">{{ lang.locals.manager_name }}</th>
<th class="px-4 py-2">{{ lang.locals.department }}</th>
<th class="px-4 py-2">{{ lang.locals.start_date }}</th>
<th class="px-4 py-2">{{ lang.locals.end_date }}</th>
<th class="px-4 py-2">{{ lang.locals.total_days }}</th>
<th class="px-4 py-2">{{ lang.locals.status }}</th>
<th class="px-4 py-2">{{ lang.locals.comments }}</th>
<th class="px-4 py-2">{{ lang.locals.actions }}</th>
</tr>
</thead>

<!-- Table Body -->
<tbody class="text-center">
@for (vacation of data; track $index) {
<tr>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
matTooltip="{{ vacation.Employee_Name }}"
matTooltipPosition="above">
{{ vacation.Employee_Name }}
</td>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
matTooltip="{{ vacation.Manager_Name }}"
matTooltipPosition="above">
{{ vacation.Manager_Name }}
</td>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
matTooltip="{{ vacation.Department }}"
matTooltipPosition="above">
{{ vacation.Department }}
</td>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
matTooltip="{{ vacation.Start_Date | date: 'short' }}"
matTooltipPosition="above">
{{ vacation.Start_Date | date: 'short' }}
</td>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
matTooltip="{{ vacation.End_Date | date: 'short' }}"
matTooltipPosition="above">
{{ vacation.End_Date | date: 'short' }}
</td>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
matTooltip="{{ vacation.Total_Days }}"
matTooltipPosition="above">
{{ vacation.Total_Days }}
</td>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
[matTooltip]="getStatus(vacation)"
matTooltipPosition="above">
<span
[ngClass]="{
'bg-green-500': isApproved(vacation),
'bg-red-500': isRejected(vacation),
'bg-yellow-500': isPending(vacation),
}"
class="p-1 text-xs text-white rounded-md lowercase"
>{{ getStatus(vacation) }}</span
>
</td>
<td
class="px-4 py-2 border-b truncate break-words max-w-40"
matTooltip="{{ vacation.Comments }}"
matTooltipPosition="above">
{{ vacation.Comments }}
</td>
<td class="px-4 py-2 border-b text-center flex justify-center items-center gap-4">
@if (!isApproved(vacation)) {
<button
type="button"
(click)="approveVacation(vacation)"
class="size-8 bg-green-500 hover:bg-green-600 disabled:bg-green-300 text-white rounded-full flex items-center justify-center"
[matTooltip]="lang.locals.approved"
matTooltipPosition="above">
<svg fill="currentColor" class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
</svg>
</button>
}
@if (!isRejected(vacation)) {
<button
type="button"
(click)="rejectVacation(vacation)"
class="size-8 bg-red-500 hover:bg-red-600 disabled:bg-red-300 text-white rounded-full flex items-center justify-center"
[matTooltip]="lang.locals.rejected"
matTooltipPosition="above">
<svg fill="currentColor" class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</svg>
</button>
}
@if (!isPending(vacation)) {
<button
type="button"
(click)="pendingVacation(vacation)"
class="size-8 bg-yellow-500 hover:bg-yellow-600 disabled:bg-yellow-300 text-white rounded-full flex items-center justify-center"
[matTooltip]="lang.locals.pending"
matTooltipPosition="above">
<svg fill="currentColor" class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.5 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z" />
</svg>
</button>
}
</td>
</tr>
}
</tbody>
</table>
</div>
<!-- Table Wrapper -->
</div>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { ChatActionResultContract } from '@/contracts/chat-action-result-contract'
import { VacationResultContract } from '@/contracts/vacation-result-contract'
import { VacationStatus } from '@/enums/vacation-status'
import { OnDestroyMixin } from '@/mixins/on-destroy-mixin'
import { InteractiveChatService } from '@/services/interactive-chat.service'
import { LocalService } from '@/services/local.service'
import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop'
import { DatePipe, NgClass } from '@angular/common'
import { Component, effect, ElementRef, inject, viewChild } from '@angular/core'
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'
import { MatTooltipModule } from '@angular/material/tooltip'
import PerfectScrollbar from 'perfect-scrollbar'
import { takeUntil, tap } from 'rxjs'

@Component({
selector: 'app-vacation-list-popup',
standalone: true,
imports: [MatTooltipModule, DatePipe, CdkDrag, CdkDragHandle, MatDialogModule, NgClass],
templateUrl: './vacation-list-popup.component.html',
styleUrl: './vacation-list-popup.component.scss',
})
export class VacationListPopupComponent extends OnDestroyMixin(class {}) {
vacationWrapper = viewChild<ElementRef<HTMLDivElement>>('vacationWrapper')
lang = inject(LocalService)
data = inject<VacationResultContract[]>(MAT_DIALOG_DATA)
ref = inject(MatDialogRef)
interactiveChatService = inject(InteractiveChatService)
declare scrollbarRef: PerfectScrollbar

chatBodyContainerEffect = effect(() => {
if (this.vacationWrapper()) {
this.scrollbarRef = new PerfectScrollbar(this.vacationWrapper()!.nativeElement, { suppressScrollX: true })
} else {
this.scrollbarRef && this.scrollbarRef.destroy()
}
})

isPending(element: VacationResultContract) {
return element.Status === VacationStatus.PENDING
}
isApproved(element: VacationResultContract) {
return element.Status === VacationStatus.APPROVED
}
isRejected(element: VacationResultContract) {
return element.Status === VacationStatus.REJECTED
}
getStatus(element: VacationResultContract) {
if (element.Status === VacationStatus.PENDING) return this.lang.locals.pending
else if (element.Status === VacationStatus.APPROVED) return this.lang.locals.approved
else return this.lang.locals.rejected
}

approveVacation(element: VacationResultContract) {
this.interactiveChatService
.approveVacation({ employee_ID: element.Employee_ID })
.pipe(takeUntil(this.destroy$))
.pipe(tap(res => this.handelVacationStatus(res, element.Employee_ID, VacationStatus.APPROVED)))
.subscribe()
}
rejectVacation(element: VacationResultContract) {
this.interactiveChatService
.rejectVacation({ employee_ID: element.Employee_ID })
.pipe(takeUntil(this.destroy$))
.pipe(tap(res => this.handelVacationStatus(res, element.Employee_ID, VacationStatus.REJECTED)))
.subscribe()
}
pendingVacation(element: VacationResultContract) {
this.interactiveChatService
.pendingVacation({ employee_ID: element.Employee_ID })
.pipe(takeUntil(this.destroy$))
.pipe(tap(res => this.handelVacationStatus(res, element.Employee_ID, VacationStatus.PENDING)))
.subscribe()
}

changeStatus(elementId: number, status: VacationStatus) {
const vacation = this.data.find(el => el.Employee_ID === elementId)
if (vacation) {
vacation.Status = status
}
}

handelVacationStatus(res: ChatActionResultContract<string>, employeeId: number, status: VacationStatus) {
if (res.status_code === 200) {
this.changeStatus(employeeId, status)
}
}
}
13 changes: 13 additions & 0 deletions src/contracts/chat-action-result-contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ChatMessageResultContract } from './chat-message-result-contract'

export interface ChatActionResultContract<TActionResult = unknown> {
status_code: number
status: string
message: string
data: Data<TActionResult>
}

export interface Data<TActionResult> {
action_results: TActionResult
final_message: ChatMessageResultContract
}
13 changes: 13 additions & 0 deletions src/contracts/lang-keys-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,17 @@ export interface LangKeysContract {
document_analyze_progress: string
vacation_request: string
interactive_chatbot: string
vacations_list: string
employee_name: string
manager_name: string
department: string
start_date: string
end_date: string
total_days: string
status: string
comments: string
actions: string
approved: string
rejected: string
pending: string
}
6 changes: 6 additions & 0 deletions src/contracts/tool-call-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ interface FunctionCallMap {
pending: {
employee_ID: number
}
submit_form: {
'confirm-text': string
}
'get-all-vacation-forms': {
text: string
}
}

export type FunctionName = keyof FunctionCallMap
Expand Down
16 changes: 16 additions & 0 deletions src/contracts/vacation-result-contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { VacationStatus } from '@/enums/vacation-status'

export interface VacationResultContract {
PartitionKey: string
RowKey: string
Vacation_Type: string
Employee_ID: number
Employee_Name: string
Manager_Name: string
Department: string
Start_Date: Date | string
End_Date: Date | string
Total_Days: number
Status: VacationStatus
Comments: string
}
5 changes: 5 additions & 0 deletions src/enums/vacation-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum VacationStatus {
APPROVED = 0,
REJECTED = 1,
PENDING = 2,
}
52 changes: 52 additions & 0 deletions src/resources/locals.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,5 +366,57 @@
"interactive_chatbot": {
"ar": "البوت التفاعلي",
"en": "Interactive Chatbot"
},
"vacations_list": {
"ar": "طلبات الإجازة",
"en": "Vacations list"
},
"employee_name": {
"ar": "اسم الموظف",
"en": "Employee Name"
},
"manager_name": {
"ar": "اسم المدير",
"en": "Manager Name"
},
"department": {
"ar": "القسم",
"en": "Department"
},
"start_date": {
"ar": "تاريخ البداية",
"en": "Start Date"
},
"end_date": {
"ar": "تاريخ النهاية",
"en": "End Date"
},
"total_days": {
"ar": "عدد الأيام",
"en": "Total Days"
},
"status": {
"ar": "حالة الإجازة",
"en": "Status"
},
"comments": {
"ar": "التعليقات",
"en": "Comments"
},
"actions": {
"ar": "الأوامر",
"en": "Actions"
},
"approved": {
"ar": "مؤكدة",
"en": "Approved"
},
"rejected": {
"ar": "مرفوضة",
"en": "Rejected"
},
"pending": {
"ar": "قيد الانتظار",
"en": "Pending"
}
}
Loading

0 comments on commit 182a513

Please sign in to comment.