From 2f68c499365d8fdd7e5d36181a998d6bbc3738e9 Mon Sep 17 00:00:00 2001 From: sepehr Date: Thu, 16 Oct 2025 17:33:47 +0300 Subject: [PATCH] feat: redesign user tasks with advanced scheduling - Complete UI/UX redesign of user tasks interface - Add cron selector with examples - Improved task scheduling interface - Enhanced task management and monitoring --- .../user_tasks/user_tasks.component.html | 503 ++++++++++++------ .../user_tasks/user_tasks.component.scss | 371 +++++++++++++ .../views/user_tasks/user_tasks.component.ts | 188 ++++++- src/app/views/user_tasks/user_tasks.module.ts | 6 + 4 files changed, 884 insertions(+), 184 deletions(-) create mode 100644 src/app/views/user_tasks/user_tasks.component.scss diff --git a/src/app/views/user_tasks/user_tasks.component.html b/src/app/views/user_tasks/user_tasks.component.html index f7da0bc..f37fd7c 100644 --- a/src/app/views/user_tasks/user_tasks.component.html +++ b/src/app/views/user_tasks/user_tasks.component.html @@ -57,146 +57,255 @@ - -
Editing device {{SelectedTask['name']}}
-
Adding new task
+ +
+ Edit Task: {{SelectedTask['name']}} +
+
+ Create New Task +
- -
- - + + +
+
+
Basic Information
+ Define the task name, description and type +
+ + + + + + + + + + +
- -
- - + +
+
+
Task Configuration
+ Configure task-specific settings and parameters +
+ + +
+ + +
Backup Configuration
+
+ +
+ + This task will create configuration backups of selected devices. Backups are stored securely and can be restored later. +
+
+
+
+ + + + +
Firmware Update Strategy
+
+ +
+ + + + + +
+ +
+ + Uses global MikroWizard update strategy settings. Check Settings page for configuration. +
+ +
+ + Downloads latest firmware from mikrotik.com. Server needs internet access. +
+ +
+ + + + + + + + + + +
+
+
+ + +
+ + +
Script/Snippet Configuration
+
+ + + + + + The selected script will be executed on all target devices when this task runs. + + +
+
- - - - - -
Update Version Strategy
- - - - - - - - - - - - - - - - - - The version of firmware will be selected based on global settings of Mikrowizard Update strategy. -
- Please check settings page for more info and configuration -
-
- - - - The version of firmware will be selected based on latest availble version from Mikrotik website!. -
- V6 Firmware update Behavior and safe install is based on global Mikrowizard setting.(check settings page) -
- **with this option MikroWizard will download latest availble firmware from mikrotik.com. Please keep in mind that server needs internet access to mikrotik.com
-
- - - - - - * The version of firmware to install routers - - - - - - - * The version of firmware to install on V6 routers - - -
-
- - - - -
- - + + +
+
+
Schedule Configuration
+ Set when this task should run automatically +
+
+ +
+
+ + +
+
+
+
{{cron.label}}
+
{{cron.value}}
+
{{cron.description}}
+
+
+
+ No matching cron presets found +
+
+ + {{getCronDescription()}} + +
+ + Quick Examples: + * * * * * = every minute | + 0 2 * * * = daily at 2 AM | + 0 */6 * * * = every 6 hours + +
+
+ + +
+
+
Target Selection
+ Choose which devices or groups this task will affect +
+
+ + + + +
- - - - - -
Members :
- - - -   {{value}} - - - - {{value}} - - - - - - - - -
- + + + +
+ Selected {{SelectedTask['selection_type'] === 'devices' ? 'Devices' : 'Groups'}} +
+
+ {{SelectedMembers.length}} selected + +
+
+ +
+ +
No {{SelectedTask['selection_type']}} selected
+

Click "Add {{SelectedTask['selection_type'] === 'devices' ? 'Devices' : 'Groups'}}" to select targets for this task

