diff --git a/apps/api/api/services/BandIdentity/branding.service.ts b/apps/api/api/services/BandIdentity/branding.service.ts
index bfa4cb955..c805d584e 100644
--- a/apps/api/api/services/BandIdentity/branding.service.ts
+++ b/apps/api/api/services/BandIdentity/branding.service.ts
@@ -26,20 +26,18 @@ import { PdfService } from '../pdf.service';
import { cacheService } from '../cache.service';
import crypto from 'crypto';
import { projectService } from '../project.service';
-import { LogoJsonToSvgService } from './logoJsonToSvg.service';
import { SvgOptimizerService } from './svgOptimizer.service';
export class BrandingService extends GenericService {
private pdfService: PdfService;
- private logoJsonToSvgService: LogoJsonToSvgService;
// Configuration LLM pour la génération de logos et variations
// Temperature modérée pour équilibrer créativité et cohérence
private static readonly LOGO_LLM_CONFIG = {
provider: LLMProvider.GEMINI,
- modelName: 'gemini-2.0-flash',
+ modelName: 'gemini-3-flash-preview',
llmOptions: {
- maxOutputTokens: 3500,
+ maxOutputTokens: 500,
temperature: 0.4, // Équilibre entre créativité et cohérence
topP: 0.9,
topK: 55,
@@ -73,7 +71,6 @@ export class BrandingService extends GenericService {
constructor(promptService: PromptService) {
super(promptService);
this.pdfService = new PdfService();
- this.logoJsonToSvgService = new LogoJsonToSvgService();
logger.info('BrandingService initialized with optimized logo generation');
}
@@ -1744,7 +1741,7 @@ export class BrandingService extends GenericService {
*/
private generateReadmeContent(project: any, extension: string, fileCount: number): string {
return `Logo Package - ${project.name}
-
+
Project: ${project.name}
Description: ${project.description || 'No description available'}
Format: ${extension.toUpperCase()}
@@ -1775,7 +1772,7 @@ Features:
Variations:
- light-background: Optimized for light backgrounds
-- dark-background: Optimized for dark backgrounds
+- dark-background: Optimized for dark backgrounds
- monochrome: Single color version
${
diff --git a/apps/api/api/services/BandIdentity/svgIconExtractor.service.ts b/apps/api/api/services/BandIdentity/svgIconExtractor.service.ts
deleted file mode 100644
index a2d574e04..000000000
--- a/apps/api/api/services/BandIdentity/svgIconExtractor.service.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import logger from '../../config/logger';
-
-/**
- * Service pour traiter les logos et variations SVG
- * Maintenant que l'IA génère directement les icônes séparément, ce service se concentre sur les variations
- */
-export class SvgIconExtractorService {
- /**
- * @deprecated Cette méthode n'est plus nécessaire car l'IA génère directement les icônes
- * Conservée pour compatibilité descendante
- */
- static extractIcon(logoSvg: string): {
- fullLogo: string;
- iconOnly: string;
- } {
- logger.warn('extractIcon is deprecated - AI now generates icons directly');
- return {
- fullLogo: logoSvg,
- iconOnly: logoSvg,
- };
- }
-
- /**
- * @deprecated Cette méthode n'est plus nécessaire car l'IA génère directement les icônes
- * Conservée pour compatibilité descendante
- */
- static extractIconsFromLogos(logos: any[]): any[] {
- logger.warn('extractIconsFromLogos is deprecated - AI now generates icons directly');
- return logos;
- }
-
- /**
- * Génère les variations d'un logo avec et sans texte
- * @param logoSvg - SVG du logo complet
- * @param variations - Variations générées par l'IA
- * @returns Variations avec texte et sans texte
- */
- static generateVariationsWithText(
- logoSvg: string,
- variations: {
- lightBackground?: string;
- darkBackground?: string;
- monochrome?: string;
- }
- ): {
- withText: {
- lightBackground?: string;
- darkBackground?: string;
- monochrome?: string;
- };
- iconOnly: {
- lightBackground?: string;
- darkBackground?: string;
- monochrome?: string;
- };
- } {
- logger.info('Generating variations with and without text');
-
- const result = {
- withText: {} as any,
- iconOnly: {} as any,
- };
-
- // Pour chaque variation, créer une version avec texte et une sans texte
- Object.entries(variations).forEach(([key, variationSvg]) => {
- if (variationSvg) {
- // Version avec texte : adapter le logo complet aux couleurs de la variation
- result.withText[key] = this.adaptLogoColors(logoSvg, variationSvg);
- // Version sans texte : utiliser la variation telle quelle (déjà sans texte)
- result.iconOnly[key] = variationSvg;
- }
- });
-
- return result;
- }
-
- /**
- * Adapte les couleurs du logo complet selon une variation
- * @param fullLogoSvg - Logo complet avec texte
- * @param variationSvg - Variation avec les bonnes couleurs
- * @returns Logo complet avec les couleurs adaptées
- */
- private static adaptLogoColors(fullLogoSvg: string, variationSvg: string): string {
- try {
- // Extraire les couleurs de la variation
- const colors = this.extractColorsFromSvg(variationSvg);
-
- // Appliquer ces couleurs au logo complet
- let adaptedLogo = fullLogoSvg;
-
- // Remplacer les couleurs principales
- colors.forEach((color, index) => {
- // Remplacer les couleurs par ordre d'apparition
- const colorRegex = new RegExp(`fill="[^"]*"`, 'g');
- if (index === 0) {
- adaptedLogo = adaptedLogo.replace(colorRegex, `fill="${color}"`);
- }
- });
-
- return adaptedLogo;
- } catch (error) {
- logger.error('Error adapting logo colors:', error);
- return fullLogoSvg;
- }
- }
-
- /**
- * Extrait les couleurs d'un SVG
- * @param svgContent - Contenu SVG
- * @returns Tableau des couleurs trouvées
- */
- private static extractColorsFromSvg(svgContent: string): string[] {
- const colors: string[] = [];
- const fillMatches = svgContent.match(/fill="([^"]*)"/g);
-
- if (fillMatches) {
- fillMatches.forEach((match) => {
- const color = match.match(/fill="([^"]*)"/)?.[1];
- if (color && color !== 'none' && !colors.includes(color)) {
- colors.push(color);
- }
- });
- }
-
- return colors;
- }
-
- /**
- * Supprime les éléments vides d'un SVG
- * @param svgElement - Élément SVG à nettoyer
- */
- private static removeEmptyElements(svgElement: SVGElement): void {
- const emptyElements = svgElement.querySelectorAll('g:empty, defs:empty');
- emptyElements.forEach((element) => element.remove());
- }
-}
diff --git a/apps/api/api/services/BusinessPlan/businessPlan.service.ts b/apps/api/api/services/BusinessPlan/businessPlan.service.ts
index 0f629cc56..d9bd4f659 100644
--- a/apps/api/api/services/BusinessPlan/businessPlan.service.ts
+++ b/apps/api/api/services/BusinessPlan/businessPlan.service.ts
@@ -143,7 +143,7 @@ export class BusinessPlanService extends GenericService {
];
const promptConfig: PromptConfig = {
provider: LLMProvider.GEMINI,
- modelName: 'gemini-2.5-flash',
+ modelName: 'gemini-3-flash-preview',
};
// Initialize empty sections array to collect results as they come in
diff --git a/apps/api/api/services/Deployment/deployment.service.ts b/apps/api/api/services/Deployment/deployment.service.ts
index 5699d1fd2..f88591562 100644
--- a/apps/api/api/services/Deployment/deployment.service.ts
+++ b/apps/api/api/services/Deployment/deployment.service.ts
@@ -770,7 +770,7 @@ Please provide only the terraform.tfvars file content as output.`;
// Use AI to generate the tfvars content
const promptConfig: PromptConfig = {
provider: LLMProvider.GEMINI,
- modelName: 'gemini-2.5-flash',
+ modelName: 'gemini-3-flash-preview',
llmOptions: {
temperature: 0.3,
maxOutputTokens: 4000,
@@ -1140,7 +1140,7 @@ Please provide only the terraform.tfvars file content as output.`;
const aiResponse = await this.promptService.runPrompt(
{
provider: LLMProvider.GEMINI,
- modelName: 'gemini-2.5-flash',
+ modelName: 'gemini-3-flash-preview',
llmOptions: {
temperature: 0.7,
maxOutputTokens: 1024,
diff --git a/apps/api/api/services/common/generic.service.ts b/apps/api/api/services/common/generic.service.ts
index b9a59aa33..4127884e8 100644
--- a/apps/api/api/services/common/generic.service.ts
+++ b/apps/api/api/services/common/generic.service.ts
@@ -99,7 +99,7 @@ export class GenericService {
contextFromPreviousSteps: string = '',
promptConfig: PromptConfig = {
provider: LLMProvider.GEMINI,
- modelName: 'gemini-2.5-flash',
+ modelName: 'gemini-3-flash-preview',
userId,
promptType: promptType || step.stepName,
}
diff --git a/apps/appgen/apps/we-dev-next/src/app/api/model/config.ts b/apps/appgen/apps/we-dev-next/src/app/api/model/config.ts
index 64796f78e..6f77de63c 100644
--- a/apps/appgen/apps/we-dev-next/src/app/api/model/config.ts
+++ b/apps/appgen/apps/we-dev-next/src/app/api/model/config.ts
@@ -16,45 +16,13 @@ interface ModelConfig {
// Default model configurations
const defaultModelConfigs: ModelConfig[] = [
{
- modelName: 'gemini-2.5-flash',
- modelKey: 'gemini-2.5-flash',
+ modelName: 'gemini-3-flash-preview',
+ modelKey: 'gemini-3-flash-preview',
useImage: true,
provider: 'gemini',
- description: 'Gemini 2.5 Flash model',
+ description: 'Gemini 3 Flash model',
functionCall: true,
- },
- {
- modelName: 'claude-3-5-sonnet',
- modelKey: 'claude-3-5-sonnet-20240620',
- useImage: true,
- provider: 'claude',
- description: 'Claude 3.5 Sonnet model',
- functionCall: true,
- },
- {
- modelName: 'gpt-4o-mini',
- modelKey: 'gpt-4o-mini',
- useImage: false,
- provider: 'openai',
- description: 'GPT-4 Optimized Mini model',
- functionCall: true,
- },
- {
- modelName: 'deepseek-R1',
- modelKey: 'deepseek-reasoner',
- useImage: false,
- provider: 'deepseek',
- description: 'Deepseek R1 model with reasoning and chain-of-thought capabilities',
- functionCall: false,
- },
- {
- modelName: 'deepseek-v3',
- modelKey: 'deepseek-chat',
- useImage: false,
- provider: 'deepseek',
- description: 'Deepseek V3 model',
- functionCall: true,
- },
+ }
];
// Function to parse model configurations from environment variable
diff --git a/apps/main-dashboard/public/assets/i18n/en.json b/apps/main-dashboard/public/assets/i18n/en.json
index fa9b382b3..f8e7e081d 100644
--- a/apps/main-dashboard/public/assets/i18n/en.json
+++ b/apps/main-dashboard/public/assets/i18n/en.json
@@ -361,7 +361,9 @@
"buttons": {
"generate": "Generate Variations",
"clickToSelect": "Click to select",
- "proceed": "Proceed with selection"
+ "proceed": "Proceed with selection",
+ "retry": "Retry",
+ "continueToSummary": "Continue to Summary"
},
"estimatedTime": "Estimated time: {{time}}",
"generating": {
@@ -385,7 +387,9 @@
},
"success": {
"title": "Logo Variations",
- "subtitle": "Select the variations you want to keep for your project."
+ "subtitle": "Select the variations you want to keep for your project.",
+ "completed": "Variations Generated Successfully!",
+ "reviewAndProceed": "Review your variations and proceed when ready."
},
"categories": {
"withText": "With Text",
@@ -397,7 +401,10 @@
"withTextMonochrome": "With Text - Monochrome",
"iconOnlyLight": "Icon Only - Light Background",
"iconOnlyDark": "Icon Only - Dark Background",
- "iconOnlyMonochrome": "Icon Only - Monochrome"
+ "iconOnlyMonochrome": "Icon Only - Monochrome",
+ "lightBackground": "Light Background",
+ "darkBackground": "Dark Background",
+ "monochrome": "Monochrome"
},
"descriptions": {
"withTextLight": "Full logo optimized for light backgrounds",
@@ -405,7 +412,18 @@
"withTextMonochrome": "Full logo in monochrome version",
"iconOnlyLight": "Icon only optimized for light backgrounds",
"iconOnlyDark": "Icon only optimized for dark backgrounds",
- "iconOnlyMonochrome": "Icon only in monochrome version"
+ "iconOnlyMonochrome": "Icon only in monochrome version",
+ "lightBackground": "Optimized for white and light backgrounds",
+ "darkBackground": "Optimized for dark and modern backgrounds",
+ "monochrome": "Version in black and white for all uses"
+ },
+ "status": {
+ "generated": "Generated",
+ "processing": "Processing variations...",
+ "preparingNext": "Preparing next step...",
+ "allGenerated": "All variations generated successfully!",
+ "readyToProceed": "Ready to proceed to the next step.",
+ "advancing": "Advancing to next step..."
},
"selectionsMade": "{{count}} selection(s) made"
},
diff --git a/apps/main-dashboard/public/assets/i18n/fr.json b/apps/main-dashboard/public/assets/i18n/fr.json
index 4fd7c0e63..a673c7286 100644
--- a/apps/main-dashboard/public/assets/i18n/fr.json
+++ b/apps/main-dashboard/public/assets/i18n/fr.json
@@ -361,7 +361,9 @@
"buttons": {
"generate": "Générer les variations",
"clickToSelect": "Cliquez pour sélectionner",
- "proceed": "Continuer avec la sélection"
+ "proceed": "Continuer avec la sélection",
+ "retry": "Réessayer",
+ "continueToSummary": "Continuer vers le résumé"
},
"estimatedTime": "Temps estimé : {{time}}",
"generating": {
@@ -385,7 +387,9 @@
},
"success": {
"title": "Variations du logo",
- "subtitle": "Sélectionnez les variations que vous souhaitez conserver pour votre projet."
+ "subtitle": "Sélectionnez les variations que vous souhaitez conserver pour votre projet.",
+ "completed": "Variations générées avec succès !",
+ "reviewAndProceed": "Examinez vos variations et continuez quand vous êtes prêt."
},
"categories": {
"withText": "Avec texte",
@@ -397,7 +401,10 @@
"withTextMonochrome": "Avec texte - Monochrome",
"iconOnlyLight": "Icône uniquement - Fond clair",
"iconOnlyDark": "Icône uniquement - Fond sombre",
- "iconOnlyMonochrome": "Icône uniquement - Monochrome"
+ "iconOnlyMonochrome": "Icône uniquement - Monochrome",
+ "lightBackground": "Fond clair",
+ "darkBackground": "Fond sombre",
+ "monochrome": "Monochrome"
},
"descriptions": {
"withTextLight": "Logo complet optimisé pour les fonds clairs",
@@ -405,7 +412,18 @@
"withTextMonochrome": "Logo complet en version monochrome",
"iconOnlyLight": "Icône uniquement optimisée pour les fonds clairs",
"iconOnlyDark": "Icône uniquement optimisée pour les fonds sombres",
- "iconOnlyMonochrome": "Icône uniquement en version monochrome"
+ "iconOnlyMonochrome": "Icône uniquement en version monochrome",
+ "lightBackground": "Optimisé pour les fonds blancs et clairs",
+ "darkBackground": "Optimisé pour les fonds sombres et modernes",
+ "monochrome": "Version en noir et blanc pour toutes les utilisations"
+ },
+ "status": {
+ "generated": "Généré",
+ "processing": "Traitement des variations...",
+ "preparingNext": "Préparation de l'étape suivante...",
+ "allGenerated": "Toutes les variations générées avec succès !",
+ "readyToProceed": "Prêt à passer à l'étape suivante.",
+ "advancing": "Passage à l'étape suivante..."
},
"selectionsMade": "{{count}} sélection(s) faite(s)"
},
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.html
index 0ffe08024..dc19c82b5 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.html
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.html
@@ -177,7 +177,9 @@
{{ 'dashboard.logoVariations.features.lightBackground.title' | translate }}
-
Optimized for white and light backgrounds
+
+ {{ 'dashboard.logoVariations.features.lightBackground.description' | translate }}
+
@@ -189,7 +191,9 @@
{{ 'dashboard.logoVariations.features.darkBackground.title' | translate }}
-
Optimized for dark and modern backgrounds
+
+ {{ 'dashboard.logoVariations.features.darkBackground.description' | translate }}
+
@@ -234,7 +238,7 @@
class="outer-button px-8 py-4 bg-danger/20 border-danger/30 hover:bg-danger/30"
>
- Retry
+ {{ 'dashboard.logoVariations.buttons.retry' | translate }}
@@ -247,123 +251,92 @@
-
- {{ 'dashboard.logoVariations.success.title' | translate }}
-
-
- {{ 'dashboard.logoVariations.success.subtitle' | translate }}
-
+ @if (isCompleted()) {
+
+
+
+
+ {{ 'dashboard.logoVariations.success.completed' | translate }}
+
+
+ {{ 'dashboard.logoVariations.success.reviewAndProceed' | translate }}
+
+ } @else {
+
+ {{ 'dashboard.logoVariations.success.title' | translate }}
+
+
+ {{ 'dashboard.logoVariations.success.subtitle' | translate }}
+
+ }
-
+
-
- @for (
- category of [
- 'dashboard.logoVariations.categories.withText' | translate,
- 'dashboard.logoVariations.categories.iconOnly' | translate,
- ];
- track category
- ) {
- @if (hasVariationsForCategory(category)) {
-
-
{{ category }}
-
-
-
+
+
+
+
+
+
+
+
+
+
{{ variation.label }}
-
-
-
-
-
-
-
{{ variation.label }}
- @if (isVariationSelected(variation.id)) {
-
-
-
- }
-
+
+
+
-
{{ variation.description }}
+
{{ variation.description }}
-
-
- @if (isVariationSelected(variation.id)) {
-
-
- Selected
-
- } @else {
-
- {{ 'dashboard.logoVariations.buttons.clickToSelect' | translate }}
-
- }
-
-
-
-
-
+
+
+
+
+ {{ 'dashboard.logoVariations.status.generated' | translate }}
+
+
+
- }
- }
+
+
-
- @if (selectedVariations().length > 0) {
+
+ @if (!isCompleted()) {
- {{
- 'dashboard.logoVariations.selectionsMade'
- | translate: { count: selectedVariations().length }
- }}
-
-
-
-
- } @else {
-
-
- {{ 'dashboard.logoVariations.errors.selectAtLeastOne' | translate }}
+ {{ 'dashboard.logoVariations.status.processing' | translate }}
+
+
+ {{ 'dashboard.logoVariations.status.preparingNext' | translate }}
+
}
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.ts
index 41e3577f1..4a785301b 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.ts
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/logo-variations/logo-variations.ts
@@ -21,13 +21,11 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core';
interface DisplayVariation {
id: string;
- type: 'withText' | 'iconOnly';
background: 'lightBackground' | 'darkBackground' | 'monochrome';
label: string;
svgContent: string;
description: string;
backgroundColor: string;
- category: string;
}
@Component({
@@ -60,7 +58,7 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
protected readonly estimatedTime = signal('1-2 minutes');
protected readonly hasStartedGeneration = signal(false);
protected readonly error = signal(null);
- protected readonly selectedVariations = signal([]);
+ protected readonly isCompleted = signal(false);
// Computed properties
protected readonly shouldShowLoader = computed(() => {
@@ -76,7 +74,7 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
});
protected readonly canProceed = computed(() => {
- return this.selectedVariations().length > 0;
+ return this.isCompleted();
});
// Track function for carousel
@@ -126,105 +124,49 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
next: (response) => {
console.log('Logo variations generated successfully:', response);
- // Transform the response into DisplayVariation objects
+ // Transform the response into DisplayVariation objects (simplified to 3 variations)
const variations: DisplayVariation[] = [];
- // Process withText variations
+ // Use withText variations as primary (since logos are now complete)
if (response.variations.withText) {
const withText = response.variations.withText;
if (withText.lightBackground) {
variations.push({
- id: 'withText-lightBackground',
- type: 'withText',
+ id: 'lightBackground',
background: 'lightBackground',
- label: this.translate.instant('dashboard.logoVariations.labels.withTextLight'),
+ label: this.translate.instant('dashboard.logoVariations.labels.lightBackground'),
svgContent: withText.lightBackground,
description: this.translate.instant(
- 'dashboard.logoVariations.descriptions.withTextLight',
+ 'dashboard.logoVariations.descriptions.lightBackground',
),
backgroundColor: '#ffffff',
- category: this.translate.instant('dashboard.logoVariations.categories.withText'),
});
}
if (withText.darkBackground) {
variations.push({
- id: 'withText-darkBackground',
- type: 'withText',
+ id: 'darkBackground',
background: 'darkBackground',
- label: this.translate.instant('dashboard.logoVariations.labels.withTextDark'),
+ label: this.translate.instant('dashboard.logoVariations.labels.darkBackground'),
svgContent: withText.darkBackground,
description: this.translate.instant(
- 'dashboard.logoVariations.descriptions.withTextDark',
+ 'dashboard.logoVariations.descriptions.darkBackground',
),
backgroundColor: '#1f2937',
- category: this.translate.instant('dashboard.logoVariations.categories.withText'),
});
}
if (withText.monochrome) {
variations.push({
- id: 'withText-monochrome',
- type: 'withText',
+ id: 'monochrome',
background: 'monochrome',
- label: this.translate.instant('dashboard.logoVariations.labels.withTextMonochrome'),
+ label: this.translate.instant('dashboard.logoVariations.labels.monochrome'),
svgContent: withText.monochrome,
description: this.translate.instant(
- 'dashboard.logoVariations.descriptions.withTextMonochrome',
+ 'dashboard.logoVariations.descriptions.monochrome',
),
backgroundColor: '#f3f4f6',
- category: this.translate.instant('dashboard.logoVariations.categories.withText'),
- });
- }
- }
-
- // Process iconOnly variations
- if (response.variations.iconOnly) {
- const iconOnly = response.variations.iconOnly;
-
- if (iconOnly.lightBackground) {
- variations.push({
- id: 'iconOnly-lightBackground',
- type: 'iconOnly',
- background: 'lightBackground',
- label: this.translate.instant('dashboard.logoVariations.labels.iconOnlyLight'),
- svgContent: iconOnly.lightBackground,
- description: this.translate.instant(
- 'dashboard.logoVariations.descriptions.iconOnlyLight',
- ),
- backgroundColor: '#ffffff',
- category: this.translate.instant('dashboard.logoVariations.categories.iconOnly'),
- });
- }
-
- if (iconOnly.darkBackground) {
- variations.push({
- id: 'iconOnly-darkBackground',
- type: 'iconOnly',
- background: 'darkBackground',
- label: this.translate.instant('dashboard.logoVariations.labels.iconOnlyDark'),
- svgContent: iconOnly.darkBackground,
- description: this.translate.instant(
- 'dashboard.logoVariations.descriptions.iconOnlyDark',
- ),
- backgroundColor: '#1f2937',
- category: this.translate.instant('dashboard.logoVariations.categories.iconOnly'),
- });
- }
-
- if (iconOnly.monochrome) {
- variations.push({
- id: 'iconOnly-monochrome',
- type: 'iconOnly',
- background: 'monochrome',
- label: this.translate.instant('dashboard.logoVariations.labels.iconOnlyMonochrome'),
- svgContent: iconOnly.monochrome,
- description: this.translate.instant(
- 'dashboard.logoVariations.descriptions.iconOnlyMonochrome',
- ),
- backgroundColor: '#f3f4f6',
- category: this.translate.instant('dashboard.logoVariations.categories.iconOnly'),
});
}
}
@@ -240,8 +182,8 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
this.translate.instant('dashboard.logoVariations.progress.completed'),
);
- // Auto-select all variations by default
- this.selectedVariations.set(variations.map((v) => v.id));
+ // Auto-accept all variations and update project immediately
+ this.autoAcceptVariations(response.variations);
},
error: (error) => {
console.error('Error in logo variation generation:', error);
@@ -253,80 +195,22 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
});
}
- protected toggleVariationSelection(variationType: string): void {
- const currentSelections = this.selectedVariations();
-
- if (currentSelections.includes(variationType)) {
- // Remove from selection
- this.selectedVariations.set(currentSelections.filter((type) => type !== variationType));
- } else {
- // Add to selection
- this.selectedVariations.set([...currentSelections, variationType]);
- }
- }
-
- protected isVariationSelected(variationType: string): boolean {
- return this.selectedVariations().includes(variationType);
- }
-
- protected getVariationsByCategory(category: string): DisplayVariation[] {
- const categoryMap: { [key: string]: 'withText' | 'iconOnly' } = {
- [this.translate.instant('dashboard.logoVariations.categories.withText')]: 'withText',
- [this.translate.instant('dashboard.logoVariations.categories.iconOnly')]: 'iconOnly',
- };
- const mappedCategory = categoryMap[category];
- return this.generatedVariations().filter((v) => v.type === mappedCategory);
- }
-
- protected hasVariationsForCategory(category: string): boolean {
- return this.getVariationsByCategory(category).length > 0;
+ // Simplified: return all variations since we only have 3 now
+ protected getAllVariations(): DisplayVariation[] {
+ return this.generatedVariations();
}
- protected goToNextStep(): void {
- if (!this.canProceed()) {
- return;
- }
-
- // Update project with selected logo variations
- const selectedVariations = this.generatedVariations().filter((variation) =>
- this.selectedVariations().includes(variation.id),
- );
-
+ /**
+ * Auto-accept all generated variations and update project
+ */
+ private autoAcceptVariations(variations: LogoVariations): void {
const currentProject = this.project();
const currentBranding = currentProject?.analysisResultModel?.branding;
- // Build the variations object from selected variations
- const withTextVariations = {
- lightBackground: selectedVariations.find(
- (v) => v.type === 'withText' && v.background === 'lightBackground',
- )?.svgContent,
- darkBackground: selectedVariations.find(
- (v) => v.type === 'withText' && v.background === 'darkBackground',
- )?.svgContent,
- monochrome: selectedVariations.find(
- (v) => v.type === 'withText' && v.background === 'monochrome',
- )?.svgContent,
- };
-
- const iconOnlyVariations = {
- lightBackground: selectedVariations.find(
- (v) => v.type === 'iconOnly' && v.background === 'lightBackground',
- )?.svgContent,
- darkBackground: selectedVariations.find(
- (v) => v.type === 'iconOnly' && v.background === 'darkBackground',
- )?.svgContent,
- monochrome: selectedVariations.find(
- (v) => v.type === 'iconOnly' && v.background === 'monochrome',
- )?.svgContent,
- };
-
- // Update the logo with variations
+ // Update the logo with all variations
const updatedLogo: LogoModel = {
...this.selectedLogo(),
- variations: {
- withText: withTextVariations,
- iconOnly: iconOnlyVariations,
- },
+ variations: variations,
};
const updatedBranding = {
@@ -334,6 +218,7 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
logo: updatedLogo,
};
+ // Update project immediately
this.projectUpdate.emit({
...currentProject,
analysisResultModel: {
@@ -342,7 +227,8 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
},
} as ProjectModel);
- this.nextStep.emit();
+ // Mark as completed - user can now proceed manually
+ this.isCompleted.set(true);
}
private simulateProgress(): void {
@@ -395,7 +281,7 @@ export class LogoVariationsComponent implements OnInit, OnDestroy {
this.hasStartedGeneration.set(false);
this.generatedVariations.set([]);
this.generationProgress.set(0);
- this.selectedVariations.set([]);
+ this.isCompleted.set(false);
// Restart generation
this.startVariationGeneration();
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.html
index e36369ddd..e018a611a 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.html
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.html
@@ -36,7 +36,7 @@
{{ 'dashboard.projectSummary.fields.type' | translate }}:
- {{ project().type }}
+ {{ formattedProjectType() }}
@@ -55,19 +55,19 @@
{{ 'dashboard.projectSummary.fields.scope' | translate }}:
- {{ project().scope }}
+ {{ formattedScope() }}
{{ 'dashboard.projectSummary.fields.budget' | translate }}:
- {{ project().budgetIntervals }}
+ {{ formattedBudget() }}
{{ 'dashboard.projectSummary.fields.target' | translate }}:
- {{ project().targets }}
+ {{ formattedTargets() }}
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.ts
index ebae28f8f..722462881 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.ts
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/project-summary/project-summary.ts
@@ -54,6 +54,48 @@ export class ProjectSummaryComponent {
return requiredPolicies && betaRequired;
});
+ // Computed properties for formatted display
+ protected readonly formattedProjectType = computed(() => {
+ const type = this.project().type;
+ if (typeof type === 'object' && type !== null) {
+ return (type as any).name || JSON.stringify(type);
+ }
+ return type || 'Non spécifié';
+ });
+
+ protected readonly formattedScope = computed(() => {
+ const scope = this.project().scope;
+ if (typeof scope === 'object' && scope !== null) {
+ return (scope as any).name || JSON.stringify(scope);
+ }
+ return scope || 'Non spécifié';
+ });
+
+ protected readonly formattedTargets = computed(() => {
+ const targets = this.project().targets;
+ if (typeof targets === 'object' && targets !== null) {
+ if (Array.isArray(targets)) {
+ return targets
+ .map((t) => (typeof t === 'object' ? (t as any).name || JSON.stringify(t) : t))
+ .join(', ');
+ }
+ return (targets as any).name || JSON.stringify(targets);
+ }
+ return targets || 'Non spécifié';
+ });
+
+ protected readonly formattedBudget = computed(() => {
+ const budget = this.project().budgetIntervals;
+ if (typeof budget === 'object' && budget !== null) {
+ const budgetObj = budget as any;
+ if (budgetObj.min && budgetObj.max) {
+ return `${budgetObj.min} - ${budgetObj.max}`;
+ }
+ return budgetObj.name || JSON.stringify(budget);
+ }
+ return budget || 'Non spécifié';
+ });
+
protected getSelectedLogo(): LogoModel | undefined {
const logo = this.logos().find((logo) => logo.id === this.selectedLogo());
console.log('Selected logo', logo);
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.css b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.css
deleted file mode 100644
index e69de29bb..000000000
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.html
deleted file mode 100644
index 84a84ed10..000000000
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.ts
deleted file mode 100644
index 01b54c6c6..000000000
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/popular-typography-list/popular-typography-list.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Component, Input, Output, EventEmitter } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { TypographyModel } from '../../../../../models/brand-identity.model';
-import { TypographyPreview } from '../../../../../../../shared/services/typography.service';
-import { TypographyCardComponent } from '../typography-card/typography-card';
-
-@Component({
- selector: 'app-popular-typography-list',
- standalone: true,
- imports: [CommonModule, TypographyCardComponent],
- templateUrl: './popular-typography-list.html',
- styleUrls: ['./popular-typography-list.css'],
-})
-export class PopularTypographyListComponent {
- @Input() typographies: TypographyPreview[] = [];
- @Input() selectedTypographyId: string | null = null;
- @Output() typographySelected = new EventEmitter();
-
- onTypographySelected(typography: TypographyModel | TypographyPreview): void {
- // Convert TypographyModel to TypographyPreview if needed
- if ('category' in typography && 'isLoaded' in typography) {
- // It's already a TypographyPreview
- this.typographySelected.emit(typography as TypographyPreview);
- } else {
- // It's a TypographyModel, convert to TypographyPreview
- const typographyPreview: TypographyPreview = {
- id: typography.id,
- name: typography.name,
- primaryFont: typography.primaryFont,
- secondaryFont: typography.secondaryFont,
- category: 'popular',
- isLoaded: true,
- };
- this.typographySelected.emit(typographyPreview);
- }
- }
-
- trackTypography(index: number, typography: TypographyPreview): string {
- return typography.id;
- }
-}
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.html
index a4eaf4d3c..3a4eb4e4e 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.html
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.html
@@ -1,36 +1,9 @@
-
-
- @if (customTypographies.length > 0) {
-
-
- {{ 'dashboard.typographySelection.custom.title' | translate }}
-
-
- @for (typography of customTypographies; track trackCustomTypography($index, typography)) {
-
-
-
- {{ typography.primaryFont }}
-
-
+ {{ typography.secondaryFont }}
-
-
-
- }
-
-
- }
-
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
{{ 'dashboard.typographySelection.custom.primaryFont' | translate }}
-
+
{{
selectedPrimaryFont || ('dashboard.typographySelection.custom.notSelected' | translate)
}}
-
-
+
+
+
+
{{ 'dashboard.typographySelection.custom.secondaryFont' | translate }}
-
+
{{
selectedSecondaryFont ||
('dashboard.typographySelection.custom.notSelected' | translate)
@@ -81,26 +66,16 @@
-
-
+
+
+
Search and select fonts to apply:
+
+
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.ts
index 8cf8744f3..426d7fd25 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.ts
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-custom-creator/typography-custom-creator.ts
@@ -1,7 +1,6 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
-import { TypographyModel } from '../../../../../models/brand-identity.model';
import {
TypographyPreview,
GoogleFont,
@@ -16,22 +15,13 @@ import { TypographySearchComponent } from '../typography-search/typography-searc
styleUrls: ['./typography-custom-creator.css'],
})
export class TypographyCustomCreatorComponent {
- @Input() customTypographies: TypographyPreview[] = [];
- @Input() selectedCustomTypography: TypographyPreview | null = null;
@Input() selectedPrimaryFont = '';
@Input() selectedSecondaryFont = '';
@Input() searchResults: GoogleFont[] = [];
@Input() isSearching = false;
- @Input() canCreateCustom = false;
- @Output() customTypographySelected = new EventEmitter
();
@Output() searchInput = new EventEmitter();
@Output() fontSelected = new EventEmitter<{ font: GoogleFont; type: 'primary' | 'secondary' }>();
- @Output() customTypographyCreated = new EventEmitter();
-
- onCustomTypographySelected(typography: TypographyPreview): void {
- this.customTypographySelected.emit(typography);
- }
onSearchInput(query: string): void {
this.searchInput.emit(query);
@@ -40,12 +30,4 @@ export class TypographyCustomCreatorComponent {
onFontSelected(event: { font: GoogleFont; type: 'primary' | 'secondary' }): void {
this.fontSelected.emit(event);
}
-
- onCreateCustomTypography(): void {
- this.customTypographyCreated.emit();
- }
-
- trackCustomTypography(index: number, typography: TypographyPreview): string {
- return typography.id;
- }
}
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-generated-list/typography-generated-list.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-generated-list/typography-generated-list.ts
index fef76ec7e..9fecfada0 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-generated-list/typography-generated-list.ts
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-generated-list/typography-generated-list.ts
@@ -2,13 +2,12 @@ import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { TypographyModel } from '../../../../../models/brand-identity.model';
-import { CarouselComponent } from '../../../../../../../shared/components/carousel/carousel.component';
import { TypographyCardComponent } from '../typography-card/typography-card';
@Component({
selector: 'app-typography-generated-list',
standalone: true,
- imports: [CommonModule, TranslateModule, CarouselComponent, TypographyCardComponent],
+ imports: [CommonModule, TranslateModule, TypographyCardComponent],
templateUrl: './typography-generated-list.html',
styleUrls: ['./typography-generated-list.css'],
})
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.css b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.css
deleted file mode 100644
index a94507019..000000000
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.css
+++ /dev/null
@@ -1 +0,0 @@
-/* Typography Preview Panel Component Styles */
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.html
deleted file mode 100644
index 756051bd8..000000000
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.html
+++ /dev/null
@@ -1,49 +0,0 @@
-
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.ts
deleted file mode 100644
index 96605f350..000000000
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview-panel/typography-preview-panel.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Component, Input, signal } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { TranslateModule } from '@ngx-translate/core';
-import { TypographyModel } from '../../../../../models/brand-identity.model';
-
-@Component({
- selector: 'app-typography-preview-panel',
- standalone: true,
- imports: [CommonModule, TranslateModule],
- templateUrl: './typography-preview-panel.html',
- styleUrls: ['./typography-preview-panel.css'],
-})
-export class TypographyPreviewPanelComponent {
- @Input() typography: TypographyModel | null = null;
- @Input() previewText = signal('Your Brand Name');
- @Input() selectedPrimaryFont = '';
- @Input() selectedSecondaryFont = '';
-
- get primaryFont(): string {
- return this.typography?.primaryFont || this.selectedPrimaryFont || 'inherit';
- }
-
- get secondaryFont(): string {
- return this.typography?.secondaryFont || this.selectedSecondaryFont || 'inherit';
- }
-}
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview/typography-preview.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview/typography-preview.html
index b339c679e..64d1d3198 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview/typography-preview.html
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-preview/typography-preview.html
@@ -1,67 +1,71 @@
-@if (typography) {
+
-} @else {
-
-
+ @if (typography) {
-
+
+
+
+
+ {{ previewText() }}
+
+
+ {{ 'dashboard.typographySelection.preview.heroSubtitle' | translate }}
+
+
+
+
+
+
+
+ "{{ 'dashboard.typographySelection.preview.quote' | translate }}"
+
+
+ — {{ 'dashboard.typographySelection.preview.author' | translate }}
+
+
+
+
+ } @else {
+
+
+
+
+ {{ 'dashboard.typographySelection.selectToPreview' | translate }}
+
+
Select fonts to see the preview
+
-
- {{ 'dashboard.typographySelection.selectToPreview' | translate }}
-
-
- Click any font card on the left to see the magic happen
-
-
+ }
-}
+
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.html
index 7e47f6036..9480a7ee8 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.html
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.html
@@ -102,89 +102,25 @@
(typographySelected)="onTypographySelected($event)"
(regenerateRequest)="onRegenerateTypographies()"
>
- }
- @else if (activeTab() === 'custom') {
+ } @else if (activeTab() === 'custom') {
}
- @if (activeTab() === 'generated' || activeTab() === 'popular') {
-
-
- } @else if (activeTab() === 'custom') {
-
-
- }
-
-
-
-
-
-
-
-
-
-
+
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.ts
index ec1e2d58d..a9c57749e 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.ts
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-selection.ts
@@ -22,7 +22,6 @@ import { ProjectService } from '../../../../services/project.service';
import { debounceTime, distinctUntilChanged, switchMap, takeUntil } from 'rxjs/operators';
import { Subject, of } from 'rxjs';
import { TypographyPreviewComponent } from './typography-preview/typography-preview';
-import { PopularTypographyListComponent } from './popular-typography-list/popular-typography-list';
import { ProjectModel } from '../../../../models/project.model';
import { BrandingService } from '../../../../services/ai-agents/branding.service';
@@ -30,7 +29,6 @@ import { BrandingService } from '../../../../services/ai-agents/branding.service
import { TypographyTabsComponent, TypographyTab } from './typography-tabs/typography-tabs';
import { TypographyGeneratedListComponent } from './typography-generated-list/typography-generated-list';
import { TypographyCustomCreatorComponent } from './typography-custom-creator/typography-custom-creator';
-import { TypographyPreviewPanelComponent } from './typography-preview-panel/typography-preview-panel';
@Component({
selector: 'app-typography-selection',
@@ -40,11 +38,9 @@ import { TypographyPreviewPanelComponent } from './typography-preview-panel/typo
FormsModule,
TranslateModule,
TypographyPreviewComponent,
- PopularTypographyListComponent,
TypographyTabsComponent,
TypographyGeneratedListComponent,
TypographyCustomCreatorComponent,
- TypographyPreviewPanelComponent,
],
templateUrl: './typography-selection.html',
styleUrls: ['./typography-selection.css'],
@@ -63,8 +59,7 @@ export class TypographySelectionComponent implements OnInit, OnDestroy {
// Outputs
@Output() readonly typographySelected = new EventEmitter
();
@Output() readonly projectUpdate = new EventEmitter>();
- @Output() readonly nextStep = new EventEmitter();
- @Output() readonly previousStep = new EventEmitter();
+ @Output() readonly typographySelectionChanged = new EventEmitter();
// Signals
protected activeTab = signal('generated');
@@ -73,11 +68,12 @@ export class TypographySelectionComponent implements OnInit, OnDestroy {
protected isGenerating = signal(false);
protected typographyModels = signal([]);
protected selectedTypographyId = signal(null);
- protected popularTypographies = signal([]);
- protected customTypographies = signal([]);
- protected selectedCustomTypography = signal(null);
+
+ // Custom Selection Signals
protected selectedPrimaryFont = signal('');
protected selectedSecondaryFont = signal('');
+
+ // Search Signals
protected searchResults = signal([]);
protected isSearching = signal(false);
protected searchQuery = signal('');
@@ -86,60 +82,50 @@ export class TypographySelectionComponent implements OnInit, OnDestroy {
// Computed properties
protected hasGeneratedTypographies = computed(() => this.typographyModels().length > 0);
- protected canCreateCustom = computed(
- () => this.selectedPrimaryFont().length > 0 && this.selectedSecondaryFont().length > 0,
- );
+
protected canContinue = computed(() => {
- return (
- this.selectedTypographyId() !== null ||
- this.selectedCustomTypography() !== null ||
- (this.selectedPrimaryFont() && this.selectedSecondaryFont())
- );
+ if (this.activeTab() === 'generated') {
+ return this.selectedTypographyId() !== null;
+ }
+ // For custom tab, we need both fonts selected
+ return this.selectedPrimaryFont().length > 0 && this.selectedSecondaryFont().length > 0;
});
- protected currentSelectedTypography = computed(() =>
- this.typographyModels().find((t) => t.id === this.selectedTypographyId()),
- );
- protected customPreviewTypography = computed(() => {
- if (this.selectedCustomTypography()) {
+ protected currentSelectedTypography = computed(() => {
+ if (this.activeTab() === 'generated') {
+ return this.typographyModels().find(
+ (t: TypographyModel) => t.id === this.selectedTypographyId(),
+ );
+ }
+
+ // For custom tab, return a live preview object
+ if (this.selectedPrimaryFont() || this.selectedSecondaryFont()) {
return {
- id: this.selectedCustomTypography()!.id,
- name: this.selectedCustomTypography()!.name,
- primaryFont: this.selectedCustomTypography()!.primaryFont,
- secondaryFont: this.selectedCustomTypography()!.secondaryFont,
- description: '',
+ id: 'custom-preview',
+ name: 'Custom Selection',
+ primaryFont: this.selectedPrimaryFont() || 'Inter', // Fallback for preview
+ secondaryFont: this.selectedSecondaryFont() || 'Inter', // Fallback for preview
+ description: 'Your custom font combination',
} as TypographyModel;
}
- return {
- id: 'preview',
- name: 'Custom Preview',
- primaryFont: this.selectedPrimaryFont(),
- secondaryFont: this.selectedSecondaryFont(),
- description: 'Preview of your custom selection',
- } as TypographyModel;
+ return null;
});
// Event handlers for new template
protected onTabChanged(tab: TypographyTab): void {
this.activeTab.set(tab);
+ // If switching to generated, ensure something is selected if possible
+ if (tab === 'generated' && !this.selectedTypographyId() && this.typographyModels().length > 0) {
+ this.selectedTypographyId.set(this.typographyModels()[0].id);
+ this.notifySelectionChange();
+ }
}
protected onTypographySelected(typography: TypographyModel): void {
this.selectedTypographyId.set(typography.id);
this.typographySelected.emit(typography);
- }
-
- protected onPopularTypographySelected(typography: TypographyPreview): void {
- const typographyModel: TypographyModel = {
- id: typography.id,
- name: typography.name,
- primaryFont: typography.primaryFont,
- secondaryFont: typography.secondaryFont,
- description: `Popular typography: ${typography.name}`,
- };
- this.selectedTypographyId.set(typography.id);
- this.typographySelected.emit(typographyModel);
+ this.notifySelectionChange();
}
protected onRegenerateTypographies(): void {
@@ -152,44 +138,50 @@ export class TypographySelectionComponent implements OnInit, OnDestroy {
this.initializeTypographies();
}
- protected goBack(): void {
- this.previousStep.emit();
- }
+ // Method to prepare and emit project data when parent requests it
+ public prepareTypographyData(): Partial | null {
+ const selectedTypography = this.currentSelectedTypography();
+ if (!selectedTypography) return null;
+
+ // For custom typography, add it to the generatedTypography list so it can be found in project-summary
+ let updatedGeneratedTypography =
+ this.project.analysisResultModel?.branding?.generatedTypography || [];
- protected saveAsDraft(): void {
- const selectedTypography = this.currentSelectedTypography() || this.customPreviewTypography();
- const draftData: Partial = {
+ if (this.activeTab() === 'custom' && selectedTypography.id === 'custom-preview') {
+ // Create a proper custom typography with unique ID
+ const customTypography: TypographyModel = {
+ ...selectedTypography,
+ id: `custom-${Date.now()}`, // Unique ID for custom typography
+ };
+
+ // Add custom typography to the list if not already present
+ const existingCustomIndex = updatedGeneratedTypography.findIndex((t) =>
+ t.id.startsWith('custom-'),
+ );
+ if (existingCustomIndex >= 0) {
+ updatedGeneratedTypography[existingCustomIndex] = customTypography;
+ } else {
+ updatedGeneratedTypography = [...updatedGeneratedTypography, customTypography];
+ }
+
+ selectedTypography.id = customTypography.id; // Update the selected typography ID
+ }
+
+ return {
analysisResultModel: {
...this.project.analysisResultModel,
branding: {
...this.project.analysisResultModel?.branding,
typography: selectedTypography,
+ generatedTypography: updatedGeneratedTypography,
},
},
};
- this.projectUpdate.emit(draftData);
- }
-
- protected continueToNext(): void {
- if (this.canContinue()) {
- const selectedTypography = this.currentSelectedTypography() || this.customPreviewTypography();
- const projectData: Partial = {
- analysisResultModel: {
- ...this.project.analysisResultModel,
- branding: {
- ...this.project.analysisResultModel?.branding,
- typography: selectedTypography,
- },
- },
- };
- this.projectUpdate.emit(projectData);
- this.nextStep.emit();
- }
}
- protected selectCustomTypography(typography: TypographyPreview): void {
- this.selectedCustomTypography.set(typography);
- this.selectedTypographyId.set(null);
+ // Notify parent about selection state changes
+ private notifySelectionChange(): void {
+ this.typographySelectionChanged.emit(this.canContinue());
}
protected onSearchInput(query: string): void {
@@ -205,31 +197,12 @@ export class TypographySelectionComponent implements OnInit, OnDestroy {
this.selectedSecondaryFont.set(font.family);
}
this.typographyService.loadGoogleFont(font.family);
- }
-
- protected createCustomTypography(): void {
- if (!this.canCreateCustom()) return;
-
- const customTypography: TypographyPreview = {
- id: `custom-${Date.now()}`,
- name: `${this.selectedPrimaryFont()} + ${this.selectedSecondaryFont()}`,
- primaryFont: this.selectedPrimaryFont(),
- secondaryFont: this.selectedSecondaryFont(),
- category: 'custom',
- isLoaded: true,
- };
-
- const current = this.customTypographies();
- this.customTypographies.set([...current, customTypography]);
- this.selectedCustomTypography.set(customTypography);
- this.selectedPrimaryFont.set('');
- this.selectedSecondaryFont.set('');
+ this.notifySelectionChange();
}
ngOnInit(): void {
this.initializeTypographies();
this.setupSearch();
- this.loadPopularTypographies();
}
ngOnDestroy(): void {
@@ -244,6 +217,11 @@ export class TypographySelectionComponent implements OnInit, OnDestroy {
if (generatedTypography && generatedTypography.length > 0) {
this.typographyModels.set(generatedTypography);
this.isLoading.set(false);
+ // Auto-select first typography if none selected
+ if (!this.selectedTypographyId()) {
+ this.selectedTypographyId.set(generatedTypography[0].id);
+ this.notifySelectionChange();
+ }
} else {
this.regenerateTypographies();
}
@@ -281,14 +259,14 @@ export class TypographySelectionComponent implements OnInit, OnDestroy {
this.typographyModels.set(mockTypographies);
this.isGenerating.set(false);
+ // Auto-select first item
+ if (mockTypographies.length > 0) {
+ this.selectedTypographyId.set(mockTypographies[0].id);
+ this.notifySelectionChange();
+ }
}, 2000);
}
- private loadPopularTypographies(): void {
- const popular = this.typographyService.getPopularTypographies();
- this.popularTypographies.set(popular);
- }
-
private setupSearch(): void {
this.searchSubject
.pipe(
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-tabs/typography-tabs.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-tabs/typography-tabs.ts
index de3d0d02e..e9387fe00 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-tabs/typography-tabs.ts
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/components/typography-selection/typography-tabs/typography-tabs.ts
@@ -2,7 +2,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
-export type TypographyTab = 'generated' | 'popular' | 'custom';
+export type TypographyTab = 'generated' | 'custom';
@Component({
selector: 'app-typography-tabs',
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.html b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.html
index 01673fd34..be3dcb41c 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.html
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.html
@@ -54,9 +54,8 @@
@if (isStepActive(3)) {
}
@if (isStepActive(4)) {
@@ -86,7 +85,7 @@
[project]="project()"
[selectedLogo]="project().analysisResultModel.branding.logo.id || ''"
[selectedColor]="project().analysisResultModel.branding.colors.id || ''"
- [selectedTypography]="project().analysisResultModel.branding.typography.id || ''"
+ [selectedTypography]="project().analysisResultModel?.branding?.typography?.id || ''"
[logos]="project().analysisResultModel.branding.generatedLogos || []"
[colorPalettes]="project().analysisResultModel.branding.generatedColors || []"
[typographyOptions]="project().analysisResultModel.branding.generatedTypography || []"
diff --git a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.ts b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.ts
index 6d2b072f2..b4b993331 100644
--- a/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.ts
+++ b/apps/main-dashboard/src/app/modules/dashboard/pages/create-project/create-project.ts
@@ -1,4 +1,4 @@
-import { Component, inject, OnInit, signal, computed } from '@angular/core';
+import { Component, inject, OnInit, signal, computed, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Router } from '@angular/router';
import { ProjectModel } from '../../models/project.model';
@@ -110,6 +110,12 @@ export class CreateProjectComponent implements OnInit {
marketing: false,
});
+ // Step-specific validation state
+ protected readonly typographySelectionValid = signal(false);
+
+ // ViewChild to access typography component
+ @ViewChild(TypographySelectionComponent) typographyComponent?: TypographySelectionComponent;
+
// Static data
protected readonly projectTypes = CreateProjectDatas.groupedProjectTypes;
protected readonly targets = CreateProjectDatas.groupedTargets;
@@ -160,7 +166,7 @@ export class CreateProjectComponent implements OnInit {
case 'colors':
return !!project.analysisResultModel?.branding?.generatedColors?.length;
case 'typography':
- return !!project.analysisResultModel?.branding?.typography;
+ return this.typographySelectionValid();
case 'logo':
return !!project.analysisResultModel?.branding?.logo;
case 'variations':
@@ -185,10 +191,26 @@ export class CreateProjectComponent implements OnInit {
}
}
+ /**
+ * Handle typography selection change
+ */
+ protected onTypographySelectionChanged(isValid: boolean): void {
+ this.typographySelectionValid.set(isValid);
+ }
+
/**
* Navigate to next step
*/
protected goToNextStep(): void {
+ // For typography step, prepare and save typography data before proceeding
+ if (this.currentStepIndex() === 3 && this.typographyComponent) {
+ // Typography step
+ const typographyData = this.typographyComponent.prepareTypographyData();
+ if (typographyData) {
+ this.onProjectUpdate(typographyData);
+ }
+ }
+
if (this.canGoNext()) {
const nextIndex = this.currentStepIndex() + 1;
if (nextIndex < this.steps.length) {