pi-kiosk-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 +85 -0
- package/dist/api.d.ts +20 -0
- package/dist/api.js +57 -0
- package/dist/components/ErrorDisplay.d.ts +12 -0
- package/dist/components/ErrorDisplay.js +8 -0
- package/dist/components/LoadingSpinner.d.ts +8 -0
- package/dist/components/LoadingSpinner.js +10 -0
- package/dist/config/deployment.d.ts +96 -0
- package/dist/config/deployment.js +153 -0
- package/dist/config/environments.d.ts +13 -0
- package/dist/config/environments.js +29 -0
- package/dist/config/environments.test.d.ts +1 -0
- package/dist/config/environments.test.js +49 -0
- package/dist/config/logger.d.ts +40 -0
- package/dist/config/logger.js +117 -0
- package/dist/constants.d.ts +66 -0
- package/dist/constants.js +85 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.js +73 -0
- package/dist/hooks/useApi.d.ts +20 -0
- package/dist/hooks/useApi.js +51 -0
- package/dist/hooks/useAsyncOperation.d.ts +21 -0
- package/dist/hooks/useAsyncOperation.js +58 -0
- package/dist/hooks/useErrorHandler.d.ts +14 -0
- package/dist/hooks/useErrorHandler.js +54 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +15 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +2 -0
- package/dist/utils/simple.test.d.ts +1 -0
- package/dist/utils/simple.test.js +45 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.js +40 -0
- package/dist/validation.d.ts +31 -0
- package/dist/validation.js +113 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @pi-kiosk/shared
|
|
2
|
+
|
|
3
|
+
Shared components, utilities, and types for the Pi Kiosk system.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pi-kiosk/shared
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Components
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { ErrorDisplay, LoadingSpinner } from '@pi-kiosk/shared';
|
|
17
|
+
|
|
18
|
+
function MyComponent() {
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
<LoadingSpinner />
|
|
22
|
+
<ErrorDisplay error={new Error('Something went wrong')} />
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Types
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { Product, ApiResponse, KioskStatus } from '@pi-kiosk/shared';
|
|
32
|
+
|
|
33
|
+
const product: Product = {
|
|
34
|
+
id: 1,
|
|
35
|
+
name: 'Coffee',
|
|
36
|
+
price: 25.0,
|
|
37
|
+
description: 'Fresh coffee',
|
|
38
|
+
image: '☕',
|
|
39
|
+
quantityInStock: 10,
|
|
40
|
+
clickedOn: 0,
|
|
41
|
+
numberOfPurchases: 0,
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Hooks
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { useApi, useErrorHandler } from '@pi-kiosk/shared';
|
|
49
|
+
|
|
50
|
+
function MyComponent() {
|
|
51
|
+
const api = useApi();
|
|
52
|
+
const { handleError } = useErrorHandler();
|
|
53
|
+
|
|
54
|
+
// Use the hooks...
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Utilities
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { formatPrice, validateEmail } from '@pi-kiosk/shared';
|
|
62
|
+
|
|
63
|
+
const price = formatPrice(25.5); // "25,50 Kč"
|
|
64
|
+
const isValid = validateEmail('user@example.com'); // true
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Development
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Install dependencies
|
|
71
|
+
npm install
|
|
72
|
+
|
|
73
|
+
# Build the package
|
|
74
|
+
npm run build
|
|
75
|
+
|
|
76
|
+
# Run tests
|
|
77
|
+
npm test
|
|
78
|
+
|
|
79
|
+
# Watch mode for development
|
|
80
|
+
npm run dev
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const API_ENDPOINTS: {
|
|
2
|
+
readonly PRODUCTS: "/api/products";
|
|
3
|
+
readonly PRODUCT_CLICK: "/api/products/:id/click";
|
|
4
|
+
readonly ADMIN_LOGIN: "/admin/login";
|
|
5
|
+
readonly ADMIN_PRODUCTS: "/admin/products";
|
|
6
|
+
readonly ADMIN_KIOSKS: "/admin/kiosks";
|
|
7
|
+
readonly HEALTH: "/health";
|
|
8
|
+
readonly CHECK_TRANSACTIONS: "/api/check-new-transactions";
|
|
9
|
+
};
|
|
10
|
+
export declare class APIClient {
|
|
11
|
+
private baseUrl;
|
|
12
|
+
private kioskSecret?;
|
|
13
|
+
constructor(baseUrl: string, kioskSecret?: string);
|
|
14
|
+
private request;
|
|
15
|
+
get<T>(endpoint: string): Promise<T>;
|
|
16
|
+
post<T>(endpoint: string, data?: any): Promise<T>;
|
|
17
|
+
put<T>(endpoint: string, data?: any): Promise<T>;
|
|
18
|
+
delete<T>(endpoint: string): Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
export declare const createAPIClient: (baseUrl?: string, kioskSecret?: string) => APIClient;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Shared API utilities
|
|
2
|
+
export const API_ENDPOINTS = {
|
|
3
|
+
PRODUCTS: '/api/products',
|
|
4
|
+
PRODUCT_CLICK: '/api/products/:id/click',
|
|
5
|
+
ADMIN_LOGIN: '/admin/login',
|
|
6
|
+
ADMIN_PRODUCTS: '/admin/products',
|
|
7
|
+
ADMIN_KIOSKS: '/admin/kiosks',
|
|
8
|
+
HEALTH: '/health',
|
|
9
|
+
CHECK_TRANSACTIONS: '/api/check-new-transactions'
|
|
10
|
+
};
|
|
11
|
+
export class APIClient {
|
|
12
|
+
constructor(baseUrl, kioskSecret) {
|
|
13
|
+
this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
14
|
+
this.kioskSecret = kioskSecret;
|
|
15
|
+
}
|
|
16
|
+
async request(endpoint, options = {}) {
|
|
17
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
18
|
+
const headers = {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
...(options.headers || {}),
|
|
21
|
+
};
|
|
22
|
+
// Add kiosk secret if available
|
|
23
|
+
if (this.kioskSecret) {
|
|
24
|
+
headers['X-Kiosk-Secret'] = this.kioskSecret;
|
|
25
|
+
}
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
...options,
|
|
28
|
+
headers,
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
32
|
+
}
|
|
33
|
+
return response.json();
|
|
34
|
+
}
|
|
35
|
+
async get(endpoint) {
|
|
36
|
+
return this.request(endpoint, { method: 'GET' });
|
|
37
|
+
}
|
|
38
|
+
async post(endpoint, data) {
|
|
39
|
+
return this.request(endpoint, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
body: data ? JSON.stringify(data) : undefined,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async put(endpoint, data) {
|
|
45
|
+
return this.request(endpoint, {
|
|
46
|
+
method: 'PUT',
|
|
47
|
+
body: data ? JSON.stringify(data) : undefined,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async delete(endpoint) {
|
|
51
|
+
return this.request(endpoint, { method: 'DELETE' });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export const createAPIClient = (baseUrl, kioskSecret) => {
|
|
55
|
+
const url = baseUrl || process.env.REACT_APP_API_URL || 'http://localhost:3015';
|
|
56
|
+
return new APIClient(url, kioskSecret);
|
|
57
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AppError } from '../errors';
|
|
2
|
+
interface ErrorDisplayProps {
|
|
3
|
+
error: Error | AppError;
|
|
4
|
+
onRetry?: () => void;
|
|
5
|
+
onDismiss?: () => void;
|
|
6
|
+
className?: string;
|
|
7
|
+
showDetails?: boolean;
|
|
8
|
+
retryLabel?: string;
|
|
9
|
+
dismissLabel?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function ErrorDisplay({ error, onRetry, onDismiss, className, showDetails, retryLabel, dismissLabel }: ErrorDisplayProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { getErrorMessage } from '../errors';
|
|
3
|
+
import { CSS_CLASSES } from '../constants';
|
|
4
|
+
export function ErrorDisplay({ error, onRetry, onDismiss, className = '', showDetails = false, retryLabel = 'Zkusit znovu', dismissLabel = 'Zavřít' }) {
|
|
5
|
+
const userMessage = getErrorMessage(error);
|
|
6
|
+
const isNetworkError = error.name === 'NetworkError';
|
|
7
|
+
return (_jsx("div", { className: `error-display ${CSS_CLASSES.ERROR} ${className}`, role: "alert", "aria-live": "assertive", children: _jsxs("div", { className: "error-content", children: [_jsx("div", { className: "error-icon", "aria-hidden": "true", children: isNetworkError ? '🌐' : '❌' }), _jsxs("div", { className: "error-text", children: [_jsx("h3", { className: "error-title", children: isNetworkError ? 'Problém s připojením' : 'Došlo k chybě' }), _jsx("p", { className: "error-message", children: userMessage })] }), showDetails && process.env.NODE_ENV === 'development' && (_jsxs("details", { className: "error-details", children: [_jsx("summary", { children: "Technick\u00E9 detaily" }), _jsx("pre", { className: "error-stack", children: _jsx("code", { children: error.stack || error.message }) })] })), _jsxs("div", { className: "error-actions", children: [onRetry && (_jsxs("button", { onClick: onRetry, className: `retry-btn ${CSS_CLASSES.BUTTON_PRIMARY}`, type: "button", children: ["\uD83D\uDD04 ", retryLabel] })), onDismiss && (_jsxs("button", { onClick: onDismiss, className: `dismiss-btn ${CSS_CLASSES.BUTTON_SECONDARY}`, type: "button", children: ["\u2715 ", dismissLabel] }))] })] }) }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface LoadingSpinnerProps {
|
|
2
|
+
size?: 'small' | 'medium' | 'large';
|
|
3
|
+
message?: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
'aria-label'?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function LoadingSpinner({ size, message, className, 'aria-label': ariaLabel }: LoadingSpinnerProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CSS_CLASSES } from '../constants';
|
|
3
|
+
export function LoadingSpinner({ size = 'medium', message, className = '', 'aria-label': ariaLabel = 'Loading' }) {
|
|
4
|
+
const sizeClass = {
|
|
5
|
+
small: 'spinner-small',
|
|
6
|
+
medium: 'spinner-medium',
|
|
7
|
+
large: 'spinner-large'
|
|
8
|
+
}[size];
|
|
9
|
+
return (_jsxs("div", { className: `loading-container ${CSS_CLASSES.LOADING} ${className}`, children: [_jsx("div", { className: `spinner ${sizeClass}`, role: "status", "aria-label": ariaLabel, children: _jsx("span", { className: "sr-only", children: ariaLabel }) }), message && (_jsx("p", { className: "loading-message", "aria-live": "polite", children: message }))] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface DeploymentConfig {
|
|
2
|
+
railway: {
|
|
3
|
+
testService: string;
|
|
4
|
+
stagingService: string;
|
|
5
|
+
productionService: string;
|
|
6
|
+
};
|
|
7
|
+
environmentVariables: {
|
|
8
|
+
test: Record<string, string>;
|
|
9
|
+
staging: Record<string, string>;
|
|
10
|
+
production: Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
database: {
|
|
13
|
+
test: string;
|
|
14
|
+
staging: string;
|
|
15
|
+
production: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare const deploymentConfig: DeploymentConfig;
|
|
19
|
+
export declare const railwayConfigs: {
|
|
20
|
+
test: {
|
|
21
|
+
build: {
|
|
22
|
+
builder: string;
|
|
23
|
+
};
|
|
24
|
+
deploy: {
|
|
25
|
+
healthcheckPath: string;
|
|
26
|
+
healthcheckTimeout: number;
|
|
27
|
+
restartPolicyType: string;
|
|
28
|
+
};
|
|
29
|
+
services: ({
|
|
30
|
+
name: string;
|
|
31
|
+
source: string;
|
|
32
|
+
env: {
|
|
33
|
+
PORT: string;
|
|
34
|
+
FIO_API_MODE: string;
|
|
35
|
+
};
|
|
36
|
+
buildCommand?: undefined;
|
|
37
|
+
startCommand?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
name: string;
|
|
40
|
+
source: string;
|
|
41
|
+
buildCommand: string;
|
|
42
|
+
startCommand: string;
|
|
43
|
+
env: Record<string, string>;
|
|
44
|
+
})[];
|
|
45
|
+
};
|
|
46
|
+
staging: {
|
|
47
|
+
build: {
|
|
48
|
+
builder: string;
|
|
49
|
+
};
|
|
50
|
+
deploy: {
|
|
51
|
+
healthcheckPath: string;
|
|
52
|
+
healthcheckTimeout: number;
|
|
53
|
+
restartPolicyType: string;
|
|
54
|
+
};
|
|
55
|
+
services: ({
|
|
56
|
+
name: string;
|
|
57
|
+
source: string;
|
|
58
|
+
env: {
|
|
59
|
+
PORT: string;
|
|
60
|
+
};
|
|
61
|
+
buildCommand?: undefined;
|
|
62
|
+
startCommand?: undefined;
|
|
63
|
+
} | {
|
|
64
|
+
name: string;
|
|
65
|
+
source: string;
|
|
66
|
+
buildCommand: string;
|
|
67
|
+
startCommand: string;
|
|
68
|
+
env: Record<string, string>;
|
|
69
|
+
})[];
|
|
70
|
+
};
|
|
71
|
+
production: {
|
|
72
|
+
build: {
|
|
73
|
+
builder: string;
|
|
74
|
+
};
|
|
75
|
+
deploy: {
|
|
76
|
+
healthcheckPath: string;
|
|
77
|
+
healthcheckTimeout: number;
|
|
78
|
+
restartPolicyType: string;
|
|
79
|
+
};
|
|
80
|
+
services: ({
|
|
81
|
+
name: string;
|
|
82
|
+
source: string;
|
|
83
|
+
env: {
|
|
84
|
+
PORT: string;
|
|
85
|
+
};
|
|
86
|
+
buildCommand?: undefined;
|
|
87
|
+
startCommand?: undefined;
|
|
88
|
+
} | {
|
|
89
|
+
name: string;
|
|
90
|
+
source: string;
|
|
91
|
+
buildCommand: string;
|
|
92
|
+
startCommand: string;
|
|
93
|
+
env: Record<string, string>;
|
|
94
|
+
})[];
|
|
95
|
+
};
|
|
96
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Deployment configuration for different modes and platforms
|
|
2
|
+
export const deploymentConfig = {
|
|
3
|
+
railway: {
|
|
4
|
+
testService: 'kiosk-test',
|
|
5
|
+
stagingService: 'kiosk-staging',
|
|
6
|
+
productionService: 'kiosk-production'
|
|
7
|
+
},
|
|
8
|
+
environmentVariables: {
|
|
9
|
+
test: {
|
|
10
|
+
'NODE_ENV': 'test',
|
|
11
|
+
'REACT_APP_ENVIRONMENT': 'test',
|
|
12
|
+
'REACT_APP_API_URL': 'https://kiosk-test.railway.app',
|
|
13
|
+
'REACT_APP_WS_URL': 'wss://kiosk-test.railway.app',
|
|
14
|
+
'REACT_APP_PAYMENT_MODE': 'mock',
|
|
15
|
+
'REACT_APP_ENABLE_MOCK_PAYMENTS': 'true',
|
|
16
|
+
'REACT_APP_SHOW_DEBUG_INFO': 'true',
|
|
17
|
+
'REACT_APP_LOG_LEVEL': 'debug'
|
|
18
|
+
},
|
|
19
|
+
staging: {
|
|
20
|
+
'NODE_ENV': 'production',
|
|
21
|
+
'REACT_APP_ENVIRONMENT': 'staging',
|
|
22
|
+
'REACT_APP_API_URL': 'https://kiosk-staging.railway.app',
|
|
23
|
+
'REACT_APP_WS_URL': 'wss://kiosk-staging.railway.app',
|
|
24
|
+
'REACT_APP_PAYMENT_MODE': 'sandbox',
|
|
25
|
+
'REACT_APP_ENABLE_MOCK_PAYMENTS': 'false',
|
|
26
|
+
'REACT_APP_SHOW_DEBUG_INFO': 'false',
|
|
27
|
+
'REACT_APP_LOG_LEVEL': 'info',
|
|
28
|
+
'REACT_APP_ENABLE_ERROR_REPORTING': 'true'
|
|
29
|
+
},
|
|
30
|
+
production: {
|
|
31
|
+
'NODE_ENV': 'production',
|
|
32
|
+
'REACT_APP_ENVIRONMENT': 'production',
|
|
33
|
+
'REACT_APP_API_URL': 'https://kiosk-prod.railway.app',
|
|
34
|
+
'REACT_APP_WS_URL': 'wss://kiosk-prod.railway.app',
|
|
35
|
+
'REACT_APP_PAYMENT_MODE': 'production',
|
|
36
|
+
'REACT_APP_ENABLE_MOCK_PAYMENTS': 'false',
|
|
37
|
+
'REACT_APP_SHOW_DEBUG_INFO': 'false',
|
|
38
|
+
'REACT_APP_LOG_LEVEL': 'warn',
|
|
39
|
+
'REACT_APP_ENABLE_ERROR_REPORTING': 'true'
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
database: {
|
|
43
|
+
test: 'postgresql://test_user:test_pass@localhost:5432/kiosk_test',
|
|
44
|
+
staging: '${DATABASE_URL}', // Railway provides this
|
|
45
|
+
production: '${DATABASE_URL}' // Railway provides this
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
// Railway deployment configuration files
|
|
49
|
+
export const railwayConfigs = {
|
|
50
|
+
test: {
|
|
51
|
+
build: {
|
|
52
|
+
builder: 'NIXPACKS'
|
|
53
|
+
},
|
|
54
|
+
deploy: {
|
|
55
|
+
healthcheckPath: '/health',
|
|
56
|
+
healthcheckTimeout: 300,
|
|
57
|
+
restartPolicyType: 'ON_FAILURE'
|
|
58
|
+
},
|
|
59
|
+
services: [
|
|
60
|
+
{
|
|
61
|
+
name: 'kiosk-backend-test',
|
|
62
|
+
source: './packages/backend',
|
|
63
|
+
env: {
|
|
64
|
+
...deploymentConfig.environmentVariables.test,
|
|
65
|
+
PORT: '3000',
|
|
66
|
+
FIO_API_MODE: 'test'
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'kiosk-frontend-test',
|
|
71
|
+
source: './packages/kiosk-app',
|
|
72
|
+
buildCommand: 'npm run build',
|
|
73
|
+
startCommand: 'npx serve -s dist -l 5173',
|
|
74
|
+
env: deploymentConfig.environmentVariables.test
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'admin-frontend-test',
|
|
78
|
+
source: './packages/admin-app',
|
|
79
|
+
buildCommand: 'npm run build',
|
|
80
|
+
startCommand: 'npx serve -s dist -l 5174',
|
|
81
|
+
env: deploymentConfig.environmentVariables.test
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
staging: {
|
|
86
|
+
build: {
|
|
87
|
+
builder: 'NIXPACKS'
|
|
88
|
+
},
|
|
89
|
+
deploy: {
|
|
90
|
+
healthcheckPath: '/health',
|
|
91
|
+
healthcheckTimeout: 300,
|
|
92
|
+
restartPolicyType: 'ON_FAILURE'
|
|
93
|
+
},
|
|
94
|
+
services: [
|
|
95
|
+
{
|
|
96
|
+
name: 'kiosk-backend-staging',
|
|
97
|
+
source: './packages/backend',
|
|
98
|
+
env: {
|
|
99
|
+
...deploymentConfig.environmentVariables.staging,
|
|
100
|
+
PORT: '3000'
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'kiosk-frontend-staging',
|
|
105
|
+
source: './packages/kiosk-app',
|
|
106
|
+
buildCommand: 'npm run build',
|
|
107
|
+
startCommand: 'npx serve -s dist -l 5173',
|
|
108
|
+
env: deploymentConfig.environmentVariables.staging
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'admin-frontend-staging',
|
|
112
|
+
source: './packages/admin-app',
|
|
113
|
+
buildCommand: 'npm run build',
|
|
114
|
+
startCommand: 'npx serve -s dist -l 5174',
|
|
115
|
+
env: deploymentConfig.environmentVariables.staging
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
production: {
|
|
120
|
+
build: {
|
|
121
|
+
builder: 'NIXPACKS'
|
|
122
|
+
},
|
|
123
|
+
deploy: {
|
|
124
|
+
healthcheckPath: '/health',
|
|
125
|
+
healthcheckTimeout: 300,
|
|
126
|
+
restartPolicyType: 'ON_FAILURE'
|
|
127
|
+
},
|
|
128
|
+
services: [
|
|
129
|
+
{
|
|
130
|
+
name: 'kiosk-backend-prod',
|
|
131
|
+
source: './packages/backend',
|
|
132
|
+
env: {
|
|
133
|
+
...deploymentConfig.environmentVariables.production,
|
|
134
|
+
PORT: '3000'
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'kiosk-frontend-prod',
|
|
139
|
+
source: './packages/kiosk-app',
|
|
140
|
+
buildCommand: 'npm run build',
|
|
141
|
+
startCommand: 'npx serve -s dist -l 5173',
|
|
142
|
+
env: deploymentConfig.environmentVariables.production
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'admin-frontend-prod',
|
|
146
|
+
source: './packages/admin-app',
|
|
147
|
+
buildCommand: 'npm run build',
|
|
148
|
+
startCommand: 'npx serve -s dist -l 5174',
|
|
149
|
+
env: deploymentConfig.environmentVariables.production
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Environment = 'development' | 'production';
|
|
2
|
+
export interface EnvironmentConfig {
|
|
3
|
+
apiUrl: string;
|
|
4
|
+
wsUrl: string;
|
|
5
|
+
enableMockPayments: boolean;
|
|
6
|
+
paymentAccountNumber: string;
|
|
7
|
+
showDebugInfo: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const environments: Record<Environment, EnvironmentConfig>;
|
|
10
|
+
export declare const getCurrentEnvironment: () => Environment;
|
|
11
|
+
export declare const getEnvironmentConfig: () => EnvironmentConfig;
|
|
12
|
+
export declare const isDevelopment: () => boolean;
|
|
13
|
+
export declare const isProduction: () => boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Simple environment configuration
|
|
2
|
+
export const environments = {
|
|
3
|
+
development: {
|
|
4
|
+
apiUrl: 'http://localhost:3015',
|
|
5
|
+
wsUrl: 'ws://localhost:3015',
|
|
6
|
+
enableMockPayments: true,
|
|
7
|
+
paymentAccountNumber: '1234567890',
|
|
8
|
+
showDebugInfo: true,
|
|
9
|
+
},
|
|
10
|
+
production: {
|
|
11
|
+
apiUrl: process.env.REACT_APP_API_URL || 'https://kiosk-prod.railway.app',
|
|
12
|
+
wsUrl: process.env.REACT_APP_WS_URL || 'wss://kiosk-prod.railway.app',
|
|
13
|
+
enableMockPayments: false,
|
|
14
|
+
paymentAccountNumber: process.env.REACT_APP_PAYMENT_ACCOUNT || '1234567890',
|
|
15
|
+
showDebugInfo: false,
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
// Simple environment detection
|
|
19
|
+
export const getCurrentEnvironment = () => {
|
|
20
|
+
return process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
|
21
|
+
};
|
|
22
|
+
// Get current environment configuration
|
|
23
|
+
export const getEnvironmentConfig = () => {
|
|
24
|
+
const env = getCurrentEnvironment();
|
|
25
|
+
return environments[env];
|
|
26
|
+
};
|
|
27
|
+
// Simple environment checks
|
|
28
|
+
export const isDevelopment = () => getCurrentEnvironment() === 'development';
|
|
29
|
+
export const isProduction = () => getCurrentEnvironment() === 'production';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Tests for environment configuration
|
|
2
|
+
import { getCurrentEnvironment, getEnvironmentConfig, isDevelopment, isProduction } from './environments';
|
|
3
|
+
describe('Environment Configuration', () => {
|
|
4
|
+
const originalEnv = process.env.NODE_ENV;
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
process.env.NODE_ENV = originalEnv;
|
|
7
|
+
});
|
|
8
|
+
describe('getCurrentEnvironment', () => {
|
|
9
|
+
test('returns development for non-production environments', () => {
|
|
10
|
+
process.env.NODE_ENV = 'development';
|
|
11
|
+
expect(getCurrentEnvironment()).toBe('development');
|
|
12
|
+
process.env.NODE_ENV = 'test';
|
|
13
|
+
expect(getCurrentEnvironment()).toBe('development');
|
|
14
|
+
delete process.env.NODE_ENV;
|
|
15
|
+
expect(getCurrentEnvironment()).toBe('development');
|
|
16
|
+
});
|
|
17
|
+
test('returns production for production environment', () => {
|
|
18
|
+
process.env.NODE_ENV = 'production';
|
|
19
|
+
expect(getCurrentEnvironment()).toBe('production');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('getEnvironmentConfig', () => {
|
|
23
|
+
test('returns development config by default', () => {
|
|
24
|
+
process.env.NODE_ENV = 'development';
|
|
25
|
+
const config = getEnvironmentConfig();
|
|
26
|
+
expect(config.apiUrl).toBe('http://localhost:3015');
|
|
27
|
+
expect(config.enableMockPayments).toBe(true);
|
|
28
|
+
expect(config.showDebugInfo).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
test('returns production config for production', () => {
|
|
31
|
+
process.env.NODE_ENV = 'production';
|
|
32
|
+
const config = getEnvironmentConfig();
|
|
33
|
+
expect(config.enableMockPayments).toBe(false);
|
|
34
|
+
expect(config.showDebugInfo).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('environment checks', () => {
|
|
38
|
+
test('isDevelopment works correctly', () => {
|
|
39
|
+
process.env.NODE_ENV = 'development';
|
|
40
|
+
expect(isDevelopment()).toBe(true);
|
|
41
|
+
expect(isProduction()).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
test('isProduction works correctly', () => {
|
|
44
|
+
process.env.NODE_ENV = 'production';
|
|
45
|
+
expect(isDevelopment()).toBe(false);
|
|
46
|
+
expect(isProduction()).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
+
export interface LogEntry {
|
|
3
|
+
level: LogLevel;
|
|
4
|
+
message: string;
|
|
5
|
+
context?: string;
|
|
6
|
+
data?: any;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
environment: string;
|
|
9
|
+
sessionId: string;
|
|
10
|
+
}
|
|
11
|
+
declare class Logger {
|
|
12
|
+
private sessionId;
|
|
13
|
+
private config;
|
|
14
|
+
constructor();
|
|
15
|
+
private shouldLog;
|
|
16
|
+
private formatMessage;
|
|
17
|
+
private log;
|
|
18
|
+
private sendToExternalService;
|
|
19
|
+
private storeLogEntry;
|
|
20
|
+
debug(message: string, context?: string, data?: any): void;
|
|
21
|
+
info(message: string, context?: string, data?: any): void;
|
|
22
|
+
warn(message: string, context?: string, data?: any): void;
|
|
23
|
+
error(message: string, context?: string, data?: any): void;
|
|
24
|
+
kioskAction(action: string, kioskId: number, data?: any): void;
|
|
25
|
+
paymentEvent(event: string, paymentId: string, data?: any): void;
|
|
26
|
+
apiCall(method: string, endpoint: string, duration?: number, error?: Error): void;
|
|
27
|
+
getLogs(): LogEntry[];
|
|
28
|
+
clearLogs(): void;
|
|
29
|
+
}
|
|
30
|
+
export declare const logger: Logger;
|
|
31
|
+
export declare const log: {
|
|
32
|
+
debug: (message: string, context?: string, data?: any) => void;
|
|
33
|
+
info: (message: string, context?: string, data?: any) => void;
|
|
34
|
+
warn: (message: string, context?: string, data?: any) => void;
|
|
35
|
+
error: (message: string, context?: string, data?: any) => void;
|
|
36
|
+
kioskAction: (action: string, kioskId: number, data?: any) => void;
|
|
37
|
+
paymentEvent: (event: string, paymentId: string, data?: any) => void;
|
|
38
|
+
apiCall: (method: string, endpoint: string, duration?: number, error?: Error) => void;
|
|
39
|
+
};
|
|
40
|
+
export {};
|