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 @@ -
- @for (typography of typographies; track trackTypography($index, typography)) { - - } -
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.createNew' | translate }}

- - - - -
-
-
+ +
+ +
+
{{ '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 @@ -
-
- -
-
- -
-

- {{ previewText() }} -

-

- {{ 'dashboard.typographySelection.preview.heroSubtitle' | translate }} -

-
- - -
- - - -
- "{{ 'dashboard.typographySelection.preview.quote' | translate }}" -
- - — {{ 'dashboard.typographySelection.preview.author' | translate }} - -
-
-
-
-
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) { +
-
- -
-

- {{ previewText() }} -

-

- {{ 'dashboard.typographySelection.preview.heroSubtitle' | translate }} -

-
- - -
- - - -
- "{{ 'dashboard.typographySelection.preview.quote' | translate }}" -
- - — {{ 'dashboard.typographySelection.preview.author' | translate }} - -
-
-
-} @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) {