@@ -196,7 +200,7 @@
diff --git a/frontend/components/settings/AnalyticsDashboard.vue b/frontend/components/settings/AnalyticsDashboard.vue
new file mode 100644
index 0000000..c01097b
--- /dev/null
+++ b/frontend/components/settings/AnalyticsDashboard.vue
@@ -0,0 +1,1042 @@
+
+
+
+
+
+
{{ title }}
+
{{ description }}
+
+
+
+
+
+
+ {{ selectedUserId ? (userList.find(u => u.id === selectedUserId)?.label ?? selectedUserId) : tx('settings.analytics.all_users', 'All users') }}
+
+
+
+
+
+
+
+
+
+
{{ tx('settings.admin_users.no_users', 'No users found') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.total_cost') }}
+
+
+
{{ formatMoney(totalCost) }}
+
+
+
+ {{ costChange >= 0 ? '+' : '' }}{{ costChange.toFixed(1) }}%
+
+
+
+
+
{{ store.getTranslation('settings.analytics.requests') }}
+
+
+
{{ formatNumber(totalRequests) }}
+
+
+
+
{{ store.getTranslation('settings.analytics.total_tokens') }}
+
+
+
{{ formatTokens(totalTokens) }}
+
+
+
+
{{ store.getTranslation('settings.analytics.avg_cost') }}
+
+
+
{{ formatMoney(avgCostPerDay) }}
+
{{ store.getTranslation('settings.analytics.per_day') }}
+
+
+
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.top_api_keys') }}
+ {{ store.getTranslation('settings.analytics.coming_soon') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.api_keys_soon') }}
+
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.top_users') }}
+
+
+
+ #
+
+ {{ store.getTranslation('settings.analytics.requests') }}
+ {{ store.getTranslation('settings.analytics.total_tokens_short') }}
+ {{ store.getTranslation('settings.analytics.cost') }}
+
+
+
{{ i + 1 }}
+
+
+ {{ (row.label || '?').slice(0, 1) }}
+
+
{{ row.label || '–' }}
+
+
{{ formatNumber(row.request_count) }}
+
{{ formatTokens(tokenTotal(row)) }}
+
{{ formatMoney(Number(row.cost_total)) }}
+
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.usage_by_model') }}
+
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.token_split') }}
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.request_volume') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
+
+
+
+
+ {{ store.getTranslation('settings.analytics.section_models') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.spend_over_time') }}
+ {{ formatMoney(totalCost) }}
+
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.trending') }}
+ {{ store.getTranslation('settings.analytics.spend') }}
+
+
+
+
+
![]()
+
+
+
+
{{ model.label }}
+
{{ formatNumber(model.requests) }} {{ store.getTranslation('settings.analytics.requests') }} | {{ formatTokens(model.tokens) }} {{ store.getTranslation('settings.analytics.tokens') }}
+
+
+
+
+
{{ formatMoney(model.cost) }}
+
{{ trendLabel(model.pct) }}
+
+
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
+
+ {{ store.getTranslation('settings.analytics.section_api_keys') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.spend_over_time') }}
+ {{ store.getTranslation('settings.analytics.coming_soon') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.api_keys_soon') }}
+
+
+
+
+
{{ store.getTranslation('settings.analytics.trending') }}
+ {{ store.getTranslation('settings.analytics.trend') }}
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
+
+ {{ store.getTranslation('settings.analytics.section_user') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.spend_over_time') }}
+ {{ formatMoney(userTotalCost) }}
+
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
{{ store.getTranslation('settings.analytics.trending') }}
+ {{ store.getTranslation('settings.analytics.spend') }}
+
+
+
+
+
+ {{ user.initial }}
+
+
+
{{ user.label }}
+
{{ formatNumber(user.requests) }} {{ store.getTranslation('settings.analytics.requests') }} | {{ formatTokens(user.tokens) }} {{ store.getTranslation('settings.analytics.tokens') }}
+
+
+
+
+
{{ formatMoney(user.cost) }}
+
{{ user.share.toFixed(0) }}%
+
+
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ tx('settings.analytics.metric', 'Metric') }}
+ {{ selectedExploreMetricLabel }}
+
+
+
+
+ {{ option.label }}
+
+
+
+
+
+
+
+ {{ tx('settings.analytics.group_by', 'Group by') }}
+ {{ selectedExploreGroupLabel }}
+
+
+
+
+ {{ option.label }}
+
+
+
+
+
+
+
+ {{ tx('settings.analytics.top', 'Top') }}
+ {{ exploreTopN }}
+
+
+
+
+ {{ option }}
+
+
+
+
+
+
+
+ {{ tx('settings.analytics.rollup', 'Rollup') }}
+ {{ selectedExploreRollupLabel }}
+
+
+
+
+ {{ option.label }}
+
+
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.no_data') }}
+
+
+
+
+
+
+ {{ tx('settings.analytics.label', 'Label') }}
+ {{ tx('settings.analytics.min', 'Min') }}
+ {{ tx('settings.analytics.max', 'Max') }}
+ {{ tx('settings.analytics.avg', 'Avg') }}
+ {{ tx('settings.analytics.sum', 'Sum') }}
+ {{ tx('settings.analytics.value', 'Value') }}
+ %
+
+
+
+
![]()
+
+
+
{{ row.label }}
+
+
{{ exploreFormatValue(row.min) }}
+
{{ exploreFormatValue(row.max) }}
+
{{ exploreFormatValue(row.avg) }}
+
{{ exploreFormatValue(row.sum) }}
+
{{ exploreFormatValue(row.value) }}
+
+
+
{{ row.pct.toFixed(1) }}%
+
+
+
+
+
+
+
+
+
+
+
{{ store.getTranslation('settings.analytics.top_models') }}
+
+
+ #
+ {{ store.getTranslation('settings.analytics.label') }}
+ {{ store.getTranslation('settings.analytics.requests') }}
+ Input
+ Output
+ Reasoning
+ {{ store.getTranslation('settings.analytics.cost') }}
+
+
+ {{ i + 1 }}
+ {{ row.label || '–' }}
+ {{ formatNumber(row.request_count) }}
+ {{ row.input_tokens.toLocaleString() }}
+ {{ row.output_tokens.toLocaleString() }}
+ {{ row.reasoning_tokens.toLocaleString() }}
+ {{ formatMoney(Number(row.cost_total)) }}
+
+
+
+
+
+
+
diff --git a/frontend/components/settings/AnalyticsDateFilter.vue b/frontend/components/settings/AnalyticsDateFilter.vue
new file mode 100644
index 0000000..6588732
--- /dev/null
+++ b/frontend/components/settings/AnalyticsDateFilter.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/settings/HorizontalAnalyticsChart.vue b/frontend/components/settings/HorizontalAnalyticsChart.vue
new file mode 100644
index 0000000..dfeb73e
--- /dev/null
+++ b/frontend/components/settings/HorizontalAnalyticsChart.vue
@@ -0,0 +1,159 @@
+
+
+
+
+
+
{{ hoveredRow.label }}
+
+
+
+ {{ valueLabel }}
+
+
{{ formatValue(hoveredRow.value) }}
+
+
+
+
+
+
diff --git a/frontend/components/settings/StackedAnalyticsChart.vue b/frontend/components/settings/StackedAnalyticsChart.vue
new file mode 100644
index 0000000..fadc14e
--- /dev/null
+++ b/frontend/components/settings/StackedAnalyticsChart.vue
@@ -0,0 +1,292 @@
+
+
+
+
+
+
{{ hoveredBar.day }}
+
+
+
+
+ {{ segment.name }}
+
+
{{ formatValue(segment.value) }}
+
+
+
+ Total
+ {{ formatValue(hoveredBar.total) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/pages/settings.vue b/frontend/pages/settings.vue
index 08460e5..171353d 100644
--- a/frontend/pages/settings.vue
+++ b/frontend/pages/settings.vue
@@ -45,7 +45,7 @@
diff --git a/frontend/pages/settings/budgets.vue b/frontend/pages/settings/budgets.vue
new file mode 100644
index 0000000..ee13db5
--- /dev/null
+++ b/frontend/pages/settings/budgets.vue
@@ -0,0 +1,653 @@
+
+
+
+
+
{{ store.getTranslation('settings.tabs.budgets') }}
+
{{ tx('settings.budgets.description', 'Limit spend for users and teams') }}
+
+
+
+
+ {{ tx('common.refresh', 'Refresh') }}
+
+
+
+ {{ tx('settings.budgets.create', 'Create budget') }}
+
+
+
+
+
+
+ {{ tx('settings.budgets.tab_users', 'Users') }}
+ {{ tx('settings.budgets.tab_teams', 'Teams') }}
+ {{ tx('settings.budgets.tab_budgets', 'Budgets') }}
+ {{ tx('settings.budgets.tab_history', 'History') }}
+
+
+
+
+
+
+ {{ filteredUserOverview.length }}
+ {{ tx('settings.budgets.users_with_budgets', 'users') }}
+
+
+
+
+
+
+
+
+ {{ tx('settings.budgets.user', 'User') }}
+ {{ tx('settings.budgets.teams', 'Teams') }}
+ {{ tx('settings.budgets.effective_budgets', 'Effective budgets') }}
+ {{ tx('settings.budgets.used', 'Used') }}
+ {{ tx('settings.budgets.remaining', 'Remaining') }}
+ {{ tx('settings.budgets.actions', 'Actions') }}
+
+
+ {{ tx('settings.budgets.no_user_budgets', 'No user budgets found') }}
+
+
+
+
+
{{ row.user_label }}
+
{{ row.budgets.length }} {{ tx('settings.budgets.budget_count', 'budgets') }}
+
+
+ {{ team.name }}
+ +{{ row.teams.length - 2 }}
+
+
+
+ {{ budget.budget.name }}
+ {{ budget.budget.kind }}
+
+
+{{ row.budgets.length - 2 }}
+
+
{{ formatMoney(row.spent) }}
+
{{ formatMoney(row.remaining) }}
+
+
+
+ {{ tx('settings.budgets.reset', 'Reset') }}
+
+
+
+
+
+
+
+
+
+
+
{{ budget.budget.name }}
+
{{ budget.budget.kind }} · {{ budget.budget.interval }} · {{ sourceLabel(budget) }}
+
+
{{ formatMoney(budget.spent) }}
+
{{ formatMoney(budget.amount) }}
+
{{ formatMoney(budget.remaining) }}
+
{{ budget.resets_at ? formatDate(budget.resets_at) : tx('settings.budgets.no_reset', 'No reset') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ team.team_name }}
+
{{ team.member_count }} {{ tx('settings.budgets.members', 'members') }} · {{ team.budgets.length }} {{ tx('settings.budgets.budget_count', 'budgets') }}
+
+
+ {{ formatMoney(team.spent) }} {{ tx('settings.budgets.used', 'Used') }}
+ ·
+ {{ formatMoney(team.remaining) }} {{ tx('settings.budgets.remaining', 'Remaining') }}
+
+
+ {{ tx('settings.budgets.reset', 'Reset') }}
+
+
+
+
+
+
+
{{ budget.budget.name }}
+
{{ budget.budget.kind }} · {{ budget.budget.interval }} · {{ budget.affected_users }} {{ tx('settings.budgets.affected_users', 'affected') }}
+
+
{{ formatMoney(budget.spent) }}
+
{{ formatMoney(budget.remaining) }}
+
{{ budget.resets_at ? formatDate(budget.resets_at) : tx('settings.budgets.no_reset', 'No reset') }}
+
+
+
+
+
+
+
+ {{ tx('settings.budgets.no_team_budgets', 'No team budgets found') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ tx('settings.budgets.create', 'Create budget') }}
+
+
+
+
+
+
+
+
+
+
{{ tx('settings.budgets.settings', 'Settings') }}
+
+
+
+
+ {{ tx('common.save', 'Save') }}
+
+
+
+
+ {{ tx('settings.budgets.name', 'Name') }}
+
+
+
+ {{ tx('settings.budgets.amount', 'Amount') }}
+
+
+
+ {{ tx('settings.budgets.notes', 'Description') }}
+
+
+
+ {{ tx('settings.budgets.kind', 'Kind') }}
+
+
+
+ pooled
+ per_user
+
+
+
+
+ {{ tx('settings.budgets.interval', 'Interval') }}
+
+
+
+ daily
+ weekly
+ monthly
+ total
+
+
+
+
+ {{ tx('settings.budgets.reset_strategy', 'Reset') }}
+
+
+
+ calendar
+ rolling
+ anchored
+
+
+
+
+ {{ tx('settings.budgets.on_exceed', 'Action') }}
+
+
+
+ block
+ warn
+ allow
+
+
+
+
+
+ form.is_enabled = Boolean(checked)"
+ />
+ {{ tx('settings.budgets.enabled', 'Enabled') }}
+
+
+
+
+
{{ tx('settings.budgets.assignments', 'Assignments') }}
+
+
+
+
+ {{ a.team_name ?? a.user_label ?? '-' }}
+ {{ a.team_id ? tx('settings.budgets.team', 'team') : tx('settings.budgets.user', 'user') }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ tx('settings.budgets.no_assignments', 'No assignments yet') }}
+
+
+
+
{{ tx('settings.budgets.assign_team', 'Assign team') }}
+
+
+
+
+ {{ selectedTeamId ? (teams.find(t => t.id === selectedTeamId)?.name ?? selectedTeamId) : tx('settings.budgets.pick_team', 'Select a team') }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ tx('settings.budgets.assign_team', 'Assign team') }}
+
+
+
+
{{ tx('settings.budgets.assign_user', 'Assign user') }}
+
+
+
+
+ {{ selectedUserId ? (users.find(u => u.id === selectedUserId)?.label ?? selectedUserId) : tx('settings.budgets.pick_user', 'Select a user') }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ tx('settings.budgets.assign_user', 'Assign user') }}
+
+
+
+
+
+
+
+
+
+
+ {{ tx('settings.budgets.reset_at', 'Reset at') }}
+ {{ tx('settings.budgets.scope', 'Scope') }}
+ {{ tx('settings.budgets.budget', 'Budget') }}
+ {{ tx('settings.budgets.reason', 'Reason') }}
+ {{ tx('settings.budgets.kind', 'Kind') }}
+ {{ tx('settings.budgets.reset_by', 'Reset by') }}
+
+
+ {{ tx('settings.budgets.no_reset_history', 'No reset history yet') }}
+
+
+ {{ formatDate(event.reset_at) }}
+ {{ resetScopeLabel(event) }}
+ {{ event.budget_name ?? '-' }}
+ {{ event.reason ?? '-' }}
+ {{ event.kind ?? '-' }}
+ {{ event.created_by_label ?? '-' }}
+
+
+
+
+
+
+
+
+ {{ tx('settings.budgets.reset_budget', 'Reset budget') }}
+ {{ resetLabel }}
+
+
+
+ {{ tx('settings.budgets.kind_scope', 'Kind') }}
+
+
+
+ {{ tx('settings.budgets.all_kinds', 'All kinds') }}
+ pooled
+ per_user
+
+
+
+
+ {{ tx('settings.budgets.reason', 'Reason') }}
+
+
+
{{ tx('settings.budgets.reset_keeps_history', 'Usage history is kept. This starts budget accounting from now for the selected scope.') }}
+
+
+ {{ tx('common.cancel', 'Cancel') }}
+
+
+ {{ tx('settings.budgets.reset', 'Reset') }}
+
+
+
+
+
+
+
+
diff --git a/frontend/pages/settings/models/[id].vue b/frontend/pages/settings/models/[id].vue
index 8ed0960..d104128 100644
--- a/frontend/pages/settings/models/[id].vue
+++ b/frontend/pages/settings/models/[id].vue
@@ -837,15 +837,61 @@
+
+
+
+
{{ store.getTranslation('settings.models.editor.pricing') }}
+
{{ store.getTranslation('settings.models.editor.pricing_desc') }}
+
+
+
+
+
+
+
+
{{ store.getTranslation('settings.models.editor.reported_price') }}
+
{{ formatPricingPair(pricing?.reported_input, pricing?.reported_output) }}
+
+
+
{{ store.getTranslation('settings.models.editor.effective_price') }}
+
{{ formatPricingPair(pricing?.effective_input, pricing?.effective_output) }}
+
+
+ {{ store.getTranslation('settings.models.editor.override_input') }}
+
+
+
+ {{ store.getTranslation('settings.models.editor.override_output') }}
+
+
+
+ {{ store.getTranslation('settings.models.editor.override_reasoning') }}
+
+
+
+ {{ store.getTranslation('settings.models.editor.override_cache_read') }}
+
+
+
+ {{ store.getTranslation('settings.models.editor.override_cache_write') }}
+
+
+
+
{{ store.getTranslation('settings.models.editor.pricing_unit') }}
+
+ {{ store.getTranslation('common.save') }}
+
+
+
+
{{ store.getTranslation('settings.models.editor.providers_user_unavailable') }}
-
+
{{ store.getTranslation('settings.models.editor.providers_empty') }}
-
+
@@ -940,6 +986,14 @@ const providerOptions = ref | null>(null);
const providerOptionsLoading = ref(false);
const providerOptionsLoaded = ref(false);
const providerParentUnavailable = computed(() => providerOptions.value?.availability_state === 'USER_UNAVAILABLE');
+const pricing = ref | null>(null);
+const pricingForm = reactive({
+ input: '0',
+ output: '0',
+ reasoning: '',
+ cache_read: '',
+ cache_write: '',
+});
type ProviderRowState = 'user_unavailable' | 'unavailable' | 'available' | 'unknown';
@@ -975,6 +1029,61 @@ function formatPrice(opt: Record): string {
return `${fmt(opt.price_input)} / ${fmt(opt.price_output)}`;
}
+function formatPricingPair(input: string | number | null | undefined, output: string | number | null | undefined): string {
+ const fmt = (value: string | number | null | undefined) => (value == null ? '-' : '$' + Number(value).toFixed(6));
+ return `${fmt(input)} / ${fmt(output)}`;
+}
+
+async function loadPricing() {
+ try {
+ pricing.value = await $customFetch(`/api/v1/admin/models/${modelId}/pricing`);
+ const override = pricing.value?.override_pricing ?? null;
+ pricingForm.input = String(override?.input ?? pricing.value?.effective_input ?? 0);
+ pricingForm.output = String(override?.output ?? pricing.value?.effective_output ?? 0);
+ pricingForm.reasoning = override?.reasoning != null ? String(override.reasoning) : '';
+ pricingForm.cache_read = override?.cache_read != null ? String(override.cache_read) : '';
+ pricingForm.cache_write = override?.cache_write != null ? String(override.cache_write) : '';
+ } catch (e) {
+ console.error(e);
+ }
+}
+
+async function savePricingOverride() {
+ const optional = (value: string): number | undefined => {
+ const trimmed = value.trim();
+ return trimmed === '' ? undefined : Number(trimmed);
+ };
+ const isValidPrice = (v: number | undefined): boolean => v === undefined || (Number.isFinite(v) && v >= 0);
+ const input = Number(pricingForm.input || 0);
+ const output = Number(pricingForm.output || 0);
+ const reasoning = optional(pricingForm.reasoning);
+ const cacheRead = optional(pricingForm.cache_read);
+ const cacheWrite = optional(pricingForm.cache_write);
+ if (!isValidPrice(input) || !isValidPrice(output) || !isValidPrice(reasoning) || !isValidPrice(cacheRead) || !isValidPrice(cacheWrite)) {
+ store.toast(store.getTranslation('common.error'), {type: 'error'});
+ return;
+ }
+ const overridePricing: Record = {
+ input,
+ output,
+ reasoning,
+ cache_read: cacheRead,
+ cache_write: cacheWrite,
+ tiers: [],
+ };
+ pricing.value = await $customFetch(`/api/v1/admin/models/${modelId}/pricing`, {
+ method: 'PUT',
+ body: {pricing: overridePricing},
+ });
+ store.toast(store.getTranslation('settings.models.editor.pricing_saved'), {type: 'success'});
+}
+
+async function deletePricingOverride() {
+ await $customFetch(`/api/v1/admin/models/${modelId}/pricing`, {method: 'DELETE'});
+ await loadPricing();
+ store.toast(store.getTranslation('settings.models.editor.pricing_deleted'), {type: 'success'});
+}
+
async function loadProviderOptions(force = false) {
providerOptionsLoading.value = true;
try {
@@ -1174,6 +1283,7 @@ function insertVariable(token: string) {
onMounted(async () => {
await loadModel();
+ await loadPricing();
});
function populateForm(data: Record) {
diff --git a/frontend/pages/settings/profile.vue b/frontend/pages/settings/profile.vue
index 1500499..12b4ac9 100644
--- a/frontend/pages/settings/profile.vue
+++ b/frontend/pages/settings/profile.vue
@@ -1,52 +1,60 @@
-
-
-
-
-
{{ store.getTranslation('settings.profile.title') }}
-
{{ store.getTranslation('settings.profile.description') }}
-
-
-
-
-
- {{ userInitials }}
-
-
+
+
+
+ {{ store.getTranslation('settings.tabs.profile') }}
+ {{ store.getTranslation('settings.analytics.my_usage') }}
+
-
-
-
-
{{ store.getTranslation('settings.profile.name') }}
-
{{ store.auth.user?.username }}
-
-
-
-
-
-
-
-
{{ store.getTranslation('settings.profile.email') }}
-
{{ store.auth.user?.email }}
-
-
-
-
-
-
-
-
{{ store.getTranslation('settings.profile.user_id') }}
-
{{ store.auth.user?.id }}
+
+
+
+
{{ store.getTranslation('settings.profile.title') }}
+
{{ store.getTranslation('settings.profile.description') }}
+
+
+
+
+ {{ userInitials }}
+
+
+
+
+
+
{{ store.getTranslation('settings.profile.name') }}
+
{{ store.auth.user?.username }}
+
+
+
+
+
+
+
{{ store.getTranslation('settings.profile.email') }}
+
{{ store.auth.user?.email }}
+
+
+
+
+
+
+
{{ store.getTranslation('settings.profile.user_id') }}
+
{{ store.auth.user?.id }}
+
+
+
-
-
-
+
+
+
+
+
+
@@ -55,6 +63,7 @@ import {Copy} from 'lucide-vue-next';
import {useMainStore} from '@/stores';
const store = useMainStore();
+const activeProfileTab = ref('profile');
const userInitials = computed(() => {
const username = store.auth.user?.username || 'U';
diff --git a/frontend/pages/settings/teams.vue b/frontend/pages/settings/teams.vue
index d35eb2e..b18d539 100644
--- a/frontend/pages/settings/teams.vue
+++ b/frontend/pages/settings/teams.vue
@@ -86,7 +86,6 @@
{{ store.getTranslation('settings.teams.members') }}
{{ store.getTranslation('settings.teams.models') }}
- {{ store.getTranslation('settings.teams.budget') }}
@@ -223,35 +222,6 @@
-
-
-
-
-
-
-
-
-
{{ store.getTranslation('settings.teams.budget_link_title') }}
-
{{ store.getTranslation('settings.teams.budget_link_hint') }}
-
-
-
-
-
-
{{ store.getTranslation('settings.teams.budget_linked') }}
-
{{ budgetId }}
-
-
- {{ store.getTranslation('settings.teams.budget_unlink') }}
-
-
-
-
-
{{ store.getTranslation('settings.teams.budget_not_linked') }}
-
{{ store.getTranslation('settings.teams.budget_none_available') }}
-
-
-
@@ -314,7 +284,7 @@