From a01ebed7af7dfee8690ad17777b1a708b5b9a9fa Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Fri, 12 Jun 2026 00:36:00 +0200 Subject: [PATCH] dead code cleanup + admin sessions UX: remove unused billingApi/useBilling functions, remove fetchAvailableFeatures ghost, add sessions link and per-user session action to AdminUsersPage, add URL param support and per-device revoke to AdminSessionsPage Co-authored-by: Cursor --- src/api/billingApi.ts | 107 -------------------------- src/api/featuresApi.ts | 17 ---- src/hooks/useBilling.ts | 61 +-------------- src/pages/admin/AdminSessionsPage.tsx | 35 +++++++-- src/pages/admin/AdminUsersPage.tsx | 23 ++++-- 5 files changed, 48 insertions(+), 195 deletions(-) diff --git a/src/api/billingApi.ts b/src/api/billingApi.ts index 52e71a9..0e28aba 100644 --- a/src/api/billingApi.ts +++ b/src/api/billingApi.ts @@ -38,29 +38,6 @@ export interface BillingTransaction { userName?: string; } -/** Pagination request for GET /api/billing/transactions with `pagination` JSON (table + grouping). */ -export interface BillingTransactionsPaginationParams { - page?: number; - pageSize?: number; - sort?: Array<{ field: string; direction: 'asc' | 'desc' }>; - filters?: Record; - search?: string; - viewKey?: string; - groupByLevels?: Array<{ field: string; nullLabel?: string; direction?: 'asc' | 'desc' }>; -} - -export interface BillingTransactionsPaginatedResponse { - items: BillingTransaction[]; - pagination?: { - currentPage: number; - pageSize: number; - totalItems: number; - totalPages: number; - }; - groupLayout?: import('./connectionApi').GroupLayout; - appliedView?: { viewKey?: string; displayName?: string }; -} - export interface BillingSettings { id: string; mandateId: string; @@ -159,46 +136,6 @@ export async function fetchBalanceForMandate( }); } -/** - * Fetch transaction history (table UI: pagination, filters, sort, saved views, grouping). - * Endpoint: GET /api/billing/transactions?pagination=... - */ -export async function fetchTransactionsPaginated( - request: ApiRequestFunction, - params?: BillingTransactionsPaginationParams -): Promise { - const paginationObj: Record = {}; - if (params?.page !== undefined) paginationObj.page = params.page; - if (params?.pageSize !== undefined) paginationObj.pageSize = params.pageSize; - if (params?.sort?.length) paginationObj.sort = params.sort; - if (params?.filters && Object.keys(params.filters).length > 0) paginationObj.filters = params.filters; - if (params?.search) paginationObj.search = params.search; - if (params?.viewKey) paginationObj.viewKey = params.viewKey; - if (params?.groupByLevels !== undefined) paginationObj.groupByLevels = params.groupByLevels; - - return await request({ - url: '/api/billing/transactions', - method: 'get', - params: { pagination: JSON.stringify(paginationObj) }, - }); -} - -/** - * Fetch transaction history (legacy array window) - * Endpoint: GET /api/billing/transactions - */ -export async function fetchTransactions( - request: ApiRequestFunction, - limit: number = 50, - offset: number = 0 -): Promise { - return await request({ - url: '/api/billing/transactions', - method: 'get', - params: { limit, offset } - }); -} - /** * Fetch usage statistics for an explicit date range. * Endpoint: GET /api/billing/statistics @@ -406,51 +343,7 @@ export async function fetchMandateViewTransactions( }); } -// ============================================================================ -// USER VIEW TYPES & API FUNCTIONS -// ============================================================================ - -export interface UserBalance { - accountId: string; - mandateId: string; - mandateName: string; - userId: string; - userName: string; - balance: number; - warningThreshold: number; - isWarning: boolean; - enabled: boolean; -} - export interface UserTransaction extends BillingTransaction { userId?: string; userName?: string; } - -/** - * Fetch user-level balances (RBAC-based) - * Endpoint: GET /api/billing/view/users/balances - */ -export async function fetchUserViewBalances( - request: ApiRequestFunction -): Promise { - return await request({ - url: '/api/billing/view/users/balances', - method: 'get' - }); -} - -/** - * Fetch user-level transactions (RBAC-based) - * Endpoint: GET /api/billing/view/users/transactions - */ -export async function fetchUserViewTransactions( - request: ApiRequestFunction, - limit: number = 100 -): Promise { - return await request({ - url: '/api/billing/view/users/transactions', - method: 'get', - params: { limit } - }); -} diff --git a/src/api/featuresApi.ts b/src/api/featuresApi.ts index ba368ea..dc96c91 100644 --- a/src/api/featuresApi.ts +++ b/src/api/featuresApi.ts @@ -11,7 +11,6 @@ import api from '../api'; import type { FeaturesMyResponse, Mandate, - MandateFeature, FeatureInstance, InstancePermissions, AccessLevel, @@ -156,22 +155,6 @@ export async function fetchMyFeatures(): Promise { } } -/** - * Lädt die verfügbaren Features (für Admin - Feature-Instanz erstellen) - * - * Endpoint: GET /api/features/available - */ -export async function fetchAvailableFeatures(): Promise { - if (USE_MOCK) { - return [ - { code: 'trustee', label: 'Treuhand', icon: 'briefcase', instances: [] }, - ]; - } - - const response = await api.get('/api/features/available'); - return response.data; -} - // ============================================================================= // TYPE GUARDS // ============================================================================= diff --git a/src/hooks/useBilling.ts b/src/hooks/useBilling.ts index 464c07d..26040a3 100644 --- a/src/hooks/useBilling.ts +++ b/src/hooks/useBilling.ts @@ -12,8 +12,6 @@ import { useApiRequest } from './useApi'; import { fetchBalances, fetchBalanceForMandate, - fetchTransactions, - fetchTransactionsPaginated, fetchStatistics, fetchAllowedProviders, fetchSettingsAdmin, @@ -34,9 +32,7 @@ import { type MandateUserSummary, type StatisticsRangeRequest, type BillingBucketSize, - type BillingTransactionsPaginationParams, } from '../api/billingApi'; -import type { GroupLayout } from '../api/connectionApi'; // Re-export types export type { @@ -52,25 +48,13 @@ export type { BillingBucketSize, }; -export type { TransactionType, ReferenceType, BillingTransactionsPaginationParams } from '../api/billingApi'; +export type { TransactionType, ReferenceType } from '../api/billingApi'; /** * Hook for user billing operations */ export function useBilling() { const [balances, setBalances] = useState([]); - const [transactions, setTransactions] = useState([]); - const [transactionsPagination, setTransactionsPagination] = useState<{ - currentPage: number; - pageSize: number; - totalItems: number; - totalPages: number; - } | null>(null); - const [transactionsGroupLayout, setTransactionsGroupLayout] = useState(null); - const [transactionsAppliedView, setTransactionsAppliedView] = useState<{ - viewKey?: string; - displayName?: string; - } | null>(null); const [statistics, setStatistics] = useState(null); const [allowedProviders, setAllowedProviders] = useState([]); const { request, isLoading: loading, error } = useApiRequest(); @@ -98,43 +82,6 @@ export function useBilling() { } }, [request]); - // Fetch transactions - const loadTransactions = useCallback(async (limit: number = 50, offset: number = 0) => { - try { - const data = await fetchTransactions(request, limit, offset); - setTransactions(Array.isArray(data) ? data : []); - setTransactionsPagination(null); - setTransactionsGroupLayout(null); - setTransactionsAppliedView(null); - return data; - } catch (err) { - console.error('Error loading transactions:', err); - setTransactions([]); - setTransactionsPagination(null); - setTransactionsGroupLayout(null); - setTransactionsAppliedView(null); - return []; - } - }, [request]); - - const refetchTransactions = useCallback(async (params?: BillingTransactionsPaginationParams) => { - try { - const data = await fetchTransactionsPaginated(request, params); - setTransactions(Array.isArray(data.items) ? data.items : []); - setTransactionsPagination(data.pagination ?? null); - setTransactionsGroupLayout(data.groupLayout ?? null); - setTransactionsAppliedView(data.appliedView ?? null); - return data; - } catch (err) { - console.error('Error loading transactions:', err); - setTransactions([]); - setTransactionsPagination(null); - setTransactionsGroupLayout(null); - setTransactionsAppliedView(null); - return null; - } - }, [request]); - const loadStatistics = useCallback(async (range: StatisticsRangeRequest) => { try { const data = await fetchStatistics(request, range); @@ -168,18 +115,12 @@ export function useBilling() { return { balances, - transactions, - transactionsPagination, - transactionsGroupLayout, - transactionsAppliedView, statistics, allowedProviders, loading, error, loadBalances, loadBalanceForMandate, - loadTransactions, - refetchTransactions, loadStatistics, loadAllowedProviders, refetch: loadBalances, diff --git a/src/pages/admin/AdminSessionsPage.tsx b/src/pages/admin/AdminSessionsPage.tsx index e51e37c..27e841b 100644 --- a/src/pages/admin/AdminSessionsPage.tsx +++ b/src/pages/admin/AdminSessionsPage.tsx @@ -6,7 +6,8 @@ * Admin page for viewing and managing active sessions and trusted devices per user. */ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useEffect } from 'react'; +import { useSearchParams } from 'react-router-dom'; import api from '../../api'; import { StackLayout } from '../../components/Layout/StackLayout'; import { Panel } from '../../components/Layout/Panel'; @@ -33,7 +34,8 @@ interface TrustedDeviceEntry { export const AdminSessionsPage: React.FC = () => { const { t } = useLanguage(); - const [userId, setUserId] = useState(''); + const [searchParams] = useSearchParams(); + const [userId, setUserId] = useState(searchParams.get('userId') || ''); const [sessions, setSessions] = useState([]); const [trustedDevices, setTrustedDevices] = useState([]); const [loading, setLoading] = useState(false); @@ -86,6 +88,21 @@ export const AdminSessionsPage: React.FC = () => { } }, [userId]); + const revokeTrustedDevice = useCallback(async (deviceId: string) => { + try { + await api.delete(`/api/admin/trusted-devices/${deviceId}`); + setTrustedDevices(prev => prev.filter(d => d.id !== deviceId)); + } catch (e: any) { + setError(e.response?.data?.detail || 'Failed to revoke device'); + } + }, []); + + useEffect(() => { + if (searchParams.get('userId') && userId) { + loadData(); + } + }, []); + const formatTimestamp = (ts: number) => { if (!ts) return '-'; return new Date(ts * 1000).toLocaleString(); @@ -175,21 +192,27 @@ export const AdminSessionsPage: React.FC = () => { IP Trusted Until Status + - {trustedDevices.map((d, i) => ( - + {trustedDevices.map((d) => ( + - {d.id} + {d.id.slice(0, 8)}... {d.ipAddress || '-'} {formatTimestamp(d.trustedUntil)} - {d.isExpired ? 'Expired' : 'Active'} + {d.isExpired ? t('Abgelaufen') : t('Aktiv')} + + + ))} diff --git a/src/pages/admin/AdminUsersPage.tsx b/src/pages/admin/AdminUsersPage.tsx index 25de0bf..3a26f0f 100644 --- a/src/pages/admin/AdminUsersPage.tsx +++ b/src/pages/admin/AdminUsersPage.tsx @@ -11,7 +11,7 @@ import { useNavigate } from 'react-router-dom'; import { useOrgUsers, useUserOperations } from '../../hooks/useUsers'; import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable'; import { FormGeneratorForm } from '../../components/FormGenerator/FormGeneratorForm'; -import { FaPlus, FaSync, FaKey, FaEnvelopeOpenText, FaUserShield } from 'react-icons/fa'; +import { FaPlus, FaSync, FaKey, FaEnvelopeOpenText, FaUserShield, FaDesktop } from 'react-icons/fa'; import { StackLayout } from '../../components/Layout/StackLayout'; import { Panel } from '../../components/Layout/Panel'; import styles from './Admin.module.css'; @@ -211,6 +211,13 @@ export const AdminUsersPage: React.FC = () => { > {t('Einladungen')} +