format
This commit is contained in:
parent
d858222af7
commit
7d9044ab29
202 changed files with 10755 additions and 10972 deletions
|
|
@ -1,16 +1,16 @@
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { exchangeApi } from '../services/exchangeApi';
|
||||
import {
|
||||
CreateExchangeRequest,
|
||||
CreateProviderMappingRequest,
|
||||
Exchange,
|
||||
ExchangeDetails,
|
||||
ExchangeStats,
|
||||
ProviderMapping,
|
||||
ProviderExchange,
|
||||
CreateExchangeRequest,
|
||||
ProviderMapping,
|
||||
UpdateExchangeRequest,
|
||||
CreateProviderMappingRequest,
|
||||
UpdateProviderMappingRequest,
|
||||
} from '../types';
|
||||
import { exchangeApi } from '../services/exchangeApi';
|
||||
|
||||
export function useExchanges() {
|
||||
const [exchanges, setExchanges] = useState<Exchange[]>([]);
|
||||
|
|
@ -62,18 +62,15 @@ export function useExchanges() {
|
|||
[fetchExchanges]
|
||||
);
|
||||
|
||||
const fetchExchangeDetails = useCallback(
|
||||
async (id: string): Promise<ExchangeDetails | null> => {
|
||||
try {
|
||||
return await exchangeApi.getExchangeById(id);
|
||||
} catch (err) {
|
||||
// Error fetching exchange details - error state will show in UI
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch exchange details');
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
const fetchExchangeDetails = useCallback(async (id: string): Promise<ExchangeDetails | null> => {
|
||||
try {
|
||||
return await exchangeApi.getExchangeById(id);
|
||||
} catch (err) {
|
||||
// Error fetching exchange details - error state will show in UI
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch exchange details');
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchStats = useCallback(async (): Promise<ExchangeStats | null> => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { FormErrors } from '../types';
|
||||
|
||||
export function useFormValidation<T>(
|
||||
initialData: T,
|
||||
validateFn: (data: T) => FormErrors
|
||||
) {
|
||||
export function useFormValidation<T>(initialData: T, validateFn: (data: T) => FormErrors) {
|
||||
const [formData, setFormData] = useState<T>(initialData);
|
||||
const [errors, setErrors] = useState<FormErrors>({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const updateField = useCallback((field: keyof T, value: T[keyof T]) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
|
||||
// Clear error when user starts typing
|
||||
if (errors[field as string]) {
|
||||
setErrors(prev => ({ ...prev, [field as string]: '' }));
|
||||
}
|
||||
}, [errors]);
|
||||
const updateField = useCallback(
|
||||
(field: keyof T, value: T[keyof T]) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
|
||||
// Clear error when user starts typing
|
||||
if (errors[field as string]) {
|
||||
setErrors(prev => ({ ...prev, [field as string]: '' }));
|
||||
}
|
||||
},
|
||||
[errors]
|
||||
);
|
||||
|
||||
const validate = useCallback((): boolean => {
|
||||
const newErrors = validateFn(formData);
|
||||
|
|
@ -30,24 +30,29 @@ export function useFormValidation<T>(
|
|||
setIsSubmitting(false);
|
||||
}, [initialData]);
|
||||
|
||||
const handleSubmit = useCallback(async (
|
||||
onSubmit: (data: T) => Promise<void>,
|
||||
onSuccess?: () => void,
|
||||
onError?: (error: unknown) => void
|
||||
) => {
|
||||
if (!validate()) {return;}
|
||||
const handleSubmit = useCallback(
|
||||
async (
|
||||
onSubmit: (data: T) => Promise<void>,
|
||||
onSuccess?: () => void,
|
||||
onError?: (error: unknown) => void
|
||||
) => {
|
||||
if (!validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await onSubmit(formData);
|
||||
reset();
|
||||
onSuccess?.();
|
||||
} catch (error) {
|
||||
onError?.(error);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [formData, validate, reset]);
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await onSubmit(formData);
|
||||
reset();
|
||||
onSuccess?.();
|
||||
} catch (error) {
|
||||
onError?.(error);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
},
|
||||
[formData, validate, reset]
|
||||
);
|
||||
|
||||
return {
|
||||
formData,
|
||||
|
|
@ -59,4 +64,4 @@ export function useFormValidation<T>(
|
|||
handleSubmit,
|
||||
setIsSubmitting,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,22 @@
|
|||
import {
|
||||
ApiResponse,
|
||||
CreateExchangeRequest,
|
||||
CreateProviderMappingRequest,
|
||||
Exchange,
|
||||
ExchangeDetails,
|
||||
ExchangeStats,
|
||||
ProviderMapping,
|
||||
ProviderExchange,
|
||||
CreateExchangeRequest,
|
||||
ProviderMapping,
|
||||
UpdateExchangeRequest,
|
||||
CreateProviderMappingRequest,
|
||||
UpdateProviderMappingRequest,
|
||||
} from '../types';
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:4000/api';
|
||||
|
||||
class ExchangeApiService {
|
||||
private async request<T>(
|
||||
endpoint: string,
|
||||
options?: RequestInit
|
||||
): Promise<ApiResponse<T>> {
|
||||
private async request<T>(endpoint: string, options?: RequestInit): Promise<ApiResponse<T>> {
|
||||
const url = `${API_BASE_URL}${endpoint}`;
|
||||
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -33,7 +30,7 @@ class ExchangeApiService {
|
|||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'API request failed');
|
||||
}
|
||||
|
|
@ -76,10 +73,10 @@ class ExchangeApiService {
|
|||
|
||||
// Provider Mappings
|
||||
async getProviderMappings(provider?: string): Promise<ProviderMapping[]> {
|
||||
const endpoint = provider
|
||||
const endpoint = provider
|
||||
? `/exchanges/provider-mappings/${provider}`
|
||||
: '/exchanges/provider-mappings/all';
|
||||
|
||||
|
||||
const response = await this.request<ProviderMapping[]>(endpoint);
|
||||
return response.data || [];
|
||||
}
|
||||
|
|
@ -96,7 +93,7 @@ class ExchangeApiService {
|
|||
}
|
||||
|
||||
async updateProviderMapping(
|
||||
id: string,
|
||||
id: string,
|
||||
data: UpdateProviderMappingRequest
|
||||
): Promise<ProviderMapping> {
|
||||
const response = await this.request<ProviderMapping>(`/exchanges/provider-mappings/${id}`, {
|
||||
|
|
@ -132,4 +129,4 @@ class ExchangeApiService {
|
|||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const exchangeApi = new ExchangeApiService();
|
||||
export const exchangeApi = new ExchangeApiService();
|
||||
|
|
|
|||
|
|
@ -66,4 +66,4 @@ export interface ExchangeStats {
|
|||
active_provider_mappings: string;
|
||||
verified_provider_mappings: string;
|
||||
providers: string;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ export interface AddExchangeDialogProps extends BaseDialogProps {
|
|||
export interface AddProviderMappingDialogProps extends BaseDialogProps {
|
||||
exchangeId: string;
|
||||
exchangeName: string;
|
||||
onCreateMapping: (request: import('./request.types').CreateProviderMappingRequest) => Promise<unknown>;
|
||||
onCreateMapping: (
|
||||
request: import('./request.types').CreateProviderMappingRequest
|
||||
) => Promise<unknown>;
|
||||
}
|
||||
|
||||
export interface DeleteExchangeDialogProps extends BaseDialogProps {
|
||||
|
|
@ -40,4 +42,4 @@ export interface DeleteExchangeDialogProps extends BaseDialogProps {
|
|||
exchangeName: string;
|
||||
providerMappingCount: number;
|
||||
onConfirmDelete: (exchangeId: string) => Promise<boolean>;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,4 @@ export interface UpdateProviderMappingRequest {
|
|||
verified?: boolean;
|
||||
confidence?: number;
|
||||
master_exchange_id?: string;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export function sortProviderMappings(mappings: ProviderMapping[]): ProviderMappi
|
|||
if (!a.active && b.active) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Then by provider name
|
||||
return a.provider.localeCompare(b.provider);
|
||||
});
|
||||
|
|
@ -32,4 +32,4 @@ export function truncateText(text: string, maxLength: number): string {
|
|||
return text;
|
||||
}
|
||||
return text.substring(0, maxLength) + '...';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,4 +35,4 @@ export function validateExchangeForm(data: {
|
|||
|
||||
export function hasValidationErrors(errors: FormErrors): boolean {
|
||||
return Object.keys(errors).length > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,11 @@ export function formatPercentage(value: number): string {
|
|||
}
|
||||
|
||||
export function getValueColor(value: number): string {
|
||||
if (value > 0) {return 'text-success';}
|
||||
if (value < 0) {return 'text-danger';}
|
||||
if (value > 0) {
|
||||
return 'text-success';
|
||||
}
|
||||
if (value < 0) {
|
||||
return 'text-danger';
|
||||
}
|
||||
return 'text-text-secondary';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,15 @@ export function formatPercentage(value: number, decimals = 2): string {
|
|||
* Format large numbers with K, M, B suffixes
|
||||
*/
|
||||
export function formatNumber(num: number): string {
|
||||
if (num >= 1e9) {return (num / 1e9).toFixed(1) + 'B';}
|
||||
if (num >= 1e6) {return (num / 1e6).toFixed(1) + 'M';}
|
||||
if (num >= 1e3) {return (num / 1e3).toFixed(1) + 'K';}
|
||||
if (num >= 1e9) {
|
||||
return (num / 1e9).toFixed(1) + 'B';
|
||||
}
|
||||
if (num >= 1e6) {
|
||||
return (num / 1e6).toFixed(1) + 'M';
|
||||
}
|
||||
if (num >= 1e3) {
|
||||
return (num / 1e3).toFixed(1) + 'K';
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
|
|
@ -33,8 +39,12 @@ export function formatNumber(num: number): string {
|
|||
* Get color class based on numeric value (profit/loss)
|
||||
*/
|
||||
export function getValueColor(value: number): string {
|
||||
if (value > 0) {return 'text-success';}
|
||||
if (value < 0) {return 'text-danger';}
|
||||
if (value > 0) {
|
||||
return 'text-success';
|
||||
}
|
||||
if (value < 0) {
|
||||
return 'text-danger';
|
||||
}
|
||||
return 'text-text-secondary';
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +52,8 @@ export function getValueColor(value: number): string {
|
|||
* Truncate text to specified length
|
||||
*/
|
||||
export function truncateText(text: string, length: number): string {
|
||||
if (text.length <= length) {return text;}
|
||||
if (text.length <= length) {
|
||||
return text;
|
||||
}
|
||||
return text.slice(0, length) + '...';
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue