fix(frontend): sync with main and finalize i18n & component optimizations
This commit is contained in:
@@ -216,7 +216,7 @@
|
||||
</template>
|
||||
|
||||
<template #cell-status="{ row }">
|
||||
<AccountStatusIndicator :account="row" />
|
||||
<AccountStatusIndicator :account="row" @show-temp-unsched="handleShowTempUnsched" />
|
||||
</template>
|
||||
|
||||
<template #cell-schedulable="{ row }">
|
||||
@@ -400,6 +400,14 @@
|
||||
<!-- Account Stats Modal -->
|
||||
<AccountStatsModal :show="showStatsModal" :account="statsAccount" @close="closeStatsModal" />
|
||||
|
||||
<!-- Temp Unschedulable Status Modal -->
|
||||
<TempUnschedStatusModal
|
||||
:show="showTempUnschedModal"
|
||||
:account="tempUnschedAccount"
|
||||
@close="closeTempUnschedModal"
|
||||
@reset="handleTempUnschedReset"
|
||||
/>
|
||||
|
||||
<!-- Delete Confirmation Dialog -->
|
||||
<ConfirmDialog
|
||||
:show="showDeleteDialog"
|
||||
@@ -512,6 +520,7 @@ import {
|
||||
BulkEditAccountModal,
|
||||
ReAuthAccountModal,
|
||||
AccountStatsModal,
|
||||
TempUnschedStatusModal,
|
||||
SyncFromCrsModal
|
||||
} from '@/components/account'
|
||||
import AccountStatusIndicator from '@/components/account/AccountStatusIndicator.vue'
|
||||
@@ -572,9 +581,9 @@ const typeOptions = computed(() => [
|
||||
|
||||
const statusOptions = computed(() => [
|
||||
{ value: '', label: t('admin.accounts.allStatus') },
|
||||
{ value: 'active', label: t('common.active') },
|
||||
{ value: 'inactive', label: t('common.inactive') },
|
||||
{ value: 'error', label: t('common.error') }
|
||||
{ value: 'active', label: t('admin.accounts.status.active') },
|
||||
{ value: 'inactive', label: t('admin.accounts.status.inactive') },
|
||||
{ value: 'error', label: t('admin.accounts.status.error') }
|
||||
])
|
||||
|
||||
// State
|
||||
@@ -604,6 +613,7 @@ const showDeleteDialog = ref(false)
|
||||
const showBulkDeleteDialog = ref(false)
|
||||
const showTestModal = ref(false)
|
||||
const showStatsModal = ref(false)
|
||||
const showTempUnschedModal = ref(false)
|
||||
const showCrsSyncModal = ref(false)
|
||||
const showBulkEditModal = ref(false)
|
||||
const editingAccount = ref<Account | null>(null)
|
||||
@@ -611,6 +621,7 @@ const reAuthAccount = ref<Account | null>(null)
|
||||
const deletingAccount = ref<Account | null>(null)
|
||||
const testingAccount = ref<Account | null>(null)
|
||||
const statsAccount = ref<Account | null>(null)
|
||||
const tempUnschedAccount = ref<Account | null>(null)
|
||||
const togglingSchedulable = ref<number | null>(null)
|
||||
const bulkDeleting = ref(false)
|
||||
|
||||
@@ -775,6 +786,21 @@ const closeReAuthModal = () => {
|
||||
reAuthAccount.value = null
|
||||
}
|
||||
|
||||
// Temp unschedulable modal
|
||||
const handleShowTempUnsched = (account: Account) => {
|
||||
tempUnschedAccount.value = account
|
||||
showTempUnschedModal.value = true
|
||||
}
|
||||
|
||||
const closeTempUnschedModal = () => {
|
||||
showTempUnschedModal.value = false
|
||||
tempUnschedAccount.value = null
|
||||
}
|
||||
|
||||
const handleTempUnschedReset = () => {
|
||||
loadAccounts()
|
||||
}
|
||||
|
||||
// Token refresh
|
||||
const handleRefreshToken = async (account: Account) => {
|
||||
try {
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
|
||||
<template #cell-status="{ value }">
|
||||
<span :class="['badge', value === 'active' ? 'badge-success' : 'badge-danger']">
|
||||
{{ t('admin.groups.statuses.' + value) }}
|
||||
{{ t('admin.accounts.status.' + value) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -683,8 +683,8 @@ const columns = computed<Column[]>(() => [
|
||||
// Filter options
|
||||
const statusOptions = computed(() => [
|
||||
{ value: '', label: t('admin.groups.allStatus') },
|
||||
{ value: 'active', label: t('common.active') },
|
||||
{ value: 'inactive', label: t('common.inactive') }
|
||||
{ value: 'active', label: t('admin.accounts.status.active') },
|
||||
{ value: 'inactive', label: t('admin.accounts.status.inactive') }
|
||||
])
|
||||
|
||||
const exclusiveOptions = computed(() => [
|
||||
@@ -709,8 +709,8 @@ const platformFilterOptions = computed(() => [
|
||||
])
|
||||
|
||||
const editStatusOptions = computed(() => [
|
||||
{ value: 'active', label: t('common.active') },
|
||||
{ value: 'inactive', label: t('common.inactive') }
|
||||
{ value: 'active', label: t('admin.accounts.status.active') },
|
||||
{ value: 'inactive', label: t('admin.accounts.status.inactive') }
|
||||
])
|
||||
|
||||
const subscriptionTypeOptions = computed(() => [
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
|
||||
<template #cell-status="{ value }">
|
||||
<span :class="['badge', value === 'active' ? 'badge-success' : 'badge-danger']">
|
||||
{{ t('admin.proxies.statuses.' + value) }}
|
||||
{{ t('admin.accounts.status.' + value) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -634,8 +634,8 @@ const protocolOptions = computed(() => [
|
||||
|
||||
const statusOptions = computed(() => [
|
||||
{ value: '', label: t('admin.proxies.allStatus') },
|
||||
{ value: 'active', label: t('common.active') },
|
||||
{ value: 'inactive', label: t('common.inactive') }
|
||||
{ value: 'active', label: t('admin.accounts.status.active') },
|
||||
{ value: 'inactive', label: t('admin.accounts.status.inactive') }
|
||||
])
|
||||
|
||||
// Form options
|
||||
@@ -647,8 +647,8 @@ const protocolSelectOptions = computed(() => [
|
||||
])
|
||||
|
||||
const editStatusOptions = computed(() => [
|
||||
{ value: 'active', label: t('admin.proxies.statuses.active') },
|
||||
{ value: 'inactive', label: t('admin.proxies.statuses.inactive') }
|
||||
{ value: 'active', label: t('admin.accounts.status.active') },
|
||||
{ value: 'inactive', label: t('admin.accounts.status.inactive') }
|
||||
])
|
||||
|
||||
const proxies = ref<Proxy[]>([])
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
: 'badge-danger'
|
||||
]"
|
||||
>
|
||||
{{ t('admin.redeem.statuses.' + value) }}
|
||||
{{ t('admin.redeem.status.' + value) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@
|
||||
href="https://dash.cloudflare.com/"
|
||||
target="_blank"
|
||||
class="text-primary-600 hover:text-primary-500"
|
||||
>Cloudflare Dashboard</a
|
||||
>{{ t('admin.settings.turnstile.cloudflareDashboard') }}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -295,12 +295,12 @@ function onTurnstileVerify(token: string): void {
|
||||
|
||||
function onTurnstileExpire(): void {
|
||||
turnstileToken.value = ''
|
||||
errors.turnstile = 'Verification expired, please try again'
|
||||
errors.turnstile = t('auth.turnstileExpired')
|
||||
}
|
||||
|
||||
function onTurnstileError(): void {
|
||||
turnstileToken.value = ''
|
||||
errors.turnstile = 'Verification failed, please try again'
|
||||
errors.turnstile = t('auth.turnstileFailed')
|
||||
}
|
||||
|
||||
// ==================== Validation ====================
|
||||
@@ -315,25 +315,25 @@ function validateForm(): boolean {
|
||||
|
||||
// Email validation
|
||||
if (!formData.email.trim()) {
|
||||
errors.email = 'Email is required'
|
||||
errors.email = t('auth.emailRequired')
|
||||
isValid = false
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
||||
errors.email = 'Please enter a valid email address'
|
||||
errors.email = t('auth.invalidEmail')
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Password validation
|
||||
if (!formData.password) {
|
||||
errors.password = 'Password is required'
|
||||
errors.password = t('auth.passwordRequired')
|
||||
isValid = false
|
||||
} else if (formData.password.length < 6) {
|
||||
errors.password = 'Password must be at least 6 characters'
|
||||
errors.password = t('auth.passwordMinLength')
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Turnstile validation
|
||||
if (turnstileEnabled.value && !turnstileToken.value) {
|
||||
errors.turnstile = 'Please complete the verification'
|
||||
errors.turnstile = t('auth.completeVerification')
|
||||
isValid = false
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ async function handleLogin(): Promise<void> {
|
||||
})
|
||||
|
||||
// Show success toast
|
||||
appStore.showSuccess('Login successful! Welcome back.')
|
||||
appStore.showSuccess(t('auth.loginSuccess'))
|
||||
|
||||
// Redirect to dashboard or intended route
|
||||
const redirectTo = (router.currentRoute.value.query.redirect as string) || '/dashboard'
|
||||
@@ -382,7 +382,7 @@ async function handleLogin(): Promise<void> {
|
||||
} else if (err.message) {
|
||||
errorMessage.value = err.message
|
||||
} else {
|
||||
errorMessage.value = 'Login failed. Please check your credentials and try again.'
|
||||
errorMessage.value = t('auth.loginFailed')
|
||||
}
|
||||
|
||||
// Also show error toast
|
||||
|
||||
@@ -340,12 +340,12 @@ function onTurnstileVerify(token: string): void {
|
||||
|
||||
function onTurnstileExpire(): void {
|
||||
turnstileToken.value = ''
|
||||
errors.turnstile = 'Verification expired, please try again'
|
||||
errors.turnstile = t('auth.turnstileExpired')
|
||||
}
|
||||
|
||||
function onTurnstileError(): void {
|
||||
turnstileToken.value = ''
|
||||
errors.turnstile = 'Verification failed, please try again'
|
||||
errors.turnstile = t('auth.turnstileFailed')
|
||||
}
|
||||
|
||||
// ==================== Validation ====================
|
||||
@@ -365,25 +365,25 @@ function validateForm(): boolean {
|
||||
|
||||
// Email validation
|
||||
if (!formData.email.trim()) {
|
||||
errors.email = 'Email is required'
|
||||
errors.email = t('auth.emailRequired')
|
||||
isValid = false
|
||||
} else if (!validateEmail(formData.email)) {
|
||||
errors.email = 'Please enter a valid email address'
|
||||
errors.email = t('auth.invalidEmail')
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Password validation
|
||||
if (!formData.password) {
|
||||
errors.password = 'Password is required'
|
||||
errors.password = t('auth.passwordRequired')
|
||||
isValid = false
|
||||
} else if (formData.password.length < 6) {
|
||||
errors.password = 'Password must be at least 6 characters'
|
||||
errors.password = t('auth.passwordMinLength')
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Turnstile validation
|
||||
if (turnstileEnabled.value && !turnstileToken.value) {
|
||||
errors.turnstile = 'Please complete the verification'
|
||||
errors.turnstile = t('auth.completeVerification')
|
||||
isValid = false
|
||||
}
|
||||
|
||||
@@ -429,7 +429,7 @@ async function handleRegister(): Promise<void> {
|
||||
})
|
||||
|
||||
// Show success toast
|
||||
appStore.showSuccess('Account created successfully! Welcome to ' + siteName.value + '.')
|
||||
appStore.showSuccess(t('auth.accountCreatedSuccess', { siteName: siteName.value }))
|
||||
|
||||
// Redirect to dashboard
|
||||
await router.push('/dashboard')
|
||||
@@ -448,7 +448,7 @@ async function handleRegister(): Promise<void> {
|
||||
} else if (err.message) {
|
||||
errorMessage.value = err.message
|
||||
} else {
|
||||
errorMessage.value = 'Registration failed. Please try again.'
|
||||
errorMessage.value = t('auth.registrationFailed')
|
||||
}
|
||||
|
||||
// Also show error toast
|
||||
|
||||
@@ -500,7 +500,7 @@ const getHistoryItemTitle = (item: RedeemHistoryItem) => {
|
||||
} else if (item.type === 'subscription') {
|
||||
return t('redeem.subscriptionAssigned')
|
||||
}
|
||||
return 'Unknown'
|
||||
return t('common.unknown')
|
||||
}
|
||||
|
||||
const formatHistoryValue = (item: RedeemHistoryItem) => {
|
||||
|
||||
@@ -279,7 +279,7 @@ async function loadSubscriptions() {
|
||||
subscriptions.value = await subscriptionsAPI.getMySubscriptions()
|
||||
} catch (error) {
|
||||
console.error('Failed to load subscriptions:', error)
|
||||
appStore.showError('Failed to load subscriptions')
|
||||
appStore.showError(t('userSubscriptions.failedToLoad'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user