scaffold initial project structure and global design system
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import api, { setToken, setStoredUser, removeToken, removeStoredUser } from './api'
|
||||
|
||||
export const adminService = {
|
||||
// ── Authentication ──
|
||||
auth: {
|
||||
login: async (email, password) => {
|
||||
const res = await api.post('/api/admin/auth/login', { email, password })
|
||||
if (res?.data?.token) {
|
||||
setToken(res.data.token)
|
||||
if (res.data.user) {
|
||||
setStoredUser(res.data.user)
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
|
||||
verifyTotp: async (email, totp, sessionToken) => {
|
||||
const res = await api.post('/api/admin/auth/verify-totp', { email, totp, sessionToken })
|
||||
if (res?.data?.token) {
|
||||
setToken(res.data.token)
|
||||
if (res.data.user) {
|
||||
setStoredUser(res.data.user)
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
|
||||
verify2fa: async (email, code) => {
|
||||
const res = await api.post('/api/admin/auth/verify-2fa', { email, code })
|
||||
if (res?.data?.token) {
|
||||
setToken(res.data.token)
|
||||
if (res.data.user) {
|
||||
setStoredUser(res.data.user)
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
|
||||
setup2fa: () => api.post('/api/admin/auth/setup-2fa'),
|
||||
confirm2fa: (totp) => api.post('/api/admin/auth/confirm-2fa', { totp }),
|
||||
changePassword: (old_password, new_password) =>
|
||||
api.post('/api/admin/auth/change-password', { old_password, new_password }),
|
||||
|
||||
logout: () => {
|
||||
removeToken()
|
||||
removeStoredUser()
|
||||
},
|
||||
},
|
||||
|
||||
// ── User Management (RBAC) ──
|
||||
users: {
|
||||
list: (params = {}) => api.get('/api/admin/users', params),
|
||||
all: () => api.get('/api/admin/users/all'),
|
||||
get: (id) => api.get(`/api/admin/users/${id}`),
|
||||
create: (data) => api.post('/api/admin/users', data),
|
||||
update: (id, data) => api.patch(`/api/admin/users/${id}`, data),
|
||||
suspend: (id, reason) => api.post(`/api/admin/users/${id}/suspend`, { reason }),
|
||||
reinstate: (id) => api.post(`/api/admin/users/${id}/reinstate`),
|
||||
reset2fa: (id, verification_evidence) =>
|
||||
api.post(`/api/admin/users/${id}/reset-2fa`, { verification_evidence }),
|
||||
forceLogout: (id) => api.post(`/api/admin/users/${id}/force-logout`),
|
||||
delete: (id) => api.delete(`/api/admin/users/${id}`),
|
||||
getActivity: (id) => api.get(`/api/admin/users/${id}/activity`),
|
||||
},
|
||||
|
||||
// ── Approvals (Two-person rule) ──
|
||||
approvals: {
|
||||
list: (params = {}) => api.get('/api/admin/approvals', params),
|
||||
create: (data) => api.post('/api/admin/approvals', data),
|
||||
approve: (id, note) => api.post(`/api/admin/approvals/${id}/approve`, { note }),
|
||||
reject: (id, reason) => api.post(`/api/admin/approvals/${id}/reject`, { reason }),
|
||||
},
|
||||
|
||||
// ── Financial Engine ──
|
||||
finance: {
|
||||
getSummary: () => api.get('/api/admin/finance/summary'),
|
||||
getRevenue: (params = {}) => api.get('/api/admin/finance/revenue', params),
|
||||
getPayouts: (params = {}) => api.get('/api/admin/finance/payouts', params),
|
||||
getRefunds: (params = {}) => api.get('/api/admin/finance/refunds', params),
|
||||
getReconciliation: () => api.get('/api/admin/finance/reconciliation'),
|
||||
getCaregiverEarnings: (params = {}) => api.get('/api/admin/finance/caregiver-earnings', params),
|
||||
getRefundAuthorisations: () => api.get('/api/admin/finance/refund-authorisations'),
|
||||
approveRefund: (id) => api.post(`/api/admin/finance/refund-authorisations/${id}/approve`),
|
||||
rejectRefund: (id, reason) => api.post(`/api/admin/finance/refund-authorisations/${id}/reject`, { reason }),
|
||||
},
|
||||
|
||||
// ── Dynamic Configuration ──
|
||||
config: {
|
||||
get: () => api.get('/api/admin/config'),
|
||||
update: (data) => api.patch('/api/admin/config', data),
|
||||
getHistory: () => api.get('/api/admin/config/history'),
|
||||
},
|
||||
|
||||
// ── Metrics & Growth ──
|
||||
metrics: {
|
||||
growth: () => api.get('/api/admin/metrics/growth'),
|
||||
engagement: () => api.get('/api/admin/metrics/engagement'),
|
||||
quality: () => api.get('/api/admin/metrics/quality'),
|
||||
},
|
||||
|
||||
// ── Break-Glass Access Grants ──
|
||||
accessGrants: {
|
||||
list: () => api.get('/api/admin/access-grants'),
|
||||
create: (data) => api.post('/api/admin/access-grants', data),
|
||||
revoke: (id) => api.post(`/api/admin/access-grants/${id}/revoke`),
|
||||
},
|
||||
|
||||
// ── Audit Logs ──
|
||||
auditLog: {
|
||||
list: (params = {}) => api.get('/api/admin/audit-log', params),
|
||||
get: (id) => api.get(`/api/admin/audit-log/${id}`),
|
||||
export: (format = 'json') => api.get('/api/admin/audit-log/export', { format }),
|
||||
},
|
||||
|
||||
// ── Compliance & Data Governance ──
|
||||
compliance: {
|
||||
getDataRequests: () => api.get('/api/admin/compliance/data-requests'),
|
||||
fulfilDataRequest: (id) => api.post(`/api/admin/compliance/data-requests/${id}/fulfil`),
|
||||
rejectDataRequest: (id, reason) => api.post(`/api/admin/compliance/data-requests/${id}/reject`, { reason }),
|
||||
getRetentionReport: () => api.get('/api/admin/compliance/retention-report'),
|
||||
},
|
||||
|
||||
// ── System Operations ──
|
||||
system: {
|
||||
getHealth: () => api.get('/api/admin/system/health'),
|
||||
getJobs: () => api.get('/api/admin/system/jobs'),
|
||||
triggerJob: (name) => api.post(`/api/admin/system/jobs/${encodeURIComponent(name)}/trigger`),
|
||||
setMaintenanceMode: (enabled, reason) =>
|
||||
api.post('/api/admin/system/maintenance-mode', { enabled, reason }),
|
||||
getIntegrations: () => api.get('/api/admin/system/integrations'),
|
||||
testIntegration: (name) =>
|
||||
api.post(`/api/admin/system/integrations/${encodeURIComponent(name)}/test`),
|
||||
},
|
||||
|
||||
// ── Announcements & Broadcasts ──
|
||||
announcements: {
|
||||
list: () => api.get('/api/admin/announcements'),
|
||||
create: (data) => api.post('/api/admin/announcements', data),
|
||||
cancel: (id) => api.post(`/api/admin/announcements/${id}/cancel`),
|
||||
},
|
||||
}
|
||||
|
||||
export default adminService
|
||||
@@ -0,0 +1,96 @@
|
||||
// Base API client configured for CureSelect Super Admin Backend
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'
|
||||
const API_KEY = import.meta.env.VITE_API_KEY || 'DUMUqJrNeyuaPDSZSsMf91qjgwc5K3N7'
|
||||
|
||||
export const getToken = () => localStorage.getItem('admin_token')
|
||||
export const setToken = (token) => localStorage.setItem('admin_token', token)
|
||||
export const removeToken = () => localStorage.removeItem('admin_token')
|
||||
|
||||
export const getStoredUser = () => {
|
||||
try {
|
||||
const raw = localStorage.getItem('admin_user')
|
||||
return raw ? JSON.parse(raw) : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
export const setStoredUser = (user) => localStorage.setItem('admin_user', JSON.stringify(user))
|
||||
export const removeStoredUser = () => localStorage.removeItem('admin_user')
|
||||
|
||||
async function request(endpoint, options = {}) {
|
||||
const url = endpoint.startsWith('http') ? endpoint : `${API_BASE_URL}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'x-api-key': API_KEY,
|
||||
...options.headers,
|
||||
}
|
||||
|
||||
const token = getToken()
|
||||
if (token && !headers['Authorization']) {
|
||||
headers['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
|
||||
const config = {
|
||||
...options,
|
||||
headers,
|
||||
}
|
||||
|
||||
if (config.body && typeof config.body === 'object' && !(config.body instanceof FormData)) {
|
||||
config.body = JSON.stringify(config.body)
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, config)
|
||||
const data = await response.json().catch(() => null)
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 && !endpoint.includes('/auth/login')) {
|
||||
// Token expired or unauthorized
|
||||
removeToken()
|
||||
removeStoredUser()
|
||||
window.dispatchEvent(new Event('auth:unauthorized'))
|
||||
}
|
||||
|
||||
const errorMessage = data?.message || data?.errors?.description || `HTTP error ${response.status}: ${response.statusText}`
|
||||
const error = new Error(errorMessage)
|
||||
error.status = response.status
|
||||
error.data = data
|
||||
throw error
|
||||
}
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
if (!err.status && err.message === 'Failed to fetch') {
|
||||
const connErr = new Error(`Cannot connect to backend server at ${API_BASE_URL}. Ensure PHP server is running.`)
|
||||
connErr.status = 0
|
||||
throw connErr
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export const api = {
|
||||
get: (endpoint, params = {}, headers = {}) => {
|
||||
const query = new URLSearchParams(
|
||||
Object.entries(params).filter(([_, v]) => v !== undefined && v !== null && v !== '')
|
||||
).toString()
|
||||
const url = query ? `${endpoint}?${query}` : endpoint
|
||||
return request(url, { method: 'GET', headers })
|
||||
},
|
||||
post: (endpoint, body = {}, headers = {}) => {
|
||||
return request(endpoint, { method: 'POST', body, headers })
|
||||
},
|
||||
patch: (endpoint, body = {}, headers = {}) => {
|
||||
return request(endpoint, { method: 'PATCH', body, headers })
|
||||
},
|
||||
put: (endpoint, body = {}, headers = {}) => {
|
||||
return request(endpoint, { method: 'PUT', body, headers })
|
||||
},
|
||||
delete: (endpoint, body = {}, headers = {}) => {
|
||||
return request(endpoint, { method: 'DELETE', body, headers })
|
||||
},
|
||||
}
|
||||
|
||||
export default api
|
||||
Reference in New Issue
Block a user