keynesol-shared 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -0
- package/dist/components/Common/ErrorBoundary.d.ts +23 -0
- package/dist/components/Common/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/Common/ErrorBoundary.js +93 -0
- package/dist/components/Common/ErrorBoundary.jsx +103 -0
- package/dist/components/Common/ErrorMessage.d.ts +8 -0
- package/dist/components/Common/ErrorMessage.d.ts.map +1 -0
- package/dist/components/Common/ErrorMessage.js +36 -0
- package/dist/components/Common/ErrorMessage.jsx +40 -0
- package/dist/components/Common/Loading.d.ts +8 -0
- package/dist/components/Common/Loading.d.ts.map +1 -0
- package/dist/components/Common/Loading.js +41 -0
- package/dist/components/Common/Loading.jsx +44 -0
- package/dist/components/Common/LoadingIndicator.d.ts +17 -0
- package/dist/components/Common/LoadingIndicator.d.ts.map +1 -0
- package/dist/components/Common/LoadingIndicator.js +95 -0
- package/dist/components/Common/LoadingIndicator.jsx +108 -0
- package/dist/components/Common/ProgramStatus.d.ts +3 -0
- package/dist/components/Common/ProgramStatus.d.ts.map +1 -0
- package/dist/components/Common/ProgramStatus.js +26 -0
- package/dist/components/Common/ProgramStatus.jsx +27 -0
- package/dist/components/Common/Skeleton.d.ts +39 -0
- package/dist/components/Common/Skeleton.d.ts.map +1 -0
- package/dist/components/Common/Skeleton.js +53 -0
- package/dist/components/Common/Skeleton.jsx +67 -0
- package/dist/components/Common/SkeletonScreen.d.ts +18 -0
- package/dist/components/Common/SkeletonScreen.d.ts.map +1 -0
- package/dist/components/Common/SkeletonScreen.js +98 -0
- package/dist/components/Common/SkeletonScreen.jsx +108 -0
- package/dist/components/Common/index.d.ts +11 -0
- package/dist/components/Common/index.d.ts.map +1 -0
- package/dist/components/Common/index.js +10 -0
- package/dist/components/Wallet/TransactionStatus.d.ts +11 -0
- package/dist/components/Wallet/TransactionStatus.d.ts.map +1 -0
- package/dist/components/Wallet/TransactionStatus.js +97 -0
- package/dist/components/Wallet/TransactionStatus.jsx +106 -0
- package/dist/components/Wallet/WalletBalance.d.ts +4 -0
- package/dist/components/Wallet/WalletBalance.d.ts.map +1 -0
- package/dist/components/Wallet/WalletBalance.js +82 -0
- package/dist/components/Wallet/WalletBalance.jsx +86 -0
- package/dist/components/Wallet/WalletButton.d.ts +3 -0
- package/dist/components/Wallet/WalletButton.d.ts.map +1 -0
- package/dist/components/Wallet/WalletButton.js +51 -0
- package/dist/components/Wallet/WalletButton.jsx +53 -0
- package/dist/components/Wallet/WalletConnectionModal.d.ts +8 -0
- package/dist/components/Wallet/WalletConnectionModal.d.ts.map +1 -0
- package/dist/components/Wallet/WalletConnectionModal.js +150 -0
- package/dist/components/Wallet/WalletConnectionModal.jsx +170 -0
- package/dist/components/Wallet/WalletProvider.d.ts +9 -0
- package/dist/components/Wallet/WalletProvider.d.ts.map +1 -0
- package/dist/components/Wallet/WalletProvider.js +70 -0
- package/dist/components/Wallet/WalletProvider.jsx +75 -0
- package/dist/components/Wallet/index.d.ts +9 -0
- package/dist/components/Wallet/index.d.ts.map +1 -0
- package/dist/components/Wallet/index.js +8 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +6 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +9 -0
- package/dist/hooks/useCache.d.ts +16 -0
- package/dist/hooks/useCache.d.ts.map +1 -0
- package/dist/hooks/useCache.js +67 -0
- package/dist/hooks/usePolling.d.ts +16 -0
- package/dist/hooks/usePolling.d.ts.map +1 -0
- package/dist/hooks/usePolling.js +79 -0
- package/dist/hooks/useProgram.d.ts +14 -0
- package/dist/hooks/useProgram.d.ts.map +1 -0
- package/dist/hooks/useProgram.js +88 -0
- package/dist/hooks/useTokenBalance.d.ts +16 -0
- package/dist/hooks/useTokenBalance.d.ts.map +1 -0
- package/dist/hooks/useTokenBalance.js +100 -0
- package/dist/hooks/useVaults.d.ts +23 -0
- package/dist/hooks/useVaults.d.ts.map +1 -0
- package/dist/hooks/useVaults.js +98 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -0
- package/dist/services/reconciliationService.d.ts +76 -0
- package/dist/services/reconciliationService.d.ts.map +1 -0
- package/dist/services/reconciliationService.js +216 -0
- package/dist/services/syncService.d.ts +51 -0
- package/dist/services/syncService.d.ts.map +1 -0
- package/dist/services/syncService.js +218 -0
- package/dist/types/index.d.ts +201 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/cacheManager.d.ts +73 -0
- package/dist/utils/cacheManager.d.ts.map +1 -0
- package/dist/utils/cacheManager.js +232 -0
- package/dist/utils/errorHandler.d.ts +76 -0
- package/dist/utils/errorHandler.d.ts.map +1 -0
- package/dist/utils/errorHandler.js +267 -0
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/performanceMonitor.d.ts +75 -0
- package/dist/utils/performanceMonitor.d.ts.map +1 -0
- package/dist/utils/performanceMonitor.js +197 -0
- package/dist/utils/rpcRetry.d.ts +12 -0
- package/dist/utils/rpcRetry.d.ts.map +1 -0
- package/dist/utils/rpcRetry.js +47 -0
- package/dist/utils/supabase.d.ts +198 -0
- package/dist/utils/supabase.d.ts.map +1 -0
- package/dist/utils/supabase.js +50 -0
- package/dist/utils/toastService.d.ts +52 -0
- package/dist/utils/toastService.d.ts.map +1 -0
- package/dist/utils/toastService.js +139 -0
- package/dist/utils/tokenUtils.d.ts +33 -0
- package/dist/utils/tokenUtils.d.ts.map +1 -0
- package/dist/utils/tokenUtils.js +66 -0
- package/dist/utils/validation.d.ts +35 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +83 -0
- package/package.json +45 -0
- package/src/components/Common/ErrorBoundary.tsx +135 -0
- package/src/components/Common/ErrorMessage.tsx +52 -0
- package/src/components/Common/Loading.tsx +56 -0
- package/src/components/Common/LoadingIndicator.tsx +143 -0
- package/src/components/Common/ProgramStatus.tsx +37 -0
- package/src/components/Common/Skeleton.tsx +83 -0
- package/src/components/Common/SkeletonScreen.tsx +166 -0
- package/src/components/Common/index.ts +10 -0
- package/src/components/Wallet/TransactionStatus.tsx +138 -0
- package/src/components/Wallet/WalletBalance.tsx +94 -0
- package/src/components/Wallet/WalletButton.tsx +65 -0
- package/src/components/Wallet/WalletConnectionModal.tsx +193 -0
- package/src/components/Wallet/WalletProvider.tsx +104 -0
- package/src/components/Wallet/index.ts +8 -0
- package/src/components/index.ts +6 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/useCache.ts +87 -0
- package/src/hooks/usePolling.ts +98 -0
- package/src/hooks/useProgram.ts +93 -0
- package/src/hooks/useTokenBalance.ts +113 -0
- package/src/hooks/useVaults.ts +122 -0
- package/src/index.ts +23 -0
- package/src/services/index.ts +6 -0
- package/src/services/reconciliationService.ts +246 -0
- package/src/services/syncService.ts +238 -0
- package/src/types/index.ts +233 -0
- package/src/utils/cacheManager.ts +286 -0
- package/src/utils/errorHandler.ts +336 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/performanceMonitor.ts +222 -0
- package/src/utils/rpcRetry.ts +55 -0
- package/src/utils/supabase.ts +253 -0
- package/src/utils/toastService.ts +166 -0
- package/src/utils/tokenUtils.ts +75 -0
- package/src/utils/validation.ts +107 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @keynesol/shared
|
|
2
|
+
|
|
3
|
+
Shared code package for Keynesol Web3 Prediction Platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @keynesol/shared
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or for local development:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm link
|
|
15
|
+
# In your app:
|
|
16
|
+
npm link @keynesol/shared
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Components
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { ErrorBoundary, WalletProvider, WalletButton } from '@keynesol/shared';
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
return (
|
|
28
|
+
<ErrorBoundary>
|
|
29
|
+
<WalletProvider>
|
|
30
|
+
<WalletButton />
|
|
31
|
+
</WalletProvider>
|
|
32
|
+
</ErrorBoundary>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Hooks
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { useProgram, useVaults, useTokenBalance } from '@keynesol/shared';
|
|
41
|
+
|
|
42
|
+
function MyComponent() {
|
|
43
|
+
const { program } = useProgram();
|
|
44
|
+
const { vaults, loading } = useVaults();
|
|
45
|
+
const { sol, usdc } = useTokenBalance();
|
|
46
|
+
|
|
47
|
+
// ...
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Utils
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import {
|
|
55
|
+
errorHandler,
|
|
56
|
+
cacheManager,
|
|
57
|
+
tokenUtils,
|
|
58
|
+
validation,
|
|
59
|
+
retryRpcCall
|
|
60
|
+
} from '@keynesol/shared';
|
|
61
|
+
|
|
62
|
+
// Error handling
|
|
63
|
+
errorHandler.handleError(error, 'MyComponent');
|
|
64
|
+
|
|
65
|
+
// Token utilities
|
|
66
|
+
const isSOL = tokenUtils.isNativeSOL(tokenMint);
|
|
67
|
+
const decimals = tokenUtils.getTokenDecimals(tokenMint);
|
|
68
|
+
|
|
69
|
+
// Validation
|
|
70
|
+
const result = validation.validateStakeAmount(amount, balance);
|
|
71
|
+
|
|
72
|
+
// RPC retry
|
|
73
|
+
const data = await retryRpcCall(() => connection.getBalance(publicKey));
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Services
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { syncService, reconciliationService } from '@keynesol/shared';
|
|
80
|
+
|
|
81
|
+
// Initialize sync service
|
|
82
|
+
syncService.initialize(connection, {
|
|
83
|
+
url: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
84
|
+
anonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Initialize reconciliation
|
|
88
|
+
reconciliationService.initialize(connection, {
|
|
89
|
+
url: process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
90
|
+
anonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Configuration
|
|
95
|
+
|
|
96
|
+
The package uses environment variables for configuration:
|
|
97
|
+
|
|
98
|
+
- `NEXT_PUBLIC_SOLANA_NETWORK` - Network (devnet, mainnet-beta, testnet)
|
|
99
|
+
- `NEXT_PUBLIC_RPC_ENDPOINT` - Custom RPC endpoint
|
|
100
|
+
- `NEXT_PUBLIC_PROGRAM_ID` - Solana program ID
|
|
101
|
+
- `NEXT_PUBLIC_SUPABASE_URL` - Supabase URL
|
|
102
|
+
- `NEXT_PUBLIC_SUPABASE_ANON_KEY` - Supabase anonymous key
|
|
103
|
+
|
|
104
|
+
## Building
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm run build
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Type Checking
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm run type-check
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ErrorBoundary Component
|
|
3
|
+
* Requirements: 11.1 - Global error handling with React error boundaries
|
|
4
|
+
*/
|
|
5
|
+
import { Component, ErrorInfo, ReactNode } from 'react';
|
|
6
|
+
interface Props {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
fallback?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
interface State {
|
|
11
|
+
hasError: boolean;
|
|
12
|
+
error: Error | null;
|
|
13
|
+
errorInfo: ErrorInfo | null;
|
|
14
|
+
}
|
|
15
|
+
export declare class ErrorBoundary extends Component<Props, State> {
|
|
16
|
+
constructor(props: Props);
|
|
17
|
+
static getDerivedStateFromError(error: Error): State;
|
|
18
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
19
|
+
handleReset: () => void;
|
|
20
|
+
render(): ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=ErrorBoundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../../src/components/Common/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAc,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI/D,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,KAAK;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;CAC7B;AA+CD,qBAAa,aAAc,SAAQ,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;gBAC5C,KAAK,EAAE,KAAK;IASxB,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK;IAQpD,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAa3D,WAAW,QAAO,IAAI,CAMpB;IAEF,MAAM,IAAI,SAAS;CA+BpB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ErrorBoundary Component
|
|
4
|
+
* Requirements: 11.1 - Global error handling with React error boundaries
|
|
5
|
+
*/
|
|
6
|
+
import { Component } from 'react';
|
|
7
|
+
import styled from 'styled-components';
|
|
8
|
+
import { ErrorMessage } from './ErrorMessage';
|
|
9
|
+
const Container = styled.div `
|
|
10
|
+
padding: var(--spacing-xl, 1.5rem);
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
min-height: 400px;
|
|
16
|
+
`;
|
|
17
|
+
const ErrorTitle = styled.h2 `
|
|
18
|
+
font-size: 1.5rem;
|
|
19
|
+
font-weight: 700;
|
|
20
|
+
color: var(--color-error, #dc3545);
|
|
21
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
22
|
+
`;
|
|
23
|
+
const ErrorDetails = styled.details `
|
|
24
|
+
margin-top: var(--spacing-lg, 1.5rem);
|
|
25
|
+
width: 100%;
|
|
26
|
+
max-width: 800px;
|
|
27
|
+
`;
|
|
28
|
+
const ErrorSummary = styled.summary `
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
color: var(--color-text-secondary, #6b7280);
|
|
31
|
+
font-size: 0.875rem;
|
|
32
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
33
|
+
|
|
34
|
+
&:hover {
|
|
35
|
+
color: var(--color-text, #1a1a1a);
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
const ErrorCode = styled.pre `
|
|
39
|
+
background: var(--color-background, #ffffff);
|
|
40
|
+
border: 1px solid var(--color-border, #e5e7eb);
|
|
41
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
42
|
+
padding: var(--spacing-md, 1rem);
|
|
43
|
+
overflow-x: auto;
|
|
44
|
+
font-size: 0.75rem;
|
|
45
|
+
color: var(--color-text-secondary, #6b7280);
|
|
46
|
+
max-height: 300px;
|
|
47
|
+
overflow-y: auto;
|
|
48
|
+
`;
|
|
49
|
+
export class ErrorBoundary extends Component {
|
|
50
|
+
constructor(props) {
|
|
51
|
+
super(props);
|
|
52
|
+
this.handleReset = () => {
|
|
53
|
+
this.setState({
|
|
54
|
+
hasError: false,
|
|
55
|
+
error: null,
|
|
56
|
+
errorInfo: null,
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
this.state = {
|
|
60
|
+
hasError: false,
|
|
61
|
+
error: null,
|
|
62
|
+
errorInfo: null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
static getDerivedStateFromError(error) {
|
|
66
|
+
return {
|
|
67
|
+
hasError: true,
|
|
68
|
+
error,
|
|
69
|
+
errorInfo: null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
componentDidCatch(error, errorInfo) {
|
|
73
|
+
// Log error to console and monitoring service
|
|
74
|
+
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
|
75
|
+
// TODO: Send to error monitoring service (e.g., Sentry)
|
|
76
|
+
// logErrorToService(error, errorInfo);
|
|
77
|
+
this.setState({
|
|
78
|
+
error,
|
|
79
|
+
errorInfo,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
render() {
|
|
83
|
+
var _a, _b, _c;
|
|
84
|
+
if (this.state.hasError) {
|
|
85
|
+
if (this.props.fallback) {
|
|
86
|
+
return this.props.fallback;
|
|
87
|
+
}
|
|
88
|
+
const isDevelopment = typeof process !== 'undefined' && ((_a = process.env) === null || _a === void 0 ? void 0 : _a.NODE_ENV) === 'development';
|
|
89
|
+
return (_jsxs(Container, { children: [_jsx(ErrorTitle, { children: "Something went wrong" }), _jsx(ErrorMessage, { message: ((_b = this.state.error) === null || _b === void 0 ? void 0 : _b.message) || 'An unexpected error occurred', onRetry: this.handleReset }), isDevelopment && this.state.errorInfo && (_jsxs(ErrorDetails, { children: [_jsx(ErrorSummary, { children: "Error Details (Development Only)" }), _jsxs(ErrorCode, { children: [(_c = this.state.error) === null || _c === void 0 ? void 0 : _c.toString(), this.state.errorInfo.componentStack] })] }))] }));
|
|
90
|
+
}
|
|
91
|
+
return this.props.children;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ErrorBoundary Component
|
|
3
|
+
* Requirements: 11.1 - Global error handling with React error boundaries
|
|
4
|
+
*/
|
|
5
|
+
import React, { Component } from 'react';
|
|
6
|
+
import styled from 'styled-components';
|
|
7
|
+
import { ErrorMessage } from './ErrorMessage';
|
|
8
|
+
const Container = styled.div `
|
|
9
|
+
padding: var(--spacing-xl, 1.5rem);
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
min-height: 400px;
|
|
15
|
+
`;
|
|
16
|
+
const ErrorTitle = styled.h2 `
|
|
17
|
+
font-size: 1.5rem;
|
|
18
|
+
font-weight: 700;
|
|
19
|
+
color: var(--color-error, #dc3545);
|
|
20
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
21
|
+
`;
|
|
22
|
+
const ErrorDetails = styled.details `
|
|
23
|
+
margin-top: var(--spacing-lg, 1.5rem);
|
|
24
|
+
width: 100%;
|
|
25
|
+
max-width: 800px;
|
|
26
|
+
`;
|
|
27
|
+
const ErrorSummary = styled.summary `
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
color: var(--color-text-secondary, #6b7280);
|
|
30
|
+
font-size: 0.875rem;
|
|
31
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
32
|
+
|
|
33
|
+
&:hover {
|
|
34
|
+
color: var(--color-text, #1a1a1a);
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
const ErrorCode = styled.pre `
|
|
38
|
+
background: var(--color-background, #ffffff);
|
|
39
|
+
border: 1px solid var(--color-border, #e5e7eb);
|
|
40
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
41
|
+
padding: var(--spacing-md, 1rem);
|
|
42
|
+
overflow-x: auto;
|
|
43
|
+
font-size: 0.75rem;
|
|
44
|
+
color: var(--color-text-secondary, #6b7280);
|
|
45
|
+
max-height: 300px;
|
|
46
|
+
overflow-y: auto;
|
|
47
|
+
`;
|
|
48
|
+
export class ErrorBoundary extends Component {
|
|
49
|
+
constructor(props) {
|
|
50
|
+
super(props);
|
|
51
|
+
this.handleReset = () => {
|
|
52
|
+
this.setState({
|
|
53
|
+
hasError: false,
|
|
54
|
+
error: null,
|
|
55
|
+
errorInfo: null,
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
this.state = {
|
|
59
|
+
hasError: false,
|
|
60
|
+
error: null,
|
|
61
|
+
errorInfo: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
static getDerivedStateFromError(error) {
|
|
65
|
+
return {
|
|
66
|
+
hasError: true,
|
|
67
|
+
error,
|
|
68
|
+
errorInfo: null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
componentDidCatch(error, errorInfo) {
|
|
72
|
+
// Log error to console and monitoring service
|
|
73
|
+
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
|
74
|
+
// TODO: Send to error monitoring service (e.g., Sentry)
|
|
75
|
+
// logErrorToService(error, errorInfo);
|
|
76
|
+
this.setState({
|
|
77
|
+
error,
|
|
78
|
+
errorInfo,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
render() {
|
|
82
|
+
var _a, _b, _c;
|
|
83
|
+
if (this.state.hasError) {
|
|
84
|
+
if (this.props.fallback) {
|
|
85
|
+
return this.props.fallback;
|
|
86
|
+
}
|
|
87
|
+
const isDevelopment = typeof process !== 'undefined' && ((_a = process.env) === null || _a === void 0 ? void 0 : _a.NODE_ENV) === 'development';
|
|
88
|
+
return (<Container>
|
|
89
|
+
<ErrorTitle>Something went wrong</ErrorTitle>
|
|
90
|
+
<ErrorMessage message={((_b = this.state.error) === null || _b === void 0 ? void 0 : _b.message) || 'An unexpected error occurred'} onRetry={this.handleReset}/>
|
|
91
|
+
|
|
92
|
+
{isDevelopment && this.state.errorInfo && (<ErrorDetails>
|
|
93
|
+
<ErrorSummary>Error Details (Development Only)</ErrorSummary>
|
|
94
|
+
<ErrorCode>
|
|
95
|
+
{(_c = this.state.error) === null || _c === void 0 ? void 0 : _c.toString()}
|
|
96
|
+
{this.state.errorInfo.componentStack}
|
|
97
|
+
</ErrorCode>
|
|
98
|
+
</ErrorDetails>)}
|
|
99
|
+
</Container>);
|
|
100
|
+
}
|
|
101
|
+
return this.props.children;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorMessage.d.ts","sourceRoot":"","sources":["../../../src/components/Common/ErrorMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,iBAAiB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAQpD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
export const ErrorMessage = ({ message, onRetry }) => {
|
|
4
|
+
return (_jsxs(Container, { children: [_jsx(Icon, { children: "\u26A0\uFE0F" }), _jsx(Message, { children: message }), onRetry && _jsx(RetryButton, { onClick: onRetry, children: "Try Again" })] }));
|
|
5
|
+
};
|
|
6
|
+
const Container = styled.div `
|
|
7
|
+
background-color: rgba(239, 68, 68, 0.1);
|
|
8
|
+
border: 1px solid var(--color-error, #dc3545);
|
|
9
|
+
border-radius: var(--border-radius-lg, 0.5rem);
|
|
10
|
+
padding: var(--spacing-xl, 1.5rem);
|
|
11
|
+
text-align: center;
|
|
12
|
+
`;
|
|
13
|
+
const Icon = styled.div `
|
|
14
|
+
font-size: 3rem;
|
|
15
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
16
|
+
`;
|
|
17
|
+
const Message = styled.p `
|
|
18
|
+
color: var(--color-error, #dc3545);
|
|
19
|
+
font-size: 1rem;
|
|
20
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
21
|
+
`;
|
|
22
|
+
const RetryButton = styled.button `
|
|
23
|
+
background-color: var(--color-error, #dc3545);
|
|
24
|
+
color: white;
|
|
25
|
+
padding: var(--spacing-sm, 0.5rem) var(--spacing-lg, 1.5rem);
|
|
26
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
27
|
+
font-weight: 600;
|
|
28
|
+
transition: all 0.3s ease;
|
|
29
|
+
border: none;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
|
|
32
|
+
&:hover {
|
|
33
|
+
transform: translateY(-2px);
|
|
34
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
export const ErrorMessage = ({ message, onRetry }) => {
|
|
4
|
+
return (<Container>
|
|
5
|
+
<Icon>⚠️</Icon>
|
|
6
|
+
<Message>{message}</Message>
|
|
7
|
+
{onRetry && <RetryButton onClick={onRetry}>Try Again</RetryButton>}
|
|
8
|
+
</Container>);
|
|
9
|
+
};
|
|
10
|
+
const Container = styled.div `
|
|
11
|
+
background-color: rgba(239, 68, 68, 0.1);
|
|
12
|
+
border: 1px solid var(--color-error, #dc3545);
|
|
13
|
+
border-radius: var(--border-radius-lg, 0.5rem);
|
|
14
|
+
padding: var(--spacing-xl, 1.5rem);
|
|
15
|
+
text-align: center;
|
|
16
|
+
`;
|
|
17
|
+
const Icon = styled.div `
|
|
18
|
+
font-size: 3rem;
|
|
19
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
20
|
+
`;
|
|
21
|
+
const Message = styled.p `
|
|
22
|
+
color: var(--color-error, #dc3545);
|
|
23
|
+
font-size: 1rem;
|
|
24
|
+
margin-bottom: var(--spacing-md, 1rem);
|
|
25
|
+
`;
|
|
26
|
+
const RetryButton = styled.button `
|
|
27
|
+
background-color: var(--color-error, #dc3545);
|
|
28
|
+
color: white;
|
|
29
|
+
padding: var(--spacing-sm, 0.5rem) var(--spacing-lg, 1.5rem);
|
|
30
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
transition: all 0.3s ease;
|
|
33
|
+
border: none;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
transform: translateY(-2px);
|
|
38
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Loading.d.ts","sourceRoot":"","sources":["../../../src/components/Common/Loading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAO1C,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styled, { keyframes } from 'styled-components';
|
|
3
|
+
export const Loading = ({ size = 'medium', text }) => {
|
|
4
|
+
return (_jsxs(Container, { children: [_jsx(Spinner, { size: size }), text && _jsx(LoadingText, { children: text })] }));
|
|
5
|
+
};
|
|
6
|
+
const spin = keyframes `
|
|
7
|
+
to { transform: rotate(360deg); }
|
|
8
|
+
`;
|
|
9
|
+
const Container = styled.div `
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
gap: var(--spacing-md, 1rem);
|
|
15
|
+
padding: var(--spacing-xl, 1.5rem);
|
|
16
|
+
`;
|
|
17
|
+
const Spinner = styled.div `
|
|
18
|
+
width: ${props => {
|
|
19
|
+
switch (props.size) {
|
|
20
|
+
case 'small': return '24px';
|
|
21
|
+
case 'large': return '64px';
|
|
22
|
+
default: return '48px';
|
|
23
|
+
}
|
|
24
|
+
}};
|
|
25
|
+
height: ${props => {
|
|
26
|
+
switch (props.size) {
|
|
27
|
+
case 'small': return '24px';
|
|
28
|
+
case 'large': return '64px';
|
|
29
|
+
default: return '48px';
|
|
30
|
+
}
|
|
31
|
+
}};
|
|
32
|
+
border: 4px solid var(--color-border, #e5e7eb);
|
|
33
|
+
border-top-color: var(--color-primary, #6a8102);
|
|
34
|
+
border-radius: 50%;
|
|
35
|
+
animation: ${spin} 1s linear infinite;
|
|
36
|
+
`;
|
|
37
|
+
const LoadingText = styled.p `
|
|
38
|
+
color: var(--color-text-secondary, #6b7280);
|
|
39
|
+
font-size: 1rem;
|
|
40
|
+
margin: 0;
|
|
41
|
+
`;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled, { keyframes } from 'styled-components';
|
|
3
|
+
export const Loading = ({ size = 'medium', text }) => {
|
|
4
|
+
return (<Container>
|
|
5
|
+
<Spinner size={size}/>
|
|
6
|
+
{text && <LoadingText>{text}</LoadingText>}
|
|
7
|
+
</Container>);
|
|
8
|
+
};
|
|
9
|
+
const spin = keyframes `
|
|
10
|
+
to { transform: rotate(360deg); }
|
|
11
|
+
`;
|
|
12
|
+
const Container = styled.div `
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
gap: var(--spacing-md, 1rem);
|
|
18
|
+
padding: var(--spacing-xl, 1.5rem);
|
|
19
|
+
`;
|
|
20
|
+
const Spinner = styled.div `
|
|
21
|
+
width: ${props => {
|
|
22
|
+
switch (props.size) {
|
|
23
|
+
case 'small': return '24px';
|
|
24
|
+
case 'large': return '64px';
|
|
25
|
+
default: return '48px';
|
|
26
|
+
}
|
|
27
|
+
}};
|
|
28
|
+
height: ${props => {
|
|
29
|
+
switch (props.size) {
|
|
30
|
+
case 'small': return '24px';
|
|
31
|
+
case 'large': return '64px';
|
|
32
|
+
default: return '48px';
|
|
33
|
+
}
|
|
34
|
+
}};
|
|
35
|
+
border: 4px solid var(--color-border, #e5e7eb);
|
|
36
|
+
border-top-color: var(--color-primary, #6a8102);
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
animation: ${spin} 1s linear infinite;
|
|
39
|
+
`;
|
|
40
|
+
const LoadingText = styled.p `
|
|
41
|
+
color: var(--color-text-secondary, #6b7280);
|
|
42
|
+
font-size: 1rem;
|
|
43
|
+
margin: 0;
|
|
44
|
+
`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loading Indicator Component
|
|
3
|
+
* Displays loading feedback appropriate to context
|
|
4
|
+
* Requirements: 5.2, 5.5
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
export type LoadingType = 'spinner' | 'progress' | 'dots';
|
|
8
|
+
export type LoadingSize = 'small' | 'medium' | 'large';
|
|
9
|
+
interface LoadingIndicatorProps {
|
|
10
|
+
type: LoadingType;
|
|
11
|
+
size?: LoadingSize;
|
|
12
|
+
message?: string;
|
|
13
|
+
progress?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const LoadingIndicator: React.FC<LoadingIndicatorProps>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=LoadingIndicator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../../src/components/Common/LoadingIndicator.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAC1D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvD,UAAU,qBAAqB;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAyFD,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAqC5D,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styled, { keyframes } from 'styled-components';
|
|
3
|
+
const spin = keyframes `
|
|
4
|
+
to { transform: rotate(360deg); }
|
|
5
|
+
`;
|
|
6
|
+
const dots = keyframes `
|
|
7
|
+
0%, 20% {
|
|
8
|
+
content: '.';
|
|
9
|
+
}
|
|
10
|
+
40% {
|
|
11
|
+
content: '..';
|
|
12
|
+
}
|
|
13
|
+
60%, 100% {
|
|
14
|
+
content: '...';
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
const Container = styled.div `
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
gap: var(--spacing-md, 1rem);
|
|
23
|
+
padding: var(--spacing-lg, 1.5rem);
|
|
24
|
+
`;
|
|
25
|
+
const Spinner = styled.div `
|
|
26
|
+
width: ${props => {
|
|
27
|
+
switch (props.size) {
|
|
28
|
+
case 'small': return '24px';
|
|
29
|
+
case 'large': return '64px';
|
|
30
|
+
default: return '48px';
|
|
31
|
+
}
|
|
32
|
+
}};
|
|
33
|
+
height: ${props => {
|
|
34
|
+
switch (props.size) {
|
|
35
|
+
case 'small': return '24px';
|
|
36
|
+
case 'large': return '64px';
|
|
37
|
+
default: return '48px';
|
|
38
|
+
}
|
|
39
|
+
}};
|
|
40
|
+
border: 4px solid var(--color-border, #e5e7eb);
|
|
41
|
+
border-top-color: var(--color-primary, #6a8102);
|
|
42
|
+
border-radius: 50%;
|
|
43
|
+
animation: ${spin} 1s linear infinite;
|
|
44
|
+
`;
|
|
45
|
+
const ProgressBarContainer = styled.div `
|
|
46
|
+
width: 100%;
|
|
47
|
+
max-width: 300px;
|
|
48
|
+
height: 8px;
|
|
49
|
+
background: var(--color-border, #e5e7eb);
|
|
50
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
`;
|
|
53
|
+
const ProgressBar = styled.div `
|
|
54
|
+
height: 100%;
|
|
55
|
+
width: ${props => props.progress}%;
|
|
56
|
+
background: linear-gradient(90deg, var(--color-primary, #6a8102), var(--color-secondary, #ffc107));
|
|
57
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
58
|
+
transition: width 0.3s ease;
|
|
59
|
+
`;
|
|
60
|
+
const Dots = styled.div `
|
|
61
|
+
font-size: 1.5rem;
|
|
62
|
+
color: var(--color-primary, #6a8102);
|
|
63
|
+
|
|
64
|
+
&::after {
|
|
65
|
+
content: '.';
|
|
66
|
+
animation: ${dots} 1.5s steps(4, end) infinite;
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
const Message = styled.p `
|
|
70
|
+
color: var(--color-text-secondary, #6b7280);
|
|
71
|
+
font-size: 0.875rem;
|
|
72
|
+
margin: 0;
|
|
73
|
+
text-align: center;
|
|
74
|
+
`;
|
|
75
|
+
const ProgressText = styled.span `
|
|
76
|
+
color: var(--color-text-secondary, #6b7280);
|
|
77
|
+
font-size: 0.75rem;
|
|
78
|
+
font-weight: 600;
|
|
79
|
+
margin-top: var(--spacing-xs, 0.25rem);
|
|
80
|
+
`;
|
|
81
|
+
export const LoadingIndicator = ({ type, size = 'medium', message, progress, }) => {
|
|
82
|
+
const renderIndicator = () => {
|
|
83
|
+
switch (type) {
|
|
84
|
+
case 'spinner':
|
|
85
|
+
return _jsx(Spinner, { size: size });
|
|
86
|
+
case 'progress':
|
|
87
|
+
return (_jsxs("div", { style: { width: '100%', maxWidth: '300px' }, children: [_jsx(ProgressBarContainer, { children: _jsx(ProgressBar, { progress: progress || 0 }) }), progress !== undefined && (_jsxs(ProgressText, { children: [Math.round(progress), "%"] }))] }));
|
|
88
|
+
case 'dots':
|
|
89
|
+
return _jsx(Dots, {});
|
|
90
|
+
default:
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
return (_jsxs(Container, { children: [renderIndicator(), message && _jsx(Message, { children: message })] }));
|
|
95
|
+
};
|