MME-8 Add basic editing action for image data to main image select grid, WIP.
This commit is contained in:
parent
9b90e1033e
commit
76ed65a144
@ -0,0 +1,5 @@
|
|||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
row-gap: 10px;
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<app-dialog-container title="Edit Image">
|
||||||
|
<form (submit)="onSubmit($event)">
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Filename</mat-label>
|
||||||
|
<input matInput [formControl]="imageForm.controls.filename" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Alt</mat-label>
|
||||||
|
<input matInput [formControl]="imageForm.controls.alt" />
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Caption</mat-label>
|
||||||
|
<input matInput [formControl]="imageForm.controls.caption" />
|
||||||
|
</mat-form-field>
|
||||||
|
<button type="submit" matButton="filled">Submit</button>
|
||||||
|
</form>
|
||||||
|
</app-dialog-container>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EditImageDialog } from './edit-image-dialog';
|
||||||
|
|
||||||
|
describe('EditImageDialog', () => {
|
||||||
|
let component: EditImageDialog;
|
||||||
|
let fixture: ComponentFixture<EditImageDialog>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [EditImageDialog],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(EditImageDialog);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { Component, inject, input, OnInit } from '@angular/core';
|
||||||
|
import { DialogContainer } from '../../../../../../shared/components/dialog-container/dialog-container';
|
||||||
|
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
||||||
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { ImageView } from '../../../../../../shared/models/ImageView.model';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { MatButton } from '@angular/material/button';
|
||||||
|
import { ImageService } from '../../../../../../shared/services/ImageService';
|
||||||
|
import { QueryClient } from '@tanstack/angular-query-experimental';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-edit-image-dialog',
|
||||||
|
imports: [DialogContainer, MatFormField, MatLabel, MatInput, ReactiveFormsModule, MatButton],
|
||||||
|
templateUrl: './edit-image-dialog.html',
|
||||||
|
styleUrl: './edit-image-dialog.css',
|
||||||
|
})
|
||||||
|
export class EditImageDialog implements OnInit {
|
||||||
|
protected readonly imageView: ImageView = inject(MAT_DIALOG_DATA);
|
||||||
|
private readonly imageService = inject(ImageService);
|
||||||
|
private readonly queryClient = inject(QueryClient);
|
||||||
|
private readonly dialogRef = inject(MatDialogRef);
|
||||||
|
|
||||||
|
protected readonly imageForm = new FormGroup({
|
||||||
|
filename: new FormControl(
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
Validators.required,
|
||||||
|
),
|
||||||
|
alt: new FormControl(''),
|
||||||
|
caption: new FormControl(''),
|
||||||
|
});
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.imageForm.patchValue({
|
||||||
|
filename: this.imageView.filename,
|
||||||
|
alt: this.imageView.alt,
|
||||||
|
caption: this.imageView.alt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onSubmit(event: SubmitEvent): Promise<void> {
|
||||||
|
event.preventDefault();
|
||||||
|
await this.imageService.updateImage(this.imageView.owner.username, this.imageView.filename, {
|
||||||
|
alt: this.imageForm.value.alt,
|
||||||
|
caption: this.imageForm.value.caption,
|
||||||
|
});
|
||||||
|
await this.queryClient.invalidateQueries({
|
||||||
|
queryKey: ['image-views'],
|
||||||
|
});
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,3 +13,8 @@
|
|||||||
mat-card-content p {
|
mat-card-content p {
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mat-card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|||||||
@ -26,7 +26,17 @@
|
|||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
[checked]="isSelected(imageData!.imageView)"
|
[checked]="isSelected(imageData!.imageView)"
|
||||||
(click)="onImageClick(imageData!.imageView)"
|
(click)="onImageClick(imageData!.imageView)"
|
||||||
>Main image</mat-checkbox>
|
>Main?</mat-checkbox
|
||||||
|
>
|
||||||
|
<button matButton="text" type="button" [matMenuTriggerFor]="imageActionsMenu">
|
||||||
|
<fa-icon [icon]="faEllipsis"></fa-icon>
|
||||||
|
</button>
|
||||||
|
<mat-menu #imageActionsMenu>
|
||||||
|
<button mat-menu-item type="button" (click)="editImage(imageData!.imageView)">Edit</button>
|
||||||
|
<button mat-menu-item type="button" (click)="deleteImage(imageData!.imageView)">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,35 @@
|
|||||||
import { Component, computed, inject, input, output, signal } from '@angular/core';
|
import { Component, computed, inject, input, output, signal } from '@angular/core';
|
||||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
import { ImageService } from '../../../../../shared/services/ImageService';
|
import { ImageService } from '../../../../../shared/services/ImageService';
|
||||||
import { injectQuery, keepPreviousData } from '@tanstack/angular-query-experimental';
|
import { injectQuery, keepPreviousData, QueryClient } from '@tanstack/angular-query-experimental';
|
||||||
import { Spinner } from '../../../../../shared/components/spinner/spinner';
|
import { Spinner } from '../../../../../shared/components/spinner/spinner';
|
||||||
import { ImageView } from '../../../../../shared/models/ImageView.model';
|
import { ImageView } from '../../../../../shared/models/ImageView.model';
|
||||||
import { injectQueries } from '@tanstack/angular-query-experimental/inject-queries-experimental';
|
import { injectQueries } from '@tanstack/angular-query-experimental/inject-queries-experimental';
|
||||||
import { MatCard, MatCardActions, MatCardContent, MatCardImage } from '@angular/material/card';
|
import { MatCard, MatCardActions, MatCardContent, MatCardImage } from '@angular/material/card';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||||
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
|
import { faEllipsis } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { MatButton } from '@angular/material/button';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { EditImageDialog } from './edit-image-dialog/edit-image-dialog';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-image-select',
|
selector: 'app-image-select',
|
||||||
imports: [MatPaginator, Spinner, MatCard, MatCardImage, MatCardContent, MatCardActions, MatCheckbox],
|
imports: [
|
||||||
|
MatPaginator,
|
||||||
|
Spinner,
|
||||||
|
MatCard,
|
||||||
|
MatCardImage,
|
||||||
|
MatCardContent,
|
||||||
|
MatCardActions,
|
||||||
|
MatCheckbox,
|
||||||
|
MatMenuTrigger,
|
||||||
|
MatMenu,
|
||||||
|
FaIconComponent,
|
||||||
|
MatButton,
|
||||||
|
MatMenuItem,
|
||||||
|
],
|
||||||
templateUrl: './image-select.html',
|
templateUrl: './image-select.html',
|
||||||
styleUrl: './image-select.css',
|
styleUrl: './image-select.css',
|
||||||
})
|
})
|
||||||
@ -22,6 +41,8 @@ export class ImageSelect {
|
|||||||
protected readonly pageSize = signal(9);
|
protected readonly pageSize = signal(9);
|
||||||
|
|
||||||
private readonly imageService = inject(ImageService);
|
private readonly imageService = inject(ImageService);
|
||||||
|
private readonly dialog = inject(MatDialog);
|
||||||
|
private readonly queryClient = inject(QueryClient);
|
||||||
|
|
||||||
protected readonly imageViewsQuery = injectQuery(() => ({
|
protected readonly imageViewsQuery = injectQuery(() => ({
|
||||||
queryKey: ['image-views', this.currentPage(), this.pageSize()],
|
queryKey: ['image-views', this.currentPage(), this.pageSize()],
|
||||||
@ -90,4 +111,22 @@ export class ImageSelect {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected editImage(imageView: ImageView): void {
|
||||||
|
this.dialog.open(EditImageDialog, {
|
||||||
|
data: imageView,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async deleteImage(imageView: ImageView): Promise<void> {
|
||||||
|
await this.imageService.deleteImage(imageView.owner.username, imageView.filename);
|
||||||
|
await this.queryClient.invalidateQueries({
|
||||||
|
queryKey: ['image-views'],
|
||||||
|
});
|
||||||
|
await this.queryClient.invalidateQueries({
|
||||||
|
queryKey: ['images'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly faEllipsis = faEllipsis;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,4 +81,27 @@ export class ImageService {
|
|||||||
.pipe(map((view) => view.exists)),
|
.pipe(map((view) => view.exists)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deleteImage(username: string, filename: string): Promise<void> {
|
||||||
|
return firstValueFrom(
|
||||||
|
this.httpClient.delete<void>(this.endpointService.getUrl('images', [username, filename])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateImage(
|
||||||
|
username: string,
|
||||||
|
filename: string,
|
||||||
|
data: {
|
||||||
|
alt?: string | null;
|
||||||
|
caption?: string | null;
|
||||||
|
isPublic?: boolean | null;
|
||||||
|
viewersToAdd?: string[] | null;
|
||||||
|
viewersToRemove?: string[] | null;
|
||||||
|
clearAllViewers?: boolean | null;
|
||||||
|
},
|
||||||
|
): Promise<ImageView> {
|
||||||
|
return firstValueFrom(
|
||||||
|
this.httpClient.put<ImageView>(this.endpointService.getUrl('images', [username, filename]), data),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user