mirror of
https://github.com/Cccc-owo/CheckInApp.git
synced 2026-06-17 05:56:29 +00:00
feat(frontend): show user credential expiry
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
} from '@/components/ui'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { extractErrorMessage, formatDateTime } from '@/utils/format'
|
||||
import { formatUserAuthorizationSummary } from '../dashboard-license'
|
||||
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
@@ -33,6 +34,10 @@ function requiresUnverifiedEmailOverride(
|
||||
return 'requires_override' in result && result.warning_code === 'UNVERIFIED_EMAIL'
|
||||
}
|
||||
|
||||
function userAuthorizationSummary(user: User) {
|
||||
return formatUserAuthorizationSummary(user.jwt_exp)
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
@@ -162,6 +167,9 @@ onMounted(load)
|
||||
<div class="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-sm text-muted-foreground">
|
||||
<span>{{ user.email || '未设置邮箱' }}</span>
|
||||
<span>{{ user.email_verified ? '邮箱已验证' : '邮箱未验证' }}</span>
|
||||
<span :class="toneClass(userAuthorizationSummary(user).tone)">{{
|
||||
userAuthorizationSummary(user).label
|
||||
}}</span>
|
||||
<span>{{ formatDateTime(user.created_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { TokenStatus } from '@/api'
|
||||
import {
|
||||
canRefreshAuthorization,
|
||||
formatAuthorizationExpiryTooltip,
|
||||
formatUserAuthorizationSummary,
|
||||
formatRemainingDays,
|
||||
} from './dashboard-license.ts'
|
||||
|
||||
@@ -65,3 +66,22 @@ test('formats remaining days label consistently', () => {
|
||||
assert.equal(formatRemainingDays(0), '0 天')
|
||||
assert.equal(formatRemainingDays(12), '12 天')
|
||||
})
|
||||
|
||||
test('formats user authorization summary from jwt expiration', () => {
|
||||
assert.deepEqual(formatUserAuthorizationSummary('0'), {
|
||||
label: '未绑定凭证',
|
||||
tone: 'neutral',
|
||||
})
|
||||
assert.deepEqual(formatUserAuthorizationSummary('1000', 2000), {
|
||||
label: '凭证过期',
|
||||
tone: 'danger',
|
||||
})
|
||||
assert.deepEqual(formatUserAuthorizationSummary(String(2000 + 2 * 24 * 60 * 60), 2000), {
|
||||
label: '2 天后过期',
|
||||
tone: 'warning',
|
||||
})
|
||||
assert.deepEqual(formatUserAuthorizationSummary(String(2000 + 9 * 24 * 60 * 60), 2000), {
|
||||
label: '9 天后过期',
|
||||
tone: 'success',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import type { TokenStatus } from '@/api'
|
||||
import type { Tone } from '@/components/ui'
|
||||
|
||||
const SECONDS_PER_DAY = 24 * 60 * 60
|
||||
|
||||
export interface UserAuthorizationSummary {
|
||||
label: string
|
||||
tone: Tone
|
||||
}
|
||||
|
||||
export function formatRemainingDays(days?: number | null) {
|
||||
return days == null ? '未知' : `${days} 天`
|
||||
@@ -36,3 +44,23 @@ export function formatAuthorizationExpiryTooltip(
|
||||
|
||||
return `过期时间:${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute}:${parts.second}`
|
||||
}
|
||||
|
||||
export function formatUserAuthorizationSummary(
|
||||
jwtExp?: string | null,
|
||||
nowSeconds = Math.floor(Date.now() / 1000),
|
||||
): UserAuthorizationSummary {
|
||||
const expiresAt = Number(jwtExp)
|
||||
if (!jwtExp || jwtExp === '0' || !Number.isFinite(expiresAt) || expiresAt <= 0) {
|
||||
return { label: '未绑定凭证', tone: 'neutral' }
|
||||
}
|
||||
|
||||
if (expiresAt <= nowSeconds) {
|
||||
return { label: '凭证过期', tone: 'danger' }
|
||||
}
|
||||
|
||||
const remainingDays = Math.ceil((expiresAt - nowSeconds) / SECONDS_PER_DAY)
|
||||
return {
|
||||
label: `${remainingDays} 天后过期`,
|
||||
tone: remainingDays <= 7 ? 'warning' : 'success',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,13 @@ def test_frontend_admin_approval_policy_warnings_are_visible() -> None:
|
||||
assert "未验证邮箱审批警告" not in email_settings
|
||||
|
||||
|
||||
def test_frontend_admin_users_show_authorization_summary() -> None:
|
||||
admin_users = (SRC_ROOT / "views" / "admin" / "AdminUsersView.vue").read_text(encoding="utf-8")
|
||||
|
||||
assert "formatUserAuthorizationSummary" in admin_users
|
||||
assert "jwt_exp" in admin_users
|
||||
|
||||
|
||||
def test_frontend_replaces_starter_component() -> None:
|
||||
app = (SRC_ROOT / "App.vue").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user