delete
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
<div class="delete-dialog-icon">
|
||||||
|
<mat-icon aria-hidden="true">delete</mat-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 mat-dialog-title>Liste loeschen?</h2>
|
||||||
|
|
||||||
|
<mat-dialog-content>
|
||||||
|
<p>
|
||||||
|
<strong>{{ data.listName }}</strong> wird geloescht und ist danach nicht
|
||||||
|
mehr in deinen Listen sichtbar.
|
||||||
|
</p>
|
||||||
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button type="button" mat-dialog-close>Abbrechen</button>
|
||||||
|
<button mat-flat-button type="button" color="warn" [mat-dialog-close]="true">
|
||||||
|
<mat-icon aria-hidden="true">delete</mat-icon>
|
||||||
|
Loeschen
|
||||||
|
</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
|
export interface ConfirmDeleteListDialogData {
|
||||||
|
listName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-confirm-delete-list-dialog',
|
||||||
|
imports: [MatButtonModule, MatDialogModule, MatIconModule],
|
||||||
|
templateUrl: './confirm-delete-list-dialog.component.html',
|
||||||
|
})
|
||||||
|
export class ConfirmDeleteListDialogComponent {
|
||||||
|
protected readonly data = inject<ConfirmDeleteListDialogData>(MAT_DIALOG_DATA);
|
||||||
|
}
|
||||||
@@ -61,6 +61,23 @@
|
|||||||
<mat-icon aria-hidden="true">{{ showEditor() ? 'close' : 'edit' }}</mat-icon>
|
<mat-icon aria-hidden="true">{{ showEditor() ? 'close' : 'edit' }}</mat-icon>
|
||||||
{{ showEditor() ? 'Abbrechen' : 'Bearbeiten' }}
|
{{ showEditor() ? 'Abbrechen' : 'Bearbeiten' }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
@if (canDeleteList()) {
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
type="button"
|
||||||
|
color="warn"
|
||||||
|
[disabled]="deletingList()"
|
||||||
|
(click)="deleteList()"
|
||||||
|
>
|
||||||
|
@if (deletingList()) {
|
||||||
|
<mat-progress-spinner mode="indeterminate" diameter="18" />
|
||||||
|
} @else {
|
||||||
|
<mat-icon aria-hidden="true">delete</mat-icon>
|
||||||
|
}
|
||||||
|
Loeschen
|
||||||
|
</button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { finalize } from 'rxjs';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
@@ -16,6 +17,7 @@ import { AuthService } from '../../auth/auth.service';
|
|||||||
import { getAuthErrorMessage } from '../../auth/error-message';
|
import { getAuthErrorMessage } from '../../auth/error-message';
|
||||||
import { PublicUserSearchResult } from '../../auth/auth.models';
|
import { PublicUserSearchResult } from '../../auth/auth.models';
|
||||||
import { OnboardingService } from '../../onboarding/onboarding.service';
|
import { OnboardingService } from '../../onboarding/onboarding.service';
|
||||||
|
import { ConfirmDeleteListDialogComponent } from '../confirm-delete-list-dialog/confirm-delete-list-dialog.component';
|
||||||
import {
|
import {
|
||||||
ListItemSuggestion,
|
ListItemSuggestion,
|
||||||
ListRealtimeEvent,
|
ListRealtimeEvent,
|
||||||
@@ -34,6 +36,7 @@ import { ListsService } from '../lists.service';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
|
MatDialogModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
@@ -47,6 +50,7 @@ export class ListDetailComponent implements OnInit {
|
|||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
private readonly formBuilder = inject(NonNullableFormBuilder);
|
private readonly formBuilder = inject(NonNullableFormBuilder);
|
||||||
private readonly authService = inject(AuthService);
|
private readonly authService = inject(AuthService);
|
||||||
|
private readonly dialog = inject(MatDialog);
|
||||||
private readonly listsService = inject(ListsService);
|
private readonly listsService = inject(ListsService);
|
||||||
private readonly listsRealtimeService = inject(ListsRealtimeService);
|
private readonly listsRealtimeService = inject(ListsRealtimeService);
|
||||||
private readonly route = inject(ActivatedRoute);
|
private readonly route = inject(ActivatedRoute);
|
||||||
@@ -59,6 +63,7 @@ export class ListDetailComponent implements OnInit {
|
|||||||
protected readonly loading = signal(true);
|
protected readonly loading = signal(true);
|
||||||
protected readonly saving = signal(false);
|
protected readonly saving = signal(false);
|
||||||
protected readonly creatingTemplate = signal(false);
|
protected readonly creatingTemplate = signal(false);
|
||||||
|
protected readonly deletingList = signal(false);
|
||||||
protected readonly editing = signal(false);
|
protected readonly editing = signal(false);
|
||||||
protected readonly addingItem = signal(false);
|
protected readonly addingItem = signal(false);
|
||||||
protected readonly loadingSuggestions = signal(false);
|
protected readonly loadingSuggestions = signal(false);
|
||||||
@@ -76,6 +81,9 @@ export class ListDetailComponent implements OnInit {
|
|||||||
protected readonly canManageShares = computed(
|
protected readonly canManageShares = computed(
|
||||||
() => this.list()?.accessRole === 'owner' && !this.isCreateMode(),
|
() => this.list()?.accessRole === 'owner' && !this.isCreateMode(),
|
||||||
);
|
);
|
||||||
|
protected readonly canDeleteList = computed(
|
||||||
|
() => this.list()?.accessRole === 'owner' && !this.isCreateMode(),
|
||||||
|
);
|
||||||
protected readonly showShareControls = computed(
|
protected readonly showShareControls = computed(
|
||||||
() => this.canManageShares() && this.showEditor(),
|
() => this.canManageShares() && this.showEditor(),
|
||||||
);
|
);
|
||||||
@@ -233,6 +241,48 @@ export class ListDetailComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected deleteList(): void {
|
||||||
|
const listId = this.listId();
|
||||||
|
const list = this.list();
|
||||||
|
|
||||||
|
if (!listId || !list || !this.canDeleteList() || this.deletingList()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialog
|
||||||
|
.open<ConfirmDeleteListDialogComponent, { listName: string }, boolean>(
|
||||||
|
ConfirmDeleteListDialogComponent,
|
||||||
|
{
|
||||||
|
data: { listName: list.name },
|
||||||
|
maxWidth: '420px',
|
||||||
|
width: 'calc(100vw - 32px)',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe((confirmed) => {
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deletingList.set(true);
|
||||||
|
|
||||||
|
this.listsService
|
||||||
|
.deleteList(listId)
|
||||||
|
.pipe(finalize(() => this.deletingList.set(false)))
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.snackBar.open('Liste geloescht.', 'OK', { duration: 3000 });
|
||||||
|
void this.router.navigateByUrl('/lists');
|
||||||
|
},
|
||||||
|
error: (error: unknown) => {
|
||||||
|
this.snackBar.open(getAuthErrorMessage(error), 'OK', {
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected loadSuggestions(): void {
|
protected loadSuggestions(): void {
|
||||||
const listId = this.listId();
|
const listId = this.listId();
|
||||||
|
|
||||||
|
|||||||
@@ -167,6 +167,23 @@
|
|||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
<mat-card-actions align="end">
|
<mat-card-actions align="end">
|
||||||
|
@if (list.accessRole === 'owner') {
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
type="button"
|
||||||
|
color="warn"
|
||||||
|
[disabled]="deletingListId() === list.id"
|
||||||
|
(click)="deleteList(list)"
|
||||||
|
>
|
||||||
|
@if (deletingListId() === list.id) {
|
||||||
|
<mat-progress-spinner mode="indeterminate" diameter="18" />
|
||||||
|
} @else {
|
||||||
|
<mat-icon aria-hidden="true">delete</mat-icon>
|
||||||
|
}
|
||||||
|
Loeschen
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
<a
|
<a
|
||||||
mat-button
|
mat-button
|
||||||
[routerLink]="['/lists', list.id]"
|
[routerLink]="['/lists', list.id]"
|
||||||
|
|||||||
@@ -2,16 +2,20 @@ import { DatePipe } from '@angular/common';
|
|||||||
import { Component, DestroyRef, OnInit, computed, inject, signal } from '@angular/core';
|
import { Component, DestroyRef, OnInit, computed, inject, signal } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { finalize } from 'rxjs';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
import { getAuthErrorMessage } from '../auth/error-message';
|
import { getAuthErrorMessage } from '../auth/error-message';
|
||||||
import { OnboardingService } from '../onboarding/onboarding.service';
|
import { OnboardingService } from '../onboarding/onboarding.service';
|
||||||
|
import { ConfirmDeleteListDialogComponent } from './confirm-delete-list-dialog/confirm-delete-list-dialog.component';
|
||||||
import { ListTemplateKind } from '../templates/templates.models';
|
import { ListTemplateKind } from '../templates/templates.models';
|
||||||
import { ListRealtimeEvent, UserList, UserListItem } from './lists.models';
|
import { ListRealtimeEvent, UserList, UserListItem } from './lists.models';
|
||||||
import { ListsRealtimeService } from './lists-realtime.service';
|
import { ListsRealtimeService } from './lists-realtime.service';
|
||||||
@@ -34,23 +38,28 @@ type ListKindFilter = ListTemplateKind | 'all';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatButtonToggleModule,
|
MatButtonToggleModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
|
MatDialogModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
|
MatSnackBarModule,
|
||||||
],
|
],
|
||||||
templateUrl: './lists.component.html',
|
templateUrl: './lists.component.html',
|
||||||
styleUrls: ['../workspace-page.scss', './lists.component.scss'],
|
styleUrls: ['../workspace-page.scss', './lists.component.scss'],
|
||||||
})
|
})
|
||||||
export class ListsComponent implements OnInit {
|
export class ListsComponent implements OnInit {
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
private readonly dialog = inject(MatDialog);
|
||||||
private readonly listsService = inject(ListsService);
|
private readonly listsService = inject(ListsService);
|
||||||
private readonly listsRealtimeService = inject(ListsRealtimeService);
|
private readonly listsRealtimeService = inject(ListsRealtimeService);
|
||||||
|
private readonly snackBar = inject(MatSnackBar);
|
||||||
protected readonly onboarding = inject(OnboardingService);
|
protected readonly onboarding = inject(OnboardingService);
|
||||||
|
|
||||||
protected readonly lists = signal<UserList[]>([]);
|
protected readonly lists = signal<UserList[]>([]);
|
||||||
protected readonly loading = signal(true);
|
protected readonly loading = signal(true);
|
||||||
|
protected readonly deletingListId = signal<string | null>(null);
|
||||||
protected readonly errorMessage = signal<string | null>(null);
|
protected readonly errorMessage = signal<string | null>(null);
|
||||||
protected readonly searchTerm = signal('');
|
protected readonly searchTerm = signal('');
|
||||||
protected readonly kindFilter = signal<ListKindFilter>('all');
|
protected readonly kindFilter = signal<ListKindFilter>('all');
|
||||||
@@ -171,6 +180,47 @@ export class ListsComponent implements OnInit {
|
|||||||
this.sortOption.set('updated-desc');
|
this.sortOption.set('updated-desc');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected deleteList(list: UserList): void {
|
||||||
|
if (list.accessRole !== 'owner' || this.deletingListId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialog
|
||||||
|
.open<ConfirmDeleteListDialogComponent, { listName: string }, boolean>(
|
||||||
|
ConfirmDeleteListDialogComponent,
|
||||||
|
{
|
||||||
|
data: { listName: list.name },
|
||||||
|
maxWidth: '420px',
|
||||||
|
width: 'calc(100vw - 32px)',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe((confirmed) => {
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deletingListId.set(list.id);
|
||||||
|
|
||||||
|
this.listsService
|
||||||
|
.deleteList(list.id)
|
||||||
|
.pipe(finalize(() => this.deletingListId.set(null)))
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.lists.update((lists) =>
|
||||||
|
lists.filter((existingList) => existingList.id !== list.id),
|
||||||
|
);
|
||||||
|
this.snackBar.open('Liste geloescht.', 'OK', { duration: 3000 });
|
||||||
|
},
|
||||||
|
error: (error: unknown) => {
|
||||||
|
this.snackBar.open(getAuthErrorMessage(error), 'OK', {
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private subscribeToRealtime(): void {
|
private subscribeToRealtime(): void {
|
||||||
this.listsRealtimeService
|
this.listsRealtimeService
|
||||||
.events()
|
.events()
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ export class ListsService {
|
|||||||
return this.http.patch<UserList>(`${this.apiUrl}/${listId}`, data);
|
return this.http.patch<UserList>(`${this.apiUrl}/${listId}`, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteList(listId: string): Observable<{ message: string }> {
|
||||||
|
return this.http.delete<{ message: string }>(`${this.apiUrl}/${listId}`);
|
||||||
|
}
|
||||||
|
|
||||||
shareList(listId: string, userId: string): Observable<UserList> {
|
shareList(listId: string, userId: string): Observable<UserList> {
|
||||||
return this.http.post<UserList>(`${this.apiUrl}/${listId}/shares`, { userId });
|
return this.http.post<UserList>(`${this.apiUrl}/${listId}/shares`, { userId });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user