ng-tablekit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,290 @@
1
+ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2
+ import { CurrencyPipe, Location } from '@angular/common';
3
+ import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
4
+ import { ArrowLeftOutline, DeleteOutline, EditOutline, EyeOutline, FileExcelOutline, FilePdfOutline, FilterOutline, FormOutline, PlusOutline, SearchOutline } from '@ant-design/icons-angular/icons';
5
+ import { jsPDF } from 'jspdf';
6
+ import * as XLSX from 'xlsx';
7
+ import { NzButtonModule } from 'ng-zorro-antd/button';
8
+ import { NzFormModule } from 'ng-zorro-antd/form';
9
+ import { NzIconModule, provideNzIconsPatch } from 'ng-zorro-antd/icon';
10
+ import { NzInputModule } from 'ng-zorro-antd/input';
11
+ import { NzSelectModule } from 'ng-zorro-antd/select';
12
+ import { NzTableModule, NzTableQueryParams } from 'ng-zorro-antd/table';
13
+ import { NzTagModule } from 'ng-zorro-antd/tag';
14
+ import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
15
+ import { NzRadioModule } from 'ng-zorro-antd/radio';
16
+ import { NzDrawerModule } from 'ng-zorro-antd/drawer';
17
+ import { NzDividerModule } from 'ng-zorro-antd/divider';
18
+ import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm';
19
+ import { NzMessageService } from 'ng-zorro-antd/message';
20
+ import { NzSwitchModule } from 'ng-zorro-antd/switch';
21
+ import { PAGE_SIZE_OPTIONS, STANDARD_COLUMNS, STANDARD_ACTIONS } from '@core/config/table.config';
22
+ import { TABLE_RECORDS } from '@core/data/record.data';
23
+ import { TableRecord } from '@core/models/table.models';
24
+ import { createColumnSortFn, formatDateValue, getStatusColor, globalRecordSearch } from '@core/utils/table.utils';
25
+ import { CopyTemplateButtonComponent } from '@shared/copy-template-button/copy-template-button.component';
26
+
27
+ import { MasterRecordDrawerComponent } from './master-record-drawer.component';
28
+ import { MasterMapDrawerComponent } from './master-map-drawer.component';
29
+
30
+ @Component({
31
+ selector: 'app-master-page',
32
+ standalone: true,
33
+ imports: [
34
+ CurrencyPipe, FormsModule, ReactiveFormsModule, NzButtonModule, NzFormModule, NzIconModule,
35
+ NzInputModule, NzSelectModule, NzTableModule, NzTagModule, NzDatePickerModule,
36
+ NzRadioModule, NzDividerModule, NzPopconfirmModule,
37
+ CopyTemplateButtonComponent, MasterRecordDrawerComponent, MasterMapDrawerComponent
38
+ ],
39
+ providers: [...provideNzIconsPatch([ArrowLeftOutline, DeleteOutline, EditOutline, EyeOutline, FileExcelOutline, FilePdfOutline, FilterOutline, FormOutline, PlusOutline, SearchOutline])],
40
+ templateUrl: './master-page.component.html',
41
+ styleUrl: './master-page.component.css'
42
+ })
43
+ export class MasterPageComponent {
44
+ private readonly fb = inject(FormBuilder);
45
+ private readonly message = inject(NzMessageService);
46
+ private readonly location = inject(Location);
47
+
48
+ protected readonly columns = STANDARD_COLUMNS;
49
+ protected readonly actions = STANDARD_ACTIONS;
50
+ protected readonly pageSizeOptions = PAGE_SIZE_OPTIONS;
51
+ protected readonly getStatusColor = getStatusColor;
52
+ protected readonly formatDateValue = formatDateValue;
53
+ protected readonly sortBy = createColumnSortFn<TableRecord>;
54
+ protected readonly categories = ['Category A', 'Category B', 'Category C', 'Category D'];
55
+ protected readonly statuses = ['Active', 'Inactive', 'Pending', 'Approved', 'Rejected'];
56
+
57
+
58
+ /* =================================================================
59
+ HEADER CONTROLS
60
+ =================================================================*/
61
+
62
+ protected reportTitle = 'Master Page';
63
+ protected showFilter = true;
64
+ protected showExcel = true;
65
+ protected showPdf = true;
66
+ protected showAddAction = true;
67
+ protected showCategoryFilter = true;
68
+ protected showMultiselectFilter = true;
69
+ protected showDateFilter = true;
70
+ protected showDateRangeFilter = true;
71
+ protected showRadioFilter = true;
72
+
73
+ /* =================================================================
74
+ ACTION CONTROLS
75
+ =================================================================*/
76
+
77
+ protected showViewAction = false;
78
+ protected showEditAction = true;
79
+ protected showDeleteAction = false;
80
+ protected showMapAction = true;
81
+
82
+ protected readonly filterForm = this.fb.nonNullable.group({
83
+ category: '',
84
+ categories: [[] as string[]],
85
+ date: [null as Date | null],
86
+ dateRange: [[] as Date[]],
87
+ status: ''
88
+ });
89
+
90
+ protected filterVisible = false;
91
+ protected searchText = '';
92
+ protected pageIndex = 1;
93
+ private allRecords: TableRecord[] = TABLE_RECORDS.map(record => ({ ...record }));
94
+ protected records: TableRecord[] = [...this.allRecords];
95
+ protected currentSort: { key: string | null; value: string | null } = { key: null, value: null };
96
+
97
+ // Drawer State
98
+ protected isDrawerVisible = false;
99
+ protected drawerMode: 'add' | 'edit' = 'add';
100
+ protected drawerTitle = '';
101
+ protected editingRecord: TableRecord | null = null;
102
+
103
+ // Map Drawer State
104
+ protected isMapDrawerVisible = false;
105
+ protected mappingRecord: TableRecord | null = null;
106
+
107
+ protected toggleFilter(): void { this.filterVisible = !this.filterVisible; }
108
+ protected goBack(): void { this.location.back(); }
109
+ protected getCurrencyValue(value: unknown): string | number {
110
+ return typeof value === 'string' || typeof value === 'number' ? value : 0;
111
+ }
112
+ protected onSearch(): void { this.applyQuery(); }
113
+ protected applyFilters(): void {
114
+ this.applyQuery();
115
+ this.filterVisible = false;
116
+ }
117
+ protected clearFilters(): void {
118
+ this.filterForm.reset({ category: '', categories: [], date: null, dateRange: [], status: '' });
119
+ this.applyQuery();
120
+ this.filterVisible = false;
121
+ }
122
+
123
+ protected onQueryParamsChange(params: NzTableQueryParams): void {
124
+ const sort = params.sort.find((item: any) => item.value !== null);
125
+ this.currentSort = { key: sort?.key ?? null, value: sort?.value ?? null };
126
+ }
127
+
128
+ // Drawer Methods
129
+ protected openAddDrawer(): void {
130
+ this.drawerMode = 'add';
131
+ this.drawerTitle = 'Add New Record';
132
+ this.editingRecord = null;
133
+ this.isDrawerVisible = true;
134
+ }
135
+
136
+ protected viewRecord(record: TableRecord): void {
137
+ this.message.info(`Viewing record: ${record.name}`);
138
+ }
139
+
140
+ protected mapRecord(record: TableRecord): void {
141
+ this.mappingRecord = record;
142
+ this.isMapDrawerVisible = true;
143
+ }
144
+
145
+ protected closeMapDrawer(): void {
146
+ this.isMapDrawerVisible = false;
147
+ }
148
+
149
+ protected openEditDrawer(record: TableRecord): void {
150
+ this.drawerMode = 'edit';
151
+ this.drawerTitle = 'Update Record';
152
+ this.editingRecord = record;
153
+ this.isDrawerVisible = true;
154
+ }
155
+
156
+ protected closeDrawer(): void {
157
+ this.isDrawerVisible = false;
158
+ }
159
+
160
+ protected saveRecord(event: { mode: 'add' | 'edit', data: any }): void {
161
+ if (event.mode === 'add') {
162
+ const nextId = Math.max(0, ...this.allRecords.map(record => record.id)) + 1;
163
+ const record: TableRecord = {
164
+ id: nextId,
165
+ name: event.data.name,
166
+ code: event.data.code,
167
+ category: event.data.category || 'Uncategorised',
168
+ type: 'Standard',
169
+ referenceNumber: `REF-${String(nextId).padStart(6, '0')}`,
170
+ sequence: nextId,
171
+ createdDate: new Date().toISOString(),
172
+ assignedTo: 'Unassigned',
173
+ amount: 0,
174
+ status: event.data.isActive ? 'Active' : 'Inactive',
175
+ active: event.data.isActive,
176
+ description: ''
177
+ };
178
+ this.allRecords = [record, ...this.allRecords];
179
+ this.message.success('Record added successfully');
180
+ } else if (this.editingRecord) {
181
+ const editedId = this.editingRecord.id;
182
+ this.allRecords = this.allRecords.map(record => record.id === editedId ? {
183
+ ...record,
184
+ name: event.data.name,
185
+ code: event.data.code,
186
+ category: event.data.category || 'Uncategorised',
187
+ status: event.data.isActive ? 'Active' : 'Inactive',
188
+ active: event.data.isActive
189
+ } : record);
190
+ this.message.success('Record updated successfully');
191
+ }
192
+ this.applyQuery();
193
+ this.closeDrawer();
194
+ }
195
+
196
+ protected deleteRecord(record: TableRecord): void {
197
+ this.allRecords = this.allRecords.filter(item => item.id !== record.id);
198
+ this.applyQuery();
199
+ this.message.success(`Record ${record.code} deleted successfully`);
200
+ }
201
+
202
+ private getSortedRecords(): TableRecord[] {
203
+ if (!this.currentSort.key || !this.currentSort.value) return this.records;
204
+ return [...this.records].sort((a: any, b: any) => {
205
+ const valA = a[this.currentSort.key!];
206
+ const valB = b[this.currentSort.key!];
207
+ if (valA === valB) return 0;
208
+ const isAsc = this.currentSort.value === 'ascend';
209
+ return valA < valB ? (isAsc ? -1 : 1) : (isAsc ? 1 : -1);
210
+ });
211
+ }
212
+
213
+ protected downloadExcel(): void {
214
+ const rows = this.getSortedRecords().map(record => ({
215
+ Name: record.name,
216
+ Code: record.code,
217
+ Category: record.category,
218
+ 'Created Date': formatDateValue(record.createdDate),
219
+ Status: record.status
220
+ }));
221
+ const sheet = XLSX.utils.json_to_sheet(rows);
222
+ const workbook = XLSX.utils.book_new();
223
+ XLSX.utils.book_append_sheet(workbook, sheet, 'Report');
224
+ XLSX.writeFile(workbook, 'master-page.xlsx');
225
+ }
226
+
227
+ protected downloadPdf(): void {
228
+ const document = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' });
229
+ const columns = [
230
+ { label: 'Name', x: 14, width: 58 },
231
+ { label: 'Code', x: 75, width: 34 },
232
+ { label: 'Category', x: 112, width: 42 },
233
+ { label: 'Created Date', x: 157, width: 38 },
234
+ { label: 'Status', x: 198, width: 34 }
235
+ ];
236
+ const drawHeader = (): number => {
237
+ document.setFont('helvetica', 'bold');
238
+ document.setFontSize(16);
239
+ document.text(this.reportTitle, 14, 16);
240
+ document.setFontSize(10);
241
+ columns.forEach(column => document.text(column.label, column.x, 26));
242
+ document.line(14, 29, 232, 29);
243
+ document.setFont('helvetica', 'normal');
244
+ return 35;
245
+ };
246
+ let y = drawHeader();
247
+ for (const record of this.getSortedRecords()) {
248
+ if (y > 195) {
249
+ document.addPage();
250
+ y = drawHeader();
251
+ }
252
+ const values = [record.name, record.code, record.category, formatDateValue(record.createdDate), record.status];
253
+ values.forEach((value, index) => {
254
+ const column = columns[index];
255
+ const text = document.splitTextToSize(String(value), column.width)[0] ?? '';
256
+ document.text(text, column.x, y);
257
+ });
258
+ y += 7;
259
+ }
260
+ document.save('master-page.pdf');
261
+ }
262
+
263
+ private applyQuery(): void {
264
+ this.pageIndex = 1;
265
+ const search = this.searchText.trim();
266
+ const { category, categories, date, dateRange, status } = this.filterForm.getRawValue();
267
+ this.records = this.allRecords.filter(record => {
268
+ let pass = true;
269
+ if (!globalRecordSearch(record, search)) pass = false;
270
+ if (category && record.category !== category) pass = false;
271
+ if (categories && categories.length > 0 && !categories.includes(record.category)) pass = false;
272
+ if (status && record.status !== status) pass = false;
273
+
274
+ if (date) {
275
+ const recordDate = new Date(record.createdDate).setHours(0, 0, 0, 0);
276
+ const filterDate = new Date(date).setHours(0, 0, 0, 0);
277
+ if (recordDate !== filterDate) pass = false;
278
+ }
279
+
280
+ if (dateRange && dateRange.length === 2) {
281
+ const recordDate = new Date(record.createdDate).setHours(0, 0, 0, 0);
282
+ const start = new Date(dateRange[0]).setHours(0, 0, 0, 0);
283
+ const end = new Date(dateRange[1]).setHours(0, 0, 0, 0);
284
+ if (recordDate < start || recordDate > end) pass = false;
285
+ }
286
+
287
+ return pass;
288
+ });
289
+ }
290
+ }
@@ -0,0 +1,3 @@
1
+ .status-switch {
2
+ min-width: 70px;
3
+ }
@@ -0,0 +1,52 @@
1
+ <nz-drawer
2
+ [nzVisible]="visible"
3
+ [nzTitle]="title"
4
+ [nzWidth]="400"
5
+ [nzFooter]="drawerFooter"
6
+ (nzOnClose)="onClose()"
7
+ >
8
+ <ng-container *nzDrawerContent>
9
+ <form [formGroup]="recordForm" nz-form nzLayout="vertical">
10
+ <nz-form-item>
11
+ <nz-form-label nzRequired>Name</nz-form-label>
12
+ <nz-form-control nzErrorTip="Please input the name">
13
+ <input nz-input formControlName="name" placeholder="Enter name" />
14
+ </nz-form-control>
15
+ </nz-form-item>
16
+
17
+ <nz-form-item>
18
+ <nz-form-label nzRequired>Code</nz-form-label>
19
+ <nz-form-control nzErrorTip="Please input the code">
20
+ <input nz-input formControlName="code" placeholder="Enter code" />
21
+ </nz-form-control>
22
+ </nz-form-item>
23
+
24
+ <nz-form-item>
25
+ <nz-form-label>Category</nz-form-label>
26
+ <nz-form-control>
27
+ <nz-select formControlName="category" nzAllowClear nzPlaceHolder="Select category">
28
+ @for(option of categories; track option) {
29
+ <nz-option [nzLabel]="option" [nzValue]="option"></nz-option>
30
+ }
31
+ </nz-select>
32
+ </nz-form-control>
33
+ </nz-form-item>
34
+
35
+ <nz-form-item>
36
+ <nz-form-label>Status</nz-form-label>
37
+ <nz-form-control>
38
+ <nz-switch formControlName="isActive" nzCheckedChildren="Active" nzUnCheckedChildren="Inactive" class="status-switch"></nz-switch>
39
+ </nz-form-control>
40
+ </nz-form-item>
41
+ </form>
42
+ </ng-container>
43
+ </nz-drawer>
44
+
45
+ <ng-template #drawerFooter>
46
+ <div style="float: right;">
47
+ <button nz-button style="margin-right: 8px;" (click)="onClose()">Cancel</button>
48
+ <button nz-button nzType="primary" (click)="onSave()">
49
+ {{ mode === 'add' ? 'Save' : 'Update' }}
50
+ </button>
51
+ </div>
52
+ </ng-template>
@@ -0,0 +1,74 @@
1
+ import { Component, EventEmitter, Input, Output, inject } from '@angular/core';
2
+ import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
3
+ import { NzButtonModule } from 'ng-zorro-antd/button';
4
+ import { NzDrawerModule } from 'ng-zorro-antd/drawer';
5
+ import { NzFormModule } from 'ng-zorro-antd/form';
6
+ import { NzInputModule } from 'ng-zorro-antd/input';
7
+ import { NzSelectModule } from 'ng-zorro-antd/select';
8
+ import { NzSwitchModule } from 'ng-zorro-antd/switch';
9
+ import { TableRecord } from '@core/models/table.models';
10
+
11
+ @Component({
12
+ selector: 'app-master-record-drawer',
13
+ standalone: true,
14
+ imports: [
15
+ FormsModule, ReactiveFormsModule, NzButtonModule, NzDrawerModule,
16
+ NzFormModule, NzInputModule, NzSelectModule, NzSwitchModule
17
+ ],
18
+ templateUrl: './master-record-drawer.component.html',
19
+ styleUrl: './master-record-drawer.component.css'
20
+ })
21
+ export class MasterRecordDrawerComponent {
22
+ private readonly fb = inject(FormBuilder);
23
+
24
+ @Input() visible = false;
25
+ @Input() mode: 'add' | 'edit' = 'add';
26
+ @Input() title = 'Add New Record';
27
+
28
+ @Input()
29
+ set record(value: TableRecord | null) {
30
+ if (value) {
31
+ this.recordForm.patchValue({
32
+ name: value.name,
33
+ code: value.code,
34
+ category: value.category,
35
+ isActive: value.status === 'Active'
36
+ });
37
+ } else {
38
+ this.recordForm.reset({ isActive: true });
39
+ }
40
+ }
41
+
42
+ @Input() categories: string[] = [];
43
+
44
+ @Output() close = new EventEmitter<void>();
45
+ @Output() save = new EventEmitter<{ mode: 'add'|'edit', data: any }>();
46
+
47
+ protected readonly recordForm = this.fb.nonNullable.group({
48
+ name: ['', [Validators.required]],
49
+ code: ['', [Validators.required]],
50
+ category: [''],
51
+ isActive: [true]
52
+ });
53
+
54
+ protected onClose(): void {
55
+ this.close.emit();
56
+ }
57
+
58
+ protected onSave(): void {
59
+ if (this.recordForm.invalid) {
60
+ Object.values(this.recordForm.controls).forEach(control => {
61
+ if (control.invalid) {
62
+ control.markAsDirty();
63
+ control.updateValueAndValidity({ onlySelf: true });
64
+ }
65
+ });
66
+ return;
67
+ }
68
+
69
+ this.save.emit({
70
+ mode: this.mode,
71
+ data: this.recordForm.getRawValue()
72
+ });
73
+ }
74
+ }
@@ -0,0 +1,274 @@
1
+ :host {
2
+ display: block;
3
+ min-width: 0;
4
+ }
5
+
6
+ /* Table Page Structure */
7
+ .template-page {
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 16px;
11
+ width: 100%;
12
+ min-width: 0;
13
+ padding: 0;
14
+ border: 0;
15
+ border-radius: 0;
16
+ background: transparent;
17
+ }
18
+
19
+ /* Toolbar */
20
+ .table-toolbar {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: space-between;
24
+ gap: 8px;
25
+ flex-wrap: wrap;
26
+ background: transparent;
27
+ padding: 0;
28
+ }
29
+
30
+ .table-toolbar__left {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: 12px;
34
+ flex-wrap: wrap;
35
+ }
36
+
37
+ .table-toolbar__right {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: flex-end;
41
+ gap: 8px;
42
+ flex-wrap: wrap;
43
+ margin-left: auto;
44
+ }
45
+
46
+ /* Page Heading */
47
+ .page-heading {
48
+ margin: 0;
49
+ font-family: Poppins, Arial, sans-serif;
50
+ font-size: 18px;
51
+ font-weight: 600;
52
+ line-height: 24px;
53
+ color: #1f1f1f;
54
+ }
55
+
56
+ /* Back Button */
57
+ .back-btn {
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ width: 32px !important;
62
+ height: 32px !important;
63
+ min-width: 32px !important;
64
+ border-radius: 50% !important;
65
+ padding: 0 !important;
66
+ border: 1px solid #d9d9d9 !important;
67
+ background: #ffffff !important;
68
+ color: #262626 !important;
69
+ font-size: 14px;
70
+ cursor: pointer;
71
+ transition: all 0.2s;
72
+ }
73
+
74
+ .back-btn:hover {
75
+ border-color: #1890ff !important;
76
+ color: #1890ff !important;
77
+ background: #ffffff !important;
78
+ }
79
+
80
+ /* Compact Buttons (Filter, Excel, Actions) */
81
+ .compact-btn {
82
+ height: 32px !important;
83
+ min-width: 32px !important;
84
+ border-radius: 6px !important;
85
+ display: inline-flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ gap: 6px;
89
+ padding: 0 12px !important;
90
+ font-size: 12px !important;
91
+ }
92
+
93
+ .compact-btn.icon-only {
94
+ padding: 0 !important;
95
+ }
96
+
97
+ /* Search Field */
98
+ .search-field {
99
+ width: 260px;
100
+ margin-bottom: 0;
101
+ }
102
+
103
+ :host ::ng-deep .search-field .ant-input {
104
+ height: 32px !important;
105
+ border-radius: 8px 0 0 8px !important;
106
+ font-size: 12px;
107
+ }
108
+
109
+ :host ::ng-deep .search-field .ant-input-group-addon .ant-btn {
110
+ height: 32px !important;
111
+ width: 40px !important;
112
+ border-radius: 0 8px 8px 0 !important;
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ }
117
+
118
+ /* Filter Panel */
119
+ .filter-panel {
120
+ margin: 0 0 14px;
121
+ padding: 12px 10px;
122
+ border: 1px solid #e5e7eb;
123
+ border-radius: 6px;
124
+ background: #ffffff;
125
+ }
126
+
127
+ .filter-panel__grid {
128
+ display: grid;
129
+ grid-template-columns: repeat(3, minmax(180px, 1fr));
130
+ gap: 12px;
131
+ }
132
+
133
+ .filter-panel__field {
134
+ display: inline-block;
135
+ margin-bottom: 5px;
136
+ width: 100%;
137
+ padding: 0 10px;
138
+ }
139
+
140
+ .filter-panel__field>label {
141
+ display: block;
142
+ font-family: Poppins, Arial, sans-serif;
143
+ color: #333;
144
+ font-size: 12px;
145
+ font-weight: 500;
146
+ margin-bottom: 4px;
147
+ }
148
+
149
+ :host ::ng-deep .filter-panel__field .ant-select,
150
+ :host ::ng-deep .filter-panel__field .ant-picker,
151
+ :host ::ng-deep .filter-panel__field .ant-input {
152
+ width: 100%;
153
+ border-radius: 4px;
154
+ }
155
+
156
+ :host ::ng-deep .filter-panel__field .ant-radio-wrapper {
157
+ display: inline-flex;
158
+ align-items: center;
159
+ margin-right: 16px;
160
+ }
161
+
162
+ .filter-panel__actions {
163
+ display: flex;
164
+ justify-content: flex-end;
165
+ gap: 10px;
166
+ margin-top: 10px;
167
+ padding: 0 10px;
168
+ }
169
+
170
+ /* Table Overrides */
171
+ .data-table {
172
+ width: 100%;
173
+ }
174
+
175
+ :host ::ng-deep .ant-table {
176
+ background: #ffffff !important;
177
+ font-family: Poppins, Arial, sans-serif;
178
+ }
179
+
180
+ :host ::ng-deep .ant-table-bordered .ant-table-container {
181
+ border: 0 !important;
182
+ border-left: 1px solid #e5e7eb !important;
183
+ }
184
+
185
+ :host ::ng-deep .ant-table-thead>tr>th {
186
+ background: #fafafa !important;
187
+ color: #262626 !important;
188
+ font-family: Poppins, Arial, sans-serif;
189
+ font-size: 14px !important;
190
+ font-weight: 600 !important;
191
+ white-space: nowrap;
192
+ padding: 8px 12px !important;
193
+ border-bottom: 1px solid #e5e7eb !important;
194
+ vertical-align: middle !important;
195
+ text-align: center !important;
196
+ }
197
+
198
+ :host ::ng-deep .ant-table-thead>tr>th .ant-table-filter-column-title {
199
+ padding: 0 !important;
200
+ display: flex;
201
+ align-items: center;
202
+ }
203
+
204
+ :host ::ng-deep .ant-table-tbody>tr:not(.ant-table-measure-now)>td {
205
+ color: #333 !important;
206
+ font-size: 12px !important;
207
+ font-weight: 400 !important;
208
+ padding: 6px 12px !important;
209
+ vertical-align: middle !important;
210
+ border-bottom: 1px solid #e5e7eb !important;
211
+ }
212
+
213
+ :host ::ng-deep .ant-table-tbody>tr:not(.ant-table-measure-now):hover>td {
214
+ background: #fafcff !important;
215
+ }
216
+
217
+ :host ::ng-deep tr.ant-table-measure-now,
218
+ :host ::ng-deep tr.ant-table-measure-now>td,
219
+ :host ::ng-deep tr.ant-table-measure-now>td>div {
220
+ height: 0 !important;
221
+ padding: 0 !important;
222
+ margin: 0 !important;
223
+ border: 0 !important;
224
+ line-height: 0 !important;
225
+ font-size: 0 !important;
226
+ visibility: hidden !important;
227
+ }
228
+
229
+ /* Pagination */
230
+ :host ::ng-deep .ant-pagination {
231
+ margin-top: 12px !important;
232
+ display: flex;
233
+ justify-content: flex-end;
234
+ }
235
+
236
+ :host ::ng-deep .ant-pagination-item,
237
+ :host ::ng-deep .ant-pagination-prev,
238
+ :host ::ng-deep .ant-pagination-next,
239
+ :host ::ng-deep .ant-pagination-options {
240
+ font-size: 12px !important;
241
+ }
242
+
243
+ /* Status Tag */
244
+ .status-tag {
245
+ min-width: 80px;
246
+ text-align: center;
247
+ }
248
+
249
+ /* Responsive */
250
+ @media (max-width: 900px) {
251
+ .filter-panel__grid {
252
+ grid-template-columns: repeat(2, minmax(0, 1fr));
253
+ }
254
+ }
255
+
256
+ @media (max-width: 640px) {
257
+ .filter-panel__grid {
258
+ grid-template-columns: 1fr;
259
+ }
260
+
261
+ .table-toolbar__left,
262
+ .table-toolbar__right {
263
+ width: 100%;
264
+ justify-content: flex-start;
265
+ }
266
+
267
+ .table-toolbar__right {
268
+ margin-left: 0;
269
+ }
270
+
271
+ .search-field {
272
+ width: 100%;
273
+ }
274
+ }