diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts index 12b412f67..bbeba9e8a 100644 --- a/src-ui/src/app/app-routing.module.ts +++ b/src-ui/src/app/app-routing.module.ts @@ -26,6 +26,7 @@ import { MailComponent } from './components/manage/mail/mail.component' import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component' import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component' import { ConfigComponent } from './components/admin/config/config.component' +import { TrashComponent } from './components/admin/trash/trash.component' export const routes: Routes = [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, @@ -144,6 +145,14 @@ export const routes: Routes = [ requireAdmin: true, }, }, + { + path: 'trash', + component: TrashComponent, + canActivate: [PermissionsGuard], + data: { + requireAdmin: true, + }, + }, // redirect old paths { path: 'settings/mail', diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index f9e04b069..c4bbc177d 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -357,6 +357,7 @@ import localeSv from '@angular/common/locales/sv' import localeTr from '@angular/common/locales/tr' import localeUk from '@angular/common/locales/uk' import localeZh from '@angular/common/locales/zh' +import { TrashComponent } from './components/admin/trash/trash.component' registerLocaleData(localeAf) registerLocaleData(localeAr) @@ -497,6 +498,7 @@ function initializeApp(settings: SettingsService) { GlobalSearchComponent, HotkeyDialogComponent, DeletePagesConfirmDialogComponent, + TrashComponent, ], imports: [ BrowserModule, diff --git a/src-ui/src/app/components/admin/trash/trash.component.html b/src-ui/src/app/components/admin/trash/trash.component.html new file mode 100644 index 000000000..2a2040126 --- /dev/null +++ b/src-ui/src/app/components/admin/trash/trash.component.html @@ -0,0 +1,94 @@ + + + + + + +
+ +
+ +
+ + + + + + + + + + + @if (isLoading) { + + + + } + @for (object of trashedObjects; track object.id) { + + + + + + + } + +
+
+ + +
+
NameDeletedActions
+
+ Loading... +
+
+ + +
+
{{ object['name'] ?? object['title'] }}{{ object['deleted_at'] | customDate }} +
+
+ +
+ + +
+
+
+
+ + +
+
+
+ +@if (!isLoading) { +
+
+ {trashedObjects.length, plural, =1 {One object in trash} other {{{trashedObjects.length || 0}} total objects in trash}} + @if (selectedObjects.size > 0) { +  ({{selectedObjects.size}} selected) + } +
+ @if (trashedObjects.length > 20) { + + } +
+} diff --git a/src-ui/src/app/components/admin/trash/trash.component.scss b/src-ui/src/app/components/admin/trash/trash.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src-ui/src/app/components/admin/trash/trash.component.spec.ts b/src-ui/src/app/components/admin/trash/trash.component.spec.ts new file mode 100644 index 000000000..10e60e3e2 --- /dev/null +++ b/src-ui/src/app/components/admin/trash/trash.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' + +import { TrashComponent } from './trash.component' + +describe('TrashComponent', () => { + let component: TrashComponent + let fixture: ComponentFixture + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TrashComponent], + }).compileComponents() + + fixture = TestBed.createComponent(TrashComponent) + component = fixture.componentInstance + fixture.detectChanges() + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) +}) diff --git a/src-ui/src/app/components/admin/trash/trash.component.ts b/src-ui/src/app/components/admin/trash/trash.component.ts new file mode 100644 index 000000000..89eb92615 --- /dev/null +++ b/src-ui/src/app/components/admin/trash/trash.component.ts @@ -0,0 +1,87 @@ +import { HttpClient } from '@angular/common/http' +import { Component } from '@angular/core' +import { ObjectWithId } from 'src/app/data/object-with-id' +import { ToastService } from 'src/app/services/toast.service' +import { TrashService } from 'src/app/services/trash.service' +import { environment } from 'src/environments/environment' + +@Component({ + selector: 'pngx-trash', + templateUrl: './trash.component.html', + styleUrl: './trash.component.scss', +}) +export class TrashComponent { + public trashedObjects: ObjectWithId[] = [] + public selectedObjects: Set = new Set() + public togggleAll: boolean = false + public page: number = 1 + public isLoading: boolean = false + + constructor( + private trashService: TrashService, + private toastService: ToastService + ) { + this.reload() + } + + reload() { + this.isLoading = true + this.trashService.getTrash().subscribe((trash) => { + this.trashedObjects = trash + this.isLoading = false + console.log('Trash:', trash) + }) + } + + deleteObject(object: ObjectWithId) { + this.trashService.emptyTrash([object.id]).subscribe(() => { + this.toastService.showInfo($localize`Object deleted`) + this.reload() + }) + } + + emptyTrash(objects: Set = null) { + console.log('Emptying trash') + this.trashService + .emptyTrash(objects ? Array.from(objects) : []) + .subscribe(() => { + this.toastService.showInfo($localize`Object(s) deleted`) + this.reload() + }) + } + + restoreObject(object: ObjectWithId) { + this.trashService.restoreObjects([object.id]).subscribe(() => { + this.toastService.showInfo($localize`Object restored`) + this.reload() + }) + } + + restoreAll(objects: Set = null) { + this.trashService + .restoreObjects(objects ? Array.from(this.selectedObjects) : []) + .subscribe(() => { + this.toastService.showInfo($localize`Object(s) restored`) + this.reload() + }) + } + + toggleAll(event: PointerEvent) { + if ((event.target as HTMLInputElement).checked) { + this.selectedObjects = new Set(this.trashedObjects.map((t) => t.id)) + } else { + this.clearSelection() + } + } + + toggleSelected(object: ObjectWithId) { + this.selectedObjects.has(object.id) + ? this.selectedObjects.delete(object.id) + : this.selectedObjects.add(object.id) + } + + clearSelection() { + this.togggleAll = false + this.selectedObjects.clear() + } +} diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index ab5759ec0..3695a731c 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -267,6 +267,15 @@ } + + +