feat(webui): add Tailwind build system and refactor frontend architecture
- Replace Tailwind CDN with local build (PostCSS + autoprefixer + daisyui) - Add CSS build scripts with automatic prepare hook on npm install - Create account-actions.js service layer with unified response format - Extend ErrorHandler.withLoading() for automatic loading state management - Add skeleton screens for initial load, silent refresh for subsequent updates - Implement loading animations for async operations (buttons, modals) - Improve empty states and add ARIA labels for accessibility - Abstract component styles using @apply (buttons, badges, inputs) - Add JSDoc documentation for Dashboard modules - Update README and CLAUDE.md with development guidelines
This commit is contained in:
@@ -19,15 +19,20 @@
|
||||
<input type="text"
|
||||
x-model="searchQuery"
|
||||
:placeholder="$store.global.t('searchAccounts')"
|
||||
class="input input-sm input-bordered bg-space-800 border-space-border text-white w-48 pl-9 text-xs h-8"
|
||||
:aria-label="$store.global.t('searchAccounts')"
|
||||
class="input-search-sm w-48 pl-9 h-8"
|
||||
@keydown.escape="searchQuery = ''">
|
||||
<svg class="w-4 h-4 absolute left-3 top-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<button class="btn btn-xs btn-outline border-space-border text-gray-400 hover:text-white transition-all gap-2 h-8"
|
||||
@click="reloadAccounts()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@click="reloadAccounts()"
|
||||
:disabled="reloading">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-3.5 h-3.5 transition-transform"
|
||||
:class="{ 'animate-spin': reloading }"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('reload')">Reload</span>
|
||||
@@ -109,17 +114,15 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-4">
|
||||
<span class="px-2 py-1 text-[10px] font-mono font-bold uppercase rounded"
|
||||
:class="acc.source === 'oauth' ? 'bg-neon-purple/10 text-neon-purple border border-neon-purple/30' : 'bg-gray-500/10 text-gray-400 border border-gray-500/30'"
|
||||
<span :class="acc.source === 'oauth' ? 'status-pill-purple' : 'status-pill-free'"
|
||||
x-text="acc.source || 'oauth'">
|
||||
</span>
|
||||
</td>
|
||||
<td class="py-4">
|
||||
<span class="px-2 py-1 text-[10px] font-mono font-bold uppercase rounded"
|
||||
:class="{
|
||||
'bg-yellow-500/10 text-yellow-400 border border-yellow-500/30': acc.subscription?.tier === 'ultra',
|
||||
'bg-blue-500/10 text-blue-400 border border-blue-500/30': acc.subscription?.tier === 'pro',
|
||||
'bg-gray-500/10 text-gray-400 border border-gray-500/30': !acc.subscription || acc.subscription.tier === 'free' || acc.subscription.tier === 'unknown'
|
||||
<span :class="{
|
||||
'status-pill-ultra': acc.subscription?.tier === 'ultra',
|
||||
'status-pill-pro': acc.subscription?.tier === 'pro',
|
||||
'status-pill-free': !acc.subscription || acc.subscription.tier === 'free' || acc.subscription.tier === 'unknown'
|
||||
}"
|
||||
x-text="(acc.subscription?.tier || 'free').toUpperCase()">
|
||||
</span>
|
||||
@@ -169,8 +172,12 @@
|
||||
FIX
|
||||
</button>
|
||||
<button class="p-2 rounded hover:bg-white/10 text-gray-500 hover:text-white transition-colors"
|
||||
@click="refreshAccount(acc.email)" :title="$store.global.t('refreshData')">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@click="refreshAccount(acc.email)"
|
||||
:disabled="refreshing"
|
||||
:title="$store.global.t('refreshData')">
|
||||
<svg class="w-4 h-4 transition-transform"
|
||||
:class="{ 'animate-spin': refreshing }"
|
||||
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
@@ -200,7 +207,7 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<p class="text-sm text-gray-600" x-text="$store.global.t('noSearchResults')">No accounts match your search</p>
|
||||
<button class="btn btn-xs btn-ghost text-gray-500" @click="searchQuery = ''" x-text="$store.global.t('clearSearch')">Clear Search</button>
|
||||
<button class="btn-action-ghost !text-gray-500" @click="searchQuery = ''" x-text="$store.global.t('clearSearch')">Clear Search</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -245,8 +252,10 @@
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn bg-red-500 hover:bg-red-600 border-none text-white"
|
||||
@click="executeDelete()">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@click="executeDelete()"
|
||||
:disabled="deleting"
|
||||
:class="{ 'loading': deleting }">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" x-show="!deleting">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
<span x-text="$store.global.t('confirmDelete')">Confirm Delete</span>
|
||||
|
||||
Reference in New Issue
Block a user