fix(itinerary): fix route optimization reactivity and replace api key provider dropdown with AI LLM providers
- fix optimizeDayOrder() dual-update: directly set days[dayIndex].items + days before saveReorderedItems() so it reads the correct post-optimization order synchronously (Svelte 4 batches reactive statements; days wasn't updated before save read it) - also patch collection.itinerary order values so reactive rebuild uses new order - replace single google_maps <option> with 8 AI LLM provider options (anthropic, openai, gemini, ollama, groq, mistral, github_models, openrouter) - add getApiKeyProviderLabel() helper for saved key display with google_maps fallback - add i18n keys for all new provider labels in en.json and de.json
This commit is contained in:
@@ -1191,8 +1191,22 @@
|
|||||||
return !getCoordinatesFromItineraryItem(item);
|
return !getCoordinatesFromItineraryItem(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
days[dayIndex].items = [...optimizedPath, ...nonCoordinateItems, ...shadowItems];
|
const newDayItems = [...optimizedPath, ...nonCoordinateItems, ...shadowItems];
|
||||||
|
|
||||||
|
// Patch collection.itinerary order values so the reactive $: days rebuild
|
||||||
|
// uses the new order when it eventually fires.
|
||||||
|
newDayItems.forEach((item, index) => {
|
||||||
|
const itineraryItem = collection.itinerary?.find((it) => it.id === item.id);
|
||||||
|
if (itineraryItem) {
|
||||||
|
itineraryItem.order = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Also directly set days so saveReorderedItems() reads the correct order synchronously
|
||||||
|
// (Svelte 4 batches reactive statements; days isn't updated yet when saveReorderedItems runs)
|
||||||
|
days[dayIndex].items = newDayItems;
|
||||||
days = [...days];
|
days = [...days];
|
||||||
|
// Trigger collection reactivity so the eventual reactive rebuild uses new orders
|
||||||
|
collection = { ...collection, itinerary: [...(collection.itinerary || [])] };
|
||||||
|
|
||||||
isSavingOrder = true;
|
isSavingOrder = true;
|
||||||
savingDay = day.date;
|
savingDay = day.date;
|
||||||
|
|||||||
@@ -748,6 +748,14 @@
|
|||||||
"no_api_keys_saved": "Noch keine API-Schlüssel gespeichert.",
|
"no_api_keys_saved": "Noch keine API-Schlüssel gespeichert.",
|
||||||
"add_api_key": "API-Schlüssel hinzufügen",
|
"add_api_key": "API-Schlüssel hinzufügen",
|
||||||
"provider": "Anbieter",
|
"provider": "Anbieter",
|
||||||
|
"api_key_provider_anthropic": "Anthropic",
|
||||||
|
"api_key_provider_openai": "OpenAI",
|
||||||
|
"api_key_provider_gemini": "Google Gemini",
|
||||||
|
"api_key_provider_ollama": "Ollama (Lokal)",
|
||||||
|
"api_key_provider_groq": "Groq",
|
||||||
|
"api_key_provider_mistral": "Mistral AI",
|
||||||
|
"api_key_provider_github_models": "GitHub Models",
|
||||||
|
"api_key_provider_openrouter": "OpenRouter",
|
||||||
"api_key_provider_google_places": "Google Places API",
|
"api_key_provider_google_places": "Google Places API",
|
||||||
"api_key_value": "API-Schlüssel",
|
"api_key_value": "API-Schlüssel",
|
||||||
"api_key_value_placeholder": "Geben Sie Ihren API-Schlüssel ein",
|
"api_key_value_placeholder": "Geben Sie Ihren API-Schlüssel ein",
|
||||||
|
|||||||
@@ -745,6 +745,14 @@
|
|||||||
"no_api_keys_saved": "No API keys saved yet.",
|
"no_api_keys_saved": "No API keys saved yet.",
|
||||||
"add_api_key": "Add API Key",
|
"add_api_key": "Add API Key",
|
||||||
"provider": "Provider",
|
"provider": "Provider",
|
||||||
|
"api_key_provider_anthropic": "Anthropic",
|
||||||
|
"api_key_provider_openai": "OpenAI",
|
||||||
|
"api_key_provider_gemini": "Google Gemini",
|
||||||
|
"api_key_provider_ollama": "Ollama (Local)",
|
||||||
|
"api_key_provider_groq": "Groq",
|
||||||
|
"api_key_provider_mistral": "Mistral AI",
|
||||||
|
"api_key_provider_github_models": "GitHub Models",
|
||||||
|
"api_key_provider_openrouter": "OpenRouter",
|
||||||
"api_key_provider_google_places": "Google Places API",
|
"api_key_provider_google_places": "Google Places API",
|
||||||
"api_key_value": "API Key",
|
"api_key_value": "API Key",
|
||||||
"api_key_value_placeholder": "Enter your API key",
|
"api_key_value_placeholder": "Enter your API key",
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
};
|
};
|
||||||
let userApiKeys: UserAPIKey[] = data.props.apiKeys ?? [];
|
let userApiKeys: UserAPIKey[] = data.props.apiKeys ?? [];
|
||||||
let apiKeysConfigError: string | null = data.props.apiKeysConfigError ?? null;
|
let apiKeysConfigError: string | null = data.props.apiKeysConfigError ?? null;
|
||||||
let newApiKeyProvider = 'google_maps';
|
let newApiKeyProvider = 'anthropic';
|
||||||
let newApiKeyValue = '';
|
let newApiKeyValue = '';
|
||||||
let isSavingApiKey = false;
|
let isSavingApiKey = false;
|
||||||
let deletingApiKeyId: string | null = null;
|
let deletingApiKeyId: string | null = null;
|
||||||
@@ -53,6 +53,30 @@
|
|||||||
let isLoadingMcpToken = false;
|
let isLoadingMcpToken = false;
|
||||||
let activeSection: string = 'profile';
|
let activeSection: string = 'profile';
|
||||||
|
|
||||||
|
const API_KEY_PROVIDER_OPTIONS = [
|
||||||
|
{ value: 'anthropic', labelKey: 'settings.api_key_provider_anthropic' },
|
||||||
|
{ value: 'openai', labelKey: 'settings.api_key_provider_openai' },
|
||||||
|
{ value: 'gemini', labelKey: 'settings.api_key_provider_gemini' },
|
||||||
|
{ value: 'ollama', labelKey: 'settings.api_key_provider_ollama' },
|
||||||
|
{ value: 'groq', labelKey: 'settings.api_key_provider_groq' },
|
||||||
|
{ value: 'mistral', labelKey: 'settings.api_key_provider_mistral' },
|
||||||
|
{ value: 'github_models', labelKey: 'settings.api_key_provider_github_models' },
|
||||||
|
{ value: 'openrouter', labelKey: 'settings.api_key_provider_openrouter' }
|
||||||
|
];
|
||||||
|
|
||||||
|
function getApiKeyProviderLabel(provider: string): string {
|
||||||
|
const option = API_KEY_PROVIDER_OPTIONS.find((entry) => entry.value === provider);
|
||||||
|
if (option) {
|
||||||
|
return $t(option.labelKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider === 'google_maps') {
|
||||||
|
return $t('settings.api_key_provider_google_places');
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
// typed alias for social providers to satisfy TypeScript
|
// typed alias for social providers to satisfy TypeScript
|
||||||
let socialProviders: Provider[] = data.props.socialProviders ?? [];
|
let socialProviders: Provider[] = data.props.socialProviders ?? [];
|
||||||
|
|
||||||
@@ -1586,7 +1610,7 @@
|
|||||||
{#each userApiKeys as apiKey}
|
{#each userApiKeys as apiKey}
|
||||||
<div class="flex items-center justify-between gap-4 p-4 bg-base-100 rounded-lg">
|
<div class="flex items-center justify-between gap-4 p-4 bg-base-100 rounded-lg">
|
||||||
<div>
|
<div>
|
||||||
<div class="font-medium">{apiKey.provider}</div>
|
<div class="font-medium">{getApiKeyProviderLabel(apiKey.provider)}</div>
|
||||||
<div class="text-sm text-base-content/70 font-mono">
|
<div class="text-sm text-base-content/70 font-mono">
|
||||||
{apiKey.masked_api_key}
|
{apiKey.masked_api_key}
|
||||||
</div>
|
</div>
|
||||||
@@ -1619,9 +1643,11 @@
|
|||||||
class="select select-bordered select-primary w-full"
|
class="select select-bordered select-primary w-full"
|
||||||
bind:value={newApiKeyProvider}
|
bind:value={newApiKeyProvider}
|
||||||
>
|
>
|
||||||
<option value="google_maps">{$t('settings.api_key_provider_google_places')}</option>
|
{#each API_KEY_PROVIDER_OPTIONS as option}
|
||||||
</select>
|
<option value={option.value}>{$t(option.labelKey)}</option>
|
||||||
</div>
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label" for="api-key-value">
|
<label class="label" for="api-key-value">
|
||||||
<span class="label-text font-medium">{$t('settings.api_key_value')}</span>
|
<span class="label-text font-medium">{$t('settings.api_key_value')}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user