diff --git a/package-lock.json b/package-lock.json index e0427cd..c5f575e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "steex", - "version": "0.5.2", + "version": "0.3.2", "lockfileVersion": 3, "requires": true, "packages": { @@ -10,7 +10,7 @@ "dependencies": { "@amcharts/amcharts5": "^5.5.7", "@amcharts/amcharts5-geodata": "^5.1.1", - "@angular-slider/ngx-slider": "^2.0.4", + "@angular-slider/ngx-slider": "^17.0.0", "@angular/animations": "^17.0.4", "@angular/cdk": "^17.0.1", "@angular/common": "^17.0.4", @@ -70,7 +70,6 @@ "ngx-pipes": "^3.2.2", "ngx-quill": "^25.3.2", "ngx-slick-carousel": "^17.0.0", - "ngx-slider-v2": "^17.0.0", "ngx-spinner": "^16.0.2", "ngx-toastr": "^18.0.0", "ngx-ui-switch": "^15.0.0", @@ -520,36 +519,21 @@ } }, "node_modules/@angular-slider/ngx-slider": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@angular-slider/ngx-slider/-/ngx-slider-2.0.4.tgz", - "integrity": "sha512-EccMcGlb2bJJWikXZBjRwdWgRiYYmWd3UCgf8l3KAlyqPAxPVgxI73wqipp4/nZwidq53a9s3OB+KC79enfM2A==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@angular-slider/ngx-slider/-/ngx-slider-17.0.2.tgz", + "integrity": "sha512-6fQ0wOH/pZvC2kbw3ZFab7eXwDaKZtj/cuA7pbhyKWHp8pwhaptoDDoEXttlAZIUEnrMG8rkg0OBcVWwwBLEJA==", + "license": "MIT", "dependencies": { "detect-passive-events": "^2.0.3", - "rxjs": "^6.5.2", - "tslib": "^1.9.0" + "rxjs": "^7.8.1", + "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": ">=6.1.0", - "@angular/core": ">=6.1.0", - "@angular/forms": ">=6.1.0" - } - }, - "node_modules/@angular-slider/ngx-slider/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" + "@angular/common": "^17.1.1", + "@angular/core": "^17.1.1", + "@angular/forms": "^17.1.1" } }, - "node_modules/@angular-slider/ngx-slider/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/@angular/animations": { "version": "17.3.0", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.0.tgz", @@ -11876,22 +11860,6 @@ "zone.js": ">=0.10.2" } }, - "node_modules/ngx-slider-v2": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/ngx-slider-v2/-/ngx-slider-v2-17.0.0.tgz", - "integrity": "sha512-226pb8TeZWhmMiQQnPVBZmos2qZua2NgMsuwumxatIBy7UwOGg8xkcUl8HvXVg+rO5w733uvPMN/yz//7rReJw==", - "deprecated": "This fork was merged into the main repo. Please use @angular-slider/ngx-slider", - "dependencies": { - "detect-passive-events": "^2.0.3", - "rxjs": "^7.8.1", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": "^17.0.3", - "@angular/core": "^17.0.3", - "@angular/forms": "^17.0.3" - } - }, "node_modules/ngx-spinner": { "version": "16.0.2", "resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-16.0.2.tgz", diff --git a/package.json b/package.json index b656e0c..bbd2080 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dependencies": { "@amcharts/amcharts5": "^5.5.7", "@amcharts/amcharts5-geodata": "^5.1.1", - "@angular-slider/ngx-slider": "^2.0.4", + "@angular-slider/ngx-slider": "^17.0.0", "@angular/animations": "^17.0.4", "@angular/cdk": "^17.0.1", "@angular/common": "^17.0.4", @@ -71,7 +71,6 @@ "ngx-pipes": "^3.2.2", "ngx-quill": "^25.3.2", "ngx-slick-carousel": "^17.0.0", - "ngx-slider-v2": "^17.0.0", "ngx-spinner": "^16.0.2", "ngx-toastr": "^18.0.0", "ngx-ui-switch": "^15.0.0", @@ -105,4 +104,4 @@ "overrides": { "autoprefixer": "10.4.16" } -} +} \ No newline at end of file diff --git a/src/app/core/services/rest-api.service.ts b/src/app/core/services/rest-api.service.ts index b582839..5b167a0 100644 --- a/src/app/core/services/rest-api.service.ts +++ b/src/app/core/services/rest-api.service.ts @@ -32,7 +32,7 @@ const httpOptions = { providedIn: 'root', }) export class restApiService { - constructor(private http: HttpClient) {} + constructor(private http: HttpClient) { } /** * Product Rest Api @@ -95,7 +95,7 @@ this.restApiService.getProjectTasks(projectId, { page: 1, page_size: 10 }, 'task } return this.http.get( - `${GlobalComponent.API_URL}/projects/tasks/${projectId}`, + `${GlobalComponent.API_URL}projects/tasks/${projectId}`, { params } ); } @@ -116,7 +116,7 @@ this.restApiService.getProjectTasks(projectId, { page: 1, page_size: 10 }, 'task } updateTaskStatus(taskId: string, status: string): Observable { - return this.http.put(`${GlobalComponent.API_URL}projects/task/${taskId}/status`, { status }); + return this.http.put(`${GlobalComponent.API_URL}projects/task/${taskId}/status`, { state: status }); } postComment(taskId: string, comment: any): Observable { @@ -134,35 +134,35 @@ this.restApiService.getProjectTasks(projectId, { page: 1, page_size: 10 }, 'task return this.http.put(`${GlobalComponent.API_URL}projects/comments/${commentId}`, comment); } -// In your service + // In your service postFile(fileData: FormData): Observable { return this.http.post(`${GlobalComponent.API_URL}common/file`, fileData); } - -downloadFile(fileId: string): Observable<{blob: Blob, filename: string}> { - return this.http.get(`${GlobalComponent.API_URL}common/file/${fileId}`, { - responseType: 'blob', - observe: 'response' - }).pipe( - map((response: HttpResponse) => { - // Log all response headers - const headers = response.headers.keys(); - headers.forEach(header => console.log(`${header}: ${response.headers.get(header)}`)); - const contentDisposition = response.headers.get('Content-Disposition') || ''; - console.log('Content-Disposition header:', contentDisposition); + downloadFile(fileId: string): Observable<{ blob: Blob, filename: string }> { + return this.http.get(`${GlobalComponent.API_URL}common/file/${fileId}`, { + responseType: 'blob', + observe: 'response' + }).pipe( + map((response: HttpResponse) => { + // Log all response headers + const headers = response.headers.keys(); + headers.forEach(header => console.log(`${header}: ${response.headers.get(header)}`)); - const matches = /filename\s*=\s*(?:"([^"]*)"|([^;]*))/i.exec(contentDisposition); - console.log('Regex matches:', matches); + const contentDisposition = response.headers.get('Content-Disposition') || ''; + console.log('Content-Disposition header:', contentDisposition); - const filename = matches && (matches[1] || matches[2]) ? matches[1] || matches[2] : 'default-filename.ext'; - console.log('Extracted filename:', filename); + const matches = /filename\s*=\s*(?:"([^"]*)"|([^;]*))/i.exec(contentDisposition); + console.log('Regex matches:', matches); - return { blob: response.body as Blob, filename }; - }) - ); -} + const filename = matches && (matches[1] || matches[2]) ? matches[1] || matches[2] : 'default-filename.ext'; + console.log('Extracted filename:', filename); + + return { blob: response.body as Blob, filename }; + }) + ); + } @@ -171,75 +171,62 @@ downloadFile(fileId: string): Observable<{blob: Blob, filename: string}> { return this.http.get(`${GlobalComponent.API_URL}common/file/${fileId}/info`); } - createChat(chatData: ChatCreateRequest): Observable { - return this.http.post(`${GlobalComponent.API_URL}communication/chat`, chatData); + // Create a new chat + createChat(chatData: any): Observable { + return this.http.post(`${GlobalComponent.API_URL}communication/chat`, chatData); } - - getChats(page: number = 1, pageSize: number = 10, name?: string): Observable { - let queryParams = `?page=${page}&page_size=${pageSize}`; - if (name) { - queryParams += `&name=${encodeURIComponent(name)}`; - } - return this.http.get(`${GlobalComponent.API_URL}communication/chat${queryParams}`).pipe( - map(response => response.content) - ); + + // Get all chats + getChats(): Observable { + return this.http.get(`${GlobalComponent.API_URL}communication/chat`); + } + + // Get specific chat by ID + getChat(chatId: string): Observable { + return this.http.get(`${GlobalComponent.API_URL}communication/chat/${chatId}`); + } + + // Update specific chat + updateChat(chatId: string, updateData: any): Observable { + return this.http.patch(`${GlobalComponent.API_URL}communication/chat/${chatId}`, updateData); + } + + // Add member to chat + addChatMember(chatId: string, userId: string): Observable { + return this.http.post(`${GlobalComponent.API_URL}communication/chat/${chatId}/add_member`, { userId }); + } + + // Leave a chat + leaveChat(chatId: string): Observable { + return this.http.post(`${GlobalComponent.API_URL}communication/chat/${chatId}/leave`, {}); + } + + // Get chat participants + getChatParticipants(chatId: string): Observable { + return this.http.get(`${GlobalComponent.API_URL}communication/chat/${chatId}/participants`); + } + + // Messaging services + createMessage(messageData: any): Observable { + return this.http.post(`${GlobalComponent.API_URL}communication/message`, messageData); + } + + getMessage(messageId: string): Observable { + return this.http.get(`${GlobalComponent.API_URL}communication/message/${messageId}`); + } + + updateMessage(messageId: string, messageData: any): Observable { + return this.http.put(`${GlobalComponent.API_URL}communication/message/${messageId}`, messageData); + } + + markMessageSeen(messageId: string): Observable { + return this.http.patch(`${GlobalComponent.API_URL}communication/message/${messageId}/seen`, {}); + } + + // Get who has seen the message + getMessageSeenBy(messageId: string): Observable { + return this.http.get(`${GlobalComponent.API_URL}communication/message/${messageId}/seen_by`); } - - // Get specific chat by ID - getChat(chatId: string): Observable { - return this.http.get(`${GlobalComponent.API_URL}communication/chat/${chatId}`); - } - - // Update specific chat - updateChat(chatId: string, updateData: ChatUpdateRequest): Observable { - return this.http.patch(`${GlobalComponent.API_URL}communication/chat/${chatId}`, updateData); - } - - // Add member to chat - addChatMember(chatId: string, accountIds: string[]): Observable { - const requestData: ChatAddMemberRequest = { account_ids: accountIds }; - return this.http.post(`${GlobalComponent.API_URL}communication/chat/${chatId}/add_member`, requestData); - } - - // Leave a chat - leaveChat(chatId: string): Observable { - return this.http.post(`${GlobalComponent.API_URL}communication/chat/${chatId}/leave`, {}); - } - - // Get chat participants - getChatParticipants(chatId: string, page: number = 1, pageSize: number = 10, name?: string): Observable { - let params = new HttpParams() - .set('page', page.toString()) - .set('page_size', pageSize.toString()); - - if (name) { - params = params.set('name', name); - } - - return this.http.get(`${GlobalComponent.API_URL}communication/chat/${chatId}/participants`, { params }); - } - - // Messaging services - createMessage(messageData: MessageCreateRequest): Observable { - return this.http.post(`${GlobalComponent.API_URL}communication/message`, messageData); - } - - getMessage(messageId: string): Observable { - return this.http.get(`${GlobalComponent.API_URL}communication/message/${messageId}`); - } - - updateMessage(messageId: string, messageData: any): Observable { - return this.http.put(`${GlobalComponent.API_URL}communication/message/${messageId}`, messageData); - } - - markMessageSeen(messageId: string): Observable { - return this.http.patch(`${GlobalComponent.API_URL}communication/message/${messageId}/seen`, {}); - } - - // Get who has seen the message - getMessageSeenBy(messageId: string): Observable { - return this.http.get(`${GlobalComponent.API_URL}communication/message/${messageId}/seen_by`); - } // Delete deleteData(id: any): Observable { diff --git a/src/app/pages/ecommerce/ecommerce.module.ts b/src/app/pages/ecommerce/ecommerce.module.ts index b97e69e..641c4b0 100644 --- a/src/app/pages/ecommerce/ecommerce.module.ts +++ b/src/app/pages/ecommerce/ecommerce.module.ts @@ -22,7 +22,7 @@ import { NgSelectModule } from '@ng-select/ng-select'; import { CountUpModule } from 'ngx-countup'; // Range Slider -import { NgxSliderModule } from 'ngx-slider-v2'; +import { NgxSliderModule } from '@angular-slider/ngx-slider'; // Swiper Slider import { SlickCarouselModule } from 'ngx-slick-carousel'; diff --git a/src/app/pages/forms/forms.module.ts b/src/app/pages/forms/forms.module.ts index c9027da..7897241 100644 --- a/src/app/pages/forms/forms.module.ts +++ b/src/app/pages/forms/forms.module.ts @@ -22,7 +22,7 @@ import { UiSwitchModule } from 'ngx-ui-switch'; import { TimepickerModule } from 'ngx-bootstrap/timepicker'; import { BsDatepickerModule } from 'ngx-bootstrap/datepicker'; import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; -import { NgxSliderModule } from 'ngx-slider-v2'; +import { NgxSliderModule } from '@angular-slider/ngx-slider'; // Color Picker import { ColorPickerModule } from 'ngx-color-picker'; @@ -60,10 +60,10 @@ import { LayoutComponent } from './layout/layout.component'; const DEFAULT_DROPZONE_CONFIG: DropzoneConfigInterface = { // Change this to your upload POST address: - url: 'https://httpbin.org/post', - maxFilesize: 50, - acceptedFiles: 'image/*' - }; + url: 'https://httpbin.org/post', + maxFilesize: 50, + acceptedFiles: 'image/*' +}; @NgModule({ declarations: [ diff --git a/src/app/pages/learning/courses/courses.module.ts b/src/app/pages/learning/courses/courses.module.ts index 1e1c11a..7c8cdd1 100644 --- a/src/app/pages/learning/courses/courses.module.ts +++ b/src/app/pages/learning/courses/courses.module.ts @@ -10,7 +10,7 @@ import { SharedModule } from 'src/app/shared/shared.module'; import { NgSelectModule } from '@ng-select/ng-select'; // Range Slider -import { NgxSliderModule } from 'ngx-slider-v2'; +import { NgxSliderModule } from '@angular-slider/ngx-slider'; //Wizard import { CdkStepperModule } from '@angular/cdk/stepper'; diff --git a/src/app/pages/learning/courses/list/list.component.ts b/src/app/pages/learning/courses/list/list.component.ts index c4ed4f0..638515b 100644 --- a/src/app/pages/learning/courses/list/list.component.ts +++ b/src/app/pages/learning/courses/list/list.component.ts @@ -8,7 +8,7 @@ import { addcourcelistData, deletecourcelistData, fetchcourcelistdata, updatecou import { selectData } from 'src/app/store/Learning-cources/cources.selector'; import { PageChangedEvent } from 'ngx-bootstrap/pagination'; import { cloneDeep } from 'lodash'; -import { Options } from 'ngx-slider-v2'; +import { Options } from '@angular-slider/ngx-slider'; @Component({ selector: 'app-list', @@ -67,7 +67,7 @@ export class ListComponent { this.store.select(selectData).subscribe((data) => { this.courses = data; this.courseList = data; - this.courses = cloneDeep(this.courseList.slice(0,10)) + this.courses = cloneDeep(this.courseList.slice(0, 10)) }); document.getElementById('elmLoader')?.classList.add('d-none') }, 1000) @@ -100,16 +100,16 @@ export class ListComponent { document.querySelector('.backdrop3')?.classList.remove('show') } - /** + /** * Range Slider Wise Data Filter */ -valueChange(value: number, boundary: boolean): void { - if (boundary) { - this.minVal = value; - } else { - this.maxVal = value; + valueChange(value: number, boundary: boolean): void { + if (boundary) { + this.minVal = value; + } else { + this.maxVal = value; + } } -} // File Upload public dropzoneConfig: DropzoneConfigInterface = { @@ -235,11 +235,11 @@ valueChange(value: number, boundary: boolean): void { } // filterdata filterdata() { - + if (this.term) { this.courses = this.courseList.filter((el: any) => el.name.toLowerCase().includes(this.term.toLowerCase())) } else { - this.courses = this.courseList.slice(0,10) + this.courses = this.courseList.slice(0, 10) } // noResultElement this.updateNoResultDisplay(); diff --git a/src/app/pages/real-estate/real-estate.module.ts b/src/app/pages/real-estate/real-estate.module.ts index 28af0d1..84af327 100644 --- a/src/app/pages/real-estate/real-estate.module.ts +++ b/src/app/pages/real-estate/real-estate.module.ts @@ -19,7 +19,7 @@ import { FlatpickrModule } from 'angularx-flatpickr'; import { NgApexchartsModule } from 'ng-apexcharts'; // Range Slider -import { NgxSliderModule } from 'ngx-slider-v2'; +import { NgxSliderModule } from '@angular-slider/ngx-slider'; // Swiper Slider import { SlickCarouselModule } from 'ngx-slick-carousel'; diff --git a/src/app/pages/tickets/list/list.component.html b/src/app/pages/tickets/list/list.component.html index 1d9235c..e1588b5 100644 --- a/src/app/pages/tickets/list/list.component.html +++ b/src/app/pages/tickets/list/list.component.html @@ -100,13 +100,14 @@
Sorry! No Result Found
- Showing {{tickets?.length}} of {{alltickets?.length}} Results + Showing {{tasks.length}} of {{tasksMeta.total}} Results
+ [totalItems]="tasksMeta.total" [itemsPerPage]="itemsPerPage" + (pageChanged)="pageChanged($event)">
diff --git a/src/app/pages/tickets/list/list.component.ts b/src/app/pages/tickets/list/list.component.ts index 7b04e83..4a20518 100644 --- a/src/app/pages/tickets/list/list.component.ts +++ b/src/app/pages/tickets/list/list.component.ts @@ -15,6 +15,7 @@ import { restApiService } from 'src/app/core/services/rest-api.service'; import { Project } from 'src/app/interfaces/api-interfaces'; import { SupportTicket, supporttickets } from 'src/app/core/data/ticket'; import { Router } from '@angular/router'; +import { PaginationParams } from '../../../interfaces/api-interfaces'; @Component({ selector: 'app-list', @@ -67,11 +68,18 @@ export class ListComponent { assignto: any = []; editData: any; alltickets: any; + tasksMeta: { + total: number; + page: number; + page_size: number; + total_pages: number; + } = { total: 0, page: 0, page_size: 0, total_pages: 0 }; tasks: any[] = []; - alltasks: any[] = []; + originalTasks: any[] = []; myGroup!: FormGroup; - - constructor( private formBuilder: FormBuilder, public store: Store,public datepipe:DatePipe, public apiService: restApiService, private router: Router) { + itemsPerPage = 10; + + constructor(private formBuilder: FormBuilder, public store: Store, public datepipe: DatePipe, public apiService: restApiService, private router: Router) { } public supportTickets: SupportTicket[] = supporttickets; @@ -80,10 +88,20 @@ export class ListComponent { this.initializeForm(); this.apiService.getCurrentProjects().subscribe((response: any) => { this.project = response; - this.tasks = response.tasks; - this.alltasks = [...this.tasks]; // Create a copy of tasks for resetting the list this.updateSupportTickets(response.tasks_count_by_status); + this.fetchTickets(1, this.itemsPerPage); }); + + } + + fetchTickets(page: number, page_size: number) { + this.apiService.getProjectTasks(this.project.project_id, { page: page, page_size: page_size }).subscribe( + (response: any) => { + this.originalTasks = response.content; + this.tasks = response.content; + this.tasksMeta = response.meta; + } + ); } viewTaskDetails(taskId: string) { @@ -103,13 +121,13 @@ export class ListComponent { filterdata() { if (this.term) { - this.tasks = this.alltasks.filter(task => + this.tasks = this.originalTasks.filter(task => task.name.toLowerCase().includes(this.term.toLowerCase()) || task.description.toLowerCase().includes(this.term.toLowerCase()) || task.state.toLowerCase().includes(this.term.toLowerCase()) ); } else { - this.tasks = [...this.alltasks]; // Reset to original list when search term is cleared + this.tasks = [...this.originalTasks]; // Reset to original list when search term is cleared } this.updateNoResultDisplay(); } @@ -133,15 +151,15 @@ export class ListComponent { case 'Open Tickets': ticket.count = counts.open; break; - case 'Tickets in Progess': - ticket.count = counts.in_progress; - break; + case 'Tickets in Progess': + ticket.count = counts.in_progress; + break; case 'Tickets in Review': ticket.count = counts.in_review; break; - case 'Tickets Done': - ticket.count = counts.resubmit; - break; + case 'Tickets Resubmit': + ticket.count = counts.resubmit; + break; case 'Tickets Done': ticket.count = counts.done; break; @@ -169,7 +187,7 @@ export class ListComponent { } else { this.assignList[id].checked = '0' } - + this.assignto = []; this.assignList.forEach((element: any) => { if (element.checked == '1') { @@ -183,16 +201,16 @@ export class ListComponent { this.submitted = true if (this.ListForm.valid) { if (this.ListForm.get('id')?.value) { - const updatedData = {assignedto:this.assignto,...this.ListForm.value}; + const updatedData = { assignedto: this.assignto, ...this.ListForm.value }; this.store.dispatch(updateticketlistData({ updatedData })); } else { this.ListForm.controls['id'].setValue((this.alltickets.length + 1).toString()); const createDate = this.datepipe.transform(this.ListForm.get('createDate')?.value, "dd MMM, yyyy") || '' const dueDate = this.datepipe.transform(this.ListForm.get('dueDate')?.value, "dd MMM, yyyy") || '' - this.ListForm.patchValue({ createDate: createDate,dueDate:dueDate }); + this.ListForm.patchValue({ createDate: createDate, dueDate: dueDate }); - const newData = {assignedto:this.assignto,...this.ListForm.value} + const newData = { assignedto: this.assignto, ...this.ListForm.value } this.store.dispatch(addticketlistData({ newData })); } this.assignto = []; @@ -270,8 +288,6 @@ export class ListComponent { // pagechanged pageChanged(event: PageChangedEvent): void { - const startItem = (event.page - 1) * event.itemsPerPage; - this.endItem = event.page * event.itemsPerPage; - this.tickets = this.alltickets.slice(startItem, this.endItem); + this.fetchTickets(event.page, event.itemsPerPage); } } \ No newline at end of file diff --git a/src/app/pages/tickets/overview/overview.component.html b/src/app/pages/tickets/overview/overview.component.html index 1ddbb26..02535ba 100644 --- a/src/app/pages/tickets/overview/overview.component.html +++ b/src/app/pages/tickets/overview/overview.component.html @@ -53,14 +53,17 @@
Коментарі
-
{{ comment.sender_name }} {{ comment.created_at | date:'medium' }}
+
{{ comment.account_details.first_name }} {{ comment.account_details.last_name }} {{ comment.created_at | + date:'medium' }}

{{ comment.text }}

{{ comment.fileInfo.file_name }}

Size: {{ comment.fileInfo.file_size }} bytes

- +
@@ -75,17 +78,19 @@
{{ comment.fileInfo.file_name }}
- + + rows="3" placeholder="Напишіть коментар тут" [(ngModel)]="commentText" + name="commentText" required>
- +
- +
@@ -105,7 +110,7 @@
Деталі задачі
{{ task.state }} - Пріорітет + Пріоритет {{ task.priority }}