From 40911460896983741f76b1dffc61b3f6d4e9d929 Mon Sep 17 00:00:00 2001 From: Gui Seek Date: Sat, 28 Dec 2024 13:01:57 -0300 Subject: [PATCH 1/4] =?UTF-8?q?refactor(album):=20adiciona=20m=C3=A9todo?= =?UTF-8?q?=20update=20tags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastrucure/photo.http.service.impl.ts | 5 ++ .../data-source/src/lib/dtos/create-photo.ts | 11 +++-- .../album/data-source/src/lib/dtos/photo.ts | 11 +++-- .../albums.mongo.service.impl.ts | 5 +- .../photos.mongo.service.impl.ts | 12 ++--- .../data-source/src/lib/schemas/photo.ts | 17 ++++--- .../data-source/src/lib/schemas/user-tag.ts | 11 +++++ .../album/data-source/src/lib/utils/index.ts | 1 + .../data-source/src/lib/utils/to-base64.ts | 3 ++ packages/album/domain/package.json | 3 +- .../album/domain/src/client/services/photo.ts | 4 +- .../src/client/use-cases/create-album.ts | 5 ++ .../src/client/use-cases/create-photo.ts | 5 ++ .../src/client/use-cases/delete-album.ts | 5 ++ .../src/client/use-cases/delete-photo.ts | 5 ++ .../src/client/use-cases/find-album-by-id.ts | 5 ++ .../src/client/use-cases/find-albums.ts | 5 ++ .../src/client/use-cases/find-photo-by-id.ts | 5 ++ .../src/client/use-cases/find-photos.ts | 5 ++ .../domain/src/client/use-cases/index.ts | 23 +++++----- .../src/client/use-cases/update-album.ts | 5 ++ .../src/client/use-cases/update-photo-tags.ts | 15 ++++++ .../src/client/use-cases/update-photo.ts | 5 ++ .../src/client/use-cases/upload-photo.ts | 5 ++ .../resource/src/lib/controllers/photos.ts | 46 ++++++++++++------- 25 files changed, 171 insertions(+), 51 deletions(-) create mode 100644 packages/album/data-source/src/lib/schemas/user-tag.ts create mode 100644 packages/album/data-source/src/lib/utils/index.ts create mode 100644 packages/album/data-source/src/lib/utils/to-base64.ts create mode 100644 packages/album/domain/src/client/use-cases/update-photo-tags.ts diff --git a/packages/album/data-access/src/lib/infrastrucure/photo.http.service.impl.ts b/packages/album/data-access/src/lib/infrastrucure/photo.http.service.impl.ts index 7d8b8159..8d16ee18 100644 --- a/packages/album/data-access/src/lib/infrastrucure/photo.http.service.impl.ts +++ b/packages/album/data-access/src/lib/infrastrucure/photo.http.service.impl.ts @@ -7,6 +7,11 @@ export class PhotoHttpServiceImpl extends HttpService implements PhotoService { + updateTags(id: string, data: Photo) { + const url = [this.url, id, 'tags']; + return this.http.patch(url.join('/'), data); + } + upload({ photo, ...value }: UploadPhoto) { const data = new FormData(); diff --git a/packages/album/data-source/src/lib/dtos/create-photo.ts b/packages/album/data-source/src/lib/dtos/create-photo.ts index c7af1947..a6fdf45b 100644 --- a/packages/album/data-source/src/lib/dtos/create-photo.ts +++ b/packages/album/data-source/src/lib/dtos/create-photo.ts @@ -1,7 +1,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsNumber, IsOptional, IsString } from 'class-validator'; -import { UserRef } from '@devmx/shared-api-interfaces'; -import { UserRefDto } from '@devmx/shared-data-source'; +import { UserTag } from '@devmx/shared-api-interfaces'; +import { UserTagDto } from '@devmx/shared-data-source'; import { CreatePhoto } from '@devmx/album-domain'; import { Exclude, Type } from 'class-transformer'; @@ -14,7 +14,8 @@ export class CreatePhotoDto implements CreatePhoto { caption?: string; @IsString() - @ApiProperty() + @IsOptional() + @ApiPropertyOptional() album: string; @IsNumber() @@ -35,8 +36,8 @@ export class CreatePhotoDto implements CreatePhoto { @ApiPropertyOptional() type: string; - @Type(() => UserRefDto) - tagged?: UserRef[]; + @Type(() => UserTagDto) + tags?: UserTag[]; owner: string; } diff --git a/packages/album/data-source/src/lib/dtos/photo.ts b/packages/album/data-source/src/lib/dtos/photo.ts index 7ca04b36..d340fe0b 100644 --- a/packages/album/data-source/src/lib/dtos/photo.ts +++ b/packages/album/data-source/src/lib/dtos/photo.ts @@ -1,6 +1,6 @@ +import { AlbumRefDto, UserRefDto, UserTagDto } from '@devmx/shared-data-source'; import { ImageMimeType, Photo } from '@devmx/shared-api-interfaces'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { AlbumRefDto, UserRefDto } from '@devmx/shared-data-source'; import { Exclude, Type } from 'class-transformer'; export class PhotoDto implements Photo { @@ -25,9 +25,12 @@ export class PhotoDto implements Photo { @Exclude() content: Buffer; - @ApiPropertyOptional({ type: () => [UserRefDto] }) - @Type(() => UserRefDto) - tagged?: UserRefDto[]; + @Exclude() + tagged: UserRefDto[]; + + @ApiProperty({ type: () => [UserTagDto] }) + @Type(() => UserTagDto) + tags: UserTagDto[] = []; @ApiProperty({ type: () => Date }) @Type(() => Date) diff --git a/packages/album/data-source/src/lib/infrastructure/albums.mongo.service.impl.ts b/packages/album/data-source/src/lib/infrastructure/albums.mongo.service.impl.ts index fecd97ac..4aad0cdd 100644 --- a/packages/album/data-source/src/lib/infrastructure/albums.mongo.service.impl.ts +++ b/packages/album/data-source/src/lib/infrastructure/albums.mongo.service.impl.ts @@ -21,7 +21,10 @@ export class AlbumsMongoServiceImpl .populate('contributors', 'name displayName'); if (method === 'findOne') { - query = query.populate('photos', 'type content caption createdAt'); + query = query.populate( + 'photos', + 'type content caption width height tags createdAt' + ); } else { query = query.select('-photos'); } diff --git a/packages/album/data-source/src/lib/infrastructure/photos.mongo.service.impl.ts b/packages/album/data-source/src/lib/infrastructure/photos.mongo.service.impl.ts index 10e748a8..b66f84c2 100644 --- a/packages/album/data-source/src/lib/infrastructure/photos.mongo.service.impl.ts +++ b/packages/album/data-source/src/lib/infrastructure/photos.mongo.service.impl.ts @@ -12,22 +12,22 @@ export class PhotosMongoServiceImpl protected override applyPopulate(query: Query) { return query .populate('owner', 'name displayName') - .populate('tagged', 'name displayName profile') - .populate('album', 'title createdAt'); + .populate('album', 'title createdAt') + .populate('tags'); } protected override applyEditableParser( data: EditableEntity ): U { - const tagged = (data.tagged ?? []).map((p) => { - return typeof p === 'string' ? p : p.id; - }); + // const tagged = (data.tags ?? []).map((p) => { + // return typeof p === 'string' ? p : p.id; + // }); const album = typeof data.album === 'string' ? data.album : data.album.id; const owner = typeof data.owner === 'string' ? data.owner : data.owner.id; - return { ...data, album, owner, tagged } as U; + return { ...data, album, owner } as U; } } diff --git a/packages/album/data-source/src/lib/schemas/photo.ts b/packages/album/data-source/src/lib/schemas/photo.ts index 87b78e58..05832d64 100644 --- a/packages/album/data-source/src/lib/schemas/photo.ts +++ b/packages/album/data-source/src/lib/schemas/photo.ts @@ -1,12 +1,16 @@ -import { Album, ImageMimeType, Photo } from '@devmx/shared-api-interfaces'; import { UserCollection } from '@devmx/account-data-source'; import { createSchema } from '@devmx/shared-data-source'; import { Prop, Schema } from '@nestjs/mongoose'; import mongoose, { Document } from 'mongoose'; +import { userTag } from './user-tag'; +import { toBase64 } from '../utils'; +import { + Album, + Photo, + UserTag, + ImageMimeType, +} from '@devmx/shared-api-interfaces'; -function toBase64(type: string, content: string) { - return `data:${type};base64,${content}`; -} @Schema({ timestamps: { createdAt: true } }) export class PhotoCollection extends Document implements Photo { @@ -49,9 +53,10 @@ export class PhotoCollection extends Document implements Photo { caption?: string; @Prop({ - type: [{ type: mongoose.Schema.Types.ObjectId, ref: UserCollection.name }], + type: [userTag], + default: [], }) - tagged: UserCollection[]; + tags?: UserTag[]; @Prop({ type: mongoose.Schema.Types.ObjectId, diff --git a/packages/album/data-source/src/lib/schemas/user-tag.ts b/packages/album/data-source/src/lib/schemas/user-tag.ts new file mode 100644 index 00000000..66995598 --- /dev/null +++ b/packages/album/data-source/src/lib/schemas/user-tag.ts @@ -0,0 +1,11 @@ +import mongoose from 'mongoose'; + +export const userTag = { + x: Number, + y: Number, + user: { + id: mongoose.Schema.Types.ObjectId, + displayName: String, + name: String, + }, +}; diff --git a/packages/album/data-source/src/lib/utils/index.ts b/packages/album/data-source/src/lib/utils/index.ts new file mode 100644 index 00000000..f3fe321f --- /dev/null +++ b/packages/album/data-source/src/lib/utils/index.ts @@ -0,0 +1 @@ +export * from './to-base64'; diff --git a/packages/album/data-source/src/lib/utils/to-base64.ts b/packages/album/data-source/src/lib/utils/to-base64.ts new file mode 100644 index 00000000..746849a5 --- /dev/null +++ b/packages/album/data-source/src/lib/utils/to-base64.ts @@ -0,0 +1,3 @@ +export function toBase64(type: string, content: string) { + return `data:${type};base64,${content}`; +} diff --git a/packages/album/domain/package.json b/packages/album/domain/package.json index d32fb3d0..d9d20cb6 100644 --- a/packages/album/domain/package.json +++ b/packages/album/domain/package.json @@ -5,7 +5,8 @@ "tslib": "^2.3.0", "@devmx/shared-api-interfaces": "0.0.1", "rxjs": "^7.8.0", - "@devmx/shared-util-errors": "0.0.1" + "@devmx/shared-util-errors": "0.0.1", + "@devmx/shared-util-data": "0.0.1" }, "type": "commonjs", "main": "./src/index.js", diff --git a/packages/album/domain/src/client/services/photo.ts b/packages/album/domain/src/client/services/photo.ts index d00a90d4..84cbe6a1 100644 --- a/packages/album/domain/src/client/services/photo.ts +++ b/packages/album/domain/src/client/services/photo.ts @@ -1,9 +1,11 @@ import { HttpProgressEvent } from '@devmx/shared-api-interfaces/client'; import { EntityService } from '@devmx/shared-api-interfaces/client'; -import { Photo } from '@devmx/shared-api-interfaces'; +import { EditablePhoto, Photo } from '@devmx/shared-api-interfaces'; import { UploadPhoto } from '../dtos'; import { Observable } from 'rxjs'; export abstract class PhotoService extends EntityService { + abstract updateTags(id: string, data: EditablePhoto): Observable; + abstract upload(data: UploadPhoto): Observable; } diff --git a/packages/album/domain/src/client/use-cases/create-album.ts b/packages/album/domain/src/client/use-cases/create-album.ts index 7565709e..b0bb7740 100644 --- a/packages/album/domain/src/client/use-cases/create-album.ts +++ b/packages/album/domain/src/client/use-cases/create-album.ts @@ -1,5 +1,6 @@ import { Album, EditableAlbum, UseCase } from '@devmx/shared-api-interfaces'; import { AlbumService } from '../services'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; export class CreateAlbumUseCase implements UseCase { constructor(private albumService: AlbumService) {} @@ -8,3 +9,7 @@ export class CreateAlbumUseCase implements UseCase { return this.albumService.create(data); } } + +export function provideCreateAlbumUseCase() { + return createUseCaseProvider(CreateAlbumUseCase, [AlbumService]); +} diff --git a/packages/album/domain/src/client/use-cases/create-photo.ts b/packages/album/domain/src/client/use-cases/create-photo.ts index 1c8b3181..fbc8bfc0 100644 --- a/packages/album/domain/src/client/use-cases/create-photo.ts +++ b/packages/album/domain/src/client/use-cases/create-photo.ts @@ -1,5 +1,6 @@ import { Photo, EditablePhoto, UseCase } from '@devmx/shared-api-interfaces'; import { PhotoService } from '../services'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; export class CreatePhotoUseCase implements UseCase { constructor(private photoService: PhotoService) {} @@ -8,3 +9,7 @@ export class CreatePhotoUseCase implements UseCase { return this.photoService.create(data); } } + +export function provideCreatePhotoUseCase() { + return createUseCaseProvider(CreatePhotoUseCase, [PhotoService]); +} diff --git a/packages/album/domain/src/client/use-cases/delete-album.ts b/packages/album/domain/src/client/use-cases/delete-album.ts index d4269b84..dc7e8f1b 100644 --- a/packages/album/domain/src/client/use-cases/delete-album.ts +++ b/packages/album/domain/src/client/use-cases/delete-album.ts @@ -1,5 +1,6 @@ import { Album, UseCase } from '@devmx/shared-api-interfaces'; import { AlbumService } from '../services'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; export class DeleteAlbumUseCase implements UseCase { constructor(private albumService: AlbumService) {} @@ -8,3 +9,7 @@ export class DeleteAlbumUseCase implements UseCase { return this.albumService.delete(id); } } + +export function provideDeleteAlbumUseCase() { + return createUseCaseProvider(DeleteAlbumUseCase, [AlbumService]); +} diff --git a/packages/album/domain/src/client/use-cases/delete-photo.ts b/packages/album/domain/src/client/use-cases/delete-photo.ts index 52eec99f..78d4a6db 100644 --- a/packages/album/domain/src/client/use-cases/delete-photo.ts +++ b/packages/album/domain/src/client/use-cases/delete-photo.ts @@ -1,5 +1,6 @@ import { Photo, UseCase } from '@devmx/shared-api-interfaces'; import { PhotoService } from '../services'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; export class DeletePhotoUseCase implements UseCase { constructor(private photoService: PhotoService) {} @@ -8,3 +9,7 @@ export class DeletePhotoUseCase implements UseCase { return this.photoService.delete(id); } } + +export function provideDeletePhotoUseCase() { + return createUseCaseProvider(DeletePhotoUseCase, [PhotoService]); +} diff --git a/packages/album/domain/src/client/use-cases/find-album-by-id.ts b/packages/album/domain/src/client/use-cases/find-album-by-id.ts index 6bf52a5b..904ce9ac 100644 --- a/packages/album/domain/src/client/use-cases/find-album-by-id.ts +++ b/packages/album/domain/src/client/use-cases/find-album-by-id.ts @@ -1,5 +1,6 @@ import { Album, UseCase } from '@devmx/shared-api-interfaces'; import { AlbumService } from '../services'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; export class FindAlbumByIDUseCase implements UseCase { constructor(private albumService: AlbumService) {} @@ -8,3 +9,7 @@ export class FindAlbumByIDUseCase implements UseCase { return this.albumService.findOne(id); } } + +export function provideFindAlbumByIDUseCase() { + return createUseCaseProvider(FindAlbumByIDUseCase, [AlbumService]); +} diff --git a/packages/album/domain/src/client/use-cases/find-albums.ts b/packages/album/domain/src/client/use-cases/find-albums.ts index c1e6d63c..04b1b872 100644 --- a/packages/album/domain/src/client/use-cases/find-albums.ts +++ b/packages/album/domain/src/client/use-cases/find-albums.ts @@ -1,3 +1,4 @@ +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; import { AlbumService } from '../services'; import { Page, @@ -15,3 +16,7 @@ export class FindAlbumsUseCase return this.albumService.find(data); } } + +export function provideFindAlbumsUseCase() { + return createUseCaseProvider(FindAlbumsUseCase, [AlbumService]); +} diff --git a/packages/album/domain/src/client/use-cases/find-photo-by-id.ts b/packages/album/domain/src/client/use-cases/find-photo-by-id.ts index ffc0676c..cdce96d0 100644 --- a/packages/album/domain/src/client/use-cases/find-photo-by-id.ts +++ b/packages/album/domain/src/client/use-cases/find-photo-by-id.ts @@ -1,3 +1,4 @@ +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; import { Photo, UseCase } from '@devmx/shared-api-interfaces'; import { PhotoService } from '../services'; @@ -8,3 +9,7 @@ export class FindPhotoByIDUseCase implements UseCase { return this.photoService.findOne(id); } } + +export function provideFindPhotoByIDUseCase() { + return createUseCaseProvider(FindPhotoByIDUseCase, [PhotoService]); +} diff --git a/packages/album/domain/src/client/use-cases/find-photos.ts b/packages/album/domain/src/client/use-cases/find-photos.ts index b66ada66..945800e2 100644 --- a/packages/album/domain/src/client/use-cases/find-photos.ts +++ b/packages/album/domain/src/client/use-cases/find-photos.ts @@ -1,3 +1,4 @@ +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; import { PhotoService } from '../services'; import { Page, @@ -15,3 +16,7 @@ export class FindPhotosUseCase return this.albumService.find(data); } } + +export function provideFindPhotosUseCase() { + return createUseCaseProvider(FindPhotosUseCase, [PhotoService]); +} diff --git a/packages/album/domain/src/client/use-cases/index.ts b/packages/album/domain/src/client/use-cases/index.ts index d3604eef..1e9a244a 100644 --- a/packages/album/domain/src/client/use-cases/index.ts +++ b/packages/album/domain/src/client/use-cases/index.ts @@ -1,11 +1,12 @@ -export * from './create-album'; -export * from './create-photo'; -export * from './delete-album'; -export * from './delete-photo'; -export * from './find-album-by-id'; -export * from './find-albums'; -export * from './find-photo-by-id'; -export * from './find-photos'; -export * from './update-album'; -export * from './update-photo'; -export * from './upload-photo'; +export * from './create-album'; +export * from './create-photo'; +export * from './delete-album'; +export * from './delete-photo'; +export * from './find-album-by-id'; +export * from './find-albums'; +export * from './find-photo-by-id'; +export * from './find-photos'; +export * from './update-album'; +export * from './update-photo-tags'; +export * from './update-photo'; +export * from './upload-photo'; diff --git a/packages/album/domain/src/client/use-cases/update-album.ts b/packages/album/domain/src/client/use-cases/update-album.ts index 2c0a08f4..00e63645 100644 --- a/packages/album/domain/src/client/use-cases/update-album.ts +++ b/packages/album/domain/src/client/use-cases/update-album.ts @@ -1,5 +1,6 @@ import { Album, EditableAlbum, UseCase } from '@devmx/shared-api-interfaces'; import { AlbumService } from '../services'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; export class UpdateAlbumUseCase implements UseCase { constructor(private albumService: AlbumService) {} @@ -8,3 +9,7 @@ export class UpdateAlbumUseCase implements UseCase { return this.albumService.update(data.id, data); } } + +export function provideUpdateAlbumUseCase() { + return createUseCaseProvider(UpdateAlbumUseCase, [AlbumService]); +} diff --git a/packages/album/domain/src/client/use-cases/update-photo-tags.ts b/packages/album/domain/src/client/use-cases/update-photo-tags.ts new file mode 100644 index 00000000..c34de5e6 --- /dev/null +++ b/packages/album/domain/src/client/use-cases/update-photo-tags.ts @@ -0,0 +1,15 @@ +import { Photo, EditablePhoto, UseCase } from '@devmx/shared-api-interfaces'; +import { PhotoService } from '../services'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; + +export class UpdatePhotoTagsUseCase implements UseCase { + constructor(private photoService: PhotoService) {} + + execute(data: EditablePhoto) { + return this.photoService.updateTags(data.id, data); + } +} + +export function provideUpdatePhotoTagsUseCase() { + return createUseCaseProvider(UpdatePhotoTagsUseCase, [PhotoService]); +} diff --git a/packages/album/domain/src/client/use-cases/update-photo.ts b/packages/album/domain/src/client/use-cases/update-photo.ts index cb7f261a..dbffb2fc 100644 --- a/packages/album/domain/src/client/use-cases/update-photo.ts +++ b/packages/album/domain/src/client/use-cases/update-photo.ts @@ -1,4 +1,5 @@ import { Photo, EditablePhoto, UseCase } from '@devmx/shared-api-interfaces'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; import { PhotoService } from '../services'; export class UpdatePhotoUseCase implements UseCase { @@ -8,3 +9,7 @@ export class UpdatePhotoUseCase implements UseCase { return this.photoService.update(data.id, data); } } + +export function provideUpdatePhotoUseCase() { + return createUseCaseProvider(UpdatePhotoUseCase, [PhotoService]); +} diff --git a/packages/album/domain/src/client/use-cases/upload-photo.ts b/packages/album/domain/src/client/use-cases/upload-photo.ts index a671ea07..5417740c 100644 --- a/packages/album/domain/src/client/use-cases/upload-photo.ts +++ b/packages/album/domain/src/client/use-cases/upload-photo.ts @@ -1,4 +1,5 @@ import { HttpProgressEvent } from '@devmx/shared-api-interfaces/client'; +import { createUseCaseProvider } from '@devmx/shared-util-data/client'; import { UseCase } from '@devmx/shared-api-interfaces'; import { AlbumService } from '../services'; import { UploadPhoto } from '../dtos'; @@ -17,3 +18,7 @@ export class UploadPhotoUseCase // return this.photoService.upload(data); // } } + +export function provideUploadPhotoUseCase() { + return createUseCaseProvider(UploadPhotoUseCase, [AlbumService]); +} diff --git a/packages/album/resource/src/lib/controllers/photos.ts b/packages/album/resource/src/lib/controllers/photos.ts index 0ec72400..483a2c19 100644 --- a/packages/album/resource/src/lib/controllers/photos.ts +++ b/packages/album/resource/src/lib/controllers/photos.ts @@ -36,6 +36,7 @@ import { UpdatePhotoDto, } from '@devmx/album-data-source'; import 'multer'; +import { plainToClass } from 'class-transformer'; @ApiTags('Fotos') @Controller('photos') @@ -77,19 +78,27 @@ export class PhotosController { } } - // @Post(':id/upload') - // @UseInterceptors(FileInterceptor('file')) - // @ApiConsumes('multipart/form-data') - // async uploadFile( - // @Param('id') albumId: string, - // @UploadedFile() photo: Express.Multer.File - // ) { - // try { - // return await this.photosFacade.addPhoto({ albumId, photo }); - // } catch (err) { - // throw new BadRequestException(err); - // } - // } + @Patch(':id/tags') + @ApiBearerAuth() + @ApiOkResponse({ type: PhotoDto }) + async photoTags( + @Param('id') id: string, + @Body() updatePhotoDto: UpdatePhotoDto + ) { + const photo = await this.photosFacade.findOne(id); + + if (!photo) { + throw new NotFoundException('Foto não encontrada'); + } + + try { + const data = { ...photo, ...updatePhotoDto }; + const value = plainToClass(UpdatePhotoDto, data); + return await this.photosFacade.update(id, value); + } catch (err) { + throw new BadRequestException(err); + } + } @Patch(':id') @ApiBearerAuth() @@ -103,15 +112,20 @@ export class PhotosController { const photo = await this.photosFacade.findOne(id); if (!photo) { - throw new NotFoundException('Não encontrado'); + throw new NotFoundException('Foto não encontrada'); } if (photo.owner.id !== auth.id && !authIsAdmin(auth.roles)) { throw new ForbiddenException('Acesso negado'); } + console.log(plainToClass(UpdatePhotoDto, { ...photo, ...updatePhotoDto })); + try { - return await this.photosFacade.update(id, updatePhotoDto); + return await this.photosFacade.update( + id, + plainToClass(UpdatePhotoDto, { ...photo, ...updatePhotoDto }) + ); } catch (err) { throw new BadRequestException(err); } @@ -125,7 +139,7 @@ export class PhotosController { const photo = await this.photosFacade.findOne(id); if (!photo) { - throw new NotFoundException('Não encontrado'); + throw new NotFoundException('Foto não encontrada'); } if (photo.owner.id !== auth.id && !authIsAdmin(auth.roles)) { From 71256bd800763990fe186de9d32e610088f0f096 Mon Sep 17 00:00:00 2001 From: Gui Seek Date: Sat, 28 Dec 2024 13:07:21 -0300 Subject: [PATCH 2/4] feat(shared): adiciona icones de tags --- packages/shared/ui-global/icon/src/lib/icon-registry.ts | 9 +++++++++ packages/shared/ui-global/icon/src/lib/icon.component.ts | 7 ------- .../icon/src/lib/types/{e-commerce.ts => commerce.ts} | 2 +- packages/shared/ui-global/icon/src/lib/types/icon.ts | 5 ++++- packages/shared/ui-global/icon/src/lib/types/index.ts | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) rename packages/shared/ui-global/icon/src/lib/types/{e-commerce.ts => commerce.ts} (94%) diff --git a/packages/shared/ui-global/icon/src/lib/icon-registry.ts b/packages/shared/ui-global/icon/src/lib/icon-registry.ts index e7a9b180..daef3e63 100644 --- a/packages/shared/ui-global/icon/src/lib/icon-registry.ts +++ b/packages/shared/ui-global/icon/src/lib/icon-registry.ts @@ -1,7 +1,9 @@ import { HttpClient } from '@angular/common/http'; import { of, tap } from 'rxjs'; import { Icon } from './types'; +import { Injectable } from '@angular/core'; +@Injectable({ providedIn: 'root' }) export class IconRegistry { #cache: Partial> = {}; @@ -19,3 +21,10 @@ export class IconRegistry { this.#cache[name] = icon; }; } + +export function provideIconRegistry() { + return { + provide: IconRegistry, + deps: [HttpClient], + }; +} diff --git a/packages/shared/ui-global/icon/src/lib/icon.component.ts b/packages/shared/ui-global/icon/src/lib/icon.component.ts index d223fd9d..989611ec 100644 --- a/packages/shared/ui-global/icon/src/lib/icon.component.ts +++ b/packages/shared/ui-global/icon/src/lib/icon.component.ts @@ -1,4 +1,3 @@ -import { HttpClient } from '@angular/common/http'; import { IconRegistry } from './icon-registry'; import { Icon } from './types'; import { take } from 'rxjs'; @@ -20,12 +19,6 @@ import { } `, standalone: true, - providers: [ - { - provide: IconRegistry, - deps: [HttpClient], - }, - ], }) export class IconComponent implements OnInit { renderer = inject(Renderer2); diff --git a/packages/shared/ui-global/icon/src/lib/types/e-commerce.ts b/packages/shared/ui-global/icon/src/lib/types/commerce.ts similarity index 94% rename from packages/shared/ui-global/icon/src/lib/types/e-commerce.ts rename to packages/shared/ui-global/icon/src/lib/types/commerce.ts index 16bc1d03..797174f5 100644 --- a/packages/shared/ui-global/icon/src/lib/types/e-commerce.ts +++ b/packages/shared/ui-global/icon/src/lib/types/commerce.ts @@ -45,4 +45,4 @@ type ECommerce = | 'ticket-alt' | 'ticket'; -export type ECommerceIcon = `e-commerce/${ECommerce}`; +export type ECommerceIcon = `commerce/${ECommerce}`; diff --git a/packages/shared/ui-global/icon/src/lib/types/icon.ts b/packages/shared/ui-global/icon/src/lib/types/icon.ts index f56a302e..6b30b31a 100644 --- a/packages/shared/ui-global/icon/src/lib/types/icon.ts +++ b/packages/shared/ui-global/icon/src/lib/types/icon.ts @@ -1,4 +1,4 @@ -import { ECommerceIcon } from './e-commerce'; +import { ECommerceIcon } from './commerce'; import { TransportIcon } from './transport'; import { SoftwareIcon } from './software'; import { BuildingIcon } from './building'; @@ -111,6 +111,9 @@ type Root = | 'star' | 'sticker' | 'subtitles' + | 'tag' + | 'tag-plus' + | 'tag-minus' | 'time-loading' | 'time-sleep' | 'timer-alt' diff --git a/packages/shared/ui-global/icon/src/lib/types/index.ts b/packages/shared/ui-global/icon/src/lib/types/index.ts index b3596ca7..576754d4 100644 --- a/packages/shared/ui-global/icon/src/lib/types/index.ts +++ b/packages/shared/ui-global/icon/src/lib/types/index.ts @@ -1,7 +1,7 @@ export * from './arrow'; export * from './building'; export * from './doc'; -export * from './e-commerce'; +export * from './commerce'; export * from './emoji'; export * from './icon'; export * from './menu'; From 43487d93335dc1c5577d9d4e68bdae1e8c895c6a Mon Sep 17 00:00:00 2001 From: Gui Seek Date: Sat, 28 Dec 2024 13:11:11 -0300 Subject: [PATCH 3/4] feat(album): adiciona funcionalidade para marcar pessoas em fotos closed #119 --- apps/devmx/public/icons/tag-minus.svg | 3 + apps/devmx/public/icons/tag-plus.svg | 3 + apps/devmx/public/icons/tag.svg | 3 + .../data-access/src/lib/album.providers.ts | 4 +- .../src/lib/application/photo.facade.ts | 12 +- .../data-access/src/lib/providers/album.ts | 23 ++++ .../data-access/src/lib/providers/facades.ts | 5 - .../data-access/src/lib/providers/index.ts | 5 +- .../data-access/src/lib/providers/photo.ts | 27 +++++ .../data-access/src/lib/providers/services.ts | 8 -- .../src/lib/providers/use-cases.ts | 73 ------------- .../lib/containers/album/album.container.html | 19 +--- .../lib/containers/album/album.container.scss | 61 ++++------- .../lib/containers/album/album.container.ts | 34 +++++- packages/album/ui-shared/.eslintrc.json | 2 +- packages/album/ui-shared/package.json | 5 +- .../ui-shared/src/lib/components/index.ts | 5 + .../photo-viewer/photo-viewer-data.ts | 6 + .../photo-viewer/photo-viewer.component.html | 53 +++++++++ .../photo-viewer/photo-viewer.component.scss | 66 +++++++++++ .../photo-viewer/photo-viewer.component.ts | 102 +++++++++++++++++ .../photo-viewer/photo-viewer.service.ts | 26 +++++ .../lib/components/photo/photo.component.ts | 54 +++++++++ .../components/tag-user/tag-user.component.ts | 22 ++++ .../components/tag-user/tag-user.service.ts | 38 +++++++ .../components/utils/detect-tagging-intent.ts | 33 ++++++ .../src/lib/components/utils/index.ts | 1 + .../api-interfaces/src/lib/entities/index.ts | 53 ++++----- .../api-interfaces/src/lib/entities/photo.ts | 5 +- .../src/lib/entities/user-tag.ts | 9 ++ .../shared/data-source/src/lib/dtos/index.ts | 43 ++++---- .../data-source/src/lib/dtos/user-tag.ts | 15 +++ .../src/lib/infrastructure/mongo.service.ts | 17 ++- .../shared/util-data/src/lib/models/index.ts | 2 +- .../shared/util-data/src/lib/utils/index.ts | 53 ++++----- .../shared/util-data/src/lib/utils/rect.ts | 103 ++++++++++++++++++ .../shared/util-data/src/lib/utils/vector2.ts | 2 +- 37 files changed, 759 insertions(+), 236 deletions(-) create mode 100644 apps/devmx/public/icons/tag-minus.svg create mode 100644 apps/devmx/public/icons/tag-plus.svg create mode 100644 apps/devmx/public/icons/tag.svg create mode 100644 packages/album/data-access/src/lib/providers/album.ts delete mode 100644 packages/album/data-access/src/lib/providers/facades.ts create mode 100644 packages/album/data-access/src/lib/providers/photo.ts delete mode 100644 packages/album/data-access/src/lib/providers/services.ts delete mode 100644 packages/album/data-access/src/lib/providers/use-cases.ts create mode 100644 packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer-data.ts create mode 100644 packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.html create mode 100644 packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.scss create mode 100644 packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.ts create mode 100644 packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.service.ts create mode 100644 packages/album/ui-shared/src/lib/components/photo/photo.component.ts create mode 100644 packages/album/ui-shared/src/lib/components/tag-user/tag-user.component.ts create mode 100644 packages/album/ui-shared/src/lib/components/tag-user/tag-user.service.ts create mode 100644 packages/album/ui-shared/src/lib/components/utils/detect-tagging-intent.ts create mode 100644 packages/album/ui-shared/src/lib/components/utils/index.ts create mode 100644 packages/shared/api-interfaces/src/lib/entities/user-tag.ts create mode 100644 packages/shared/data-source/src/lib/dtos/user-tag.ts create mode 100644 packages/shared/util-data/src/lib/utils/rect.ts diff --git a/apps/devmx/public/icons/tag-minus.svg b/apps/devmx/public/icons/tag-minus.svg new file mode 100644 index 00000000..cdd210b3 --- /dev/null +++ b/apps/devmx/public/icons/tag-minus.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/devmx/public/icons/tag-plus.svg b/apps/devmx/public/icons/tag-plus.svg new file mode 100644 index 00000000..db532dab --- /dev/null +++ b/apps/devmx/public/icons/tag-plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/devmx/public/icons/tag.svg b/apps/devmx/public/icons/tag.svg new file mode 100644 index 00000000..243651ac --- /dev/null +++ b/apps/devmx/public/icons/tag.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/album/data-access/src/lib/album.providers.ts b/packages/album/data-access/src/lib/album.providers.ts index d53dda63..70e54a7f 100644 --- a/packages/album/data-access/src/lib/album.providers.ts +++ b/packages/album/data-access/src/lib/album.providers.ts @@ -1,5 +1,5 @@ -import { provideFacades, provideServices, provideUseCases } from './providers'; +import { providePhoto, provideAlbum as $provideAlbum } from './providers'; export function provideAlbum() { - return [...provideServices(), ...provideUseCases(), ...provideFacades()]; + return [...providePhoto(), ...$provideAlbum()]; } diff --git a/packages/album/data-access/src/lib/application/photo.facade.ts b/packages/album/data-access/src/lib/application/photo.facade.ts index ec0b423a..40515f67 100644 --- a/packages/album/data-access/src/lib/application/photo.facade.ts +++ b/packages/album/data-access/src/lib/application/photo.facade.ts @@ -5,10 +5,12 @@ import { DeletePhotoUseCase, FindPhotoByIDUseCase, FindPhotosUseCase, + UpdatePhotoTagsUseCase, UpdatePhotoUseCase, UploadPhoto, UploadPhotoUseCase, } from '@devmx/album-domain/client'; +import { take } from 'rxjs'; export class PhotoFacade extends EntityFacade { constructor( @@ -16,6 +18,7 @@ export class PhotoFacade extends EntityFacade { private findPhotosUseCase: FindPhotosUseCase, private findPhotoByIdUseCase: FindPhotoByIDUseCase, private updatePhotoUseCase: UpdatePhotoUseCase, + private updatePhotoTagsUseCase: UpdatePhotoTagsUseCase, private deletePhotoUseCase: DeletePhotoUseCase, private uploadPhotoUseCase: UploadPhotoUseCase ) { @@ -49,11 +52,15 @@ export class PhotoFacade extends EntityFacade { update(data: EditablePhoto) { const request$ = this.updatePhotoUseCase.execute(data); - this.onUpdate(request$); + // this.onUpdate(request$); this.loadOne(data.id); - return request$; + return request$.pipe(take(1)); + } + + updateTags(data: EditablePhoto) { + this.onUpdate(this.updatePhotoTagsUseCase.execute(data)); } delete(id: string) { @@ -67,6 +74,7 @@ export function providePhotoFacade() { FindPhotosUseCase, FindPhotoByIDUseCase, UpdatePhotoUseCase, + UpdatePhotoTagsUseCase, DeletePhotoUseCase, UploadPhotoUseCase, ]); diff --git a/packages/album/data-access/src/lib/providers/album.ts b/packages/album/data-access/src/lib/providers/album.ts new file mode 100644 index 00000000..91c19ec0 --- /dev/null +++ b/packages/album/data-access/src/lib/providers/album.ts @@ -0,0 +1,23 @@ +import { provideAlbumHttpService } from '../infrastrucure'; +import { provideAlbumFacade } from '../application'; +import { + provideCreateAlbumUseCase, + provideDeleteAlbumUseCase, + provideFindAlbumByIDUseCase, + provideFindAlbumsUseCase, + provideUpdateAlbumUseCase, +} from '@devmx/album-domain/client'; + +export function provideAlbum() { + return [ + provideAlbumHttpService(), + + provideCreateAlbumUseCase(), + provideDeleteAlbumUseCase(), + provideFindAlbumByIDUseCase(), + provideFindAlbumsUseCase(), + provideUpdateAlbumUseCase(), + + provideAlbumFacade(), + ]; +} diff --git a/packages/album/data-access/src/lib/providers/facades.ts b/packages/album/data-access/src/lib/providers/facades.ts deleted file mode 100644 index 8e6734da..00000000 --- a/packages/album/data-access/src/lib/providers/facades.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { provideAlbumFacade, providePhotoFacade } from '../application'; - -export function provideFacades() { - return [provideAlbumFacade(), providePhotoFacade()]; -} diff --git a/packages/album/data-access/src/lib/providers/index.ts b/packages/album/data-access/src/lib/providers/index.ts index 7134c954..8a0c44ba 100644 --- a/packages/album/data-access/src/lib/providers/index.ts +++ b/packages/album/data-access/src/lib/providers/index.ts @@ -1,3 +1,2 @@ -export * from './facades'; -export * from './services'; -export * from './use-cases'; +export * from './album'; +export * from './photo'; diff --git a/packages/album/data-access/src/lib/providers/photo.ts b/packages/album/data-access/src/lib/providers/photo.ts new file mode 100644 index 00000000..a5837ddb --- /dev/null +++ b/packages/album/data-access/src/lib/providers/photo.ts @@ -0,0 +1,27 @@ +import { providePhotoHttpService } from '../infrastrucure'; +import { providePhotoFacade } from '../application'; +import { + provideCreatePhotoUseCase, + provideDeletePhotoUseCase, + provideFindPhotoByIDUseCase, + provideFindPhotosUseCase, + provideUpdatePhotoTagsUseCase, + provideUpdatePhotoUseCase, + provideUploadPhotoUseCase, +} from '@devmx/album-domain/client'; + +export function providePhoto() { + return [ + providePhotoHttpService(), + + provideCreatePhotoUseCase(), + provideDeletePhotoUseCase(), + provideFindPhotoByIDUseCase(), + provideFindPhotosUseCase(), + provideUpdatePhotoTagsUseCase(), + provideUpdatePhotoUseCase(), + provideUploadPhotoUseCase(), + + providePhotoFacade(), + ]; +} diff --git a/packages/album/data-access/src/lib/providers/services.ts b/packages/album/data-access/src/lib/providers/services.ts deleted file mode 100644 index a80bca15..00000000 --- a/packages/album/data-access/src/lib/providers/services.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { - provideAlbumHttpService, - providePhotoHttpService, -} from '../infrastrucure'; - -export function provideServices() { - return [provideAlbumHttpService(), providePhotoHttpService()]; -} diff --git a/packages/album/data-access/src/lib/providers/use-cases.ts b/packages/album/data-access/src/lib/providers/use-cases.ts deleted file mode 100644 index 4aa88415..00000000 --- a/packages/album/data-access/src/lib/providers/use-cases.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { createUseCaseProvider } from '@devmx/shared-data-access'; -import { - AlbumService, - PhotoService, - CreateAlbumUseCase, - CreatePhotoUseCase, - DeleteAlbumUseCase, - DeletePhotoUseCase, - FindAlbumByIDUseCase, - FindAlbumsUseCase, - FindPhotoByIDUseCase, - FindPhotosUseCase, - UpdateAlbumUseCase, - UpdatePhotoUseCase, - UploadPhotoUseCase, -} from '@devmx/album-domain/client'; - -export function provideCreateAlbumUseCase() { - return createUseCaseProvider(CreateAlbumUseCase, [AlbumService]); -} - -export function provideFindAlbumsUseCase() { - return createUseCaseProvider(FindAlbumsUseCase, [AlbumService]); -} - -export function provideFindAlbumByIDUseCase() { - return createUseCaseProvider(FindAlbumByIDUseCase, [AlbumService]); -} -export function provideUpdateAlbumUseCase() { - return createUseCaseProvider(UpdateAlbumUseCase, [AlbumService]); -} -export function provideDeleteAlbumUseCase() { - return createUseCaseProvider(DeleteAlbumUseCase, [AlbumService]); -} - -export function provideCreatePhotoUseCase() { - return createUseCaseProvider(CreatePhotoUseCase, [PhotoService]); -} - -export function provideFindPhotosUseCase() { - return createUseCaseProvider(FindPhotosUseCase, [PhotoService]); -} - -export function provideFindPhotoByIDUseCase() { - return createUseCaseProvider(FindPhotoByIDUseCase, [PhotoService]); -} -export function provideUpdatePhotoUseCase() { - return createUseCaseProvider(UpdatePhotoUseCase, [PhotoService]); -} -export function provideDeletePhotoUseCase() { - return createUseCaseProvider(DeletePhotoUseCase, [PhotoService]); -} -export function provideUploadPhotoUseCase() { - return createUseCaseProvider(UploadPhotoUseCase, [AlbumService]); -} - -export function provideUseCases() { - return [ - provideCreateAlbumUseCase(), - provideFindAlbumsUseCase(), - provideFindAlbumByIDUseCase(), - provideUpdateAlbumUseCase(), - provideDeleteAlbumUseCase(), - - provideCreatePhotoUseCase(), - provideFindPhotosUseCase(), - provideFindPhotoByIDUseCase(), - provideUpdatePhotoUseCase(), - provideDeletePhotoUseCase(), - - provideUploadPhotoUseCase(), - ]; -} diff --git a/packages/album/feature-shell/src/lib/containers/album/album.container.html b/packages/album/feature-shell/src/lib/containers/album/album.container.html index 302b5f96..c245f988 100644 --- a/packages/album/feature-shell/src/lib/containers/album/album.container.html +++ b/packages/album/feature-shell/src/lib/containers/album/album.container.html @@ -1,17 +1,10 @@ @if (album$ | async; as album) { - - - -@for (photo of album.photos; track photo.id) { - -
- - -
{{ photo.caption }}
-
- -} - +@let auth = (authFacade.auth$ | async)!; +
+ @for (photo of album.photos; track photo.id) { + + } +
} diff --git a/packages/album/feature-shell/src/lib/containers/album/album.container.scss b/packages/album/feature-shell/src/lib/containers/album/album.container.scss index 68cc02a6..6f4e42a2 100644 --- a/packages/album/feature-shell/src/lib/containers/album/album.container.scss +++ b/packages/album/feature-shell/src/lib/containers/album/album.container.scss @@ -1,24 +1,30 @@ :host { - gap: 1em; + flex: 1; padding: 1em; - display: grid; - grid-auto-flow: dense; + display: flex; + flex-direction: column; - grid-template-columns: repeat(4, 1fr); + .grid { + gap: 1em; + display: grid; + grid-auto-flow: dense; - @media (max-width: 1918px) { - grid-template-columns: repeat(3, 1fr); - } + grid-template-columns: repeat(4, 1fr); - @media (max-width: 1278px) { - grid-template-columns: repeat(2, 1fr); - } + @media (max-width: 1918px) { + grid-template-columns: repeat(3, 1fr); + } + + @media (max-width: 1278px) { + grid-template-columns: repeat(2, 1fr); + } - @media (max-width: 767px) { - grid-template-columns: repeat(1, 1fr); + @media (max-width: 767px) { + grid-template-columns: repeat(1, 1fr); + } } - figure { + .photo { margin: 0; // width: calc(100% - 1em); box-sizing: border-box; @@ -29,34 +35,5 @@ background-color: rgba(0, 0, 0, 0.04); transition: width 300ms ease-in-out; - - &.selected { - width: 16em; - transition: width 300ms ease-in-out; - } - - img { - width: 100%; - display: flex; - object-fit: cover; - height: 24em; - } - - figcaption { - display: flex; - width: 100%; - padding: 0.4em; - position: absolute; - bottom: 0; - - background-color: rgba(0, 0, 0, 0.4); - color: rgba(255, 255, 255, 0.6); - - z-index: 100; - - &:empty { - display: none; - } - } } } diff --git a/packages/album/feature-shell/src/lib/containers/album/album.container.ts b/packages/album/feature-shell/src/lib/containers/album/album.container.ts index febd32bf..9975e766 100644 --- a/packages/album/feature-shell/src/lib/containers/album/album.container.ts +++ b/packages/album/feature-shell/src/lib/containers/album/album.container.ts @@ -1,21 +1,47 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { inject, Component, ChangeDetectionStrategy } from '@angular/core'; +import { Album, Authentication, Photo } from '@devmx/shared-api-interfaces'; +import { AlbumFacade, PhotoFacade } from '@devmx/album-data-access'; +import { AuthenticationFacade } from '@devmx/account-data-access'; import { ActivatedRoute, RouterModule } from '@angular/router'; -import { Album } from '@devmx/shared-api-interfaces'; +import { MatButtonModule } from '@angular/material/button'; import { AsyncPipe } from '@angular/common'; -import { filter, map } from 'rxjs'; +import { filter, map, take } from 'rxjs'; +import { + PhotoComponent, + PhotoViewerService, + providePhotoViewer, +} from '@devmx/album-ui-shared'; @Component({ selector: 'devmx-album', templateUrl: './album.container.html', styleUrl: './album.container.scss', changeDetection: ChangeDetectionStrategy.OnPush, - imports: [RouterModule, AsyncPipe], + imports: [RouterModule, MatButtonModule, PhotoComponent, AsyncPipe], + providers: [providePhotoViewer()], }) export class AlbumContainer { route = inject(ActivatedRoute); + authFacade = inject(AuthenticationFacade); + albumFacade = inject(AlbumFacade); + photoFacade = inject(PhotoFacade); + + photoViewer = inject(PhotoViewerService); + album$ = this.route.data.pipe( filter((data) => 'album' in data), map((data) => data['album'] as Album) ); + + open(photo: Photo, auth: Authentication) { + this.photoViewer + .open({ photo, auth }) + .closed.pipe(take(1)) + .subscribe(this.updateTags); + } + + updateTags = (photo?: Photo) => { + if (photo) this.photoFacade.updateTags(photo); + }; } diff --git a/packages/album/ui-shared/.eslintrc.json b/packages/album/ui-shared/.eslintrc.json index af5f2dfa..fd4a7acc 100644 --- a/packages/album/ui-shared/.eslintrc.json +++ b/packages/album/ui-shared/.eslintrc.json @@ -20,7 +20,7 @@ "@angular-eslint/component-selector": [ "error", { - "type": "element", + "type": ["element", "attribute"], "prefix": "devmx", "style": "kebab-case" } diff --git a/packages/album/ui-shared/package.json b/packages/album/ui-shared/package.json index 3c30d2c8..2f6b2450 100644 --- a/packages/album/ui-shared/package.json +++ b/packages/album/ui-shared/package.json @@ -6,7 +6,10 @@ "@angular/core": "19.0.0", "@devmx/shared-ui-global": "0.0.1", "@angular/material": "19.0.0", - "@devmx/shared-api-interfaces": "0.0.1" + "@devmx/shared-api-interfaces": "0.0.1", + "@angular/cdk": "19.0.0", + "@devmx/account-ui-shared": "0.0.1", + "@devmx/shared-util-data": "0.0.1" }, "sideEffects": false } diff --git a/packages/album/ui-shared/src/lib/components/index.ts b/packages/album/ui-shared/src/lib/components/index.ts index 1be4c09c..25c51fa3 100644 --- a/packages/album/ui-shared/src/lib/components/index.ts +++ b/packages/album/ui-shared/src/lib/components/index.ts @@ -1 +1,6 @@ +export * from './photo-viewer/photo-viewer.component'; +export * from './photo-viewer/photo-viewer.service'; export * from './album-card/album-card.component'; +export * from './tag-user/tag-user.component'; +export * from './tag-user/tag-user.service'; +export * from './photo/photo.component'; diff --git a/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer-data.ts b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer-data.ts new file mode 100644 index 00000000..32890e9a --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer-data.ts @@ -0,0 +1,6 @@ +import { Authentication, Photo } from '@devmx/shared-api-interfaces'; + +export interface PhotoViewerData { + photo: Photo; + auth: Authentication; +} diff --git a/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.html b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.html new file mode 100644 index 00000000..62740887 --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.html @@ -0,0 +1,53 @@ +@if (data) { +
+ + + + + + +
+ +
+
+ + @for (tag of data.photo.tags; track $index) { +
+ +
+ } +
+ +
{{ data.photo.caption }}
+
+ +
+ +
+} diff --git a/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.scss b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.scss new file mode 100644 index 00000000..2e7d5c03 --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.scss @@ -0,0 +1,66 @@ +:host { + border: 0; + outline: none; + display: flex; + flex-direction: column; + + background-color: white; + border-radius: 1em; + + header { + gap: 1em; + display: flex; + padding: 0.6em; + justify-content: flex-end; + } + + figure { + margin: 0; + width: 100%; + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + overflow: hidden; + user-select: none; + + img { + display: flex; + z-index: 10; + width: 100%; + height: 100%; + max-width: 100%; + cursor: context-menu; + object-fit: pointer; + user-select: none; + } + + .photo-viewer { + position: relative; + } + + .user-tag { + position: absolute; + transform: translate(-50%, -50%); + display: flex; + align-items: center; + justify-content: flex-end; + color: #f9e000; + opacity: 0.2; + transition: opacity 250ms ease-in-out; + width: 64px; + height: 64px; + &:hover { + opacity: 1; + } + } + } + + footer { + gap: 1em; + padding: 0.6em; + display: flex; + justify-content: flex-end; + } +} diff --git a/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.ts b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.ts new file mode 100644 index 00000000..572669f6 --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.component.ts @@ -0,0 +1,102 @@ +import { Authentication, Photo, UserTag } from '@devmx/shared-api-interfaces'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; +import { IconComponent } from '@devmx/shared-ui-global/icon'; +import { MatButtonModule } from '@angular/material/button'; +import { MatMenuModule } from '@angular/material/menu'; +import { PhotoViewerData } from './photo-viewer-data'; +import { detectTaggingIntent } from '../utils'; +import { + inject, + signal, + Component, + viewChild, + ElementRef, + AfterViewInit, + ChangeDetectorRef, + ChangeDetectionStrategy, +} from '@angular/core'; +import { + TagUserService, + provideTagUserService, +} from '../tag-user/tag-user.service'; + +@Component({ + selector: 'devmx-photo-viewer', + templateUrl: './photo-viewer.component.html', + styleUrl: './photo-viewer.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [provideTagUserService()], + imports: [MatTooltipModule, MatButtonModule, MatMenuModule, IconComponent], +}) +export class PhotoViewerComponent implements AfterViewInit { + tagUser = inject(TagUserService); + + cdr = inject(ChangeDetectorRef); + + ref = inject>(DialogRef); + + data = inject(DIALOG_DATA); + + originalTags: UserTag[] = []; + + imageRef = viewChild.required>('imageRef'); + + image!: HTMLImageElement; + + hasUpdates = signal(false); + + hasTagWithMe(user: Authentication) { + return this.data.photo.tags?.some((tag) => tag.user.id === user.id); + } + + removeMyTag(user: Authentication) { + this.data.photo.tags = this.data.photo.tags?.filter( + (tag) => tag.user.id !== user.id + ); + const hasUpdates = this.arraysAreEqual( + this.originalTags, + this.data.photo.tags ?? [] + ); + this.hasUpdates.set(!hasUpdates); + } + + ngAfterViewInit() { + this.originalTags = [...(this.data.photo.tags ?? [])]; + + this.image = this.imageRef().nativeElement; + + this.image.oncontextmenu = (ev) => ev.preventDefault(); + + detectTaggingIntent(this.image).subscribe(({ x, y }) => { + const offsetX = x * this.image.width; + const offsetY = y * this.image.height; + + const closed$ = this.tagUser.open(this.image, offsetX, offsetY).closed; + + closed$.subscribe((userRef) => { + if (userRef && this.data.photo.tags) { + const { id, displayName, name } = userRef; + const user = { id, displayName, name }; + this.data.photo.tags.push({ x: x * 100, y: y * 100, user }); + const hasUpdates = this.arraysAreEqual( + this.originalTags, + this.data.photo.tags + ); + this.hasUpdates.set(!hasUpdates); + this.cdr.detectChanges(); + } + }); + }); + } + + arraysAreEqual(a: T[], b: T[]) { + if (a.length !== b.length) return false; + + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + + return true; + } +} diff --git a/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.service.ts b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.service.ts new file mode 100644 index 00000000..41f0baf4 --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/photo-viewer/photo-viewer.service.ts @@ -0,0 +1,26 @@ +import { PhotoViewerComponent } from './photo-viewer.component'; +import { PhotoViewerData } from './photo-viewer-data'; +import { Photo } from '@devmx/shared-api-interfaces'; +import { Dialog } from '@angular/cdk/dialog'; + +export class PhotoViewerService { + constructor(private dialog: Dialog) {} + + open(data: PhotoViewerData) { + const width = `${data.photo.width}px`; + const height = `${data.photo.height}px`; + const disableClose = true; + + return this.dialog.open( + PhotoViewerComponent, + { data, width, height, disableClose } + ); + } +} + +export function providePhotoViewer() { + return { + provide: PhotoViewerService, + deps: [Dialog], + }; +} diff --git a/packages/album/ui-shared/src/lib/components/photo/photo.component.ts b/packages/album/ui-shared/src/lib/components/photo/photo.component.ts new file mode 100644 index 00000000..a5e9d1e0 --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/photo/photo.component.ts @@ -0,0 +1,54 @@ +import { Photo } from '@devmx/shared-api-interfaces'; +import { + input, + output, + Component, + ChangeDetectionStrategy, +} from '@angular/core'; + +@Component({ + selector: 'devmx-photo', + template: ` + @if (photo(); as photo) { +
+ + +
{{ photo.caption }}
+
+ } + `, + styles: ` + :host { + display: flex; + + figure { + margin: 0; + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + overflow: hidden; + + img { + display: flex; + z-index: 1; + width: 100%; + height: 100%; + object-fit: cover; + cursor: pointer; + } + } + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PhotoComponent { + photo = input.required(); + + open = output(); +} diff --git a/packages/album/ui-shared/src/lib/components/tag-user/tag-user.component.ts b/packages/album/ui-shared/src/lib/components/tag-user/tag-user.component.ts new file mode 100644 index 00000000..fb04bb8c --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/tag-user/tag-user.component.ts @@ -0,0 +1,22 @@ +import { inject, Component, ChangeDetectionStrategy } from '@angular/core'; +import { SearchUserComponent } from '@devmx/account-ui-shared'; +import { UserRef } from '@devmx/shared-api-interfaces'; +import { DialogRef } from '@angular/cdk/dialog'; + +@Component({ + selector: 'devmx-tag-user', + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [SearchUserComponent], + styles: ` + :host { + display: flex; + padding: 0.8em 0.6em 0; + border-radius: 0.6em; + background-color: rgba(255,255,255,.8); + } + `, +}) +export class TagUserComponent { + dialogRef = inject>(DialogRef); +} diff --git a/packages/album/ui-shared/src/lib/components/tag-user/tag-user.service.ts b/packages/album/ui-shared/src/lib/components/tag-user/tag-user.service.ts new file mode 100644 index 00000000..ef233baf --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/tag-user/tag-user.service.ts @@ -0,0 +1,38 @@ +import { TagUserComponent } from './tag-user.component'; +import { UserRef } from '@devmx/shared-api-interfaces'; +import { Overlay } from '@angular/cdk/overlay'; +import { Dialog } from '@angular/cdk/dialog'; + +export class TagUserService { + constructor(private overlay: Overlay, private dialog: Dialog) {} + + open(target: HTMLElement, offsetX = 0, offsetY = 0) { + const panelClass = 'tag-user-position'; + + const positionStrategy = this.overlay + .position() + .flexibleConnectedTo(target) + .withPositions([ + { + overlayX: 'center', + overlayY: 'top', + originX: 'start', + originY: 'top', + panelClass, + offsetX, + offsetY, + }, + ]); + + return this.dialog.open(TagUserComponent, { + positionStrategy, + }); + } +} + +export function provideTagUserService() { + return { + provide: TagUserService, + deps: [Overlay, Dialog], + }; +} diff --git a/packages/album/ui-shared/src/lib/components/utils/detect-tagging-intent.ts b/packages/album/ui-shared/src/lib/components/utils/detect-tagging-intent.ts new file mode 100644 index 00000000..60b4e28b --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/utils/detect-tagging-intent.ts @@ -0,0 +1,33 @@ +import { Vector2Like } from '@devmx/shared-util-data'; + +export const detectTaggingIntent = (element: HTMLElement) => { + const subscribe = (callback: (position: Vector2Like) => void) => { + const emit = (start: number, { clientX, clientY }: MouseEvent | Touch) => { + const end = new Date().getTime(); + + if (end - start > 600) { + const { left, top, width, height } = element.getBoundingClientRect(); + callback({ x: (clientX - left) / width, y: (clientY - top) / height }); + } + }; + + element.onmousedown = () => { + const start = new Date().getTime(); + element.onmouseup = (event) => { + event.preventDefault(); + emit(start, event); + }; + }; + + element.ontouchstart = () => { + const start = new Date().getTime(); + element.ontouchend = (event) => { + event.preventDefault(); + const { changedTouches } = event; + emit(start, changedTouches[0]); + }; + }; + }; + + return { subscribe }; +}; diff --git a/packages/album/ui-shared/src/lib/components/utils/index.ts b/packages/album/ui-shared/src/lib/components/utils/index.ts new file mode 100644 index 00000000..a5e0340a --- /dev/null +++ b/packages/album/ui-shared/src/lib/components/utils/index.ts @@ -0,0 +1 @@ +export * from './detect-tagging-intent'; diff --git a/packages/shared/api-interfaces/src/lib/entities/index.ts b/packages/shared/api-interfaces/src/lib/entities/index.ts index 79bf5b12..c2d4cc8a 100644 --- a/packages/shared/api-interfaces/src/lib/entities/index.ts +++ b/packages/shared/api-interfaces/src/lib/entities/index.ts @@ -1,26 +1,27 @@ -export * from './account'; -export * from './album'; -export * from './city'; -export * from './course-subject'; -export * from './course'; -export * from './event'; -export * from './institution'; -export * from './job-opening'; -export * from './job-skill'; -export * from './job'; -export * from './photo'; -export * from './place'; -export * from './presentation-comment'; -export * from './presentation-reaction'; -export * from './presentation'; -export * from './rsvp'; -export * from './skill'; -export * from './subject'; -export * from './user-code'; -export * from './user-contact'; -export * from './user-password'; -export * from './user-profile'; -export * from './user-skill'; -export * from './user-social'; -export * from './user-visibility'; -export * from './user'; +export * from './account'; +export * from './album'; +export * from './city'; +export * from './course-subject'; +export * from './course'; +export * from './event'; +export * from './institution'; +export * from './job-opening'; +export * from './job-skill'; +export * from './job'; +export * from './photo'; +export * from './place'; +export * from './presentation-comment'; +export * from './presentation-reaction'; +export * from './presentation'; +export * from './rsvp'; +export * from './skill'; +export * from './subject'; +export * from './user-code'; +export * from './user-contact'; +export * from './user-password'; +export * from './user-profile'; +export * from './user-skill'; +export * from './user-social'; +export * from './user-tag'; +export * from './user-visibility'; +export * from './user'; diff --git a/packages/shared/api-interfaces/src/lib/entities/photo.ts b/packages/shared/api-interfaces/src/lib/entities/photo.ts index 887286ef..dcfdfdc2 100644 --- a/packages/shared/api-interfaces/src/lib/entities/photo.ts +++ b/packages/shared/api-interfaces/src/lib/entities/photo.ts @@ -1,5 +1,6 @@ -import { UserRef } from '../dtos'; import { ImageMimeType } from '../types'; +import { UserTag } from './user-tag'; +import { UserRef } from '../dtos'; export interface Photo { id: string; @@ -14,5 +15,5 @@ export interface Photo { caption?: string; - tagged?: UserRef[]; + tags?: UserTag[]; } diff --git a/packages/shared/api-interfaces/src/lib/entities/user-tag.ts b/packages/shared/api-interfaces/src/lib/entities/user-tag.ts new file mode 100644 index 00000000..f018e1fa --- /dev/null +++ b/packages/shared/api-interfaces/src/lib/entities/user-tag.ts @@ -0,0 +1,9 @@ +import { UserRef } from '../dtos'; + +export interface UserTag { + x: number; + + y: number; + + user: UserRef; +} diff --git a/packages/shared/data-source/src/lib/dtos/index.ts b/packages/shared/data-source/src/lib/dtos/index.ts index f8da96ee..e5fb1d35 100644 --- a/packages/shared/data-source/src/lib/dtos/index.ts +++ b/packages/shared/data-source/src/lib/dtos/index.ts @@ -1,21 +1,22 @@ -export * from './album-ref'; -export * from './city-ref'; -export * from './event-ref'; -export * from './filter-operator'; -export * from './find-filter'; -export * from './find-params'; -export * from './image-ref'; -export * from './job'; -export * from './location'; -export * from './page'; -export * from './query-by-role-params'; -export * from './query-filter'; -export * from './query-location-params'; -export * from './query-location'; -export * from './query-params'; -export * from './query-sort'; -export * from './query'; -export * from './range'; -export * from './skill'; -export * from './user-profile'; -export * from './user-ref'; +export * from './album-ref'; +export * from './city-ref'; +export * from './event-ref'; +export * from './filter-operator'; +export * from './find-filter'; +export * from './find-params'; +export * from './image-ref'; +export * from './job'; +export * from './location'; +export * from './page'; +export * from './query-by-role-params'; +export * from './query-filter'; +export * from './query-location-params'; +export * from './query-location'; +export * from './query-params'; +export * from './query-sort'; +export * from './query'; +export * from './range'; +export * from './skill'; +export * from './user-profile'; +export * from './user-ref'; +export * from './user-tag'; diff --git a/packages/shared/data-source/src/lib/dtos/user-tag.ts b/packages/shared/data-source/src/lib/dtos/user-tag.ts new file mode 100644 index 00000000..ef8bbd4e --- /dev/null +++ b/packages/shared/data-source/src/lib/dtos/user-tag.ts @@ -0,0 +1,15 @@ +import { UserRef, UserTag } from '@devmx/shared-api-interfaces'; +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { UserRefDto } from './user-ref'; + +export class UserTagDto implements UserTag { + @ApiProperty() + x: number; + + @ApiProperty() + y: number; + + @Type(() => UserRefDto) + user: UserRef; +} diff --git a/packages/shared/data-source/src/lib/infrastructure/mongo.service.ts b/packages/shared/data-source/src/lib/infrastructure/mongo.service.ts index 210d9d04..9c7332f6 100644 --- a/packages/shared/data-source/src/lib/infrastructure/mongo.service.ts +++ b/packages/shared/data-source/src/lib/infrastructure/mongo.service.ts @@ -87,11 +87,18 @@ export abstract class MongoService async update(id: string, data: EditableEntity) { const value = this.applyEditableParser(data); - const updated = await this.entityModel - .findOneAndUpdate({ _id: id }, value) - .exec(); - - return updated?.toJSON() as T; + try { + const updated = await this.entityModel + .findOneAndUpdate({ _id: id }, value) + .exec(); + + return updated?.toJSON() as T; + } catch (err) { + if (err instanceof Error) { + throw err; + } + throw new Error('erro'); + } } async delete(id: string) { diff --git a/packages/shared/util-data/src/lib/models/index.ts b/packages/shared/util-data/src/lib/models/index.ts index ae7cc4d6..031927f7 100644 --- a/packages/shared/util-data/src/lib/models/index.ts +++ b/packages/shared/util-data/src/lib/models/index.ts @@ -1 +1 @@ -export * from './account-level'; +export * from './account-level'; diff --git a/packages/shared/util-data/src/lib/utils/index.ts b/packages/shared/util-data/src/lib/utils/index.ts index 30df794c..ee357be4 100644 --- a/packages/shared/util-data/src/lib/utils/index.ts +++ b/packages/shared/util-data/src/lib/utils/index.ts @@ -1,26 +1,27 @@ -export * from './async'; -export * from './by'; -export * from './create-code'; -export * from './create-form-data'; -export * from './create-mail'; -export * from './create-params'; -export * from './create-query-filter'; -export * from './create-query-params'; -export * from './create'; -export * from './deep-diff'; -export * from './deep-merge'; -export * from './entries'; -export * from './escape-reg-exp'; -export * from './freeze'; -export * from './hide-email'; -export * from './is-same-typeof'; -export * from './is-typeof'; -export * from './keys'; -export * from './like'; -export * from './merge'; -export * from './paginate'; -export * from './percent'; -export * from './predicate'; -export * from './same-keys'; -export * from './values'; -export * from './vector2'; +export * from './async'; +export * from './by'; +export * from './create-code'; +export * from './create-form-data'; +export * from './create-mail'; +export * from './create-params'; +export * from './create-query-filter'; +export * from './create-query-params'; +export * from './create'; +export * from './deep-diff'; +export * from './deep-merge'; +export * from './entries'; +export * from './escape-reg-exp'; +export * from './freeze'; +export * from './hide-email'; +export * from './is-same-typeof'; +export * from './is-typeof'; +export * from './keys'; +export * from './like'; +export * from './merge'; +export * from './paginate'; +export * from './percent'; +export * from './predicate'; +export * from './rect'; +export * from './same-keys'; +export * from './values'; +export * from './vector2'; diff --git a/packages/shared/util-data/src/lib/utils/rect.ts b/packages/shared/util-data/src/lib/utils/rect.ts new file mode 100644 index 00000000..f387887c --- /dev/null +++ b/packages/shared/util-data/src/lib/utils/rect.ts @@ -0,0 +1,103 @@ +import { Vector2Like } from './vector2'; + +export interface RectLike { + x: number; + y: number; + width: number; + height: number; +} + +export class Rect implements RectLike { + constructor( + public x = 0, + public y = 0, + public width = 0, + public height = 0 + ) {} + + // Propriedades adicionais da DOMRect + get top() { + return this.y; + } + + get left() { + return this.x; + } + + get bottom() { + return this.y + this.height; + } + + get right() { + return this.x + this.width; + } + + toJSON() { + return { + x: this.x, + y: this.y, + width: this.width, + height: this.height, + top: this.top, + left: this.left, + bottom: this.bottom, + right: this.right, + }; + } + + clone() { + return new Rect(this.x, this.y, this.width, this.height); + } + + contains(point: Vector2Like) { + return ( + point.x >= this.left && + point.x <= this.right && + point.y >= this.top && + point.y <= this.bottom + ); + } + + intersects(rect: Rect) { + return !( + rect.left > this.right || + rect.right < this.left || + rect.top > this.bottom || + rect.bottom < this.top + ); + } + + union(rect: Rect) { + const left = Math.min(this.left, rect.left); + const top = Math.min(this.top, rect.top); + const right = Math.max(this.right, rect.right); + const bottom = Math.max(this.bottom, rect.bottom); + + return new Rect(left, top, right - left, bottom - top); + } + + intersection(rect: Rect) { + const left = Math.max(this.left, rect.left); + const top = Math.max(this.top, rect.top); + const right = Math.min(this.right, rect.right); + const bottom = Math.min(this.bottom, rect.bottom); + + if (left < right && top < bottom) { + return new Rect(left, top, right - left, bottom - top); + } + + return null; + } + + translate(vector: Vector2Like) { + this.x += vector.x; + this.y += vector.y; + return this; + } + + scale(scaleX: number, scaleY: number) { + this.width *= scaleX; + this.height *= scaleY; + return this; + } +} diff --git a/packages/shared/util-data/src/lib/utils/vector2.ts b/packages/shared/util-data/src/lib/utils/vector2.ts index da57fbce..bb78a9bb 100644 --- a/packages/shared/util-data/src/lib/utils/vector2.ts +++ b/packages/shared/util-data/src/lib/utils/vector2.ts @@ -3,7 +3,7 @@ export interface Vector2Like { y: number; } -export class Vector2 { +export class Vector2 implements Vector2Like { constructor(public x = 0, public y = 0) {} set(x: number, y: number) { From a82fa176f327508f45b8f2a2c7a309ea1f2f4801 Mon Sep 17 00:00:00 2001 From: Gui Seek Date: Sat, 28 Dec 2024 13:11:59 -0300 Subject: [PATCH 4/4] fix(devmx): altera nome de categoria de icones e-commerce p/ commerce --- apps/devmx/public/icons/{E-Commerce => commerce}/Flamable.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/Fragile.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/Frost.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/box-stack.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/chat-percent.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/discount-15.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/discount-50.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/discount-70.svg | 0 .../public/icons/{E-Commerce => commerce}/discount-heart.svg | 0 .../public/icons/{E-Commerce => commerce}/double-shopping-bag.svg | 0 .../devmx/public/icons/{E-Commerce => commerce}/fast-shopping.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/fire-circle.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/keep-upwards.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/no-rain.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/outside-tab.svg | 0 .../public/icons/{E-Commerce => commerce}/percent-dashed.svg | 0 .../public/icons/{E-Commerce => commerce}/percent-discounts.svg | 0 .../devmx/public/icons/{E-Commerce => commerce}/receipt-close.svg | 0 .../devmx/public/icons/{E-Commerce => commerce}/receipt-minus.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/receipt-plus.svg | 0 .../public/icons/{E-Commerce => commerce}/receipt-return.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/receipt.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/scan-barcode.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/scan-code.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/scan-qr.svg | 0 .../public/icons/{E-Commerce => commerce}/search-percentage.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/shop.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-bag-alt.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-bag-check.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-bag-happy.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-bag-minus.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-bag-plus.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/shopping-bag.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-basket-box.svg | 0 .../icons/{E-Commerce => commerce}/shopping-basket-circle.svg | 0 .../icons/{E-Commerce => commerce}/shopping-basket-dashed.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-basket.svg | 0 .../public/icons/{E-Commerce => commerce}/shopping-cart-box.svg | 0 .../icons/{E-Commerce => commerce}/shopping-cart-circle.svg | 0 .../icons/{E-Commerce => commerce}/shopping-cart-dashed.svg | 0 .../devmx/public/icons/{E-Commerce => commerce}/shopping-cart.svg | 0 .../public/icons/{E-Commerce => commerce}/sold-within-eea.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/tag-dashed.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/ticket-alt.svg | 0 apps/devmx/public/icons/{E-Commerce => commerce}/ticket.svg | 0 45 files changed, 0 insertions(+), 0 deletions(-) rename apps/devmx/public/icons/{E-Commerce => commerce}/Flamable.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/Fragile.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/Frost.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/box-stack.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/chat-percent.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/discount-15.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/discount-50.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/discount-70.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/discount-heart.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/double-shopping-bag.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/fast-shopping.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/fire-circle.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/keep-upwards.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/no-rain.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/outside-tab.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/percent-dashed.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/percent-discounts.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/receipt-close.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/receipt-minus.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/receipt-plus.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/receipt-return.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/receipt.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/scan-barcode.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/scan-code.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/scan-qr.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/search-percentage.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shop.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-bag-alt.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-bag-check.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-bag-happy.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-bag-minus.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-bag-plus.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-bag.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-basket-box.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-basket-circle.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-basket-dashed.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-basket.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-cart-box.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-cart-circle.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-cart-dashed.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/shopping-cart.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/sold-within-eea.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/tag-dashed.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/ticket-alt.svg (100%) rename apps/devmx/public/icons/{E-Commerce => commerce}/ticket.svg (100%) diff --git a/apps/devmx/public/icons/E-Commerce/Flamable.svg b/apps/devmx/public/icons/commerce/Flamable.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/Flamable.svg rename to apps/devmx/public/icons/commerce/Flamable.svg diff --git a/apps/devmx/public/icons/E-Commerce/Fragile.svg b/apps/devmx/public/icons/commerce/Fragile.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/Fragile.svg rename to apps/devmx/public/icons/commerce/Fragile.svg diff --git a/apps/devmx/public/icons/E-Commerce/Frost.svg b/apps/devmx/public/icons/commerce/Frost.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/Frost.svg rename to apps/devmx/public/icons/commerce/Frost.svg diff --git a/apps/devmx/public/icons/E-Commerce/box-stack.svg b/apps/devmx/public/icons/commerce/box-stack.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/box-stack.svg rename to apps/devmx/public/icons/commerce/box-stack.svg diff --git a/apps/devmx/public/icons/E-Commerce/chat-percent.svg b/apps/devmx/public/icons/commerce/chat-percent.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/chat-percent.svg rename to apps/devmx/public/icons/commerce/chat-percent.svg diff --git a/apps/devmx/public/icons/E-Commerce/discount-15.svg b/apps/devmx/public/icons/commerce/discount-15.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/discount-15.svg rename to apps/devmx/public/icons/commerce/discount-15.svg diff --git a/apps/devmx/public/icons/E-Commerce/discount-50.svg b/apps/devmx/public/icons/commerce/discount-50.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/discount-50.svg rename to apps/devmx/public/icons/commerce/discount-50.svg diff --git a/apps/devmx/public/icons/E-Commerce/discount-70.svg b/apps/devmx/public/icons/commerce/discount-70.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/discount-70.svg rename to apps/devmx/public/icons/commerce/discount-70.svg diff --git a/apps/devmx/public/icons/E-Commerce/discount-heart.svg b/apps/devmx/public/icons/commerce/discount-heart.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/discount-heart.svg rename to apps/devmx/public/icons/commerce/discount-heart.svg diff --git a/apps/devmx/public/icons/E-Commerce/double-shopping-bag.svg b/apps/devmx/public/icons/commerce/double-shopping-bag.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/double-shopping-bag.svg rename to apps/devmx/public/icons/commerce/double-shopping-bag.svg diff --git a/apps/devmx/public/icons/E-Commerce/fast-shopping.svg b/apps/devmx/public/icons/commerce/fast-shopping.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/fast-shopping.svg rename to apps/devmx/public/icons/commerce/fast-shopping.svg diff --git a/apps/devmx/public/icons/E-Commerce/fire-circle.svg b/apps/devmx/public/icons/commerce/fire-circle.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/fire-circle.svg rename to apps/devmx/public/icons/commerce/fire-circle.svg diff --git a/apps/devmx/public/icons/E-Commerce/keep-upwards.svg b/apps/devmx/public/icons/commerce/keep-upwards.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/keep-upwards.svg rename to apps/devmx/public/icons/commerce/keep-upwards.svg diff --git a/apps/devmx/public/icons/E-Commerce/no-rain.svg b/apps/devmx/public/icons/commerce/no-rain.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/no-rain.svg rename to apps/devmx/public/icons/commerce/no-rain.svg diff --git a/apps/devmx/public/icons/E-Commerce/outside-tab.svg b/apps/devmx/public/icons/commerce/outside-tab.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/outside-tab.svg rename to apps/devmx/public/icons/commerce/outside-tab.svg diff --git a/apps/devmx/public/icons/E-Commerce/percent-dashed.svg b/apps/devmx/public/icons/commerce/percent-dashed.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/percent-dashed.svg rename to apps/devmx/public/icons/commerce/percent-dashed.svg diff --git a/apps/devmx/public/icons/E-Commerce/percent-discounts.svg b/apps/devmx/public/icons/commerce/percent-discounts.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/percent-discounts.svg rename to apps/devmx/public/icons/commerce/percent-discounts.svg diff --git a/apps/devmx/public/icons/E-Commerce/receipt-close.svg b/apps/devmx/public/icons/commerce/receipt-close.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/receipt-close.svg rename to apps/devmx/public/icons/commerce/receipt-close.svg diff --git a/apps/devmx/public/icons/E-Commerce/receipt-minus.svg b/apps/devmx/public/icons/commerce/receipt-minus.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/receipt-minus.svg rename to apps/devmx/public/icons/commerce/receipt-minus.svg diff --git a/apps/devmx/public/icons/E-Commerce/receipt-plus.svg b/apps/devmx/public/icons/commerce/receipt-plus.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/receipt-plus.svg rename to apps/devmx/public/icons/commerce/receipt-plus.svg diff --git a/apps/devmx/public/icons/E-Commerce/receipt-return.svg b/apps/devmx/public/icons/commerce/receipt-return.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/receipt-return.svg rename to apps/devmx/public/icons/commerce/receipt-return.svg diff --git a/apps/devmx/public/icons/E-Commerce/receipt.svg b/apps/devmx/public/icons/commerce/receipt.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/receipt.svg rename to apps/devmx/public/icons/commerce/receipt.svg diff --git a/apps/devmx/public/icons/E-Commerce/scan-barcode.svg b/apps/devmx/public/icons/commerce/scan-barcode.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/scan-barcode.svg rename to apps/devmx/public/icons/commerce/scan-barcode.svg diff --git a/apps/devmx/public/icons/E-Commerce/scan-code.svg b/apps/devmx/public/icons/commerce/scan-code.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/scan-code.svg rename to apps/devmx/public/icons/commerce/scan-code.svg diff --git a/apps/devmx/public/icons/E-Commerce/scan-qr.svg b/apps/devmx/public/icons/commerce/scan-qr.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/scan-qr.svg rename to apps/devmx/public/icons/commerce/scan-qr.svg diff --git a/apps/devmx/public/icons/E-Commerce/search-percentage.svg b/apps/devmx/public/icons/commerce/search-percentage.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/search-percentage.svg rename to apps/devmx/public/icons/commerce/search-percentage.svg diff --git a/apps/devmx/public/icons/E-Commerce/shop.svg b/apps/devmx/public/icons/commerce/shop.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shop.svg rename to apps/devmx/public/icons/commerce/shop.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-bag-alt.svg b/apps/devmx/public/icons/commerce/shopping-bag-alt.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-bag-alt.svg rename to apps/devmx/public/icons/commerce/shopping-bag-alt.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-bag-check.svg b/apps/devmx/public/icons/commerce/shopping-bag-check.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-bag-check.svg rename to apps/devmx/public/icons/commerce/shopping-bag-check.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-bag-happy.svg b/apps/devmx/public/icons/commerce/shopping-bag-happy.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-bag-happy.svg rename to apps/devmx/public/icons/commerce/shopping-bag-happy.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-bag-minus.svg b/apps/devmx/public/icons/commerce/shopping-bag-minus.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-bag-minus.svg rename to apps/devmx/public/icons/commerce/shopping-bag-minus.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-bag-plus.svg b/apps/devmx/public/icons/commerce/shopping-bag-plus.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-bag-plus.svg rename to apps/devmx/public/icons/commerce/shopping-bag-plus.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-bag.svg b/apps/devmx/public/icons/commerce/shopping-bag.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-bag.svg rename to apps/devmx/public/icons/commerce/shopping-bag.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-basket-box.svg b/apps/devmx/public/icons/commerce/shopping-basket-box.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-basket-box.svg rename to apps/devmx/public/icons/commerce/shopping-basket-box.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-basket-circle.svg b/apps/devmx/public/icons/commerce/shopping-basket-circle.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-basket-circle.svg rename to apps/devmx/public/icons/commerce/shopping-basket-circle.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-basket-dashed.svg b/apps/devmx/public/icons/commerce/shopping-basket-dashed.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-basket-dashed.svg rename to apps/devmx/public/icons/commerce/shopping-basket-dashed.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-basket.svg b/apps/devmx/public/icons/commerce/shopping-basket.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-basket.svg rename to apps/devmx/public/icons/commerce/shopping-basket.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-cart-box.svg b/apps/devmx/public/icons/commerce/shopping-cart-box.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-cart-box.svg rename to apps/devmx/public/icons/commerce/shopping-cart-box.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-cart-circle.svg b/apps/devmx/public/icons/commerce/shopping-cart-circle.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-cart-circle.svg rename to apps/devmx/public/icons/commerce/shopping-cart-circle.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-cart-dashed.svg b/apps/devmx/public/icons/commerce/shopping-cart-dashed.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-cart-dashed.svg rename to apps/devmx/public/icons/commerce/shopping-cart-dashed.svg diff --git a/apps/devmx/public/icons/E-Commerce/shopping-cart.svg b/apps/devmx/public/icons/commerce/shopping-cart.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/shopping-cart.svg rename to apps/devmx/public/icons/commerce/shopping-cart.svg diff --git a/apps/devmx/public/icons/E-Commerce/sold-within-eea.svg b/apps/devmx/public/icons/commerce/sold-within-eea.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/sold-within-eea.svg rename to apps/devmx/public/icons/commerce/sold-within-eea.svg diff --git a/apps/devmx/public/icons/E-Commerce/tag-dashed.svg b/apps/devmx/public/icons/commerce/tag-dashed.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/tag-dashed.svg rename to apps/devmx/public/icons/commerce/tag-dashed.svg diff --git a/apps/devmx/public/icons/E-Commerce/ticket-alt.svg b/apps/devmx/public/icons/commerce/ticket-alt.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/ticket-alt.svg rename to apps/devmx/public/icons/commerce/ticket-alt.svg diff --git a/apps/devmx/public/icons/E-Commerce/ticket.svg b/apps/devmx/public/icons/commerce/ticket.svg similarity index 100% rename from apps/devmx/public/icons/E-Commerce/ticket.svg rename to apps/devmx/public/icons/commerce/ticket.svg