+
+
+ + + +
+ + {{value}} +
+
+
+ + + {{value}} + + + + + + + +
+
+
+
+
- - - - + +
+ All fields marked with * are required +
+
+ + + +
- - -
Editing Group
+ + +
+ Add {{SelectedTask['selection_type'] === 'devices' ? 'Devices' : 'Groups'}} to Task +
- - -
Group Members :
- - - -   {{value}} - - - - {{value}} - - - - - {{value}} - - - -
-
-
+ + +
+ + + {{NewMemberRows.length}} {{SelectedTask['selection_type']}}(s) selected for addition to this task + +
+ + + + +
+ + Available {{SelectedTask['selection_type'] === 'devices' ? 'Devices' : 'Groups'}} ({{availbleMembers.length}} total) +
+
+ +
+ +
All {{SelectedTask['selection_type']}} are already assigned
+

No available {{SelectedTask['selection_type']}} to add to this task

+
+
+ + + +
+ + {{value}} +
+
+
+ + + {{value}} + + + + + {{value}} + + +
+
+
+
- - - + +
+ Select {{SelectedTask['selection_type']}} from the list above to add them to this task +
+
+ + +
@@ -340,4 +491,6 @@ Close
- \ No newline at end of file + + + \ No newline at end of file diff --git a/src/app/views/user_tasks/user_tasks.component.scss b/src/app/views/user_tasks/user_tasks.component.scss new file mode 100644 index 0000000..ccea1e6 --- /dev/null +++ b/src/app/views/user_tasks/user_tasks.component.scss @@ -0,0 +1,371 @@ +/* Task Form Sections */ +.task-form-section, .task-config-section, .schedule-section, .target-section { + border: 1px solid #e9ecef; + border-radius: 6px; + padding: 0.75rem; + background: #f8f9fa; +} + +.section-header { + border-bottom: 1px solid #dee2e6; + padding-bottom: 0.375rem; + margin-bottom: 0.75rem; +} + +.section-title { + color: #495057; + font-weight: 600; + font-size: 0.875rem; +} + +/* Form Inputs */ +.form-input { + border: 1px solid #ced4da; + border-radius: 4px; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +.form-input:focus { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +/* Strategy Buttons */ +.strategy-buttons .btn-group { + box-shadow: 0 1px 3px rgba(0,0,0,0.1); +} + +.strategy-buttons button { + font-size: 0.8rem; + padding: 0.375rem 0.75rem; +} + +/* Cron Dropdown Styles */ +.cron-input-wrapper { + position: relative; +} + +.input-group { + position: relative; + display: flex; + flex-wrap: nowrap; + align-items: stretch; + width: 100%; +} + +.input-group .search-input { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: none; + flex: 1; +} + +.input-group .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: 1px solid #ced4da; + flex-shrink: 0; +} + +.search-select-wrapper { + position: relative; + width: 100%; +} + +.search-input { + width: 100%; + padding: 0.5rem 0.75rem; + border: 1px solid #ced4da; + border-radius: 6px; + font-size: 0.9rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +.search-input:focus { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); + outline: 0; +} + +.search-dropdown { + position: absolute; + top: 100%; + left: 0; + right: 0; + background: white; + border: 1px solid #ced4da; + border-top: none; + border-radius: 0 0 6px 6px; + max-height: 300px; + overflow-y: auto; + z-index: 1000; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +.search-option { + padding: 0.5rem 0.75rem; + cursor: pointer; + border-bottom: 1px solid #f8f9fa; + transition: background-color 0.15s ease-in-out; +} + +.search-option:hover { + background-color: #f8f9fa; +} + +.search-option:last-child { + border-bottom: none; +} + +.search-no-results { + padding: 0.75rem; + text-align: center; + color: #6c757d; + font-style: italic; +} + +/* Cron-specific dropdown styles */ +.cron-dropdown { + max-height: 400px; +} + +.cron-option { + padding: 0.75rem; + border-bottom: 1px solid #e9ecef; +} + +.cron-option:hover { + background-color: #e3f2fd; +} + +.cron-option.selected { + background-color: #bbdefb; + border-left: 4px solid #2196f3; +} + +.cron-option.selected:hover { + background-color: #90caf9; +} + +.cron-label { + font-weight: 600; + color: #495057; + font-size: 0.9rem; +} + +.cron-value { + font-family: 'Courier New', monospace; + color: #007bff; + font-size: 0.85rem; + margin: 0.25rem 0; + background: #f8f9fa; + padding: 0.25rem 0.5rem; + border-radius: 4px; + display: inline-block; +} + +.cron-description { + color: #6c757d; + font-size: 0.8rem; + font-style: italic; +} + +/* Target Type Selector */ +.target-type-selector .btn-group { + box-shadow: 0 1px 3px rgba(0,0,0,0.1); +} + +.target-type-selector button { + font-size: 0.8rem; + padding: 0.375rem 0.75rem; +} + +/* Card Enhancements */ +.c-card { + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + border: 1px solid #e9ecef; +} + +.c-card-header { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); + border-bottom: 1px solid #dee2e6; + font-weight: 600; +} + +/* Alert Enhancements */ +.alert { + border-radius: 8px; + border: none; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.alert-info { + background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%); + color: #0c5460; +} + +.alert-success { + background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); + color: #155724; +} + +.alert-warning { + background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%); + color: #856404; +} + +/* Badge Enhancements */ +.badge { + font-size: 0.7rem; + padding: 0.25em 0.5em; + border-radius: 4px; +} + +/* Button Enhancements */ +.btn { + border-radius: 4px; + font-weight: 500; + transition: all 0.15s ease-in-out; +} + +.btn-sm { + padding: 0.25rem 0.5rem; + font-size: 0.8rem; +} + +.btn:hover { + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0,0,0,0.15); +} + +/* Modal Enhancements */ +.modal-header { + border-bottom: 1px solid #dee2e6; + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); +} + +.modal-footer { + border-top: 1px solid #dee2e6; + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); +} + +/* Grid Enhancements */ +.gui-grid { + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .task-form-section, .task-config-section, .schedule-section, .target-section { + padding: 0.5rem; + } + + .section-title { + font-size: 0.8rem; + } + + .strategy-buttons button, + .target-type-selector button { + font-size: 0.75rem; + padding: 0.25rem 0.5rem; + } + + .form-input { + padding: 0.25rem 0.5rem; + font-size: 0.8rem; + } + + .cron-label { + font-size: 0.8rem; + } + + .cron-value { + font-size: 0.75rem; + } + + .cron-description { + font-size: 0.7rem; + } + + .c-modal-body { + padding: 0.75rem !important; + } +} + +@media (max-width: 576px) { + .task-form-section, .task-config-section, .schedule-section, .target-section { + padding: 0.375rem; + margin-bottom: 0.75rem; + } + + .section-header { + margin-bottom: 0.5rem; + } + + .strategy-buttons button, + .target-type-selector button { + font-size: 0.7rem; + padding: 0.25rem 0.375rem; + } + + .input-group .btn { + padding: 0.25rem 0.5rem; + font-size: 0.8rem; + } +} + +/* Animation for smooth transitions */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +.search-dropdown { + animation: fadeIn 0.2s ease-out; +} + +/* Focus states for accessibility */ +.search-option:focus, +.cron-option:focus { + outline: 2px solid #007bff; + outline-offset: -2px; + background-color: #e3f2fd; +} + +/* Loading states */ +.loading-spinner { + display: inline-block; + width: 1rem; + height: 1rem; + border: 2px solid #f3f3f3; + border-top: 2px solid #007bff; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Code examples */ +code { + background-color: #f8f9fa; + color: #e83e8c; + padding: 0.2em 0.4em; + border-radius: 3px; + font-size: 0.875em; + font-family: 'Courier New', monospace; +} + +/* Improved spacing */ +.mt-1 { margin-top: 0.25rem !important; } +.mt-2 { margin-top: 0.5rem !important; } +.me-1 { margin-right: 0.25rem !important; } +.me-2 { margin-right: 0.5rem !important; } +.ms-2 { margin-left: 0.5rem !important; } +.d-block { display: block !important; } \ No newline at end of file diff --git a/src/app/views/user_tasks/user_tasks.component.ts b/src/app/views/user_tasks/user_tasks.component.ts index 63d6163..b19e2e6 100644 --- a/src/app/views/user_tasks/user_tasks.component.ts +++ b/src/app/views/user_tasks/user_tasks.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy } from "@angular/core"; +import { Component, OnInit, OnDestroy, QueryList, ViewChildren } from "@angular/core"; import { dataProvider } from "../../providers/mikrowizard/data"; import { Router } from "@angular/router"; import { loginChecker } from "../../providers/login_checker"; @@ -16,16 +16,28 @@ import { } from "@generic-ui/ngx-grid"; import { NgxSuperSelectOptions } from "ngx-super-select"; import { _getFocusedElementPierceShadowDom } from "@angular/cdk/platform"; +import { AppToastComponent } from "../toast-simple/toast.component"; +import { ToasterComponent } from "@coreui/angular"; @Component({ templateUrl: "user_tasks.component.html", + styleUrls: ["user_tasks.component.scss"] }) export class UserTasksComponent implements OnInit { public uid: number; public uname: string; public ispro: boolean = false; + @ViewChildren(ToasterComponent) viewChildren!: QueryList; + toasterForm = { + autohide: true, + delay: 10000, + position: "fixed", + fade: true, + closeButton: true, + }; + constructor( private data_provider: dataProvider, private router: Router, @@ -76,6 +88,55 @@ export class UserTasksComponent implements OnInit { public firmwaretoinstallv6: string = "none"; public updateBehavior: string = "keep"; public firms_loaded: boolean = false; + public predefinedCrons: any[] = [ + // High Frequency Monitoring + { label: 'Every minute', value: '* * * * *', description: 'Critical monitoring - runs every minute' }, + { label: 'Every 2 minutes', value: '*/2 * * * *', description: 'High frequency monitoring' }, + { label: 'Every 5 minutes', value: '*/5 * * * *', description: 'Standard monitoring interval' }, + { label: 'Every 10 minutes', value: '*/10 * * * *', description: 'Regular monitoring checks' }, + { label: 'Every 15 minutes', value: '*/15 * * * *', description: 'Moderate monitoring frequency' }, + { label: 'Every 30 minutes', value: '*/30 * * * *', description: 'Low frequency monitoring' }, + + // Hourly Operations + { label: 'Every hour', value: '0 * * * *', description: 'Hourly network checks' }, + { label: 'Every 2 hours', value: '0 */2 * * *', description: 'Bi-hourly operations' }, + { label: 'Every 4 hours', value: '0 */4 * * *', description: 'Quarterly daily checks' }, + { label: 'Every 6 hours', value: '0 */6 * * *', description: 'Four times daily' }, + { label: 'Every 8 hours', value: '0 */8 * * *', description: 'Three times daily' }, + { label: 'Every 12 hours', value: '0 */12 * * *', description: 'Twice daily operations' }, + + // Daily Maintenance + { label: 'Daily at midnight', value: '0 0 * * *', description: 'Daily maintenance at 00:00' }, + { label: 'Daily at 1 AM', value: '0 1 * * *', description: 'Daily backup at 01:00' }, + { label: 'Daily at 2 AM', value: '0 2 * * *', description: 'Daily maintenance at 02:00' }, + { label: 'Daily at 3 AM', value: '0 3 * * *', description: 'Low traffic maintenance at 03:00' }, + { label: 'Daily at 6 AM', value: '0 6 * * *', description: 'Pre-business hours check' }, + { label: 'Daily at 6 PM', value: '0 18 * * *', description: 'End of business day backup' }, + { label: 'Daily at 10 PM', value: '0 22 * * *', description: 'Evening maintenance at 22:00' }, + + // Business Hours + { label: 'Workdays at 8 AM', value: '0 8 * * 1-5', description: 'Start of business day - Mon to Fri' }, + { label: 'Workdays at 9 AM', value: '0 9 * * 1-5', description: 'Business hours start check' }, + { label: 'Workdays at 12 PM', value: '0 12 * * 1-5', description: 'Midday check - Mon to Fri' }, + { label: 'Workdays at 5 PM', value: '0 17 * * 1-5', description: 'End of business day - Mon to Fri' }, + { label: 'Workdays at 6 PM', value: '0 18 * * 1-5', description: 'After hours backup - Mon to Fri' }, + + // Weekly Operations + { label: 'Weekly (Sunday midnight)', value: '0 0 * * 0', description: 'Weekly maintenance - Sunday 00:00' }, + { label: 'Weekly (Monday midnight)', value: '0 0 * * 1', description: 'Weekly start - Monday 00:00' }, + { label: 'Weekly (Friday 6 PM)', value: '0 18 * * 5', description: 'End of week backup - Friday 18:00' }, + { label: 'Weekly (Saturday 2 AM)', value: '0 2 * * 6', description: 'Weekend maintenance - Saturday 02:00' }, + + // Monthly Operations + { label: 'Monthly (1st at midnight)', value: '0 0 1 * *', description: 'Monthly maintenance - 1st of month' }, + { label: 'Monthly (1st at 2 AM)', value: '0 2 1 * *', description: 'Monthly backup - 1st at 02:00' }, + { label: 'Monthly (15th at midnight)', value: '0 0 15 * *', description: 'Mid-month maintenance - 15th' }, + { label: 'Monthly (last day)', value: '0 0 28-31 * *', description: 'End of month operations' } + ]; + public showCronDropdown: boolean = false; + public cronSearch: string = ''; + public selectedCronPreset: any = null; + public filteredCrons: any[] = []; public sorting = { enabled: true, multiSorting: true, @@ -156,22 +217,43 @@ export class UserTasksComponent implements OnInit { this.initGridTable(); } + show_toast(title: string, body: string, color: string) { + const { ...props } = { ...this.toasterForm, color, title, body }; + const componentRef = this.viewChildren.first.addToast( + AppToastComponent, + props, + {} + ); + componentRef.instance["closeButton"] = props.closeButton; + } + submit(action: string) { var _self = this; if (action == "add") { this.data_provider .Add_task(_self.SelectedTask, _self.SelectedTaskItems) .then((res) => { - _self.initGridTable(); + if (res && res.status === 'failed') { + _self.show_toast("Error", res.err, "danger"); + } else { + _self.show_toast("Success", "Task created successfully", "success"); + _self.initGridTable(); + _self.EditTaskModalVisible = false; + } }); } else { this.data_provider .Edit_task(_self.SelectedTask, _self.SelectedTaskItems) .then((res) => { - _self.initGridTable(); + if (res && res.status === 'failed') { + _self.show_toast("Error", res.err, "danger"); + } else { + _self.show_toast("Success", "Task updated successfully", "success"); + _self.initGridTable(); + _self.EditTaskModalVisible = false; + } }); } - this.EditTaskModalVisible = false; } onSelectedRowsNewMembers(rows: Array): void { @@ -197,7 +279,7 @@ export class UserTasksComponent implements OnInit { this.SelectedTask = { id: 0, action: "add", - taskcron: "* * * * *", + cron: "0 2 * * *", desc_cron: "", description: "", members: "", @@ -206,7 +288,9 @@ export class UserTasksComponent implements OnInit { snippetid: "", task_type: "backup", }; - this.SelectedTask['data'] = { 'strategy': 'system', 'version_to_install': '', 'version_to_install_6': '' } + this.SelectedTask['data'] = { 'strategy': 'system', 'version_to_install': '', 'version_to_install_6': '' }; + this.cronSearch = ''; + this.selectedCronPreset = null; this.SelectedMembers = []; this.SelectedTaskItems = []; this.EditTaskModalVisible = true; @@ -215,6 +299,12 @@ export class UserTasksComponent implements OnInit { var _self = this; this.SelectedTask = { ...item }; + + // Initialize cron search and preset tracking + this.cronSearch = ''; + const currentCron = this.SelectedTask['cron']; + this.selectedCronPreset = this.predefinedCrons.find(cron => cron.value === currentCron) || null; + if (this.SelectedTask['task_type'] == 'firmware' && 'data' in this.SelectedTask && this.SelectedTask['data']) { this.SelectedTask['data'] = JSON.parse(this.SelectedTask['data']); if (this.SelectedTask['data']['strategy'] == 'defined') { @@ -243,7 +333,7 @@ export class UserTasksComponent implements OnInit { } } - _self.data_provider.get_snippets("", "", "", 0, 1000).then((res) => { + _self.data_provider.get_snippets("", "", "", 0, 1000,false).then((res) => { _self.Snippets = res.map((x: any) => { return { id: x.id, name: x.name }; }); @@ -315,7 +405,7 @@ export class UserTasksComponent implements OnInit { onSnippetsValueChanged(v: any) { var _self = this; if (v == "" || v.length < 3) return; - _self.data_provider.get_snippets(v, "", "", 0, 1000).then((res) => { + _self.data_provider.get_snippets(v, "", "", 0, 1000,false).then((res) => { _self.Snippets = res.map((x: any) => { return { id: String(x.id), name: x.name }; }); @@ -333,7 +423,12 @@ export class UserTasksComponent implements OnInit { } else { var _self = this; this.data_provider.Delete_task(_self.SelectedTask["id"]).then((res) => { - _self.initGridTable(); + if (res && res.status === 'failed') { + _self.show_toast("Error", res.err, "danger"); + } else { + _self.show_toast("Success", "Task deleted successfully", "success"); + _self.initGridTable(); + } _self.DeleteConfirmModalVisible = false; }); } @@ -363,4 +458,79 @@ export class UserTasksComponent implements OnInit { _self.loading = false; }); } + + selectCron(cron: any): void { + this.SelectedTask['cron'] = cron.value; + this.selectedCronPreset = cron; + this.cronSearch = ''; + this.showCronDropdown = false; + } + + filterCrons(event: any): void { + const searchTerm = event.target.value.toLowerCase(); + this.cronSearch = searchTerm; + this.selectedCronPreset = null; + + if (searchTerm.length > 0) { + this.filteredCrons = this.predefinedCrons.filter(cron => + cron.label.toLowerCase().includes(searchTerm) || + cron.description.toLowerCase().includes(searchTerm) || + cron.value.includes(searchTerm) + ); + } else { + this.filteredCrons = this.predefinedCrons; + } + } + + hideCronDropdown(): void { + setTimeout(() => { + this.showCronDropdown = false; + }, 200); + } + + onCronInputFocus(): void { + this.filteredCrons = this.predefinedCrons; + this.showCronDropdown = true; + + // If current cron matches a preset, highlight it + const currentCron = this.SelectedTask['cron']; + this.selectedCronPreset = this.predefinedCrons.find(cron => cron.value === currentCron) || null; + } + + onCronInputChange(event: any): void { + this.SelectedTask['cron'] = event.target.value; + this.selectedCronPreset = null; + + // Check if the entered value matches any preset + const enteredValue = event.target.value; + const matchingPreset = this.predefinedCrons.find(cron => cron.value === enteredValue); + if (matchingPreset) { + this.selectedCronPreset = matchingPreset; + } + } + + getCronDescription(): string { + if (this.selectedCronPreset) { + return this.selectedCronPreset.description; + } + + const currentCron = this.SelectedTask['cron']; + const matchingPreset = this.predefinedCrons.find(cron => cron.value === currentCron); + return matchingPreset ? matchingPreset.description : 'Custom cron expression'; + } + + onTaskTypeChange(): void { + if (this.SelectedTask['task_type'] === 'snippet') { + this.loadSnippets(); + } + } + + loadSnippets(): void { + var _self = this; + _self.data_provider.get_snippets("", "", "", 0, 10).then((res) => { + _self.Snippets = res.map((x: any) => { + return { id: x.id, name: x.name }; + }); + }); + } } diff --git a/src/app/views/user_tasks/user_tasks.module.ts b/src/app/views/user_tasks/user_tasks.module.ts index fdc3f47..e59ef63 100644 --- a/src/app/views/user_tasks/user_tasks.module.ts +++ b/src/app/views/user_tasks/user_tasks.module.ts @@ -9,6 +9,9 @@ import { GridModule, ModalModule, ButtonGroupModule, + BadgeModule, + AlertModule, + ToastModule, } from "@coreui/angular"; import { UserTasksRoutingModule } from "./user_tasks-routing.module"; import { UserTasksComponent } from "./user_tasks.component"; @@ -25,6 +28,9 @@ import { NgxSuperSelectModule} from "ngx-super-select"; FormModule, ButtonModule, ButtonGroupModule, + BadgeModule, + AlertModule, + ToastModule, GuiGridModule, ModalModule, ReactiveFormsModule,