소스 검색

wokring search tool bar

Dr-Swopt 1 개월 전
부모
커밋
3761437e2b

+ 30 - 2
src/app/activity/activity.component.css

@@ -2,11 +2,27 @@
   padding: 1.5rem;
 }
 
+/* Toolbar layout */
 .toolbar {
   display: flex;
   justify-content: space-between;
   align-items: center;
   margin-bottom: 1.5rem;
+  flex-wrap: wrap;
+}
+
+/* Left section: title + search + refresh */
+.left-section {
+  display: flex;
+  align-items: center;
+  gap: 1rem;
+  flex-wrap: wrap;
+}
+
+.search-field {
+  width: 250px;
+  max-width: 400px;
+  margin-top: 10px;
 }
 
 table {
@@ -18,7 +34,6 @@ mat-progress-spinner {
   margin: 2rem auto;
 }
 
-/* activity.component.css */
 .clickable-row {
   cursor: pointer;
   transition: background-color 0.2s;
@@ -26,4 +41,17 @@ mat-progress-spinner {
 
 .clickable-row:hover {
   background-color: #f5f5f5;
-}
+}
+
+.spin {
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}

+ 69 - 45
src/app/activity/activity.component.html

@@ -1,49 +1,73 @@
-<div class="activity-container">
-  <div class="toolbar">
+<div class="toolbar">
+  <!-- Left side: Title + Search + Refresh -->
+  <div class="left-section">
     <h2>Activities</h2>
-    <button mat-flat-button color="primary" (click)="openCreateDialog()">
-      <mat-icon>add</mat-icon>
-      New Activity
+
+    <!-- 🔍 Search -->
+    <mat-form-field appearance="outline" class="search-field">
+      <mat-label>Search activities</mat-label>
+      <input type="text" matInput [formControl]="searchControl" [matAutocomplete]="auto"
+        placeholder="Type to search..." />
+
+      <!-- ❌ Clear button -->
+      <button *ngIf="searchControl.value" mat-icon-button matSuffix aria-label="Clear search" (click)="clearSearch()">
+        <mat-icon>close</mat-icon>
+      </button>
+
+      <mat-autocomplete #auto="matAutocomplete">
+        <mat-option *ngFor="let name of uniqueActivityNames" [value]="name">
+          {{ name }}
+        </mat-option>
+      </mat-autocomplete>
+    </mat-form-field>
+
+    <!-- 🔄 Refresh -->
+    <button mat-icon-button color="primary" (click)="refresh()" [disabled]="loading" [class.spin]="loading"
+      matTooltip="Reload activities">
+      <mat-icon>refresh</mat-icon>
     </button>
   </div>
 
-  <mat-progress-spinner *ngIf="loading" mode="indeterminate" diameter="48"></mat-progress-spinner>
-
-  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8" *ngIf="!loading">
-    <ng-container matColumnDef="name">
-      <th mat-header-cell *matHeaderCellDef>Name</th>
-      <td mat-cell *matCellDef="let row">{{ row.name }}</td>
-    </ng-container>
-
-    <ng-container matColumnDef="type">
-      <th mat-header-cell *matHeaderCellDef>Type</th>
-      <td mat-cell *matCellDef="let row">{{ row.type }}</td>
-    </ng-container>
-
-    <ng-container matColumnDef="dateStart">
-      <th mat-header-cell *matHeaderCellDef>Start</th>
-      <td mat-cell *matCellDef="let row">
-        {{ row.dateStart | date: 'shortDate' }}
-      </td>
-    </ng-container>
-
-    <ng-container matColumnDef="dateEnd">
-      <th mat-header-cell *matHeaderCellDef>End</th>
-      <td mat-cell *matCellDef="let row">
-        {{ row.dateEnd | date: 'shortDate' }}
-      </td>
-    </ng-container>
-
-    <ng-container matColumnDef="actions">
-      <th mat-header-cell *matHeaderCellDef></th>
-      <td mat-cell *matCellDef="let row">
-        <button mat-icon-button color="warn">
-          <mat-icon (click)="deleteActivity(row._id); $event.stopPropagation()">delete</mat-icon>
-        </button>
-      </td>
-    </ng-container>
-
-    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
-    <tr mat-row *matRowDef="let row; columns: displayedColumns" (click)="onRowClick(row)" class="clickable-row"></tr>
-  </table>
-</div>
+  <!-- Right side: Create Activity -->
+  <button mat-flat-button color="primary" (click)="openCreateDialog()">
+    <mat-icon>add_circle</mat-icon>
+    New Activity
+  </button>
+</div>
+
+<mat-progress-spinner *ngIf="loading" mode="indeterminate" diameter="48"></mat-progress-spinner>
+
+<!-- 🧾 Table -->
+<table mat-table [dataSource]="filteredActivities" class="mat-elevation-z8" *ngIf="!loading">
+  <ng-container matColumnDef="name">
+    <th mat-header-cell *matHeaderCellDef>Name</th>
+    <td mat-cell *matCellDef="let row">{{ row.name }}</td>
+  </ng-container>
+
+  <ng-container matColumnDef="type">
+    <th mat-header-cell *matHeaderCellDef>Type</th>
+    <td mat-cell *matCellDef="let row">{{ row.type }}</td>
+  </ng-container>
+
+  <ng-container matColumnDef="dateStart">
+    <th mat-header-cell *matHeaderCellDef>Start</th>
+    <td mat-cell *matCellDef="let row">{{ row.dateStart | date: 'shortDate' }}</td>
+  </ng-container>
+
+  <ng-container matColumnDef="dateEnd">
+    <th mat-header-cell *matHeaderCellDef>End</th>
+    <td mat-cell *matCellDef="let row">{{ row.dateEnd | date: 'shortDate' }}</td>
+  </ng-container>
+
+  <ng-container matColumnDef="actions">
+    <th mat-header-cell *matHeaderCellDef></th>
+    <td mat-cell *matCellDef="let row">
+      <button mat-icon-button color="warn">
+        <mat-icon (click)="deleteActivity(row._id); $event.stopPropagation()">delete</mat-icon>
+      </button>
+    </td>
+  </ng-container>
+
+  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+  <tr mat-row *matRowDef="let row; columns: displayedColumns" (click)="onRowClick(row)" class="clickable-row"></tr>
+</table>

+ 57 - 3
src/app/activity/activity.component.ts

@@ -8,6 +8,10 @@ import { MatIconModule } from '@angular/material/icon';
 import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
 import { webConfig } from '../config';
 import { CreateActivityDialogComponent } from '../components/activity-dialog/create-activity-dialog.component';
+import { MatInputModule } from '@angular/material/input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatAutocompleteModule } from '@angular/material/autocomplete';
+import { ReactiveFormsModule, FormControl } from '@angular/forms';
 
 @Component({
   selector: 'app-activity',
@@ -20,6 +24,10 @@ import { CreateActivityDialogComponent } from '../components/activity-dialog/cre
     MatTableModule,
     MatIconModule,
     MatProgressSpinnerModule,
+    MatFormFieldModule,
+    MatInputModule,
+    MatAutocompleteModule,
+    ReactiveFormsModule,
     CreateActivityDialogComponent,
   ],
   templateUrl: './activity.component.html',
@@ -28,6 +36,11 @@ import { CreateActivityDialogComponent } from '../components/activity-dialog/cre
 export class ActivityComponent implements OnInit {
   private http = inject(HttpClient);
   private dialog = inject(MatDialog);
+  searchControl = new FormControl('');
+  dataSource: any[] = [];
+  filteredActivities: any[] = [];
+  uniqueActivityNames: string[] = [];
+
 
   displayedColumns: string[] = [
     'name',
@@ -36,18 +49,51 @@ export class ActivityComponent implements OnInit {
     'dateEnd',
     'actions',
   ];
-  dataSource: any[] = [];
   loading = false;
 
   ngOnInit() {
     this.loadActivities();
+
+    this.searchControl.valueChanges.subscribe((value) => {
+      const search = value?.toLowerCase() || '';
+
+      // Filter for table
+      this.filteredActivities = this.dataSource.filter((activity) =>
+        activity.name.toLowerCase().includes(search)
+      );
+
+      // Extract unique names for the dropdown
+      this.uniqueActivityNames = [
+        ...new Set(
+          this.dataSource
+            .filter((activity) =>
+              activity.name.toLowerCase().includes(search)
+            )
+            .map((a) => a.name)
+        ),
+      ];
+    });
+  }
+
+  refresh() {
+    this.loadActivities();
+  }
+
+  clearSearch() {
+    this.searchControl.setValue('');
+    this.filteredActivities = [...this.dataSource];
   }
 
   loadActivities() {
     this.loading = true;
-    this.http.get(`${webConfig.exposedUrl}/api/activity`).subscribe({
-      next: (data: any) => {
+    this.http.get<any[]>(`${webConfig.exposedUrl}/api/activity`).subscribe({
+      next: (data) => {
         this.dataSource = data;
+        this.filteredActivities = [...data];
+
+        // ✅ Extract unique names for the autocomplete dropdown
+        this.uniqueActivityNames = [...new Set(data.map(a => a.name))];
+
         this.loading = false;
       },
       error: (err) => {
@@ -57,6 +103,14 @@ export class ActivityComponent implements OnInit {
     });
   }
 
+
+  applyFilter(value: string) {
+    const filterValue = value.toLowerCase();
+    this.filteredActivities = this.dataSource.filter((activity) =>
+      activity.name.toLowerCase().includes(filterValue)
+    );
+  }
+
   onRowClick(row: any): void {
     console.log('Clicked row:', row);
     this.openEditDialog(row);

+ 6 - 0
src/app/components/activity-dialog/create-activity-dialog.component.css

@@ -56,3 +56,9 @@
   gap: 8px;
   margin-top: 16px;
 }
+
+/* create-activity-dialog.component.css */
+button[color='warn'] {
+  background-color: #d32f2f; /* Stronger red */
+  color: #fff;
+}

+ 6 - 0
src/app/components/activity-dialog/create-activity-dialog.component.html

@@ -177,8 +177,14 @@
   <!-- Footer -->
   <div class="dialog-footer">
     <button mat-stroked-button type="button" (click)="cancel()">Cancel</button>
+
+    <button *ngIf="data" mat-flat-button color="warn" type="button" (click)="onDelete()">
+      Delete
+    </button>
+
     <button mat-flat-button color="primary" type="submit">
       {{ data ? 'Update' : 'Save' }}
     </button>
   </div>
+
 </form>

+ 18 - 0
src/app/components/activity-dialog/create-activity-dialog.component.ts

@@ -190,6 +190,24 @@ export class CreateActivityDialogComponent {
     array.removeAt(index);
   }
 
+  onDelete() {
+    if (!this.data?._id) return;
+
+    const confirmed = confirm('Are you sure you want to delete this activity?');
+    if (!confirmed) return;
+
+    this.http.delete(`${webConfig.exposedUrl}/api/activity/${this.data._id}`).subscribe({
+      next: () => {
+        alert('Activity deleted successfully.');
+        this.dialogRef.close('refresh');
+      },
+      error: (err) => {
+        console.error('Failed to delete activity:', err);
+        alert('Failed to delete activity. Please try again.');
+      },
+    });
+  }
+
   onSubmit() {
     if (this.form.invalid) return;
 

+ 1 - 1
src/app/config.ts

@@ -1,3 +1,3 @@
 export const webConfig = {
-    exposedUrl: `https://localhost:3000`,
+    exposedUrl: `https://192.168.100.100:3000`,
 }