feat: TTLock diagnostics — test cloud API button + fix api_server SELECT bug
- Fix: api_server was missing from SELECT in issue-card route (always fell back to EU default) - Add "Проверить API" button in Step 1 that calls ttlock:test_api via agent - Agent: testCloudApi() — gets OAuth token + lists locks, returns count - Agent: apiPost() now includes response body in error message (was just HTTP status) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import * as XLSX from 'xlsx'
|
||||
import {
|
||||
KeyRound, Save, RefreshCw, Check, X, AlertCircle,
|
||||
ChevronDown, ChevronRight, Trash2, Plus, Eye, EyeOff,
|
||||
Lock, Unlock, Info, HardDriveDownload, Zap, Terminal, Upload,
|
||||
Lock, Unlock, Info, HardDriveDownload, Zap, Terminal, Upload, Wifi,
|
||||
} from 'lucide-react'
|
||||
import { api, type TTLockConfig, type RoomLockMapping, type Workstation } from '../lib/api'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
@@ -134,6 +134,7 @@ export function TTLockPage() {
|
||||
const [workstations, setWorkstations] = useState<Workstation[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [testingApi, setTestingApi] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [success, setSuccess] = useState<string | null>(null)
|
||||
|
||||
@@ -277,6 +278,21 @@ export function TTLockPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleTestApi = async () => {
|
||||
const ws = workstations[0]
|
||||
if (!ws) { setError('Нет рабочих мест — добавьте на странице «Оборудование»'); return }
|
||||
setTestingApi(true)
|
||||
setError(null)
|
||||
try {
|
||||
const result = await api.ttlock.testApi(slug, ws.id)
|
||||
showSuccess(`TTLock Cloud API работает — найдено замков: ${result.lockCount ?? 0}`)
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : 'Ошибка проверки API')
|
||||
} finally {
|
||||
setTestingApi(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddMapping = async () => {
|
||||
if (!addRoomId || !addLockMac.trim()) return
|
||||
setAddLoading(true)
|
||||
@@ -435,10 +451,16 @@ export function TTLockPage() {
|
||||
<SectorPicker value={cardSectors} onChange={setCardSectors} />
|
||||
</div>
|
||||
|
||||
<button onClick={handleSave} disabled={saving} className="btn-primary flex items-center gap-2">
|
||||
{saving ? <RefreshCw size={15} className="animate-spin" /> : <Save size={15} />}
|
||||
Сохранить
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={handleTestApi} disabled={testingApi || saving} className="btn-secondary flex items-center gap-2">
|
||||
{testingApi ? <RefreshCw size={15} className="animate-spin" /> : <Wifi size={15} />}
|
||||
Проверить API
|
||||
</button>
|
||||
<button onClick={handleSave} disabled={saving} className="btn-primary flex items-center gap-2">
|
||||
{saving ? <RefreshCw size={15} className="animate-spin" /> : <Save size={15} />}
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user