Merge pull request #107 from YasinKose/main

Feat(ui): add Turkish language support and UI enhancements

Introduces Turkish language support and several UI/UX improvements to the web management interface.
This commit is contained in:
Yasin Köse
2026-01-14 11:08:50 +03:00
committed by GitHub
parent 84cdf3571f
commit cc64b93f32
10 changed files with 393 additions and 28 deletions

View File

@@ -129,7 +129,7 @@
x-text="(acc.subscription?.tier || 'free').toUpperCase()">
</span>
</td>
<td class="py-4">
<td class="py-4 cursor-pointer" @click="openQuotaModal(acc)">
<div x-data="{ quota: getMainModelQuota(acc) }">
<template x-if="quota.percent !== null">
<div class="flex items-center gap-2">
@@ -268,4 +268,62 @@
<button>close</button>
</form>
</dialog>
<!-- Quota Distribution Modal -->
<dialog id="quota_modal" class="modal backdrop-blur-sm">
<div class="modal-box max-w-2xl w-full bg-space-900 border border-space-border text-gray-300 shadow-2xl p-6">
<h3 class="font-bold text-xl text-white mb-2 flex items-center gap-2">
<svg class="w-6 h-6 text-neon-cyan" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
<span x-text="$store.global.t('quotaDistribution')">Quota Distribution</span>
</h3>
<p class="text-sm text-gray-500 font-mono mb-6" x-text="selectedAccountEmail"></p>
<div class="grid grid-cols-1 gap-4 overflow-y-auto max-h-[60vh] pr-2 custom-scrollbar">
<template x-for="(limit, modelId) in selectedAccountLimits" :key="modelId">
<div class="p-3 bg-space-800/50 border border-space-border/30 rounded-lg">
<div class="flex justify-between items-center mb-2">
<span class="text-sm font-semibold text-gray-200" x-text="modelId"></span>
<div class="flex items-center gap-2">
<span class="text-xs font-mono"
:class="limit.remainingFraction > 0.5 ? 'text-neon-green' : (limit.remainingFraction > 0.2 ? 'text-yellow-500' : 'text-red-500')"
x-text="Math.round(limit.remainingFraction * 100) + '%'"></span>
</div>
</div>
<div class="w-full bg-gray-700 rounded-full h-2.5 mb-2 overflow-hidden">
<div class="h-full rounded-full transition-all duration-500"
:class="limit.remainingFraction > 0.5 ? 'bg-neon-green' : (limit.remainingFraction > 0.2 ? 'bg-yellow-500' : 'bg-red-500')"
:style="`width: ${limit.remainingFraction * 100}%`">
</div>
</div>
<div class="flex justify-between items-center">
<span class="text-[10px] text-gray-500 uppercase font-bold" x-text="$store.data.getModelFamily(modelId)"></span>
<template x-if="limit.resetTime">
<span class="text-[10px] text-yellow-500/80 font-mono italic"
x-text="$store.global.t('resetsIn', { time: window.utils.formatTimeUntil(limit.resetTime) })"></span>
</template>
</div>
</div>
</template>
<template x-if="Object.keys(selectedAccountLimits).length === 0">
<div class="text-center py-8 text-gray-500" x-text="$store.global.t('noQuotaData')">
No quota data available for this account yet.
</div>
</template>
</div>
<div class="modal-action">
<form method="dialog">
<button class="btn btn-ghost hover:bg-white/10" x-text="$store.global.t('close')">Close</button>
</form>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
</div>

View File

@@ -48,7 +48,7 @@
<div class="text-[10px] font-mono text-gray-600 hidden lg:block">
<span x-text="filteredLogs.length"></span>/<span x-text="logs.length"></span>
</div>
<label class="cursor-pointer flex items-center gap-2" title="Auto-scroll to bottom">
<label class="cursor-pointer flex items-center gap-2">
<span class="text-[10px] font-mono text-gray-500 uppercase hidden sm:inline-block"
x-text="$store.global.t('autoScroll')">Auto-Scroll</span>
<input type="checkbox" class="toggle toggle-xs toggle-success" x-model="isAutoScroll">

View File

