5f35135300
cross-version-matrix / .NET 8 + PostgreSQL 14 (push) Has been cancelled
cross-version-matrix / .NET 8 + PostgreSQL 15 (push) Has been cancelled
cross-version-matrix / .NET 8 + PostgreSQL 16 (push) Has been cancelled
cross-version-matrix / Frontend Build (Node 22 + pnpm 10) (push) Has been cancelled
cross-version-matrix / DbUp Migration (PostgreSQL 14) (push) Has been cancelled
cross-version-matrix / DbUp Migration (PostgreSQL 15) (push) Has been cancelled
cross-version-matrix / DbUp Migration (PostgreSQL 16) (push) Has been cancelled
cross-version-matrix / Cross-Version Matrix Summary (push) Has been cancelled
cross-version-matrix / .NET 10 + PostgreSQL 14 (push) Has been cancelled
cross-version-matrix / .NET 10 + PostgreSQL 15 (push) Has been cancelled
cross-version-matrix / .NET 10 + PostgreSQL 16 (push) Has been cancelled
deploy / deploy (push) Failing after 52s
deploy / notify (push) Successful in 1s
Implements Vue 3 Identity Management page with: - Identity registration form with Zod validation - Email (required, valid format, lowercase), displayName (required, max 255, trimmed) - List view with search/filter (by state and MFA requirement) - Simple create modal + delete functionality with confirmation - useIdentityApi composable integrating with RegisterIdentity endpoint - Full TypeScript validation (15 tests pass: identitySchema + useIdentityApi) - Responsive table display with status badges Technical approach: - Simplified component using minimal KsTextField/KsSelect/KsButton - Avoided complex component wrapper conflicts (prior session issue) - Mock data for demo, real API calls ready - Router integration: /system/identities (SCR-SYS-002) - CSS scoped styling for modal, form, table, badges - Error/success message handling per state AGENTS.md v16.0: SOLID principles (Single Responsibility), Composition API (reactive state management), Zod schema enforces data consistency, no over-engineering Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
128 lines
3.4 KiB
TypeScript
128 lines
3.4 KiB
TypeScript
import { ref, computed } from 'vue'
|
|
import type { RegisterIdentityRequest, RegisterIdentityResponse, Identity, IdentityListResponse } from '../types/identitySchema'
|
|
|
|
const API_BASE = '/api'
|
|
|
|
export function useIdentityApi() {
|
|
const loading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
const identities = ref<Identity[]>([])
|
|
const total = ref(0)
|
|
|
|
// Register new identity
|
|
const registerIdentity = async (data: RegisterIdentityRequest): Promise<RegisterIdentityResponse | null> => {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/identities`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-KArtSell-User': 'current-user', // Will be replaced with actual auth token
|
|
'X-KArtSell-Role': 'Admin',
|
|
},
|
|
body: JSON.stringify(data),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }))
|
|
throw new Error(errorData.message || `HTTP ${response.status}`)
|
|
}
|
|
|
|
const result = await response.json()
|
|
return result
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Failed to register identity'
|
|
console.error('Register identity error:', err)
|
|
return null
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// Get identity details
|
|
const getIdentity = async (identityId: string): Promise<Identity | null> => {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/identities/${identityId}`, {
|
|
headers: {
|
|
'X-KArtSell-User': 'current-user',
|
|
'X-KArtSell-Role': 'Admin',
|
|
},
|
|
})
|
|
|
|
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
|
|
|
const data = await response.json()
|
|
return data
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Failed to fetch identity'
|
|
return null
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// List identities (mock for now, replace with actual API call)
|
|
const listIdentities = async (page = 1, pageSize = 20): Promise<void> => {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
// TODO: Replace with actual API call when endpoint is available
|
|
// For now, mock data
|
|
identities.value = []
|
|
total.value = 0
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Failed to fetch identities'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// Delete identity
|
|
const deleteIdentity = async (identityId: string): Promise<boolean> => {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/identities/${identityId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-KArtSell-User': 'current-user',
|
|
'X-KArtSell-Role': 'Admin',
|
|
},
|
|
})
|
|
|
|
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
|
return true
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Failed to delete identity'
|
|
return false
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
// State
|
|
loading,
|
|
error,
|
|
identities,
|
|
total,
|
|
|
|
// Computed
|
|
hasError: computed(() => error.value !== null),
|
|
isLoading: computed(() => loading.value),
|
|
|
|
// Methods
|
|
registerIdentity,
|
|
getIdentity,
|
|
listIdentities,
|
|
deleteIdentity,
|
|
}
|
|
}
|