Skip to content

Commit df730ad

Browse files
feat: Add Veil, Gallery, and Treatments pages, a shared CardView component, and backend gallery schema and repository.
1 parent a767102 commit df730ad

24 files changed

Lines changed: 367 additions & 511 deletions

backend/src/modules/gallery/infrastructure/repositories/gallery.repository.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ export class GalleryRepository {
1616

1717
async findAll(): Promise<Gallery[]> {
1818
const docs = await this.galleryModel.find().exec();
19-
return docs.map((doc) => this.toDomain(doc));
19+
const doc = docs.map((doc) => this.toDomain(doc));
20+
console.log('DOCS: ', doc);
21+
22+
return doc;
2023
}
2124

2225
async create(gallery: Omit<Gallery, 'id' | 'createdAt'>): Promise<Gallery> {
@@ -49,14 +52,16 @@ export class GalleryRepository {
4952
}
5053

5154
private toDomain(doc: GalleryDocument): Gallery {
52-
const d = doc as any;
55+
const { _id, title, imageUrl, category, tags, createdAt } = doc as any;
56+
console.log('createdAT: ', createdAt);
57+
5358
return new Gallery(
54-
d._id.toString(),
55-
d.title,
56-
d.imageUrl,
57-
d.category,
58-
d.tags,
59-
d.createdAt,
59+
_id.toString(),
60+
title,
61+
imageUrl,
62+
category,
63+
tags,
64+
createdAt,
6065
);
6166
}
6267
}

backend/src/modules/gallery/infrastructure/schemas/gallery.schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ export class GallerySchemaEntity {
2020
@Prop()
2121
alt: string;
2222

23+
@Prop()
24+
createdAt: Date;
25+
26+
@Prop()
27+
updatedAt: Date;
2328
}
2429

2530
export const GallerySchema = SchemaFactory.createForClass(GallerySchemaEntity);

frontend/src/pages/gallery/gallery.component.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@ <h2 class="font-serif text-4xl text-gray-900 mb-2" i18n="@@galleryTitle">Gallery
5050
<!-- Image Grid -->
5151
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8 animate-page-enter">
5252
@for(image of filteredImages(); track image.id; let i = $index) {
53-
<app-gallery-card
54-
[image]="image"
53+
<app-card-view
54+
[data]="image"
55+
[config]="galleryCardConfig"
5556
[index]="i"
5657
(edit)="openModal($event)"
5758
(viewImage)="openImageModal($event)"
58-
(deleteCard)="deleteImage($event)"
59-
></app-gallery-card>
59+
(delete)="deleteImage($event.toString())"
60+
></app-card-view>
6061
}
6162
</div>
6263
} @else {

frontend/src/pages/gallery/gallery.component.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,22 @@ import { FormsModule } from "@angular/forms";
1111
import { GalleryService, GALLERY_CATEGORIES, GalleryCategories } from "@entities/gallery";
1212
import { Gallery, ImageCategory } from "@shared/models";
1313
import { GalleryFormComponent } from "./ui/gallery-form/gallery-form.component";
14-
import { GalleryCardComponent } from "./ui/gallery-card/gallery-card.component";
15-
import { ImagePopupComponent, ListViewComponent, ListViewColumn } from "@shared/ui";
14+
import { ImagePopupComponent, ListViewComponent, ListViewColumn, CardViewComponent, CardViewConfig } from "@shared/ui";
1615
import { convertFormData, excludeFormDataProperties } from "@shared/lib/object";
1716
import { linkServerConvert } from "@shared/lib";
17+
import { environment } from "@environments/environment";
1818

1919
@Component({
2020
selector: "app-gallery",
2121
standalone: true,
22-
imports: [CommonModule, FormsModule, GalleryFormComponent, GalleryCardComponent, ImagePopupComponent, ListViewComponent],
22+
imports: [CommonModule, FormsModule, GalleryFormComponent, CardViewComponent, ImagePopupComponent, ListViewComponent],
2323
changeDetection: ChangeDetectionStrategy.OnPush,
2424
templateUrl: "./gallery.component.html",
2525
styleUrls: ["./gallery.component.scss"],
2626
})
2727
export class GalleryComponent implements OnInit {
2828
private galleryService = inject(GalleryService);
29+
env = signal(environment);
2930

3031
isDragging = signal(false);
3132
isModalOpen = signal(false);
@@ -45,6 +46,18 @@ export class GalleryComponent implements OnInit {
4546
{ key: 'actions', label: 'Actions', type: 'actions' }
4647
]);
4748

49+
galleryCardConfig: CardViewConfig = {
50+
imageField: 'imageUrl',
51+
titleField: 'title',
52+
subtitleField: 'category',
53+
topRightField: 'createdAt',
54+
topRightType: 'date',
55+
details: [
56+
{ label: 'Status', field: 'status', type: 'status' },
57+
{ label: 'Created At', field: 'createdAt', type: 'date' }
58+
]
59+
};
60+
4861
filteredImages = computed(() => {
4962
const filter = this.activeFilter();
5063
if (filter === GalleryCategories.ALL) {
@@ -112,7 +125,9 @@ export class GalleryComponent implements OnInit {
112125
}
113126

114127
openImageModal(imageUrl: string) {
115-
this.selectedImage.set(linkServerConvert(imageUrl));
128+
if (!imageUrl) return;
129+
const isAbsolute = imageUrl.startsWith("http") || imageUrl.startsWith("blob") || imageUrl.includes(this.env().apiUrl);
130+
this.selectedImage.set(isAbsolute ? imageUrl : linkServerConvert(imageUrl));
116131
}
117132

118133
closeImageModal() {

frontend/src/pages/gallery/ui/gallery-card/gallery-card.component.html

Lines changed: 0 additions & 30 deletions
This file was deleted.

frontend/src/pages/gallery/ui/gallery-card/gallery-card.component.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

frontend/src/pages/gallery/ui/gallery-form/gallery-form.component.html

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,19 @@ <h3 class="font-display text-xl text-gray-900">
2020
<div (click)="fileInput.click()" class="aspect-[4/3] w-full rounded-xl border-2 border-dashed border-gray-300 flex flex-col items-center justify-center relative group cursor-pointer hover:border-primary transition-colors bg-gray-50">
2121
@if (previewImage()) {
2222
<img [src]="previewImage()" alt="Image preview" class="w-full h-full object-cover rounded-lg absolute inset-0">
23-
<div class="absolute inset-0 bg-black/50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-lg">
23+
<div class="absolute inset-0 bg-black/50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-lg gap-4">
2424
<div class="text-center text-white">
2525
<span class="material-symbols-outlined text-3xl">edit</span>
2626
<p class="text-xs mt-1 font-semibold" i18n="@@galleryChangeImage">Change Image</p>
2727
</div>
28+
<button
29+
type="button"
30+
(click)="$event.stopPropagation(); openImageModal(previewImage())"
31+
class="p-2 bg-white/20 hover:bg-white/40 rounded-full backdrop-blur-md transition-all text-white"
32+
title="Zoom In"
33+
>
34+
<span class="material-symbols-outlined text-2xl">zoom_in</span>
35+
</button>
2836
</div>
2937
} @else {
3038
<div class="text-center text-gray-500">
@@ -90,3 +98,10 @@ <h3 class="font-display text-xl text-gray-900">
9098
</div>
9199
</div>
92100
</div>
101+
102+
<!-- Fullscreen Image Modal -->
103+
<app-image-popup
104+
[imageUrl]="selectedImage()"
105+
title="Mavluda Beauty • Gallery Preview"
106+
(close)="closeImageModal()"
107+
></app-image-popup>

frontend/src/pages/gallery/ui/gallery-form/gallery-form.component.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,20 @@ import { form, FormField } from "@angular/forms/signals";
1212
import { Gallery, ImageCategory } from "@shared/models";
1313
import { linkServerConvert } from "@shared/lib";
1414
import { galleryFormData, galleryValidationSchema, resetGalleryData } from "@features/gallery";
15+
import { ImagePopupComponent } from "@shared/ui";
16+
import { environment } from "@environments/environment";
1517

1618
@Component({
1719
selector: "app-gallery-form",
1820
standalone: true,
19-
imports: [CommonModule, FormField],
21+
imports: [CommonModule, FormField, ImagePopupComponent],
2022
changeDetection: ChangeDetectionStrategy.OnPush,
2123
templateUrl: "./gallery-form.component.html",
2224
})
2325
export class GalleryFormComponent implements OnInit {
2426
image = input.required<Gallery>();
2527
filters = input.required<ImageCategory[]>();
28+
env = signal(environment);
2629

2730
save = output<{ data: any; file: File | null }>();
2831
cancel = output<void>();
@@ -36,6 +39,9 @@ export class GalleryFormComponent implements OnInit {
3639
});
3740
isEditMode = signal(false);
3841

42+
// Image Preview State for Popup
43+
selectedImage = signal<string | null>(null);
44+
3945
ngOnInit(): void {
4046
this.initForm();
4147
}
@@ -92,4 +98,15 @@ export class GalleryFormComponent implements OnInit {
9298
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
9399
.join(' ');
94100
}
101+
102+
// Image Modal Methods
103+
openImageModal(imageUrl: string | null) {
104+
if (!imageUrl) return;
105+
const isAbsolute = imageUrl.startsWith("http") || imageUrl.startsWith("blob") || imageUrl.includes(this.env().apiUrl);
106+
this.selectedImage.set(isAbsolute ? imageUrl : linkServerConvert(imageUrl));
107+
}
108+
109+
closeImageModal() {
110+
this.selectedImage.set(null);
111+
}
95112
}

0 commit comments

Comments
 (0)