From 46deb5ea48c47993fbc21e4b26aedee399431111 Mon Sep 17 00:00:00 2001 From: roboshyim Date: Fri, 24 Jul 2026 05:14:36 +0000 Subject: [PATCH] chore: apply shopware-cli extension format Run `shopware-cli extension format` (PHP-CS-Fixer + Prettier/Stylelint tooling from shopware-cli 0.16.10) so admin/PHP sources match the canonical formatter used by `extension validate --full`. --- src/Auth/DeviceAuthClient.php | 4 +- src/Catalog/ModelsDevCatalog.php | 70 +-- src/Client/AiClient.php | 22 +- src/Client/PromptBuilder.php | 16 +- src/Client/ProviderRegistry.php | 4 +- src/Config/CapabilityDefaultsStore.php | 4 +- src/Entity/BulkJob/BulkJobEntity.php | 12 +- src/Entity/UsageLog/UsageLogEntity.php | 4 +- src/Provider/Anthropic/AnthropicProvider.php | 4 +- src/Provider/Gemini/GeminiClient.php | 1 - src/Provider/Gemini/GeminiProvider.php | 4 +- src/Provider/Mistral/MistralProvider.php | 4 +- src/Provider/OpenAi/OpenAiLiveModelClient.php | 74 +-- .../frosh-ai-bulk-generate-modal.html.twig | 16 +- .../frosh-ai-bulk-generate-modal.scss | 6 +- .../frosh-ai-bulk-generate-modal/index.js | 121 +++-- ...frosh-ai-category-generate-modal.html.twig | 11 +- .../frosh-ai-category-generate-modal.scss | 6 +- .../frosh-ai-category-generate-modal/index.js | 181 +++++--- ...h-ai-manufacturer-generate-modal.html.twig | 11 +- .../frosh-ai-manufacturer-generate-modal.scss | 6 +- .../index.js | 175 ++++--- .../frosh-ai-product-generate-modal.html.twig | 13 +- .../frosh-ai-product-generate-modal.scss | 8 +- .../frosh-ai-product-generate-modal/index.js | 429 ++++++++++++------ .../frosh-ai-review-summary-modal.html.twig | 24 +- .../frosh-ai-review-summary-modal.scss | 6 +- .../frosh-ai-review-summary-modal/index.js | 32 +- .../sw-category-detail-menu/index.js | 11 +- .../sw-category-detail-menu.html.twig | 17 +- .../extension/sw-manufacturer-detail/index.js | 14 +- .../sw-manufacturer-detail.html.twig | 17 +- .../extension/sw-product-basic-form/index.js | 22 +- .../sw-product-basic-form.html.twig | 11 +- .../sw-product-detail-reviews/index.js | 11 +- .../sw-product-detail-reviews.html.twig | 17 +- .../src/extension/sw-product-list/index.js | 27 +- .../sw-product-list/sw-product-list.html.twig | 40 +- .../extension/sw-product-seo-form/index.js | 22 +- .../sw-product-seo-form.html.twig | 4 +- .../src/module/frosh-ai-settings/acl/index.js | 13 +- .../frosh-ai-provider-anthropic.html.twig | 10 +- .../frosh-ai-provider-anthropic.scss | 2 +- .../frosh-ai-provider-anthropic/index.js | 70 +-- .../frosh-ai-provider-gemini.html.twig | 11 +- .../frosh-ai-provider-gemini.scss | 2 +- .../frosh-ai-provider-gemini/index.js | 78 ++-- .../frosh-ai-provider-mistral.html.twig | 10 +- .../frosh-ai-provider-mistral.scss | 2 +- .../frosh-ai-provider-mistral/index.js | 57 ++- ...sh-ai-provider-openai-compatible.html.twig | 10 +- .../index.js | 103 +++-- .../frosh-ai-provider-openai.html.twig | 51 +-- .../frosh-ai-provider-openai.scss | 9 +- .../frosh-ai-provider-openai/index.js | 161 +++++-- .../frosh-ai-settings-tabs.html.twig | 2 +- .../frosh-ai-settings/init/services.init.js | 2 +- .../frosh-ai-settings-index.html.twig | 58 +-- .../frosh-ai-settings-index.scss | 23 +- .../page/frosh-ai-settings-index/index.js | 239 ++++++---- .../frosh-ai-settings-playground.html.twig | 27 +- .../frosh-ai-settings-playground.scss | 9 +- .../frosh-ai-settings-playground/index.js | 183 +++++--- .../frosh-ai-settings-product.html.twig | 11 +- .../frosh-ai-settings-product.scss | 2 +- .../page/frosh-ai-settings-product/index.js | 59 +-- .../frosh-ai-settings-provider.html.twig | 11 +- .../frosh-ai-settings-provider.scss | 2 +- .../page/frosh-ai-settings-provider/index.js | 29 +- .../frosh-ai-settings-usage.html.twig | 106 +++-- .../frosh-ai-settings-usage.scss | 14 +- .../page/frosh-ai-settings-usage/index.js | 70 ++- .../service/frosh-ai-api.service.js | 187 +++++--- 73 files changed, 1844 insertions(+), 1260 deletions(-) diff --git a/src/Auth/DeviceAuthClient.php b/src/Auth/DeviceAuthClient.php index 8961245..8ca2781 100644 --- a/src/Auth/DeviceAuthClient.php +++ b/src/Auth/DeviceAuthClient.php @@ -19,7 +19,9 @@ */ final class DeviceAuthClient { - /** Public Codex CLI OAuth client id (same as openai/codex). */ + /** + * Public Codex CLI OAuth client id (same as openai/codex). + */ public const CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'; public const DEFAULT_ISSUER = 'https://auth.openai.com'; diff --git a/src/Catalog/ModelsDevCatalog.php b/src/Catalog/ModelsDevCatalog.php index 358f2a1..cd72246 100644 --- a/src/Catalog/ModelsDevCatalog.php +++ b/src/Catalog/ModelsDevCatalog.php @@ -24,7 +24,9 @@ final class ModelsDevCatalog implements ModelCatalogInterface private const CACHE_TTL = 86400; // 24h - /** @var array>|null */ + /** + * @var array>|null + */ private ?array $byProvider = null; public function __construct( @@ -80,6 +82,39 @@ public function refresh(): void $this->load(); } + /** + * @param array $payload + * + * @return array> + */ + public function parsePayload(array $payload): array + { + $catalog = []; + + foreach ($payload as $providerKey => $providerData) { + if (!\is_array($providerData) || !isset($providerData['models']) || !\is_array($providerData['models'])) { + continue; + } + + $providerId = (string) ($providerData['id'] ?? $providerKey); + $models = []; + + foreach ($providerData['models'] as $modelKey => $modelData) { + if (!\is_array($modelData)) { + continue; + } + + $models[] = $this->mapModel($providerId, (string) $modelKey, $modelData); + } + + if ($models !== []) { + $catalog[$providerId] = $models; + } + } + + return $catalog; + } + /** * @return array> */ @@ -132,39 +167,6 @@ private function fetchAndParse(): array return $this->parsePayload($payload); } - /** - * @param array $payload - * - * @return array> - */ - public function parsePayload(array $payload): array - { - $catalog = []; - - foreach ($payload as $providerKey => $providerData) { - if (!\is_array($providerData) || !isset($providerData['models']) || !\is_array($providerData['models'])) { - continue; - } - - $providerId = (string) ($providerData['id'] ?? $providerKey); - $models = []; - - foreach ($providerData['models'] as $modelKey => $modelData) { - if (!\is_array($modelData)) { - continue; - } - - $models[] = $this->mapModel($providerId, (string) $modelKey, $modelData); - } - - if ($models !== []) { - $catalog[$providerId] = $models; - } - } - - return $catalog; - } - /** * @param array $data */ diff --git a/src/Client/AiClient.php b/src/Client/AiClient.php index f7f30ec..ba2f90c 100644 --- a/src/Client/AiClient.php +++ b/src/Client/AiClient.php @@ -231,17 +231,6 @@ public function isImageGenerationAvailable(?string $salesChannelId = null): bool return $this->hasImageCapableProvider($salesChannelId); } - private function hasImageCapableProvider(?string $salesChannelId = null): bool - { - foreach ($this->providers->configured($salesChannelId) as $provider) { - if (\in_array(Capability::ImageGeneration, $provider->getCapabilities(), true)) { - return true; - } - } - - return false; - } - public function featureFlags(): FeatureFlags { return $this->featureFlags; @@ -272,4 +261,15 @@ public function getCatalog(): ModelCatalogInterface { return $this->catalog; } + + private function hasImageCapableProvider(?string $salesChannelId = null): bool + { + foreach ($this->providers->configured($salesChannelId) as $provider) { + if (\in_array(Capability::ImageGeneration, $provider->getCapabilities(), true)) { + return true; + } + } + + return false; + } } diff --git a/src/Client/PromptBuilder.php b/src/Client/PromptBuilder.php index 29bd9ca..5d0ed12 100644 --- a/src/Client/PromptBuilder.php +++ b/src/Client/PromptBuilder.php @@ -31,7 +31,9 @@ */ final class PromptBuilder { - /** @var list */ + /** + * @var list + */ private array $messages = []; private ?string $provider = null; @@ -46,7 +48,9 @@ final class PromptBuilder private ?int $maxTokens = null; - /** @var array|null */ + /** + * @var array|null + */ private ?array $responseSchema = null; private int $candidateCount = 1; @@ -57,12 +61,16 @@ final class PromptBuilder private string $imageResponseFormat = ImageRequest::FORMAT_B64; - /** @var list */ + /** + * @var list + */ private array $sourceImages = []; private ?ImageInput $mask = null; - /** @var array */ + /** + * @var array + */ private array $metadata = []; public function __construct( diff --git a/src/Client/ProviderRegistry.php b/src/Client/ProviderRegistry.php index 6a01a14..0775d53 100644 --- a/src/Client/ProviderRegistry.php +++ b/src/Client/ProviderRegistry.php @@ -14,7 +14,9 @@ */ final class ProviderRegistry { - /** @var array */ + /** + * @var array + */ private array $providers = []; /** diff --git a/src/Config/CapabilityDefaultsStore.php b/src/Config/CapabilityDefaultsStore.php index 87e1983..3ae89e9 100644 --- a/src/Config/CapabilityDefaultsStore.php +++ b/src/Config/CapabilityDefaultsStore.php @@ -17,7 +17,9 @@ final class CapabilityDefaultsStore { private const CONFIG_PREFIX = 'FroshAI.config.'; - /** @var array */ + /** + * @var array + */ private const KEYS = [ 'text_generation' => [ 'provider' => 'defaultTextProvider', diff --git a/src/Entity/BulkJob/BulkJobEntity.php b/src/Entity/BulkJob/BulkJobEntity.php index b83abf1..aaf7c96 100644 --- a/src/Entity/BulkJob/BulkJobEntity.php +++ b/src/Entity/BulkJob/BulkJobEntity.php @@ -21,13 +21,19 @@ class BulkJobEntity extends Entity protected int $failed = 0; - /** @var list|null */ + /** + * @var list|null + */ protected ?array $entityIds = null; - /** @var list|null */ + /** + * @var list|null + */ protected ?array $errors = null; - /** @var array|null */ + /** + * @var array|null + */ protected ?array $context = null; public function getType(): string diff --git a/src/Entity/UsageLog/UsageLogEntity.php b/src/Entity/UsageLog/UsageLogEntity.php index 58d6eca..a8440b2 100644 --- a/src/Entity/UsageLog/UsageLogEntity.php +++ b/src/Entity/UsageLog/UsageLogEntity.php @@ -37,7 +37,9 @@ class UsageLogEntity extends Entity protected ?string $errorMessage = null; - /** @var array|null */ + /** + * @var array|null + */ protected ?array $metadata = null; public function getType(): string diff --git a/src/Provider/Anthropic/AnthropicProvider.php b/src/Provider/Anthropic/AnthropicProvider.php index 6c8175b..5393eb2 100644 --- a/src/Provider/Anthropic/AnthropicProvider.php +++ b/src/Provider/Anthropic/AnthropicProvider.php @@ -21,12 +21,12 @@ final class AnthropicProvider implements AiProviderInterface { public const ID = 'anthropic'; - private const CONFIG_PREFIX = 'FroshAI.config.'; - public const DEFAULT_BASE_URL = 'https://api.anthropic.com'; public const DEFAULT_TEXT_MODEL = 'claude-sonnet-4-5'; + private const CONFIG_PREFIX = 'FroshAI.config.'; + public function __construct( private readonly HttpClientInterface $httpClient, private readonly SystemConfigService $systemConfigService, diff --git a/src/Provider/Gemini/GeminiClient.php b/src/Provider/Gemini/GeminiClient.php index cd4626b..36ff108 100644 --- a/src/Provider/Gemini/GeminiClient.php +++ b/src/Provider/Gemini/GeminiClient.php @@ -6,7 +6,6 @@ use Frosh\AI\Enum\FinishReason; use Frosh\AI\Enum\MessageRole; use Frosh\AI\Exception\ProviderHttpException; -use Frosh\AI\Message\ChatMessage; use Frosh\AI\Request\ChatRequest; use Frosh\AI\Request\ImageRequest; use Frosh\AI\Response\ChatResponse; diff --git a/src/Provider/Gemini/GeminiProvider.php b/src/Provider/Gemini/GeminiProvider.php index 71536f4..5ef2f73 100644 --- a/src/Provider/Gemini/GeminiProvider.php +++ b/src/Provider/Gemini/GeminiProvider.php @@ -19,14 +19,14 @@ final class GeminiProvider implements AiProviderInterface { public const ID = 'gemini'; - private const CONFIG_PREFIX = 'FroshAI.config.'; - public const DEFAULT_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta'; public const DEFAULT_TEXT_MODEL = 'gemini-2.5-flash'; public const DEFAULT_IMAGE_MODEL = 'gemini-2.5-flash-image'; + private const CONFIG_PREFIX = 'FroshAI.config.'; + public function __construct( private readonly HttpClientInterface $httpClient, private readonly SystemConfigService $systemConfigService, diff --git a/src/Provider/Mistral/MistralProvider.php b/src/Provider/Mistral/MistralProvider.php index 27c2cf2..3d92d71 100644 --- a/src/Provider/Mistral/MistralProvider.php +++ b/src/Provider/Mistral/MistralProvider.php @@ -20,12 +20,12 @@ final class MistralProvider implements AiProviderInterface { public const ID = 'mistral'; - private const CONFIG_PREFIX = 'FroshAI.config.'; - public const DEFAULT_BASE_URL = 'https://api.mistral.ai/v1'; public const DEFAULT_MODEL = 'mistral-large-latest'; + private const CONFIG_PREFIX = 'FroshAI.config.'; + public function __construct( private readonly HttpClientInterface $httpClient, private readonly SystemConfigService $systemConfigService, diff --git a/src/Provider/OpenAi/OpenAiLiveModelClient.php b/src/Provider/OpenAi/OpenAiLiveModelClient.php index d5163c0..5258427 100644 --- a/src/Provider/OpenAi/OpenAiLiveModelClient.php +++ b/src/Provider/OpenAi/OpenAiLiveModelClient.php @@ -54,43 +54,6 @@ public function listModels(?string $preferredSource = null): array }; } - /** - * @param list> $models - * - * @return array{source: string, models: list>, rawCount: int} - */ - private function wrap(string $source, array $models): array - { - return [ - 'source' => $source, - 'models' => $models, - 'rawCount' => \count($models), - ]; - } - - /** - * @return array{source: string, models: list>, rawCount?: int} - */ - private function listAuto(): array - { - if ($this->chatGptAuth->isConnected()) { - try { - return $this->wrap('chatgpt', $this->listChatGptModels()); - } catch (\Throwable $e) { - $this->logger->warning('FroshAI: ChatGPT models fetch failed: {message}', [ - 'message' => $e->getMessage(), - 'exception' => $e, - ]); - } - } - - if ($this->apiKey() !== null) { - return $this->wrap('api_key', $this->listApiKeyModels()); - } - - throw new AiException('No live model source available. Connect ChatGPT or configure an API key.'); - } - /** * Models available on the ChatGPT / Codex subscription backend. * @@ -335,6 +298,43 @@ public function mapApiKeyModels(array $payload): array return $rows; } + /** + * @param list> $models + * + * @return array{source: string, models: list>, rawCount: int} + */ + private function wrap(string $source, array $models): array + { + return [ + 'source' => $source, + 'models' => $models, + 'rawCount' => \count($models), + ]; + } + + /** + * @return array{source: string, models: list>, rawCount?: int} + */ + private function listAuto(): array + { + if ($this->chatGptAuth->isConnected()) { + try { + return $this->wrap('chatgpt', $this->listChatGptModels()); + } catch (\Throwable $e) { + $this->logger->warning('FroshAI: ChatGPT models fetch failed: {message}', [ + 'message' => $e->getMessage(), + 'exception' => $e, + ]); + } + } + + if ($this->apiKey() !== null) { + return $this->wrap('api_key', $this->listApiKeyModels()); + } + + throw new AiException('No live model source available. Connect ChatGPT or configure an API key.'); + } + private function apiKey(): ?string { $value = $this->systemConfigService->getString('FroshAI.config.openAiApiKey'); diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.html.twig b/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.html.twig index 5390cec..5699d78 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.html.twig +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.html.twig @@ -18,7 +18,6 @@

{{ languageHint }}

- - -

- {# Progress phase: poll the bulk job until it reaches a terminal status. #}

- {{ $tc('frosh-ai-product.bulk.statusLabel') }}: {{ jobStatusLabel }} + {{ $tc('frosh-ai-product.bulk.statusLabel') }} + : + {{ jobStatusLabel }}

- -

{{ $tc('frosh-ai-product.bulk.progressCounts', 0, { processed, @@ -70,14 +66,12 @@ failed }) }}

-

{{ $tc('frosh-ai-product.bulk.summary', 0, { succeeded, failed }) }}

- -

- -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.scss b/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.scss index e7eb7d5..5387fb1 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.scss +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/frosh-ai-bulk-generate-modal.scss @@ -24,7 +24,7 @@ margin: 0 0 16px; font-size: 12px; line-height: 1.4; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__tone { @@ -58,13 +58,13 @@ padding-left: 18px; font-size: 12px; line-height: 1.4; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__hint { margin: 0 0 8px; font-size: 12px; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__error { diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/index.js b/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/index.js index 90c2a6e..361bd57 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/index.js +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-bulk-generate-modal/index.js @@ -22,13 +22,9 @@ const STORAGE_PREFIX = 'frosh-ai.bulk-generate'; Shopware.Component.register('frosh-ai-bulk-generate-modal', { template, - inject: [ - 'froshAiApiService', - ], + inject: ['froshAiApiService'], - mixins: [ - Mixin.getByName('notification'), - ], + mixins: [Mixin.getByName('notification')], props: { productIds: { @@ -37,9 +33,7 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { }, }, - emits: [ - 'close', - ], + emits: ['close'], data() { return { @@ -74,27 +68,43 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { } return [ - { value: 'professional', label: this.$tc('frosh-ai-product.modal.tones.professional') }, - { value: 'casual', label: this.$tc('frosh-ai-product.modal.tones.casual') }, - { value: 'luxury', label: this.$tc('frosh-ai-product.modal.tones.luxury') }, - { value: 'technical', label: this.$tc('frosh-ai-product.modal.tones.technical') }, - { value: 'playful', label: this.$tc('frosh-ai-product.modal.tones.playful') }, + { + value: 'professional', + label: this.$tc( + 'frosh-ai-product.modal.tones.professional' + ), + }, + { + value: 'casual', + label: this.$tc('frosh-ai-product.modal.tones.casual'), + }, + { + value: 'luxury', + label: this.$tc('frosh-ai-product.modal.tones.luxury'), + }, + { + value: 'technical', + label: this.$tc('frosh-ai-product.modal.tones.technical'), + }, + { + value: 'playful', + label: this.$tc('frosh-ai-product.modal.tones.playful'), + }, ]; }, contentLanguage() { const storeLanguage = Shopware.Store.get('context')?.api?.language; - const languageId = Shopware.Context?.api?.languageId - || storeLanguage?.id - || null; + const languageId = + Shopware.Context?.api?.languageId || storeLanguage?.id || null; - const locale = storeLanguage?.translationCode?.code - || storeLanguage?.locale?.code - || null; + const locale = + storeLanguage?.translationCode?.code || + storeLanguage?.locale?.code || + null; - const name = storeLanguage?.name - || storeLanguage?.translated?.name - || null; + const name = + storeLanguage?.name || storeLanguage?.translated?.name || null; return { id: languageId, @@ -106,10 +116,15 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { languageHint() { const { locale, name } = this.contentLanguage; if (name && locale) { - return this.$tc('frosh-ai-product.bulk.languageHintNamed', 0, { name, locale }); + return this.$tc('frosh-ai-product.bulk.languageHintNamed', 0, { + name, + locale, + }); } if (locale) { - return this.$tc('frosh-ai-product.bulk.languageHintLocale', 0, { locale }); + return this.$tc('frosh-ai-product.bulk.languageHintLocale', 0, { + locale, + }); } return this.$tc('frosh-ai-product.bulk.languageHintFallback'); @@ -131,7 +146,9 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { }, isJobFinished() { - return this.jobStatus === 'completed' || this.jobStatus === 'failed'; + return ( + this.jobStatus === 'completed' || this.jobStatus === 'failed' + ); }, processed() { @@ -162,7 +179,10 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { hasMoreErrors() { const errors = this.job?.errors; - return Array.isArray(errors) && errors.length > this.errorEntries.length; + return ( + Array.isArray(errors) && + errors.length > this.errorEntries.length + ); }, footerCloseLabel() { @@ -189,11 +209,11 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { async loadTones() { try { - const status = await this.froshAiApiService.productDescriptionStatus(); + const status = + await this.froshAiApiService.productDescriptionStatus(); this.tones = Array.isArray(status.tones) ? status.tones : []; - const defaultTone = status.defaultTone - || this.tones[0]?.id - || 'professional'; + const defaultTone = + status.defaultTone || this.tones[0]?.id || 'professional'; this.defaultTone = defaultTone; this.tone = defaultTone; } catch (error) { @@ -232,10 +252,13 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { persistPreferences() { try { - window.localStorage.setItem(this.storageKey, JSON.stringify({ - tone: this.tone, - maxWords: this.maxWords, - })); + window.localStorage.setItem( + this.storageKey, + JSON.stringify({ + tone: this.tone, + maxWords: this.maxWords, + }) + ); } catch (e) { // ignore } @@ -265,11 +288,13 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { try { const result = await this.froshAiApiService.productBulkGenerate( this.productIds, - this.buildOptions(), + this.buildOptions() ); if (!result.success || !result.jobId) { - throw new Error(result.error || this.$tc('frosh-ai-product.bulk.error')); + throw new Error( + result.error || this.$tc('frosh-ai-product.bulk.error') + ); } this.jobId = result.jobId; @@ -284,9 +309,10 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { this.phase = 'progress'; this.startPolling(); } catch (error) { - this.fieldError = error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-product.bulk.error'); + this.fieldError = + error?.response?.data?.error || + error?.message || + this.$tc('frosh-ai-product.bulk.error'); this.createNotificationError({ message: this.fieldError, }); @@ -316,9 +342,13 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { } try { - const status = await this.froshAiApiService.bulkJobStatus(this.jobId); + const status = await this.froshAiApiService.bulkJobStatus( + this.jobId + ); if (!status.success) { - throw new Error(status.error || this.$tc('frosh-ai-product.bulk.error')); + throw new Error( + status.error || this.$tc('frosh-ai-product.bulk.error') + ); } this.job = status; @@ -328,9 +358,10 @@ Shopware.Component.register('frosh-ai-bulk-generate-modal', { } } catch (error) { this.stopPolling(); - this.fieldError = error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-product.bulk.error'); + this.fieldError = + error?.response?.data?.error || + error?.message || + this.$tc('frosh-ai-product.bulk.error'); } }, diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.html.twig b/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.html.twig index 65863bf..bc0b4bc 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.html.twig +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.html.twig @@ -11,7 +11,6 @@ > {{ $tc('frosh-ai-category.modal.noneAvailable') }} -
{{ languageHint }}

- -

{{ $tc('frosh-ai-category.modal.overwriteHint') }}

- - - -

{{ $tc('frosh-ai-category.modal.needSeed') }}

-

- -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.scss b/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.scss index 06eb749..bb60902 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.scss +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/frosh-ai-category-generate-modal.scss @@ -16,7 +16,7 @@ margin: 0 0 16px; font-size: 12px; line-height: 1.4; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__fill-empty { @@ -49,7 +49,7 @@ } &__hint { - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); font-size: 12px; } @@ -58,6 +58,6 @@ } &__empty { - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } } diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/index.js b/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/index.js index e1756ee..729cf05 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/index.js +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-category-generate-modal/index.js @@ -16,14 +16,9 @@ const STORAGE_PREFIX = 'frosh-ai.category-generate'; Shopware.Component.register('frosh-ai-category-generate-modal', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], - mixins: [ - Mixin.getByName('notification'), - ], + mixins: [Mixin.getByName('notification')], props: { category: { @@ -37,10 +32,7 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { }, }, - emits: [ - 'close', - 'generated', - ], + emits: ['close', 'generated'], data() { return { @@ -76,27 +68,43 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { } return [ - { value: 'professional', label: this.$tc('frosh-ai-product.modal.tones.professional') }, - { value: 'casual', label: this.$tc('frosh-ai-product.modal.tones.casual') }, - { value: 'luxury', label: this.$tc('frosh-ai-product.modal.tones.luxury') }, - { value: 'technical', label: this.$tc('frosh-ai-product.modal.tones.technical') }, - { value: 'playful', label: this.$tc('frosh-ai-product.modal.tones.playful') }, + { + value: 'professional', + label: this.$tc( + 'frosh-ai-product.modal.tones.professional' + ), + }, + { + value: 'casual', + label: this.$tc('frosh-ai-product.modal.tones.casual'), + }, + { + value: 'luxury', + label: this.$tc('frosh-ai-product.modal.tones.luxury'), + }, + { + value: 'technical', + label: this.$tc('frosh-ai-product.modal.tones.technical'), + }, + { + value: 'playful', + label: this.$tc('frosh-ai-product.modal.tones.playful'), + }, ]; }, contentLanguage() { const storeLanguage = Shopware.Store.get('context')?.api?.language; - const languageId = Shopware.Context?.api?.languageId - || storeLanguage?.id - || null; + const languageId = + Shopware.Context?.api?.languageId || storeLanguage?.id || null; - const locale = storeLanguage?.translationCode?.code - || storeLanguage?.locale?.code - || null; + const locale = + storeLanguage?.translationCode?.code || + storeLanguage?.locale?.code || + null; - const name = storeLanguage?.name - || storeLanguage?.translated?.name - || null; + const name = + storeLanguage?.name || storeLanguage?.translated?.name || null; return { id: languageId, @@ -108,10 +116,18 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { languageHint() { const { locale, name } = this.contentLanguage; if (name && locale) { - return this.$tc('frosh-ai-category.modal.languageHintNamed', 0, { name, locale }); + return this.$tc( + 'frosh-ai-category.modal.languageHintNamed', + 0, + { name, locale } + ); } if (locale) { - return this.$tc('frosh-ai-category.modal.languageHintLocale', 0, { locale }); + return this.$tc( + 'frosh-ai-category.modal.languageHintLocale', + 0, + { locale } + ); } return this.$tc('frosh-ai-category.modal.languageHintFallback'); @@ -120,8 +136,10 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { descriptionHasContent() { const category = this.category || {}; const plain = String( - category.description || category.translated?.description || '', - ).replace(/<[^>]+>/g, '').trim(); + category.description || category.translated?.description || '' + ) + .replace(/<[^>]+>/g, '') + .trim(); return plain !== ''; }, @@ -198,12 +216,12 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { async loadStatus() { this.isLoadingStatus = true; try { - const status = await this.froshAiApiService.categoryDescriptionStatus(); + const status = + await this.froshAiApiService.categoryDescriptionStatus(); this.descriptionEnabled = !!status.enabled; this.tones = Array.isArray(status.tones) ? status.tones : []; - const defaultTone = status.defaultTone - || this.tones[0]?.id - || 'professional'; + const defaultTone = + status.defaultTone || this.tones[0]?.id || 'professional'; this.defaultTone = defaultTone; this.tone = defaultTone; } catch (error) { @@ -244,11 +262,14 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { persistPreferences() { try { - window.localStorage.setItem(this.storageKey, JSON.stringify({ - fillEmptyOnly: this.fillEmptyOnly, - tone: this.tone, - maxWords: this.maxWords, - })); + window.localStorage.setItem( + this.storageKey, + JSON.stringify({ + fillEmptyOnly: this.fillEmptyOnly, + tone: this.tone, + maxWords: this.maxWords, + }) + ); } catch (e) { // ignore } @@ -264,9 +285,7 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { buildPayload() { const category = this.category || {}; - const name = category.name - ?? category.translated?.name - ?? ''; + const name = category.name ?? category.translated?.name ?? ''; const parentCategories = []; // Assumption: category breadcrumb names (parent categories) are available @@ -281,24 +300,28 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { } const contentLanguage = this.contentLanguage; - const extraInstructions = String(this.extraInstructions || '').trim(); + const extraInstructions = String( + this.extraInstructions || '' + ).trim(); return { categoryId: category.id || null, name, parentCategories, - keywords: category.keywords - ?? category.translated?.keywords - ?? null, - metaTitle: category.metaTitle - ?? category.translated?.metaTitle - ?? null, - metaDescription: category.metaDescription - ?? category.translated?.metaDescription - ?? null, - existingDescription: category.description - ?? category.translated?.description - ?? null, + keywords: + category.keywords ?? category.translated?.keywords ?? null, + metaTitle: + category.metaTitle ?? + category.translated?.metaTitle ?? + null, + metaDescription: + category.metaDescription ?? + category.translated?.metaDescription ?? + null, + existingDescription: + category.description ?? + category.translated?.description ?? + null, tone: this.tone || this.defaultTone, languageId: contentLanguage.id, language: contentLanguage.locale, @@ -309,11 +332,13 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { }, languageLabel(result) { - return result?.languageName - || result?.language - || this.contentLanguage.name - || this.contentLanguage.locale - || '—'; + return ( + result?.languageName || + result?.language || + this.contentLanguage.name || + this.contentLanguage.locale || + '—' + ); }, confirmOverwriteIfNeeded() { @@ -323,7 +348,7 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { // eslint-disable-next-line no-alert return window.confirm( - this.$tc('frosh-ai-category.modal.overwriteConfirm'), + this.$tc('frosh-ai-category.modal.overwriteConfirm') ); }, @@ -341,20 +366,28 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { this.fieldError = null; try { - const result = await this.froshAiApiService.generateCategoryDescription( - this.buildPayload(), - ); + const result = + await this.froshAiApiService.generateCategoryDescription( + this.buildPayload() + ); if (!result.success || !result.description) { - throw new Error(result.error || this.$tc('frosh-ai-category.modal.error')); + throw new Error( + result.error || + this.$tc('frosh-ai-category.modal.error') + ); } // Snapshot everything needed after close (parent v-if destroys this component). const description = result.description; const categoryRef = this.category; - const successMessage = this.$tc('frosh-ai-category.modal.success', 0, { - model: result.model || '—', - language: this.languageLabel(result), - }); + const successMessage = this.$tc( + 'frosh-ai-category.modal.success', + 0, + { + model: result.model || '—', + language: this.languageLabel(result), + } + ); this.isGenerating = false; this.persistPreferences(); @@ -367,7 +400,10 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { window.setTimeout(() => { if (categoryRef && description) { categoryRef.description = description; - if (categoryRef.translated && typeof categoryRef.translated === 'object') { + if ( + categoryRef.translated && + typeof categoryRef.translated === 'object' + ) { categoryRef.translated.description = description; } } @@ -383,9 +419,10 @@ Shopware.Component.register('frosh-ai-category-generate-modal', { } }, 450); } catch (error) { - this.fieldError = error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-category.modal.error'); + this.fieldError = + error?.response?.data?.error || + error?.message || + this.$tc('frosh-ai-category.modal.error'); this.createNotificationError({ message: this.fieldError, }); diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.html.twig b/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.html.twig index d606d33..6e72e40 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.html.twig +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.html.twig @@ -11,7 +11,6 @@ > {{ $tc('frosh-ai-manufacturer.modal.noneAvailable') }} -
{{ languageHint }}

- -

{{ $tc('frosh-ai-manufacturer.modal.overwriteHint') }}

- - - -

{{ $tc('frosh-ai-manufacturer.modal.needSeed') }}

-

- -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.scss b/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.scss index ee1e316..772e91b 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.scss +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/frosh-ai-manufacturer-generate-modal.scss @@ -16,7 +16,7 @@ margin: 0 0 16px; font-size: 12px; line-height: 1.4; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__fill-empty { @@ -49,7 +49,7 @@ } &__hint { - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); font-size: 12px; } @@ -58,6 +58,6 @@ } &__empty { - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } } diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/index.js b/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/index.js index 46487bb..70fde7e 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/index.js +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-manufacturer-generate-modal/index.js @@ -16,14 +16,9 @@ const STORAGE_PREFIX = 'frosh-ai.manufacturer-generate'; Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], - mixins: [ - Mixin.getByName('notification'), - ], + mixins: [Mixin.getByName('notification')], props: { manufacturer: { @@ -37,10 +32,7 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { }, }, - emits: [ - 'close', - 'generated', - ], + emits: ['close', 'generated'], data() { return { @@ -61,7 +53,9 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { canEdit() { // Assumption: 'product_manufacturer.editor' is the Shopware acl key used // by the manufacturer module (same key as the trigger in sw-manufacturer-detail). - return this.allowEdit && this.acl.can('product_manufacturer.editor'); + return ( + this.allowEdit && this.acl.can('product_manufacturer.editor') + ); }, storageKey() { @@ -77,27 +71,43 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { } return [ - { value: 'professional', label: this.$tc('frosh-ai-product.modal.tones.professional') }, - { value: 'casual', label: this.$tc('frosh-ai-product.modal.tones.casual') }, - { value: 'luxury', label: this.$tc('frosh-ai-product.modal.tones.luxury') }, - { value: 'technical', label: this.$tc('frosh-ai-product.modal.tones.technical') }, - { value: 'playful', label: this.$tc('frosh-ai-product.modal.tones.playful') }, + { + value: 'professional', + label: this.$tc( + 'frosh-ai-product.modal.tones.professional' + ), + }, + { + value: 'casual', + label: this.$tc('frosh-ai-product.modal.tones.casual'), + }, + { + value: 'luxury', + label: this.$tc('frosh-ai-product.modal.tones.luxury'), + }, + { + value: 'technical', + label: this.$tc('frosh-ai-product.modal.tones.technical'), + }, + { + value: 'playful', + label: this.$tc('frosh-ai-product.modal.tones.playful'), + }, ]; }, contentLanguage() { const storeLanguage = Shopware.Store.get('context')?.api?.language; - const languageId = Shopware.Context?.api?.languageId - || storeLanguage?.id - || null; + const languageId = + Shopware.Context?.api?.languageId || storeLanguage?.id || null; - const locale = storeLanguage?.translationCode?.code - || storeLanguage?.locale?.code - || null; + const locale = + storeLanguage?.translationCode?.code || + storeLanguage?.locale?.code || + null; - const name = storeLanguage?.name - || storeLanguage?.translated?.name - || null; + const name = + storeLanguage?.name || storeLanguage?.translated?.name || null; return { id: languageId, @@ -109,10 +119,18 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { languageHint() { const { locale, name } = this.contentLanguage; if (name && locale) { - return this.$tc('frosh-ai-manufacturer.modal.languageHintNamed', 0, { name, locale }); + return this.$tc( + 'frosh-ai-manufacturer.modal.languageHintNamed', + 0, + { name, locale } + ); } if (locale) { - return this.$tc('frosh-ai-manufacturer.modal.languageHintLocale', 0, { locale }); + return this.$tc( + 'frosh-ai-manufacturer.modal.languageHintLocale', + 0, + { locale } + ); } return this.$tc('frosh-ai-manufacturer.modal.languageHintFallback'); @@ -121,8 +139,12 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { descriptionHasContent() { const manufacturer = this.manufacturer || {}; const plain = String( - manufacturer.description || manufacturer.translated?.description || '', - ).replace(/<[^>]+>/g, '').trim(); + manufacturer.description || + manufacturer.translated?.description || + '' + ) + .replace(/<[^>]+>/g, '') + .trim(); return plain !== ''; }, @@ -137,7 +159,8 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { hasSeedData() { const manufacturer = this.manufacturer || {}; - const name = manufacturer.name || manufacturer.translated?.name || ''; + const name = + manufacturer.name || manufacturer.translated?.name || ''; return String(name).trim() !== ''; }, @@ -199,12 +222,12 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { async loadStatus() { this.isLoadingStatus = true; try { - const status = await this.froshAiApiService.manufacturerDescriptionStatus(); + const status = + await this.froshAiApiService.manufacturerDescriptionStatus(); this.descriptionEnabled = !!status.enabled; this.tones = Array.isArray(status.tones) ? status.tones : []; - const defaultTone = status.defaultTone - || this.tones[0]?.id - || 'professional'; + const defaultTone = + status.defaultTone || this.tones[0]?.id || 'professional'; this.defaultTone = defaultTone; this.tone = defaultTone; } catch (error) { @@ -245,11 +268,14 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { persistPreferences() { try { - window.localStorage.setItem(this.storageKey, JSON.stringify({ - fillEmptyOnly: this.fillEmptyOnly, - tone: this.tone, - maxWords: this.maxWords, - })); + window.localStorage.setItem( + this.storageKey, + JSON.stringify({ + fillEmptyOnly: this.fillEmptyOnly, + tone: this.tone, + maxWords: this.maxWords, + }) + ); } catch (e) { // ignore } @@ -265,20 +291,22 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { buildPayload() { const manufacturer = this.manufacturer || {}; - const name = manufacturer.name - ?? manufacturer.translated?.name - ?? ''; + const name = + manufacturer.name ?? manufacturer.translated?.name ?? ''; const contentLanguage = this.contentLanguage; - const extraInstructions = String(this.extraInstructions || '').trim(); + const extraInstructions = String( + this.extraInstructions || '' + ).trim(); return { manufacturerId: manufacturer.id || null, name, link: manufacturer.link || null, - existingDescription: manufacturer.description - ?? manufacturer.translated?.description - ?? null, + existingDescription: + manufacturer.description ?? + manufacturer.translated?.description ?? + null, tone: this.tone || this.defaultTone, languageId: contentLanguage.id, language: contentLanguage.locale, @@ -289,11 +317,13 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { }, languageLabel(result) { - return result?.languageName - || result?.language - || this.contentLanguage.name - || this.contentLanguage.locale - || '—'; + return ( + result?.languageName || + result?.language || + this.contentLanguage.name || + this.contentLanguage.locale || + '—' + ); }, confirmOverwriteIfNeeded() { @@ -303,7 +333,7 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { // eslint-disable-next-line no-alert return window.confirm( - this.$tc('frosh-ai-manufacturer.modal.overwriteConfirm'), + this.$tc('frosh-ai-manufacturer.modal.overwriteConfirm') ); }, @@ -321,20 +351,28 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { this.fieldError = null; try { - const result = await this.froshAiApiService.generateManufacturerDescription( - this.buildPayload(), - ); + const result = + await this.froshAiApiService.generateManufacturerDescription( + this.buildPayload() + ); if (!result.success || !result.description) { - throw new Error(result.error || this.$tc('frosh-ai-manufacturer.modal.error')); + throw new Error( + result.error || + this.$tc('frosh-ai-manufacturer.modal.error') + ); } // Snapshot everything needed after close (parent v-if destroys this component). const description = result.description; const manufacturerRef = this.manufacturer; - const successMessage = this.$tc('frosh-ai-manufacturer.modal.success', 0, { - model: result.model || '—', - language: this.languageLabel(result), - }); + const successMessage = this.$tc( + 'frosh-ai-manufacturer.modal.success', + 0, + { + model: result.model || '—', + language: this.languageLabel(result), + } + ); this.isGenerating = false; this.persistPreferences(); @@ -347,8 +385,12 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { window.setTimeout(() => { if (manufacturerRef && description) { manufacturerRef.description = description; - if (manufacturerRef.translated && typeof manufacturerRef.translated === 'object') { - manufacturerRef.translated.description = description; + if ( + manufacturerRef.translated && + typeof manufacturerRef.translated === 'object' + ) { + manufacturerRef.translated.description = + description; } } @@ -363,9 +405,10 @@ Shopware.Component.register('frosh-ai-manufacturer-generate-modal', { } }, 450); } catch (error) { - this.fieldError = error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-manufacturer.modal.error'); + this.fieldError = + error?.response?.data?.error || + error?.message || + this.$tc('frosh-ai-manufacturer.modal.error'); this.createNotificationError({ message: this.fieldError, }); diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.html.twig b/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.html.twig index fc9ce66..f634d53 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.html.twig +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.html.twig @@ -11,7 +11,6 @@ > {{ $tc('frosh-ai-product.modal.noneAvailable') }} -
{{ languageHint }}

-
{{ $tc('frosh-ai-product.modal.fieldsTitle') }} -
- -

{{ overwriteHintText }}

- - -

{{ $tc('frosh-ai-product.modal.needSeed') }}

-

{{ progressText }}

-

- -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.scss b/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.scss index b9ba93c..c9ed3ac 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.scss +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/frosh-ai-product-generate-modal.scss @@ -16,7 +16,7 @@ margin: 0 0 16px; font-size: 12px; line-height: 1.4; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__section { @@ -28,7 +28,7 @@ margin-bottom: 8px; font-size: 12px; font-weight: 600; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__fields { @@ -70,7 +70,7 @@ } &__hint { - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); font-size: 12px; } @@ -79,6 +79,6 @@ } &__empty { - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } } diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/index.js b/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/index.js index a34d0ea..30a2be6 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/index.js +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-product-generate-modal/index.js @@ -24,14 +24,9 @@ const STORAGE_PREFIX = 'frosh-ai.product-generate'; Shopware.Component.register('frosh-ai-product-generate-modal', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], - mixins: [ - Mixin.getByName('notification'), - ], + mixins: [Mixin.getByName('notification')], props: { product: { @@ -55,10 +50,7 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { }, }, - emits: [ - 'close', - 'generated', - ], + emits: ['close', 'generated'], data() { return { @@ -91,7 +83,9 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { }, anyFeatureAvailable() { - return this.titleEnabled || this.descriptionEnabled || this.seoEnabled; + return ( + this.titleEnabled || this.descriptionEnabled || this.seoEnabled + ); }, storageKey() { @@ -126,27 +120,43 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { } return [ - { value: 'professional', label: this.$tc('frosh-ai-product.modal.tones.professional') }, - { value: 'casual', label: this.$tc('frosh-ai-product.modal.tones.casual') }, - { value: 'luxury', label: this.$tc('frosh-ai-product.modal.tones.luxury') }, - { value: 'technical', label: this.$tc('frosh-ai-product.modal.tones.technical') }, - { value: 'playful', label: this.$tc('frosh-ai-product.modal.tones.playful') }, + { + value: 'professional', + label: this.$tc( + 'frosh-ai-product.modal.tones.professional' + ), + }, + { + value: 'casual', + label: this.$tc('frosh-ai-product.modal.tones.casual'), + }, + { + value: 'luxury', + label: this.$tc('frosh-ai-product.modal.tones.luxury'), + }, + { + value: 'technical', + label: this.$tc('frosh-ai-product.modal.tones.technical'), + }, + { + value: 'playful', + label: this.$tc('frosh-ai-product.modal.tones.playful'), + }, ]; }, contentLanguage() { const storeLanguage = Shopware.Store.get('context')?.api?.language; - const languageId = Shopware.Context?.api?.languageId - || storeLanguage?.id - || null; + const languageId = + Shopware.Context?.api?.languageId || storeLanguage?.id || null; - const locale = storeLanguage?.translationCode?.code - || storeLanguage?.locale?.code - || null; + const locale = + storeLanguage?.translationCode?.code || + storeLanguage?.locale?.code || + null; - const name = storeLanguage?.name - || storeLanguage?.translated?.name - || null; + const name = + storeLanguage?.name || storeLanguage?.translated?.name || null; return { id: languageId, @@ -158,10 +168,17 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { languageHint() { const { locale, name } = this.contentLanguage; if (name && locale) { - return this.$tc('frosh-ai-product.modal.languageHintNamed', 0, { name, locale }); + return this.$tc('frosh-ai-product.modal.languageHintNamed', 0, { + name, + locale, + }); } if (locale) { - return this.$tc('frosh-ai-product.modal.languageHintLocale', 0, { locale }); + return this.$tc( + 'frosh-ai-product.modal.languageHintLocale', + 0, + { locale } + ); } return this.$tc('frosh-ai-product.modal.languageHintFallback'); @@ -188,11 +205,16 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { if (this.fillEmptyOnly) { return []; } - return this.selectedFieldKeys.filter((key) => this.fieldHasContent(key)); + return this.selectedFieldKeys.filter((key) => + this.fieldHasContent(key) + ); }, willSkipAllSelected() { - return this.selectedFieldCount > 0 && this.effectiveFieldKeys.length === 0; + return ( + this.selectedFieldCount > 0 && + this.effectiveFieldKeys.length === 0 + ); }, showOverwriteHint() { @@ -202,18 +224,22 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { overwriteHintText() { return this.$tc('frosh-ai-product.modal.overwriteHint', 0, { fields: this.fieldsToOverwrite - .map((key) => this.$tc(`frosh-ai-product.modal.fields.${key}`)) + .map((key) => + this.$tc(`frosh-ai-product.modal.fields.${key}`) + ) .join(', '), }); }, anySeoFieldSelected() { - return this.effectiveFieldKeys.some((key) => [ - 'metaTitle', - 'metaDescription', - 'keywords', - 'openGraph', - ].includes(key)); + return this.effectiveFieldKeys.some((key) => + [ + 'metaTitle', + 'metaDescription', + 'keywords', + 'openGraph', + ].includes(key) + ); }, willGenerateTitle() { @@ -233,20 +259,29 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { hasSeedData() { const product = this.product || {}; const name = product.name || product.translated?.name || ''; - const description = product.description || product.translated?.description || ''; - const manufacturer = product.manufacturer?.name - || product.manufacturer?.translated?.name - || ''; + const description = + product.description || product.translated?.description || ''; + const manufacturer = + product.manufacturer?.name || + product.manufacturer?.translated?.name || + ''; const productNumber = product.productNumber || ''; - if (String(name).trim() - || String(description).replace(/<[^>]+>/g, '').trim() - || String(manufacturer).trim() - || String(productNumber).trim()) { + if ( + String(name).trim() || + String(description) + .replace(/<[^>]+>/g, '') + .trim() || + String(manufacturer).trim() || + String(productNumber).trim() + ) { return true; } - if (product.categories && typeof product.categories.forEach === 'function') { + if ( + product.categories && + typeof product.categories.forEach === 'function' + ) { let has = false; product.categories.forEach(() => { has = true; @@ -284,9 +319,15 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { return false; } - const name = this.product?.name || this.product?.translated?.name || ''; - const needsName = effective.includes('description') || this.anySeoFieldSelected; - if (needsName && !String(name).trim() && !effective.includes('title')) { + const name = + this.product?.name || this.product?.translated?.name || ''; + const needsName = + effective.includes('description') || this.anySeoFieldSelected; + if ( + needsName && + !String(name).trim() && + !effective.includes('title') + ) { return false; } @@ -342,22 +383,50 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { fieldHasContent(key) { const product = this.product || {}; - const plain = (value) => String(value || '').replace(/<[^>]+>/g, '').trim(); + const plain = (value) => + String(value || '') + .replace(/<[^>]+>/g, '') + .trim(); switch (key) { case 'title': - return plain(product.name || product.translated?.name) !== ''; + return ( + plain(product.name || product.translated?.name) !== '' + ); case 'description': - return plain(product.description || product.translated?.description) !== ''; + return ( + plain( + product.description || + product.translated?.description + ) !== '' + ); case 'metaTitle': - return plain(product.metaTitle || product.translated?.metaTitle) !== ''; + return ( + plain( + product.metaTitle || product.translated?.metaTitle + ) !== '' + ); case 'metaDescription': - return plain(product.metaDescription || product.translated?.metaDescription) !== ''; + return ( + plain( + product.metaDescription || + product.translated?.metaDescription + ) !== '' + ); case 'keywords': - return plain(product.keywords || product.translated?.keywords) !== ''; + return ( + plain( + product.keywords || product.translated?.keywords + ) !== '' + ); case 'openGraph': { - const ogTitle = plain(product.ogTitle || product.translated?.ogTitle); - const ogDescription = plain(product.ogDescription || product.translated?.ogDescription); + const ogTitle = plain( + product.ogTitle || product.translated?.ogTitle + ); + const ogDescription = plain( + product.ogDescription || + product.translated?.ogDescription + ); return ogTitle !== '' || ogDescription !== ''; } default: @@ -368,14 +437,14 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { async loadStatus() { this.isLoadingStatus = true; try { - const status = await this.froshAiApiService.productDescriptionStatus(); + const status = + await this.froshAiApiService.productDescriptionStatus(); this.titleEnabled = !!status.titleEnabled; this.descriptionEnabled = !!status.enabled; this.seoEnabled = !!status.seoEnabled; this.tones = Array.isArray(status.tones) ? status.tones : []; - const defaultTone = status.defaultTone - || this.tones[0]?.id - || 'professional'; + const defaultTone = + status.defaultTone || this.tones[0]?.id || 'professional'; this.defaultTone = defaultTone; this.tone = defaultTone; } catch (error) { @@ -457,11 +526,14 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { persistPreferences() { try { - window.localStorage.setItem(this.storageKey, JSON.stringify({ - fields: { ...this.fields }, - fillEmptyOnly: this.fillEmptyOnly, - tone: this.tone, - })); + window.localStorage.setItem( + this.storageKey, + JSON.stringify({ + fields: { ...this.fields }, + fillEmptyOnly: this.fillEmptyOnly, + tone: this.tone, + }) + ); } catch (e) { // ignore } @@ -477,12 +549,13 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { buildPayload(draft = {}, extra = {}) { const product = this.product || {}; - const name = draft.name - ?? product.name - ?? product.translated?.name - ?? ''; + const name = + draft.name ?? product.name ?? product.translated?.name ?? ''; const categories = []; - if (product.categories && typeof product.categories.forEach === 'function') { + if ( + product.categories && + typeof product.categories.forEach === 'function' + ) { product.categories.forEach((category) => { const label = category.name || category.translated?.name; if (label) { @@ -492,9 +565,15 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { } const properties = []; - if (product.properties && typeof product.properties.forEach === 'function') { + if ( + product.properties && + typeof product.properties.forEach === 'function' + ) { product.properties.forEach((option) => { - const group = option.group?.name || option.group?.translated?.name || ''; + const group = + option.group?.name || + option.group?.translated?.name || + ''; const value = option.name || option.translated?.name || ''; if (value) { properties.push(group ? `${group}: ${value}` : value); @@ -503,33 +582,40 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { } const contentLanguage = this.contentLanguage; - const extraInstructions = String(this.extraInstructions || '').trim(); + const extraInstructions = String( + this.extraInstructions || '' + ).trim(); return { productId: product.id || null, name, productNumber: product.productNumber || null, - manufacturer: product.manufacturer?.name - || product.manufacturer?.translated?.name - || null, + manufacturer: + product.manufacturer?.name || + product.manufacturer?.translated?.name || + null, categories, properties, - keywords: draft.keywords - ?? product.keywords - ?? product.translated?.keywords - ?? null, - existingDescription: draft.description - ?? product.description - ?? product.translated?.description - ?? null, - existingMetaTitle: draft.metaTitle - ?? product.metaTitle - ?? product.translated?.metaTitle - ?? null, - existingMetaDescription: draft.metaDescription - ?? product.metaDescription - ?? product.translated?.metaDescription - ?? null, + keywords: + draft.keywords ?? + product.keywords ?? + product.translated?.keywords ?? + null, + existingDescription: + draft.description ?? + product.description ?? + product.translated?.description ?? + null, + existingMetaTitle: + draft.metaTitle ?? + product.metaTitle ?? + product.translated?.metaTitle ?? + null, + existingMetaDescription: + draft.metaDescription ?? + product.metaDescription ?? + product.translated?.metaDescription ?? + null, tone: this.tone || this.defaultTone, languageId: contentLanguage.id, language: contentLanguage.locale, @@ -540,11 +626,13 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { }, languageLabel(result) { - return result?.languageName - || result?.language - || this.contentLanguage.name - || this.contentLanguage.locale - || '—'; + return ( + result?.languageName || + result?.language || + this.contentLanguage.name || + this.contentLanguage.locale || + '—' + ); }, /** @@ -552,7 +640,8 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { */ resolveProductEntity() { try { - const fromStore = Shopware.Store.get('swProductDetail')?.product; + const fromStore = + Shopware.Store.get('swProductDetail')?.product; if (fromStore && fromStore.id) { return fromStore; } @@ -574,7 +663,10 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { return; } product[field] = value; - if (product.translated && typeof product.translated === 'object') { + if ( + product.translated && + typeof product.translated === 'object' + ) { product.translated[field] = value; } }); @@ -589,9 +681,11 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { return window.confirm( this.$tc('frosh-ai-product.modal.overwriteConfirm', 0, { fields: this.fieldsToOverwrite - .map((key) => this.$tc(`frosh-ai-product.modal.fields.${key}`)) + .map((key) => + this.$tc(`frosh-ai-product.modal.fields.${key}`) + ) .join(', '), - }), + }) ); }, @@ -618,7 +712,8 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { const generated = []; const updates = {}; const draft = {}; - let skipped = this.selectedFieldKeys.length - this.effectiveFieldKeys.length; + let skipped = + this.selectedFieldKeys.length - this.effectiveFieldKeys.length; let lastResult = null; let modelLabel = '—'; let step = 0; @@ -630,21 +725,31 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { const setProgress = (label) => { step += 1; - this.progressText = this.$tc('frosh-ai-product.modal.progressDetail', 0, { - step, - total, - label, - }); + this.progressText = this.$tc( + 'frosh-ai-product.modal.progressDetail', + 0, + { + step, + total, + label, + } + ); }; try { if (this.willGenerateTitle) { - setProgress(this.$tc('frosh-ai-product.modal.fields.title')); - const result = await this.froshAiApiService.generateProductTitle( - this.buildPayload(draft, { maxChars: 80 }), + setProgress( + this.$tc('frosh-ai-product.modal.fields.title') ); + const result = + await this.froshAiApiService.generateProductTitle( + this.buildPayload(draft, { maxChars: 80 }) + ); if (!result.success || !result.title) { - throw new Error(result.error || this.$tc('frosh-ai-product.modal.error')); + throw new Error( + result.error || + this.$tc('frosh-ai-product.modal.error') + ); } updates.name = result.title; draft.name = result.title; @@ -654,19 +759,28 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { } if (this.willGenerateDescription) { - setProgress(this.$tc('frosh-ai-product.modal.fields.description')); - const name = draft.name - || this.product?.name - || this.product?.translated?.name - || ''; + setProgress( + this.$tc('frosh-ai-product.modal.fields.description') + ); + const name = + draft.name || + this.product?.name || + this.product?.translated?.name || + ''; if (!String(name).trim()) { - throw new Error(this.$tc('frosh-ai-product.modal.needName')); + throw new Error( + this.$tc('frosh-ai-product.modal.needName') + ); } - const result = await this.froshAiApiService.generateProductDescription( - this.buildPayload(draft, { maxWords: 180 }), - ); + const result = + await this.froshAiApiService.generateProductDescription( + this.buildPayload(draft, { maxWords: 180 }) + ); if (!result.success || !result.description) { - throw new Error(result.error || this.$tc('frosh-ai-product.modal.error')); + throw new Error( + result.error || + this.$tc('frosh-ai-product.modal.error') + ); } updates.description = result.description; draft.description = result.description; @@ -677,35 +791,47 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { if (this.anySeoFieldSelected) { setProgress(this.$tc('frosh-ai-product.modal.seoStep')); - const seoKeys = this.effectiveFieldKeys.filter((key) => [ - 'metaTitle', - 'metaDescription', - 'keywords', - 'openGraph', - ].includes(key)); - - const name = draft.name - || this.product?.name - || this.product?.translated?.name - || ''; + const seoKeys = this.effectiveFieldKeys.filter((key) => + [ + 'metaTitle', + 'metaDescription', + 'keywords', + 'openGraph', + ].includes(key) + ); + + const name = + draft.name || + this.product?.name || + this.product?.translated?.name || + ''; if (!String(name).trim()) { - throw new Error(this.$tc('frosh-ai-product.modal.needName')); + throw new Error( + this.$tc('frosh-ai-product.modal.needName') + ); } - const result = await this.froshAiApiService.generateProductSeo( - this.buildPayload(draft, { - includeOpenGraph: seoKeys.includes('openGraph'), - }), - ); + const result = + await this.froshAiApiService.generateProductSeo( + this.buildPayload(draft, { + includeOpenGraph: seoKeys.includes('openGraph'), + }) + ); if (!result.success) { - throw new Error(result.error || this.$tc('frosh-ai-product.modal.error')); + throw new Error( + result.error || + this.$tc('frosh-ai-product.modal.error') + ); } if (seoKeys.includes('metaTitle') && result.metaTitle) { updates.metaTitle = result.metaTitle; generated.push('metaTitle'); } - if (seoKeys.includes('metaDescription') && result.metaDescription) { + if ( + seoKeys.includes('metaDescription') && + result.metaDescription + ) { updates.metaDescription = result.metaDescription; generated.push('metaDescription'); } @@ -731,7 +857,7 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { throw new Error( skipped > 0 ? this.$tc('frosh-ai-product.modal.allSkipped') - : this.$tc('frosh-ai-product.modal.error'), + : this.$tc('frosh-ai-product.modal.error') ); } @@ -745,9 +871,10 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { const updatesCopy = { ...updates }; const generatedCopy = [...generated]; const productRef = this.resolveProductEntity(); - const successKey = skipped > 0 - ? 'frosh-ai-product.modal.successWithSkipped' - : 'frosh-ai-product.modal.success'; + const successKey = + skipped > 0 + ? 'frosh-ai-product.modal.successWithSkipped' + : 'frosh-ai-product.modal.success'; const successMessage = this.$tc(successKey, 0, successPayload); this.isGenerating = false; @@ -765,7 +892,10 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { return; } productRef[field] = value; - if (productRef.translated && typeof productRef.translated === 'object') { + if ( + productRef.translated && + typeof productRef.translated === 'object' + ) { productRef.translated[field] = value; } }); @@ -782,9 +912,10 @@ Shopware.Component.register('frosh-ai-product-generate-modal', { } }, 450); } catch (error) { - this.fieldError = error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-product.modal.error'); + this.fieldError = + error?.response?.data?.error || + error?.message || + this.$tc('frosh-ai-product.modal.error'); this.createNotificationError({ message: this.fieldError, }); diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.html.twig b/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.html.twig index 9ef1ef1..0113642 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.html.twig +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.html.twig @@ -8,19 +8,17 @@ v-if="isLoading" class="frosh-ai-review-summary-modal__loading" > - +

{{ $tc('frosh-ai-review.modal.loading') }}

-

{{ fieldError }}

-
{{ summary }}

-
-
-
-
-
{{ $tc('frosh-ai-review.modal.reviewCount') }}
-
{{ reviewCount !== null ? reviewCount : '—' }}
+
+ {{ $tc('frosh-ai-review.modal.reviewCount') }} +
+
+ {{ reviewCount !== null ? reviewCount : '—' }} +
-
{{ $tc('frosh-ai-review.modal.averageRating') }}
+
+ {{ $tc('frosh-ai-review.modal.averageRating') }} +
{{ formattedAverageRating }}
-

- -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.scss b/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.scss index 763aa32..cad90a0 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.scss +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/frosh-ai-review-summary-modal.scss @@ -10,7 +10,7 @@ &__loading-text { margin: 0; font-size: 13px; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__body { @@ -65,7 +65,7 @@ &__stat dt { font-size: 12px; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__stat dd { @@ -78,7 +78,7 @@ &__meta { margin: 0; font-size: 12px; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); } &__error { diff --git a/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/index.js b/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/index.js index 0878081..7a3c037 100644 --- a/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/index.js +++ b/src/Resources/app/administration/src/extension/component/frosh-ai-review-summary-modal/index.js @@ -14,9 +14,7 @@ import './frosh-ai-review-summary-modal.scss'; Shopware.Component.register('frosh-ai-review-summary-modal', { template, - inject: [ - 'froshAiApiService', - ], + inject: ['froshAiApiService'], props: { productId: { @@ -25,9 +23,7 @@ Shopware.Component.register('frosh-ai-review-summary-modal', { }, }, - emits: [ - 'close', - ], + emits: ['close'], data() { return { @@ -72,24 +68,34 @@ Shopware.Component.register('frosh-ai-review-summary-modal', { this.fieldError = null; try { - const result = await this.froshAiApiService.productReviewSummary(this.productId); + const result = + await this.froshAiApiService.productReviewSummary( + this.productId + ); if (!result.success) { - throw new Error(result.error || this.$tc('frosh-ai-review.modal.error')); + throw new Error( + result.error || this.$tc('frosh-ai-review.modal.error') + ); } this.summary = result.summary || null; this.pros = Array.isArray(result.pros) ? result.pros : []; this.cons = Array.isArray(result.cons) ? result.cons : []; this.themes = Array.isArray(result.themes) ? result.themes : []; - this.reviewCount = Number.isFinite(result.reviewCount) ? result.reviewCount : null; - this.averageRating = Number.isFinite(result.averageRating) ? result.averageRating : null; + this.reviewCount = Number.isFinite(result.reviewCount) + ? result.reviewCount + : null; + this.averageRating = Number.isFinite(result.averageRating) + ? result.averageRating + : null; this.provider = result.provider || null; this.model = result.model || null; } catch (error) { - this.fieldError = error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-review.modal.error'); + this.fieldError = + error?.response?.data?.error || + error?.message || + this.$tc('frosh-ai-review.modal.error'); } finally { this.isLoading = false; } diff --git a/src/Resources/app/administration/src/extension/sw-category-detail-menu/index.js b/src/Resources/app/administration/src/extension/sw-category-detail-menu/index.js index e336312..d32bc93 100644 --- a/src/Resources/app/administration/src/extension/sw-category-detail-menu/index.js +++ b/src/Resources/app/administration/src/extension/sw-category-detail-menu/index.js @@ -15,10 +15,7 @@ import './sw-category-detail-menu.scss'; Shopware.Component.override('sw-category-detail-menu', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], data() { return { @@ -29,8 +26,7 @@ Shopware.Component.override('sw-category-detail-menu', { computed: { canOpenFroshAiGenerate() { - return this.froshAiAnyAvailable - && this.acl.can('category.editor'); + return this.froshAiAnyAvailable && this.acl.can('category.editor'); }, }, @@ -41,7 +37,8 @@ Shopware.Component.override('sw-category-detail-menu', { methods: { async loadFroshAiAvailability() { try { - const status = await this.froshAiApiService.categoryDescriptionStatus(); + const status = + await this.froshAiApiService.categoryDescriptionStatus(); this.froshAiAnyAvailable = !!status.enabled; } catch (error) { this.froshAiAnyAvailable = false; diff --git a/src/Resources/app/administration/src/extension/sw-category-detail-menu/sw-category-detail-menu.html.twig b/src/Resources/app/administration/src/extension/sw-category-detail-menu/sw-category-detail-menu.html.twig index eca631e..45c9e26 100644 --- a/src/Resources/app/administration/src/extension/sw-category-detail-menu/sw-category-detail-menu.html.twig +++ b/src/Resources/app/administration/src/extension/sw-category-detail-menu/sw-category-detail-menu.html.twig @@ -1,8 +1,6 @@ -{# - Overrides the description editor block of Shopware's `sw-category-detail-menu` +{# Overrides the description editor block of Shopware's `sw-category-detail-menu` component (where the category description field lives in Shopware 6.7) to add - a "Generate description with AI" trigger directly above it. -#} + a "Generate description with AI" trigger directly above it. #} {% block sw_category_detail_menu_description %}

-{% parent %} +{% parent() %} + +{# Mount modal only while open. Parent v-if destroys the whole component after close, + which is safer with sw-modal (it appends itself to document.body). #} -{# - Mount modal only while open. Parent v-if destroys the whole component after close, - which is safer with sw-modal (it appends itself to document.body). -#} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/sw-manufacturer-detail/index.js b/src/Resources/app/administration/src/extension/sw-manufacturer-detail/index.js index 812caec..0be8946 100644 --- a/src/Resources/app/administration/src/extension/sw-manufacturer-detail/index.js +++ b/src/Resources/app/administration/src/extension/sw-manufacturer-detail/index.js @@ -11,10 +11,7 @@ import './sw-manufacturer-detail.scss'; Shopware.Component.override('sw-manufacturer-detail', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], data() { return { @@ -25,8 +22,10 @@ Shopware.Component.override('sw-manufacturer-detail', { computed: { canOpenFroshAiGenerate() { - return this.froshAiAnyAvailable - && this.acl.can('product_manufacturer.editor'); + return ( + this.froshAiAnyAvailable && + this.acl.can('product_manufacturer.editor') + ); }, }, @@ -37,7 +36,8 @@ Shopware.Component.override('sw-manufacturer-detail', { methods: { async loadFroshAiAvailability() { try { - const status = await this.froshAiApiService.manufacturerDescriptionStatus(); + const status = + await this.froshAiApiService.manufacturerDescriptionStatus(); this.froshAiAnyAvailable = !!status.enabled; } catch (error) { this.froshAiAnyAvailable = false; diff --git a/src/Resources/app/administration/src/extension/sw-manufacturer-detail/sw-manufacturer-detail.html.twig b/src/Resources/app/administration/src/extension/sw-manufacturer-detail/sw-manufacturer-detail.html.twig index eb61aaf..0d93a0c 100644 --- a/src/Resources/app/administration/src/extension/sw-manufacturer-detail/sw-manufacturer-detail.html.twig +++ b/src/Resources/app/administration/src/extension/sw-manufacturer-detail/sw-manufacturer-detail.html.twig @@ -1,8 +1,6 @@ -{# - Overrides the description field block of Shopware's `sw-manufacturer-detail` +{# Overrides the description field block of Shopware's `sw-manufacturer-detail` template so the "Generate description with AI" trigger sits directly above the - description editor. -#} + description editor. #} {% block sw_manufacturer_detail_base_info_field_description %}
-{% parent %} +{% parent() %} + +{# Mount modal only while open. Parent v-if destroys the whole component after close, + which is safer with sw-modal (it appends itself to document.body). #} -{# - Mount modal only while open. Parent v-if destroys the whole component after close, - which is safer with sw-modal (it appends itself to document.body). -#} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/sw-product-basic-form/index.js b/src/Resources/app/administration/src/extension/sw-product-basic-form/index.js index d89e04f..21bf98b 100644 --- a/src/Resources/app/administration/src/extension/sw-product-basic-form/index.js +++ b/src/Resources/app/administration/src/extension/sw-product-basic-form/index.js @@ -7,10 +7,7 @@ import './sw-product-basic-form.scss'; Shopware.Component.override('sw-product-basic-form', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], data() { return { @@ -21,9 +18,11 @@ Shopware.Component.override('sw-product-basic-form', { computed: { canOpenFroshAiGenerate() { - return this.froshAiAnyAvailable - && this.acl.can('product.editor') - && this.allowEdit !== false; + return ( + this.froshAiAnyAvailable && + this.acl.can('product.editor') && + this.allowEdit !== false + ); }, }, @@ -34,11 +33,12 @@ Shopware.Component.override('sw-product-basic-form', { methods: { async loadFroshAiAvailability() { try { - const status = await this.froshAiApiService.productDescriptionStatus(); + const status = + await this.froshAiApiService.productDescriptionStatus(); this.froshAiAnyAvailable = !!( - status.enabled - || status.titleEnabled - || status.seoEnabled + status.enabled || + status.titleEnabled || + status.seoEnabled ); } catch (error) { this.froshAiAnyAvailable = false; diff --git a/src/Resources/app/administration/src/extension/sw-product-basic-form/sw-product-basic-form.html.twig b/src/Resources/app/administration/src/extension/sw-product-basic-form/sw-product-basic-form.html.twig index 4268b1c..d2d00ac 100644 --- a/src/Resources/app/administration/src/extension/sw-product-basic-form/sw-product-basic-form.html.twig +++ b/src/Resources/app/administration/src/extension/sw-product-basic-form/sw-product-basic-form.html.twig @@ -1,5 +1,5 @@ {% block sw_product_basic_form_title_field %} -{% parent %} +{% parent() %}
-{# - Mount modal only while open. Parent v-if destroys the whole component after close, - which is safer with sw-modal (it appends itself to document.body). -#} +{# Mount modal only while open. Parent v-if destroys the whole component after close, + which is safer with sw-modal (it appends itself to document.body). #} + -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/sw-product-detail-reviews/index.js b/src/Resources/app/administration/src/extension/sw-product-detail-reviews/index.js index 4802dc7..6baa29c 100644 --- a/src/Resources/app/administration/src/extension/sw-product-detail-reviews/index.js +++ b/src/Resources/app/administration/src/extension/sw-product-detail-reviews/index.js @@ -18,10 +18,7 @@ import './sw-product-detail-reviews.scss'; Shopware.Component.override('sw-product-detail-reviews', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], data() { return { @@ -32,8 +29,7 @@ Shopware.Component.override('sw-product-detail-reviews', { computed: { canSummarizeFroshAiReviews() { - return this.froshAiSummaryEnabled - && this.acl.can('product.editor'); + return this.froshAiSummaryEnabled && this.acl.can('product.editor'); }, }, @@ -44,7 +40,8 @@ Shopware.Component.override('sw-product-detail-reviews', { methods: { async loadFroshAiAvailability() { try { - const status = await this.froshAiApiService.productReviewSummaryStatus(); + const status = + await this.froshAiApiService.productReviewSummaryStatus(); this.froshAiSummaryEnabled = !!status.enabled; } catch (error) { this.froshAiSummaryEnabled = false; diff --git a/src/Resources/app/administration/src/extension/sw-product-detail-reviews/sw-product-detail-reviews.html.twig b/src/Resources/app/administration/src/extension/sw-product-detail-reviews/sw-product-detail-reviews.html.twig index 42ce123..34b9028 100644 --- a/src/Resources/app/administration/src/extension/sw-product-detail-reviews/sw-product-detail-reviews.html.twig +++ b/src/Resources/app/administration/src/extension/sw-product-detail-reviews/sw-product-detail-reviews.html.twig @@ -1,8 +1,6 @@ -{# - Overrides the outer block of Shopware's `sw-product-detail-reviews` template so the +{# Overrides the outer block of Shopware's `sw-product-detail-reviews` template so the "Summarize reviews with AI" trigger sits at the very top of the Reviews tab, - above the review listing rendered by `{% parent %}`. -#} + above the review listing rendered by `{% parent %}`. #} {% block sw_product_detail_reviews %}
-{% parent %} +{% parent() %} + +{# Mount modal only while open. Parent v-if destroys the whole component after close, + which is safer with sw-modal (it appends itself to document.body). #} -{# - Mount modal only while open. Parent v-if destroys the whole component after close, - which is safer with sw-modal (it appends itself to document.body). -#} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/extension/sw-product-list/index.js b/src/Resources/app/administration/src/extension/sw-product-list/index.js index 39453ef..ea866df 100644 --- a/src/Resources/app/administration/src/extension/sw-product-list/index.js +++ b/src/Resources/app/administration/src/extension/sw-product-list/index.js @@ -13,10 +13,7 @@ import './sw-product-list.scss'; Shopware.Component.override('sw-product-list', { template, - inject: [ - 'froshAiApiService', - 'acl', - ], + inject: ['froshAiApiService', 'acl'], data() { return { @@ -33,9 +30,11 @@ Shopware.Component.override('sw-product-list', { }, canBulkGenerateFroshAi() { - return this.froshAiBulkAvailable - && this.acl.can('product.editor') - && this.froshAiSelectedProductIds.length > 0; + return ( + this.froshAiBulkAvailable && + this.acl.can('product.editor') && + this.froshAiSelectedProductIds.length > 0 + ); }, }, @@ -51,11 +50,17 @@ Shopware.Component.override('sw-product-list', { this.froshAiApiService.productDescriptionStatus(), ]); - const featureList = Array.isArray(features?.features) ? features.features : []; - const bulkFlag = featureList.find((feature) => feature && feature.id === 'bulk_generation'); - const bulkEnabled = !!features?.master && !!(bulkFlag && bulkFlag.enabled); + const featureList = Array.isArray(features?.features) + ? features.features + : []; + const bulkFlag = featureList.find( + (feature) => feature && feature.id === 'bulk_generation' + ); + const bulkEnabled = + !!features?.master && !!(bulkFlag && bulkFlag.enabled); - this.froshAiBulkAvailable = bulkEnabled && !!productStatus?.enabled; + this.froshAiBulkAvailable = + bulkEnabled && !!productStatus?.enabled; } catch (error) { this.froshAiBulkAvailable = false; } diff --git a/src/Resources/app/administration/src/extension/sw-product-list/sw-product-list.html.twig b/src/Resources/app/administration/src/extension/sw-product-list/sw-product-list.html.twig index ec25eef..925499c 100644 --- a/src/Resources/app/administration/src/extension/sw-product-list/sw-product-list.html.twig +++ b/src/Resources/app/administration/src/extension/sw-product-list/sw-product-list.html.twig @@ -1,5 +1,4 @@ -{# - Appends an "AI bulk generate" button to the product list smart-bar actions. +{# Appends an "AI bulk generate" button to the product list smart-bar actions. Shopware renders the smart-bar actions through the ` - {{ $tc('frosh-ai-settings.chatgpt.help') }} -
{{ deviceVerificationUrl }} - -

- +

{{ $tc('frosh-ai-settings.chatgpt.step2') }}

-
{{ deviceCodeDisplay }}
-

{{ $tc('frosh-ai-settings.chatgpt.waiting', 0, { seconds: deviceSecondsLeft }) }}

-
-
{{ $tc('frosh-ai-settings.chatgpt.signIn') }} - -

+

{{ effectiveAuthLabel }}

- - - - - -
- {{ $tc('frosh-ai-settings.openai.modelsLiveChatGpt') }} + + @@ -210,10 +197,18 @@ - - - - + + + + @@ -232,8 +227,12 @@ {{ model.description }} - - + +
{{ $tc('frosh-ai-settings.models.columns.name') }}{{ $tc('frosh-ai-settings.models.columns.id') }}{{ $tc('frosh-ai-settings.models.columns.context') }}{{ $tc('frosh-ai-settings.models.columns.capabilities') }} + {{ $tc('frosh-ai-settings.models.columns.name') }} + + {{ $tc('frosh-ai-settings.models.columns.id') }} + + {{ $tc('frosh-ai-settings.models.columns.context') }} + + {{ $tc('frosh-ai-settings.models.columns.capabilities') }} +
{{ model.id }}{{ model.contextLimit ? formatNumber(model.contextLimit) : '—' }} + {{ model.id }} + + {{ model.contextLimit ? formatNumber(model.contextLimit) : '—' }} + {% endblock %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/frosh-ai-provider-openai.scss b/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/frosh-ai-provider-openai.scss index 5c39c31..d70c952 100644 --- a/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/frosh-ai-provider-openai.scss +++ b/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/frosh-ai-provider-openai.scss @@ -8,7 +8,7 @@ } &__hint { - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); font-size: 13px; } @@ -67,7 +67,7 @@ &__device-waiting { margin: 0 0 12px; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); font-size: 13px; } @@ -92,7 +92,8 @@ td { text-align: left; padding: 10px 12px; - border-bottom: 1px solid var(--color-border-primary-default, #d1d9e0); + border-bottom: 1px solid + var(--color-border-primary-default, #d1d9e0); vertical-align: top; } @@ -124,7 +125,7 @@ &__model-desc { margin-top: 4px; - color: var(--color-text-secondary-default, #758CA3); + color: var(--color-text-secondary-default, #758ca3); font-size: 12px; line-height: 1.4; max-width: 360px; diff --git a/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/index.js b/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/index.js index 2f06944..be54e61 100644 --- a/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/index.js +++ b/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-provider-openai/index.js @@ -12,14 +12,9 @@ const DOMAIN = 'FroshAI.config'; Shopware.Component.register('frosh-ai-provider-openai', { template, - inject: [ - 'froshAiApiService', - 'systemConfigApiService', - ], + inject: ['froshAiApiService', 'systemConfigApiService'], - mixins: [ - Mixin.getByName('notification'), - ], + mixins: [Mixin.getByName('notification')], props: { provider: { @@ -83,8 +78,10 @@ Shopware.Component.register('frosh-ai-provider-openai', { }); if ( - this.openAiDefaultModel - && !options.some((option) => option.value === this.openAiDefaultModel) + this.openAiDefaultModel && + !options.some( + (option) => option.value === this.openAiDefaultModel + ) ) { options.unshift({ value: this.openAiDefaultModel, @@ -97,9 +94,18 @@ Shopware.Component.register('frosh-ai-provider-openai', { authModeOptions() { return [ - { value: 'auto', label: this.$tc('frosh-ai-settings.openai.authModeAuto') }, - { value: 'device', label: this.$tc('frosh-ai-settings.openai.authModeDevice') }, - { value: 'api_key', label: this.$tc('frosh-ai-settings.openai.authModeApiKey') }, + { + value: 'auto', + label: this.$tc('frosh-ai-settings.openai.authModeAuto'), + }, + { + value: 'device', + label: this.$tc('frosh-ai-settings.openai.authModeDevice'), + }, + { + value: 'api_key', + label: this.$tc('frosh-ai-settings.openai.authModeApiKey'), + }, ]; }, @@ -119,7 +125,10 @@ Shopware.Component.register('frosh-ai-provider-openai', { }, deviceVerificationUrl() { - return this.deviceSession?.verificationUrl || 'https://auth.openai.com/codex/device'; + return ( + this.deviceSession?.verificationUrl || + 'https://auth.openai.com/codex/device' + ); }, }, @@ -146,7 +155,8 @@ Shopware.Component.register('frosh-ai-provider-openai', { this.openAiApiKeySet = !!status.hasApiKey; this.openAiBaseUrl = status.baseUrl || this.openAiBaseUrl; this.openAiOrganization = status.organization || ''; - this.openAiDefaultModel = status.defaultModel || this.openAiDefaultModel; + this.openAiDefaultModel = + status.defaultModel || this.openAiDefaultModel; this.openAiAuthMode = status.authMode || 'auto'; this.effectiveAuthMode = status.effectiveAuthMode || null; @@ -161,7 +171,11 @@ Shopware.Component.register('frosh-ai-provider-openai', { async loadModels() { try { // Prefer live ChatGPT / API models when authenticated; falls back to models.dev. - const response = await this.froshAiApiService.models('openai', 'text_generation', 'auto'); + const response = await this.froshAiApiService.models( + 'openai', + 'text_generation', + 'auto' + ); this.models = response.models || []; this.modelsSource = response.source || 'catalog'; } catch (error) { @@ -171,14 +185,18 @@ Shopware.Component.register('frosh-ai-provider-openai', { }, formatNumber(value) { - return new Intl.NumberFormat(undefined, { notation: 'compact' }).format(value); + return new Intl.NumberFormat(undefined, { + notation: 'compact', + }).format(value); }, buildSavePayload() { const payload = { - [`${DOMAIN}.openAiBaseUrl`]: this.openAiBaseUrl || 'https://api.openai.com/v1', + [`${DOMAIN}.openAiBaseUrl`]: + this.openAiBaseUrl || 'https://api.openai.com/v1', [`${DOMAIN}.openAiOrganization`]: this.openAiOrganization || '', - [`${DOMAIN}.openAiDefaultModel`]: this.openAiDefaultModel || 'gpt-4o', + [`${DOMAIN}.openAiDefaultModel`]: + this.openAiDefaultModel || 'gpt-4o', [`${DOMAIN}.openAiAuthMode`]: this.openAiAuthMode || 'auto', }; @@ -197,15 +215,21 @@ Shopware.Component.register('frosh-ai-provider-openai', { this.isSaving = true; try { - await this.systemConfigApiService.saveValues(this.buildSavePayload()); + await this.systemConfigApiService.saveValues( + this.buildSavePayload() + ); this.openAiApiKey = ''; this.createNotificationSuccess({ - message: this.$tc('frosh-ai-settings.notifications.saveSuccess'), + message: this.$tc( + 'frosh-ai-settings.notifications.saveSuccess' + ), }); this.$emit('updated'); } catch (error) { this.createNotificationError({ - message: this.$tc('frosh-ai-settings.notifications.saveError'), + message: this.$tc( + 'frosh-ai-settings.notifications.saveError' + ), }); } finally { this.isSaving = false; @@ -220,31 +244,42 @@ Shopware.Component.register('frosh-ai-provider-openai', { this.isTesting = true; try { - await this.systemConfigApiService.saveValues(this.buildSavePayload()); + await this.systemConfigApiService.saveValues( + this.buildSavePayload() + ); this.openAiApiKey = ''; const result = await this.froshAiApiService.testConnection( 'openai', - this.openAiDefaultModel, + this.openAiDefaultModel ); if (result.success) { this.createNotificationSuccess({ - message: this.$tc('frosh-ai-settings.notifications.testSuccess', 0, { - model: result.model, - }), + message: this.$tc( + 'frosh-ai-settings.notifications.testSuccess', + 0, + { + model: result.model, + } + ), }); this.$emit('updated'); } else { this.createNotificationError({ - message: result.error || this.$tc('frosh-ai-settings.notifications.testError'), + message: + result.error || + this.$tc( + 'frosh-ai-settings.notifications.testError' + ), }); } } catch (error) { this.createNotificationError({ - message: error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-settings.notifications.testError'), + message: + error?.response?.data?.error || + error?.message || + this.$tc('frosh-ai-settings.notifications.testError'), }); } finally { this.isTesting = false; @@ -258,14 +293,20 @@ Shopware.Component.register('frosh-ai-provider-openai', { await this.froshAiApiService.refreshModels(); await this.loadModels(); this.createNotificationSuccess({ - message: this.$tc('frosh-ai-settings.notifications.refreshSuccess', 0, { - count: this.models.length, - }), + message: this.$tc( + 'frosh-ai-settings.notifications.refreshSuccess', + 0, + { + count: this.models.length, + } + ), }); this.$emit('updated'); } catch (error) { this.createNotificationError({ - message: this.$tc('frosh-ai-settings.notifications.refreshError'), + message: this.$tc( + 'frosh-ai-settings.notifications.refreshError' + ), }); } finally { this.isRefreshingModels = false; @@ -293,18 +334,24 @@ Shopware.Component.register('frosh-ai-provider-openai', { interval: session.interval || 5, expiresIn: session.expiresIn || 900, }; - this.deviceExpiresAt = Date.now() + (this.deviceSession.expiresIn * 1000); + this.deviceExpiresAt = + Date.now() + this.deviceSession.expiresIn * 1000; this.startDeviceCountdown(); this.startDevicePolling(); this.createNotificationSuccess({ - message: this.$tc('frosh-ai-settings.notifications.deviceStarted'), + message: this.$tc( + 'frosh-ai-settings.notifications.deviceStarted' + ), }); } catch (error) { this.createNotificationError({ - message: error?.response?.data?.error - || error?.message - || this.$tc('frosh-ai-settings.notifications.deviceStartError'), + message: + error?.response?.data?.error || + error?.message || + this.$tc( + 'frosh-ai-settings.notifications.deviceStartError' + ), }); } finally { this.isStartingDeviceAuth = false; @@ -316,7 +363,8 @@ Shopware.Component.register('frosh-ai-provider-openai', { return; } - const intervalMs = Math.max(2, this.deviceSession.interval || 5) * 1000; + const intervalMs = + Math.max(2, this.deviceSession.interval || 5) * 1000; const tick = async () => { if (!this.deviceSession) { @@ -327,7 +375,9 @@ Shopware.Component.register('frosh-ai-provider-openai', { this.stopDevicePolling(); this.deviceSession = null; this.createNotificationError({ - message: this.$tc('frosh-ai-settings.notifications.deviceExpired'), + message: this.$tc( + 'frosh-ai-settings.notifications.deviceExpired' + ), }); return; } @@ -335,16 +385,20 @@ Shopware.Component.register('frosh-ai-provider-openai', { try { const result = await this.froshAiApiService.pollDeviceAuth( this.deviceSession.deviceAuthId, - this.deviceSession.userCode, + this.deviceSession.userCode ); if (result.status === 'complete') { this.stopDevicePolling(); this.deviceSession = null; this.createNotificationSuccess({ - message: this.$tc('frosh-ai-settings.notifications.deviceConnected', 0, { - email: result.credentials?.email || '', - }), + message: this.$tc( + 'frosh-ai-settings.notifications.deviceConnected', + 0, + { + email: result.credentials?.email || '', + } + ), }); this.$emit('updated'); return; @@ -368,7 +422,10 @@ Shopware.Component.register('frosh-ai-provider-openai', { this.deviceSecondsLeft = 0; return; } - this.deviceSecondsLeft = Math.max(0, Math.ceil((this.deviceExpiresAt - Date.now()) / 1000)); + this.deviceSecondsLeft = Math.max( + 0, + Math.ceil((this.deviceExpiresAt - Date.now()) / 1000) + ); }; update(); this.deviceCountdownTimer = window.setInterval(update, 1000); @@ -404,12 +461,16 @@ Shopware.Component.register('frosh-ai-provider-openai', { try { await this.froshAiApiService.disconnectDeviceAuth(); this.createNotificationSuccess({ - message: this.$tc('frosh-ai-settings.notifications.deviceDisconnected'), + message: this.$tc( + 'frosh-ai-settings.notifications.deviceDisconnected' + ), }); this.$emit('updated'); } catch (error) { this.createNotificationError({ - message: this.$tc('frosh-ai-settings.notifications.deviceDisconnectError'), + message: this.$tc( + 'frosh-ai-settings.notifications.deviceDisconnectError' + ), }); } finally { this.isDisconnectingDevice = false; @@ -423,7 +484,9 @@ Shopware.Component.register('frosh-ai-provider-openai', { navigator.clipboard.writeText(this.deviceCodeDisplay).then(() => { this.createNotificationSuccess({ - message: this.$tc('frosh-ai-settings.notifications.deviceCodeCopied'), + message: this.$tc( + 'frosh-ai-settings.notifications.deviceCodeCopied' + ), }); }); }, diff --git a/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-settings-tabs/frosh-ai-settings-tabs.html.twig b/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-settings-tabs/frosh-ai-settings-tabs.html.twig index 13729ba..045baf5 100644 --- a/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-settings-tabs/frosh-ai-settings-tabs.html.twig +++ b/src/Resources/app/administration/src/module/frosh-ai-settings/component/frosh-ai-settings-tabs/frosh-ai-settings-tabs.html.twig @@ -16,4 +16,4 @@ {{ $tc('frosh-ai-settings.tabs.playground') }} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/frosh-ai-settings/init/services.init.js b/src/Resources/app/administration/src/module/frosh-ai-settings/init/services.init.js index 08836e7..b049bf2 100644 --- a/src/Resources/app/administration/src/module/frosh-ai-settings/init/services.init.js +++ b/src/Resources/app/administration/src/module/frosh-ai-settings/init/services.init.js @@ -3,6 +3,6 @@ import FroshAiApiService from '../service/frosh-ai-api.service'; Shopware.Service().register('froshAiApiService', () => { return new FroshAiApiService( Shopware.Application.getContainer('init').httpClient, - Shopware.Service('loginService'), + Shopware.Service('loginService') ); }); diff --git a/src/Resources/app/administration/src/module/frosh-ai-settings/page/frosh-ai-settings-index/frosh-ai-settings-index.html.twig b/src/Resources/app/administration/src/module/frosh-ai-settings/page/frosh-ai-settings-index/frosh-ai-settings-index.html.twig index 65cf34c..4558c3c 100644 --- a/src/Resources/app/administration/src/module/frosh-ai-settings/page/frosh-ai-settings-index/frosh-ai-settings-index.html.twig +++ b/src/Resources/app/administration/src/module/frosh-ai-settings/page/frosh-ai-settings-index/frosh-ai-settings-index.html.twig @@ -23,7 +23,6 @@ > {{ $tc('frosh-ai-settings.actions.refreshModels') }} - - - + -
-
{{ $tc('frosh-ai-settings.general.featureFlagsHelp') }}

- {{ $tc('frosh-ai-settings.defaults.help') }}

-
-

+

{{ $tc('frosh-ai-settings.defaults.textTitle') }}

-
-

+

{{ $tc('frosh-ai-settings.defaults.imageTitle') }}

- -
- -
-
-
+
{{ provider.name }} - {{ provider.id }} + {{ provider.id }}
-

+

{{ provider.description }}

-

+

{{ provider.summary }} - · {{ $tc('frosh-ai-settings.list.modelCount', provider.modelCount, { count: provider.modelCount }) }} - + · + {{ $tc('frosh-ai-settings.list.modelCount', provider.modelCount, { count: provider.modelCount }) }} +

-
- {% endblock %}
- {% block frosh_ai_settings_index_create_modal %} - - - - -

{{ createTestMessage }}

-