Skip to content

Commit e6e5f7b

Browse files
feat: implement admin settings module with backend persistence and frontend management UI
1 parent f73b3a4 commit e6e5f7b

15 files changed

Lines changed: 583 additions & 326 deletions

File tree

.gemini/commands/test/gen.toml

Whitespace-only changes.

.gemini/gemini.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello World

backend/src/modules/admin-settings/domain/admin-settings.entity.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export class AdminSettings implements IAdminSettings {
1111
public readonly socialLinks: Record<string, string>,
1212
public readonly workHours: Record<string, string>,
1313
public readonly ownerInfo: IOwnerInfo,
14+
public readonly biography: string,
15+
public readonly philosophy: string,
1416
public readonly galleryCategories: string[],
1517
public readonly treatmentCategories: string[],
1618
public readonly veilSilhouettes: string[],

backend/src/modules/admin-settings/domain/interfaces/admin-settings.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface IAdminSettings {
1515
socialLinks: Record<string, string>;
1616
workHours: Record<string, string>;
1717
ownerInfo: IOwnerInfo;
18+
biography: string;
19+
philosophy: string;
1820
galleryCategories: string[];
1921
treatmentCategories: string[];
2022
veilSilhouettes: string[];

backend/src/modules/admin-settings/infrastructure/repositories/admin-settings.repository.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export class AdminSettingsRepository {
5454
socialLinks,
5555
workHours,
5656
doc.ownerInfo,
57+
doc.biography || '',
58+
doc.philosophy || '',
5759
doc.galleryCategories || [],
5860
doc.treatmentCategories || [],
5961
doc.veilSilhouettes || [],

backend/src/modules/admin-settings/infrastructure/schemas/admin-settings.schema.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ export class AdminSettingsSchemaEntity {
2727
phoneNumber: string;
2828
};
2929

30+
@Prop({ type: String, default: '' })
31+
biography: string;
32+
33+
@Prop({ type: String, default: '' })
34+
philosophy: string;
35+
3036
@Prop({ type: [String], default: [] })
3137
galleryCategories: string[];
3238

backend/src/modules/admin-settings/presentation/dto/create-admin-settings.dto.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ export class CreateAdminSettingsDto {
5454
@IsNotEmpty()
5555
ownerInfo: OwnerInfoDto;
5656

57+
@IsString()
58+
@IsOptional()
59+
biography: string;
60+
61+
@IsString()
62+
@IsOptional()
63+
philosophy: string;
64+
5765
@IsArray()
5866
@IsString({ each: true })
5967
@IsOptional()

frontend/src/entities/admin-settings/admin-settings.model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface AdminSettings {
1515
socialLinks: Record<string, string>;
1616
workHours: Record<string, string>;
1717
ownerInfo: OwnerInfo;
18+
biography: string;
19+
philosophy: string;
1820
galleryCategories: string[];
1921
treatmentCategories: string[];
2022
veilSilhouettes: string[];

frontend/src/pages/settings/settings.component.html

Lines changed: 40 additions & 286 deletions
Large diffs are not rendered by default.
Lines changed: 77 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
import { Component, ChangeDetectionStrategy, signal, inject, OnInit } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { FormsModule } from '@angular/forms';
4-
import { AdminSettingsService } from '@entities/admin-settings';
4+
import { AdminSettingsService, AdminLocation, OwnerInfo } from '@entities/admin-settings';
5+
import { BusinessProfileComponent } from './ui/business-profile.component';
6+
import { SocialMatrixComponent, SocialPlatform } from './ui/social-matrix.component';
7+
import { GeneralInfoComponent } from './ui/general-info.component';
8+
import { AdditionalLinksComponent, AdditionalLink } from './ui/additional-links.component';
9+
import { SelectsSettingsComponent, SelectListType } from './ui/selects-settings.component';
510

611
type SettingsTab = 'Business Profile' | 'Social Matrix' | 'General Info' | 'Additional Links' | 'SELECTS';
712

8-
interface SocialPlatform {
9-
id: number;
10-
name: string;
11-
url: string;
12-
iconUrl: string;
13-
alt: string;
14-
}
15-
16-
interface AdditionalLink {
17-
id: number;
18-
label: string;
19-
targetUrl: string;
20-
category: string;
21-
categoryColor: 'blue' | 'green';
22-
}
23-
2413
@Component({
2514
selector: 'app-settings',
2615
standalone: true,
27-
imports: [CommonModule, FormsModule],
16+
imports: [
17+
CommonModule,
18+
FormsModule,
19+
BusinessProfileComponent,
20+
SocialMatrixComponent,
21+
GeneralInfoComponent,
22+
AdditionalLinksComponent,
23+
SelectsSettingsComponent
24+
],
2825
changeDetection: ChangeDetectionStrategy.OnPush,
2926
templateUrl: './settings.component.html',
3027
styleUrls: ['./settings.component.scss']
@@ -35,6 +32,12 @@ export class SettingsComponent implements OnInit {
3532
tabs: SettingsTab[] = ['Business Profile', 'Social Matrix', 'General Info', 'Additional Links', 'SELECTS'];
3633
activeTab = signal<SettingsTab>('Business Profile');
3734

35+
// Admin Settings State (Signals)
36+
location = signal<AdminLocation>({ address: '', latitude: 0, longitude: 0 });
37+
ownerInfo = signal<OwnerInfo>({ name: '', phoneNumber: '' });
38+
biography = signal<string>('');
39+
philosophy = signal<string>('');
40+
3841
// Selection Lists (Signals)
3942
galleryCategories = signal<string[]>([]);
4043
treatmentCategories = signal<string[]>([]);
@@ -43,6 +46,7 @@ export class SettingsComponent implements OnInit {
4346
veilTrainLengths = signal<string[]>([]);
4447
veilNecklines = signal<string[]>([]);
4548

49+
// UI-only for now or needs conversion
4650
socialPlatforms = signal<SocialPlatform[]>([
4751
{ id: 1, name: 'Instagram', url: 'https://instagram.com/mavluda_azizova', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/a/a5/Instagram_icon.png', alt: 'Instagram' },
4852
{ id: 2, name: 'Telegram', url: 'https://t.me/mavluda_beauty', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/82/Telegram_logo.svg', alt: 'Telegram' },
@@ -57,6 +61,10 @@ export class SettingsComponent implements OnInit {
5761
ngOnInit() {
5862
this.adminSettingsService.getSettings().subscribe(settings => {
5963
if (settings) {
64+
this.location.set(settings.location || { address: '', latitude: 0, longitude: 0 });
65+
this.ownerInfo.set(settings.ownerInfo || { name: '', phoneNumber: '' });
66+
this.biography.set(settings.biography || '');
67+
this.philosophy.set(settings.philosophy || '');
6068
this.galleryCategories.set(settings.galleryCategories || []);
6169
this.treatmentCategories.set(settings.treatmentCategories || []);
6270
this.veilSilhouettes.set(settings.veilSilhouettes || []);
@@ -67,11 +75,44 @@ export class SettingsComponent implements OnInit {
6775
});
6876
}
6977

78+
// --- Tab-specific Save methods ---
79+
80+
saveBusinessProfile() {
81+
this.adminSettingsService.updateSettings({
82+
location: this.location(),
83+
ownerInfo: this.ownerInfo()
84+
}).subscribe();
85+
}
86+
87+
saveSocialMatrix() {
88+
// Logic to convert SocialPlatform[] back to Record if needed
89+
// Currently just local state update or dummy save
90+
console.log('Saving social matrix:', this.socialPlatforms());
91+
}
92+
93+
saveGeneralInfo() {
94+
this.adminSettingsService.updateSettings({
95+
biography: this.biography(),
96+
philosophy: this.philosophy()
97+
}).subscribe();
98+
}
99+
100+
saveSelectionLists() {
101+
this.adminSettingsService.updateSettings({
102+
galleryCategories: this.galleryCategories(),
103+
treatmentCategories: this.treatmentCategories(),
104+
veilSilhouettes: this.veilSilhouettes(),
105+
veilFabrics: this.veilFabrics(),
106+
veilTrainLengths: this.veilTrainLengths(),
107+
veilNecklines: this.veilNecklines()
108+
}).subscribe();
109+
}
110+
70111
// --- CRUD for Selection Lists ---
71112

72-
addItem(list: 'gallery' | 'treatment' | 'silhouette' | 'fabric' | 'train' | 'neckline') {
113+
addItem(type: SelectListType) {
73114
const newItem = 'New Item';
74-
switch(list) {
115+
switch(type) {
75116
case 'gallery': this.galleryCategories.update(items => [...items, newItem]); break;
76117
case 'treatment': this.treatmentCategories.update(items => [...items, newItem]); break;
77118
case 'silhouette': this.veilSilhouettes.update(items => [...items, newItem]); break;
@@ -81,8 +122,8 @@ export class SettingsComponent implements OnInit {
81122
}
82123
}
83124

84-
updateItem(list: 'gallery' | 'treatment' | 'silhouette' | 'fabric' | 'train' | 'neckline', index: number, value: string) {
85-
switch(list) {
125+
updateItem(type: SelectListType, index: number, value: string) {
126+
switch(type) {
86127
case 'gallery': this.galleryCategories.update(items => { items[index] = value; return [...items]; }); break;
87128
case 'treatment': this.treatmentCategories.update(items => { items[index] = value; return [...items]; }); break;
88129
case 'silhouette': this.veilSilhouettes.update(items => { items[index] = value; return [...items]; }); break;
@@ -92,8 +133,8 @@ export class SettingsComponent implements OnInit {
92133
}
93134
}
94135

95-
removeItem(list: 'gallery' | 'treatment' | 'silhouette' | 'fabric' | 'train' | 'neckline', index: number) {
96-
switch(list) {
136+
removeItem(type: SelectListType, index: number) {
137+
switch(type) {
97138
case 'gallery': this.galleryCategories.update(items => items.filter((_, i) => i !== index)); break;
98139
case 'treatment': this.treatmentCategories.update(items => items.filter((_, i) => i !== index)); break;
99140
case 'silhouette': this.veilSilhouettes.update(items => items.filter((_, i) => i !== index)); break;
@@ -103,28 +144,14 @@ export class SettingsComponent implements OnInit {
103144
}
104145
}
105146

106-
saveSelectionLists() {
107-
const settings = this.adminSettingsService.settings();
108-
if (settings) {
109-
this.adminSettingsService.updateSettings({
110-
galleryCategories: this.galleryCategories(),
111-
treatmentCategories: this.treatmentCategories(),
112-
veilSilhouettes: this.veilSilhouettes(),
113-
veilFabrics: this.veilFabrics(),
114-
veilTrainLengths: this.veilTrainLengths(),
115-
veilNecklines: this.veilNecklines()
116-
}).subscribe();
117-
}
118-
}
119-
120-
// --- Existing Methods ---
147+
// --- Social Platform methods ---
121148

122149
addSocialPlatform() {
123150
const newPlatform: SocialPlatform = {
124151
id: Date.now(),
125152
name: 'New Platform',
126153
url: 'https://',
127-
iconUrl: 'https://lh3.googleusercontent.com/aida-public/AB6AXuDAh_4_s1A2wU4k2pXsv2gAFpqfGSL2P9yA6_LC9Sl_3_k39n-u-f_WjI-QO2J_o73A1I5k8gGf6f_h_Y2P-N3g-T_w_o-g_k-Z_4_w_o-g_k-Z_4_w_o-g_k-Z_4_w_o-g_k-Z_4_w_o-g_k-Z_4_w_o-g_k-Z_4_w_o-g_k-Z_4', // Placeholder icon
154+
iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/a/a5/Instagram_icon.png', // Default icon
128155
alt: 'New'
129156
};
130157
this.socialPlatforms.update(platforms => [...platforms, newPlatform]);
@@ -134,6 +161,12 @@ export class SettingsComponent implements OnInit {
134161
this.socialPlatforms.update(platforms => platforms.filter(p => p.id !== id));
135162
}
136163

164+
updateSocialPlatform(platform: SocialPlatform) {
165+
this.socialPlatforms.update(platforms => platforms.map(p => p.id === platform.id ? platform : p));
166+
}
167+
168+
// --- Additional Links methods ---
169+
137170
addLink() {
138171
const newLink: AdditionalLink = {
139172
id: Date.now(),
@@ -148,4 +181,8 @@ export class SettingsComponent implements OnInit {
148181
removeLink(id: number) {
149182
this.additionalLinks.update(links => links.filter(l => l.id !== id));
150183
}
184+
185+
updateLink(link: AdditionalLink) {
186+
this.additionalLinks.update(links => links.map(l => l.id === link.id ? link : l));
187+
}
151188
}

0 commit comments

Comments
 (0)