@@ -86,6 +86,18 @@
@click="$store.data.filters.family = 'gemini'; $store.data.computeQuotaRows()"
x-text="$store.global.t('geminiCaps')">GEMINI</button>
</div>
<!-- Show Hidden Toggle -->
<button
class="btn h-9 px-4 ml-2 border border-space-border/50 bg-space-800 hover:bg-space-700 hover:border-space-border hover:text-white transition-all gap-2 min-h-0"
:class="$store.settings.showHiddenModels ? 'text-neon-purple border-neon-purple/50 bg-neon-purple/10' : 'text-gray-400'"
@click="$store.settings.toggle('showHiddenModels'); $store.data.computeQuotaRows()"
:title="$store.settings.showHiddenModels ? $store.global.t('hideHidden') : $store.global.t('showHidden')">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
</svg>
<span class="text-[10px] font-medium uppercase tracking-wide" x-text="$store.settings.showHiddenModels ? $store.global.t('hiddenOn') : $store.global.t('hiddenOff')"></span>
</button>
</div>
</div>
@@ -156,7 +168,7 @@
</thead>
<tbody class="text-sm">
<template x-for="row in $store.data.quotaRows" :key="row.modelId">
<tr class="group">
<tr class="group transition-all duration-300" :class="row.hidden ? 'opacity-40 grayscale hover:opacity-100 hover:grayscale-0 bg-space-900/50' : ''">
<td class="pl-4">
<div class="w-2 h-2 rounded-full transition-all duration-500"
:class="row.avgQuota > 0 ? 'bg-neon-green shadow-[0_0_8px_rgba(34,197,94,0.6)]' : 'bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.6)]'">

View File

@@ -71,20 +71,14 @@
<label class="label">
<span class="label-text text-gray-300" x-text="$store.global.t('language')">Language</span>
</label>
<div class="join w-full grid grid-cols-2">
<button
class="join-item btn btn-sm border-space-border/50 bg-space-800 transition-all font-medium"
:class="$store.global.lang === 'en'
? 'bg-neon-purple/20 text-neon-purple border-neon-purple/60 shadow-lg shadow-neon-purple/10'
: 'text-gray-400 hover:text-white hover:bg-space-700 hover:border-space-border'"
@click="$store.global.setLang('en')">English</button>
<button
class="join-item btn btn-sm border-space-border/50 bg-space-800 transition-all font-medium"
:class="$store.global.lang === 'zh'
? 'bg-neon-purple/20 text-neon-purple border-neon-purple/60 shadow-lg shadow-neon-purple/10'
: 'text-gray-400 hover:text-white hover:bg-space-700 hover:border-space-border'"
@click="$store.global.setLang('zh')">中文</button>
</div>
<select
class="select select-bordered select-sm w-full bg-space-800 border-space-border/50 text-gray-300 focus:border-neon-purple focus:ring-1 focus:ring-neon-purple/50 font-medium transition-all !py-0 leading-tight"
:value="$store.global.lang"
@change="$store.global.setLang($event.target.value)">
<option value="en">English</option>
<option value="zh">中文</option>
<option value="tr">Türkçe</option>
</select>
</div>
<!-- Polling Interval -->
@@ -753,15 +747,19 @@
</svg>
</div>
<div x-show="isEditing(modelId)" class="flex items-center gap-1">
<input type="text" x-model="newMapping"
<select x-model="newMapping"
:x-ref="'input-' + modelId"
class="input input-xs bg-space-800 border-space-border text-white focus:outline-none focus:border-neon-cyan flex-1 font-mono text-xs"
placeholder="e.g. claude-sonnet-4-5 or gemini-3-flash"
class="select select-sm bg-space-800 border-space-border text-white focus:outline-none focus:border-neon-cyan flex-1 font-mono text-xs !h-8 min-h-0"
@keydown.enter="await updateModelConfig(modelId, { mapping: newMapping }); stopEditing()"
@keydown.escape="newMapping = config.mapping || ''; stopEditing()">
<option value="" x-text="$store.global.t('none')">None</option>
<template x-for="mId in $store.data.models" :key="mId">
<option :value="mId" x-text="mId" :selected="mId === newMapping"></option>
</template>
</select>
<button class="btn-action-success"
@click="await updateModelConfig(modelId, { mapping: newMapping }); stopEditing()"
title="Save">
:title="$store.global.t('saveChanges')">
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
@@ -770,7 +768,7 @@
</button>
<button class="btn-action-neutral"
@click="newMapping = config.mapping || ''; stopEditing()"
title="Cancel">
:title="$store.global.t('cancel')">
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
@@ -780,7 +778,7 @@
<button x-show="config.mapping"
class="btn-action-danger"
@click="await updateModelConfig(modelId, { mapping: '' }); stopEditing()"
title="Clear mapping">
:title="$store.global.t('delete')">
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"