mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-10 19:27:41 +08:00
Show UI drift marker for env draft vs Vercel config
This commit is contained in:
@@ -101,6 +101,7 @@ export default function AccountManagerContainer({ config, onRefresh, onMessage,
|
||||
onPageSizeChange={changePageSize}
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={handleSearchChange}
|
||||
envBacked={Boolean(config?.env_backed)}
|
||||
/>
|
||||
|
||||
<AddKeyModal
|
||||
|
||||
@@ -26,6 +26,7 @@ export default function AccountsTable({
|
||||
onPageSizeChange,
|
||||
searchQuery,
|
||||
onSearchChange,
|
||||
envBacked = false,
|
||||
}) {
|
||||
const [copiedId, setCopiedId] = useState(null)
|
||||
|
||||
@@ -101,14 +102,16 @@ export default function AccountsTable({
|
||||
) : accounts.length > 0 ? (
|
||||
accounts.map((acc, i) => {
|
||||
const id = resolveAccountIdentifier(acc)
|
||||
const runtimeUnknown = envBacked && !acc.test_status
|
||||
const isActive = acc.test_status === 'ok' || acc.has_token
|
||||
return (
|
||||
<div key={i} className="p-4 flex flex-col md:flex-row md:items-center justify-between gap-4 hover:bg-muted/50 transition-colors">
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<div className={clsx(
|
||||
"w-2 h-2 rounded-full shrink-0",
|
||||
acc.test_status === 'failed' ? "bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.5)]" :
|
||||
(acc.test_status === 'ok' || acc.has_token) ? "bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)]" :
|
||||
"bg-amber-500"
|
||||
isActive ? "bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)]" :
|
||||
runtimeUnknown ? "bg-blue-500 shadow-[0_0_8px_rgba(59,130,246,0.5)]" : "bg-amber-500"
|
||||
)} />
|
||||
<div className="min-w-0">
|
||||
<div
|
||||
@@ -122,7 +125,7 @@ export default function AccountsTable({
|
||||
}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-0.5">
|
||||
<span>{acc.test_status === 'failed' ? t('accountManager.testStatusFailed') : (acc.test_status === 'ok' || acc.has_token) ? t('accountManager.sessionActive') : t('accountManager.reauthRequired')}</span>
|
||||
<span>{acc.test_status === 'failed' ? t('accountManager.testStatusFailed') : isActive ? t('accountManager.sessionActive') : runtimeUnknown ? t('accountManager.runtimeStatusUnknown') : t('accountManager.reauthRequired')}</span>
|
||||
{acc.token_preview && (
|
||||
<span className="font-mono bg-muted px-1.5 py-0.5 rounded text-[10px]">
|
||||
{acc.token_preview}
|
||||
|
||||
@@ -4,7 +4,7 @@ import VercelSyncForm from './VercelSyncForm'
|
||||
import VercelSyncStatus from './VercelSyncStatus'
|
||||
import VercelGuide from './VercelGuide'
|
||||
|
||||
export default function VercelSyncContainer({ onMessage, authFetch, isVercel = false }) {
|
||||
export default function VercelSyncContainer({ onMessage, authFetch, isVercel = false, config = null }) {
|
||||
const { t } = useI18n()
|
||||
const apiFetch = authFetch || fetch
|
||||
|
||||
@@ -28,6 +28,7 @@ export default function VercelSyncContainer({ onMessage, authFetch, isVercel = f
|
||||
onMessage,
|
||||
t,
|
||||
isVercel,
|
||||
config,
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
@@ -69,6 +69,11 @@ export default function VercelSyncForm({
|
||||
{t('vercel.lastSyncTime', { time: new Date(syncStatus.last_sync_time * 1000).toLocaleString() })}
|
||||
</p>
|
||||
)}
|
||||
{syncStatus?.draft_differs && (
|
||||
<p className="text-xs text-amber-500 mt-2">
|
||||
{t('vercel.draftDiffers')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
|
||||
@@ -8,7 +8,7 @@ function pollDelayMs(attempt) {
|
||||
return 60000
|
||||
}
|
||||
|
||||
export function useVercelSyncState({ apiFetch, onMessage, t, isVercel = false }) {
|
||||
export function useVercelSyncState({ apiFetch, onMessage, t, isVercel = false, config = null }) {
|
||||
const [vercelToken, setVercelToken] = useState('')
|
||||
const [projectId, setProjectId] = useState('')
|
||||
const [teamId, setTeamId] = useState('')
|
||||
@@ -22,7 +22,14 @@ export function useVercelSyncState({ apiFetch, onMessage, t, isVercel = false })
|
||||
|
||||
const fetchSyncStatus = useCallback(async ({ manual = false } = {}) => {
|
||||
try {
|
||||
const res = await apiFetch('/admin/vercel/status')
|
||||
const hasConfig = Boolean(config && (Array.isArray(config?.keys) || Array.isArray(config?.accounts)))
|
||||
const res = await apiFetch('/admin/vercel/status', hasConfig ? {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
config_override: { keys: config?.keys || [], accounts: config?.accounts || [] },
|
||||
}),
|
||||
} : undefined)
|
||||
if (!res.ok) {
|
||||
throw new Error(`status ${res.status}`)
|
||||
}
|
||||
@@ -50,7 +57,7 @@ export function useVercelSyncState({ apiFetch, onMessage, t, isVercel = false })
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to fetch sync status:', e)
|
||||
}
|
||||
}, [apiFetch, isVercel, onMessage, t])
|
||||
}, [apiFetch, config, isVercel, onMessage, t])
|
||||
|
||||
useEffect(() => {
|
||||
const loadPreconfig = async () => {
|
||||
@@ -117,6 +124,7 @@ export function useVercelSyncState({ apiFetch, onMessage, t, isVercel = false })
|
||||
vercel_token: tokenToUse,
|
||||
project_id: projectId,
|
||||
team_id: teamId || undefined,
|
||||
config_override: config ? { keys: config.keys || [], accounts: config.accounts || [] } : undefined,
|
||||
}),
|
||||
})
|
||||
const data = await res.json()
|
||||
@@ -133,7 +141,7 @@ export function useVercelSyncState({ apiFetch, onMessage, t, isVercel = false })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [apiFetch, fetchSyncStatus, onMessage, preconfig?.has_token, projectId, t, teamId, vercelToken])
|
||||
}, [apiFetch, config, fetchSyncStatus, onMessage, preconfig?.has_token, projectId, t, teamId, vercelToken])
|
||||
|
||||
return {
|
||||
vercelToken,
|
||||
|
||||
Reference in New Issue
Block a user