pi-kiosk-shared 1.0.9 → 1.0.11
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/dist/api.js +5 -10
- package/dist/components/ErrorDisplay.js +6 -9
- package/dist/components/LoadingSpinner.js +4 -7
- package/dist/config/deployment.js +11 -14
- package/dist/config/environments.js +22 -36
- package/dist/config/logger.js +12 -15
- package/dist/constants.d.ts +8 -0
- package/dist/constants.js +13 -7
- package/dist/errors.js +7 -17
- package/dist/hooks/useApi.js +8 -11
- package/dist/hooks/useAsyncOperation.js +8 -11
- package/dist/hooks/useErrorHandler.js +9 -12
- package/dist/index.js +13 -29
- package/dist/types.js +1 -2
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +19 -18
- package/dist/validation.js +22 -26
- package/package.json +5 -2
package/dist/api.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Shared API utilities
|
|
3
|
-
|
|
4
|
-
exports.createAPIClient = exports.APIClient = exports.API_ENDPOINTS = void 0;
|
|
5
|
-
exports.API_ENDPOINTS = {
|
|
2
|
+
export const API_ENDPOINTS = {
|
|
6
3
|
PRODUCTS: '/api/products',
|
|
7
4
|
PRODUCT_CLICK: '/api/products/:id/click',
|
|
8
5
|
ADMIN_LOGIN: '/admin/login',
|
|
@@ -11,7 +8,7 @@ exports.API_ENDPOINTS = {
|
|
|
11
8
|
HEALTH: '/health',
|
|
12
9
|
CHECK_TRANSACTIONS: '/api/check-new-transactions'
|
|
13
10
|
};
|
|
14
|
-
class APIClient {
|
|
11
|
+
export class APIClient {
|
|
15
12
|
constructor(baseUrl, kioskSecret) {
|
|
16
13
|
this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
17
14
|
this.kioskSecret = kioskSecret;
|
|
@@ -54,10 +51,8 @@ class APIClient {
|
|
|
54
51
|
return this.request(endpoint, { method: 'DELETE' });
|
|
55
52
|
}
|
|
56
53
|
}
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const url = baseUrl || (0, environments_1.getEnvironmentConfig)().apiUrl;
|
|
54
|
+
import { getEnvironmentConfig } from './config/environments';
|
|
55
|
+
export const createAPIClient = (baseUrl, kioskSecret) => {
|
|
56
|
+
const url = baseUrl || getEnvironmentConfig().apiUrl;
|
|
61
57
|
return new APIClient(url, kioskSecret);
|
|
62
58
|
};
|
|
63
|
-
exports.createAPIClient = createAPIClient;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const constants_1 = require("../constants");
|
|
7
|
-
function ErrorDisplay({ error, onRetry, onDismiss, className = '', showDetails = false, retryLabel = 'Zkusit znovu', dismissLabel = 'Zavřít' }) {
|
|
8
|
-
const userMessage = (0, errors_1.getErrorMessage)(error);
|
|
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);
|
|
9
6
|
const isNetworkError = error.name === 'NetworkError';
|
|
10
|
-
return ((
|
|
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] }))] })] }) }));
|
|
11
8
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const constants_1 = require("../constants");
|
|
6
|
-
function LoadingSpinner({ size = 'medium', message, className = '', 'aria-label': ariaLabel = 'Loading' }) {
|
|
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' }) {
|
|
7
4
|
const sizeClass = {
|
|
8
5
|
small: 'spinner-small',
|
|
9
6
|
medium: 'spinner-medium',
|
|
10
7
|
large: 'spinner-large'
|
|
11
8
|
}[size];
|
|
12
|
-
return ((
|
|
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 }))] }));
|
|
13
10
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Deployment configuration for different modes and platforms
|
|
3
|
-
|
|
4
|
-
exports.railwayConfigs = exports.deploymentConfig = void 0;
|
|
5
|
-
exports.deploymentConfig = {
|
|
2
|
+
export const deploymentConfig = {
|
|
6
3
|
railway: {
|
|
7
4
|
testService: 'kiosk-test',
|
|
8
5
|
stagingService: 'kiosk-staging',
|
|
@@ -49,7 +46,7 @@ exports.deploymentConfig = {
|
|
|
49
46
|
}
|
|
50
47
|
};
|
|
51
48
|
// Railway deployment configuration files
|
|
52
|
-
|
|
49
|
+
export const railwayConfigs = {
|
|
53
50
|
test: {
|
|
54
51
|
build: {
|
|
55
52
|
builder: 'NIXPACKS'
|
|
@@ -64,7 +61,7 @@ exports.railwayConfigs = {
|
|
|
64
61
|
name: 'kiosk-backend-test',
|
|
65
62
|
source: './packages/backend',
|
|
66
63
|
env: {
|
|
67
|
-
...
|
|
64
|
+
...deploymentConfig.environmentVariables.test,
|
|
68
65
|
PORT: '3000',
|
|
69
66
|
FIO_API_MODE: 'test'
|
|
70
67
|
}
|
|
@@ -74,14 +71,14 @@ exports.railwayConfigs = {
|
|
|
74
71
|
source: './packages/kiosk-app',
|
|
75
72
|
buildCommand: 'npm run build',
|
|
76
73
|
startCommand: 'npx serve -s dist -l 5173',
|
|
77
|
-
env:
|
|
74
|
+
env: deploymentConfig.environmentVariables.test
|
|
78
75
|
},
|
|
79
76
|
{
|
|
80
77
|
name: 'admin-frontend-test',
|
|
81
78
|
source: './packages/admin-app',
|
|
82
79
|
buildCommand: 'npm run build',
|
|
83
80
|
startCommand: 'npx serve -s dist -l 5174',
|
|
84
|
-
env:
|
|
81
|
+
env: deploymentConfig.environmentVariables.test
|
|
85
82
|
}
|
|
86
83
|
]
|
|
87
84
|
},
|
|
@@ -99,7 +96,7 @@ exports.railwayConfigs = {
|
|
|
99
96
|
name: 'kiosk-backend-staging',
|
|
100
97
|
source: './packages/backend',
|
|
101
98
|
env: {
|
|
102
|
-
...
|
|
99
|
+
...deploymentConfig.environmentVariables.staging,
|
|
103
100
|
PORT: '3000'
|
|
104
101
|
}
|
|
105
102
|
},
|
|
@@ -108,14 +105,14 @@ exports.railwayConfigs = {
|
|
|
108
105
|
source: './packages/kiosk-app',
|
|
109
106
|
buildCommand: 'npm run build',
|
|
110
107
|
startCommand: 'npx serve -s dist -l 5173',
|
|
111
|
-
env:
|
|
108
|
+
env: deploymentConfig.environmentVariables.staging
|
|
112
109
|
},
|
|
113
110
|
{
|
|
114
111
|
name: 'admin-frontend-staging',
|
|
115
112
|
source: './packages/admin-app',
|
|
116
113
|
buildCommand: 'npm run build',
|
|
117
114
|
startCommand: 'npx serve -s dist -l 5174',
|
|
118
|
-
env:
|
|
115
|
+
env: deploymentConfig.environmentVariables.staging
|
|
119
116
|
}
|
|
120
117
|
]
|
|
121
118
|
},
|
|
@@ -133,7 +130,7 @@ exports.railwayConfigs = {
|
|
|
133
130
|
name: 'kiosk-backend-prod',
|
|
134
131
|
source: './packages/backend',
|
|
135
132
|
env: {
|
|
136
|
-
...
|
|
133
|
+
...deploymentConfig.environmentVariables.production,
|
|
137
134
|
PORT: '3000'
|
|
138
135
|
}
|
|
139
136
|
},
|
|
@@ -142,14 +139,14 @@ exports.railwayConfigs = {
|
|
|
142
139
|
source: './packages/kiosk-app',
|
|
143
140
|
buildCommand: 'npm run build',
|
|
144
141
|
startCommand: 'npx serve -s dist -l 5173',
|
|
145
|
-
env:
|
|
142
|
+
env: deploymentConfig.environmentVariables.production
|
|
146
143
|
},
|
|
147
144
|
{
|
|
148
145
|
name: 'admin-frontend-prod',
|
|
149
146
|
source: './packages/admin-app',
|
|
150
147
|
buildCommand: 'npm run build',
|
|
151
148
|
startCommand: 'npx serve -s dist -l 5174',
|
|
152
|
-
env:
|
|
149
|
+
env: deploymentConfig.environmentVariables.production
|
|
153
150
|
}
|
|
154
151
|
]
|
|
155
152
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Centralized environment configuration for all services
|
|
3
2
|
// This ensures consistency across backend, kiosk, and admin apps
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.getUIConfig = exports.getPaymentConfig = exports.getWsUrl = exports.getApiUrl = exports.getAdminUrl = exports.getKioskUrl = exports.getBackendUrl = exports.isProduction = exports.isDevelopment = exports.getEnvironmentConfig = exports.getCurrentEnvironment = void 0;
|
|
6
3
|
// Helper function to get environment variables dynamically
|
|
7
4
|
function getEnvVar(key, defaultValue) {
|
|
8
5
|
// Try to get from process.env (Node.js) or import.meta.env (Vite)
|
|
@@ -14,11 +11,11 @@ function getEnvVar(key, defaultValue) {
|
|
|
14
11
|
// If not found and we're in a browser, try to access Vite's environment
|
|
15
12
|
if (!value && typeof window !== 'undefined') {
|
|
16
13
|
try {
|
|
17
|
-
//
|
|
14
|
+
// Access import.meta.env safely using dynamic import
|
|
18
15
|
// @ts-ignore
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
21
|
-
value =
|
|
16
|
+
const meta = globalThis.import?.meta;
|
|
17
|
+
if (meta && meta.env) {
|
|
18
|
+
value = meta.env[key];
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
21
|
catch (e) {
|
|
@@ -85,7 +82,7 @@ function getConfigForEnvironment(env) {
|
|
|
85
82
|
}
|
|
86
83
|
}
|
|
87
84
|
// Simple environment detection
|
|
88
|
-
const getCurrentEnvironment = () => {
|
|
85
|
+
export const getCurrentEnvironment = () => {
|
|
89
86
|
let nodeEnv;
|
|
90
87
|
// Check for process.env (Node.js environment)
|
|
91
88
|
if (typeof process !== 'undefined' && process.env) {
|
|
@@ -94,11 +91,11 @@ const getCurrentEnvironment = () => {
|
|
|
94
91
|
// If not found and we're in a browser, try to access Vite's environment
|
|
95
92
|
if (!nodeEnv && typeof window !== 'undefined') {
|
|
96
93
|
try {
|
|
97
|
-
//
|
|
94
|
+
// Access import.meta.env safely using dynamic import
|
|
98
95
|
// @ts-ignore
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
nodeEnv =
|
|
96
|
+
const meta = globalThis.import?.meta;
|
|
97
|
+
if (meta && meta.env) {
|
|
98
|
+
nodeEnv = meta.env.NODE_ENV;
|
|
102
99
|
}
|
|
103
100
|
}
|
|
104
101
|
catch (e) {
|
|
@@ -114,44 +111,33 @@ const getCurrentEnvironment = () => {
|
|
|
114
111
|
}
|
|
115
112
|
return nodeEnv === 'production' ? 'production' : 'development';
|
|
116
113
|
};
|
|
117
|
-
exports.getCurrentEnvironment = getCurrentEnvironment;
|
|
118
114
|
// Get current environment configuration
|
|
119
|
-
const getEnvironmentConfig = () => {
|
|
120
|
-
const env =
|
|
115
|
+
export const getEnvironmentConfig = () => {
|
|
116
|
+
const env = getCurrentEnvironment();
|
|
121
117
|
return getConfigForEnvironment(env);
|
|
122
118
|
};
|
|
123
|
-
exports.getEnvironmentConfig = getEnvironmentConfig;
|
|
124
119
|
// Simple environment checks
|
|
125
|
-
const isDevelopment = () =>
|
|
126
|
-
|
|
127
|
-
const isProduction = () => (0, exports.getCurrentEnvironment)() === 'production';
|
|
128
|
-
exports.isProduction = isProduction;
|
|
120
|
+
export const isDevelopment = () => getCurrentEnvironment() === 'development';
|
|
121
|
+
export const isProduction = () => getCurrentEnvironment() === 'production';
|
|
129
122
|
// Utility functions for easy access to service URLs
|
|
130
|
-
const getBackendUrl = () =>
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
exports.getAdminUrl = getAdminUrl;
|
|
136
|
-
const getApiUrl = () => (0, exports.getEnvironmentConfig)().apiUrl;
|
|
137
|
-
exports.getApiUrl = getApiUrl;
|
|
138
|
-
const getWsUrl = () => (0, exports.getEnvironmentConfig)().wsUrl;
|
|
139
|
-
exports.getWsUrl = getWsUrl;
|
|
123
|
+
export const getBackendUrl = () => getEnvironmentConfig().backendUrl;
|
|
124
|
+
export const getKioskUrl = () => getEnvironmentConfig().kioskUrl;
|
|
125
|
+
export const getAdminUrl = () => getEnvironmentConfig().adminUrl;
|
|
126
|
+
export const getApiUrl = () => getEnvironmentConfig().apiUrl;
|
|
127
|
+
export const getWsUrl = () => getEnvironmentConfig().wsUrl;
|
|
140
128
|
// Service-specific configuration helpers
|
|
141
|
-
const getPaymentConfig = () => {
|
|
142
|
-
const config =
|
|
129
|
+
export const getPaymentConfig = () => {
|
|
130
|
+
const config = getEnvironmentConfig();
|
|
143
131
|
return {
|
|
144
132
|
enableMockPayments: config.enableMockPayments,
|
|
145
133
|
paymentAccountNumber: config.paymentAccountNumber,
|
|
146
134
|
paymentMode: config.paymentMode,
|
|
147
135
|
};
|
|
148
136
|
};
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const config = (0, exports.getEnvironmentConfig)();
|
|
137
|
+
export const getUIConfig = () => {
|
|
138
|
+
const config = getEnvironmentConfig();
|
|
152
139
|
return {
|
|
153
140
|
showDebugInfo: config.showDebugInfo,
|
|
154
141
|
logLevel: config.logLevel,
|
|
155
142
|
};
|
|
156
143
|
};
|
|
157
|
-
exports.getUIConfig = getUIConfig;
|
package/dist/config/logger.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Centralized logging system with environment-aware configuration
|
|
3
|
-
|
|
4
|
-
exports.log = exports.logger = void 0;
|
|
5
|
-
const environments_1 = require("./environments");
|
|
2
|
+
import { getEnvironmentConfig, getCurrentEnvironment } from './environments';
|
|
6
3
|
class Logger {
|
|
7
4
|
constructor() {
|
|
8
|
-
this.config =
|
|
5
|
+
this.config = getEnvironmentConfig();
|
|
9
6
|
this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
10
7
|
}
|
|
11
8
|
shouldLog(level) {
|
|
@@ -26,7 +23,7 @@ class Logger {
|
|
|
26
23
|
context,
|
|
27
24
|
data,
|
|
28
25
|
timestamp: new Date().toISOString(),
|
|
29
|
-
environment:
|
|
26
|
+
environment: getCurrentEnvironment(),
|
|
30
27
|
sessionId: this.sessionId
|
|
31
28
|
};
|
|
32
29
|
}
|
|
@@ -107,14 +104,14 @@ class Logger {
|
|
|
107
104
|
}
|
|
108
105
|
}
|
|
109
106
|
// Export singleton instance
|
|
110
|
-
|
|
107
|
+
export const logger = new Logger();
|
|
111
108
|
// Export convenience functions
|
|
112
|
-
|
|
113
|
-
debug: (message, context, data) =>
|
|
114
|
-
info: (message, context, data) =>
|
|
115
|
-
warn: (message, context, data) =>
|
|
116
|
-
error: (message, context, data) =>
|
|
117
|
-
kioskAction: (action, kioskId, data) =>
|
|
118
|
-
paymentEvent: (event, paymentId, data) =>
|
|
119
|
-
apiCall: (method, endpoint, duration, error) =>
|
|
109
|
+
export const log = {
|
|
110
|
+
debug: (message, context, data) => logger.debug(message, context, data),
|
|
111
|
+
info: (message, context, data) => logger.info(message, context, data),
|
|
112
|
+
warn: (message, context, data) => logger.warn(message, context, data),
|
|
113
|
+
error: (message, context, data) => logger.error(message, context, data),
|
|
114
|
+
kioskAction: (action, kioskId, data) => logger.kioskAction(action, kioskId, data),
|
|
115
|
+
paymentEvent: (event, paymentId, data) => logger.paymentEvent(event, paymentId, data),
|
|
116
|
+
apiCall: (method, endpoint, duration, error) => logger.apiCall(method, endpoint, duration, error),
|
|
120
117
|
};
|
package/dist/constants.d.ts
CHANGED
|
@@ -36,11 +36,19 @@ export declare const UI_MESSAGES: {
|
|
|
36
36
|
readonly REQUIRED_FIELD: "Toto pole je povinné";
|
|
37
37
|
readonly INVALID_EMAIL: "Zadejte platnou emailovou adresu";
|
|
38
38
|
readonly INVALID_PRICE: "Cena musí být větší než 0";
|
|
39
|
+
readonly EMAIL_LABEL: "Váš email:";
|
|
40
|
+
readonly SELECT_PRODUCT: "Vyberte si produkt";
|
|
41
|
+
readonly PAYMENT_WAITING: "Čekám na platbu...";
|
|
42
|
+
readonly PAYMENT_CONFIRMED: "Platba potvrzena!";
|
|
43
|
+
readonly CONTINUE_SHOPPING: "Pokračovat v nákupu";
|
|
44
|
+
readonly BACK_TO_PRODUCTS: "Zpět na produkty";
|
|
39
45
|
};
|
|
40
46
|
export declare const CSS_CLASSES: {
|
|
41
47
|
readonly CONTAINER: "container";
|
|
42
48
|
readonly GRID: "grid";
|
|
43
49
|
readonly FLEX: "flex";
|
|
50
|
+
readonly APP: "kiosk-app";
|
|
51
|
+
readonly SCREEN: "screen";
|
|
44
52
|
readonly LOADING: "loading";
|
|
45
53
|
readonly ERROR: "error";
|
|
46
54
|
readonly SUCCESS: "success";
|
package/dist/constants.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Shared constants across all packages
|
|
3
|
-
|
|
4
|
-
exports.ROUTES = exports.CSS_CLASSES = exports.UI_MESSAGES = exports.APP_CONFIG = void 0;
|
|
5
|
-
exports.APP_CONFIG = {
|
|
2
|
+
export const APP_CONFIG = {
|
|
6
3
|
// API Configuration
|
|
7
4
|
DEFAULT_API_URL: 'http://localhost:3015',
|
|
8
5
|
DEFAULT_WS_URL: 'ws://localhost:3015',
|
|
@@ -32,7 +29,7 @@ exports.APP_CONFIG = {
|
|
|
32
29
|
QR_CODE_WIDTH: 300,
|
|
33
30
|
QR_CODE_FORMAT: 'SPD*1.0', // Czech QR payment standard
|
|
34
31
|
};
|
|
35
|
-
|
|
32
|
+
export const UI_MESSAGES = {
|
|
36
33
|
// Loading states
|
|
37
34
|
LOADING_PRODUCTS: 'Načítání produktů...',
|
|
38
35
|
LOADING_PAYMENT: 'Zpracovávám platbu...',
|
|
@@ -52,12 +49,21 @@ exports.UI_MESSAGES = {
|
|
|
52
49
|
REQUIRED_FIELD: 'Toto pole je povinné',
|
|
53
50
|
INVALID_EMAIL: 'Zadejte platnou emailovou adresu',
|
|
54
51
|
INVALID_PRICE: 'Cena musí být větší než 0',
|
|
52
|
+
EMAIL_LABEL: 'Váš email:',
|
|
53
|
+
// Kiosk specific
|
|
54
|
+
SELECT_PRODUCT: 'Vyberte si produkt',
|
|
55
|
+
PAYMENT_WAITING: 'Čekám na platbu...',
|
|
56
|
+
PAYMENT_CONFIRMED: 'Platba potvrzena!',
|
|
57
|
+
CONTINUE_SHOPPING: 'Pokračovat v nákupu',
|
|
58
|
+
BACK_TO_PRODUCTS: 'Zpět na produkty',
|
|
55
59
|
};
|
|
56
|
-
|
|
60
|
+
export const CSS_CLASSES = {
|
|
57
61
|
// Layout
|
|
58
62
|
CONTAINER: 'container',
|
|
59
63
|
GRID: 'grid',
|
|
60
64
|
FLEX: 'flex',
|
|
65
|
+
APP: 'kiosk-app',
|
|
66
|
+
SCREEN: 'screen',
|
|
61
67
|
// States
|
|
62
68
|
LOADING: 'loading',
|
|
63
69
|
ERROR: 'error',
|
|
@@ -75,7 +81,7 @@ exports.CSS_CLASSES = {
|
|
|
75
81
|
CONNECTED: 'connected',
|
|
76
82
|
DISCONNECTED: 'disconnected',
|
|
77
83
|
};
|
|
78
|
-
|
|
84
|
+
export const ROUTES = {
|
|
79
85
|
// Kiosk routes
|
|
80
86
|
KIOSK_HOME: '/',
|
|
81
87
|
KIOSK_PAYMENT: '/payment',
|
package/dist/errors.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Shared error handling system
|
|
3
|
-
|
|
4
|
-
exports.getErrorMessage = exports.formatError = exports.NotFoundError = exports.AuthenticationError = exports.NetworkError = exports.ValidationError = exports.AppError = void 0;
|
|
5
|
-
class AppError extends Error {
|
|
2
|
+
export class AppError extends Error {
|
|
6
3
|
constructor(message, code = 'UNKNOWN_ERROR', statusCode = 500, isOperational = true) {
|
|
7
4
|
super(message);
|
|
8
5
|
this.name = 'AppError';
|
|
@@ -13,37 +10,32 @@ class AppError extends Error {
|
|
|
13
10
|
Error.captureStackTrace(this, this.constructor);
|
|
14
11
|
}
|
|
15
12
|
}
|
|
16
|
-
exports.AppError = AppError;
|
|
17
13
|
// Predefined error types
|
|
18
|
-
class ValidationError extends AppError {
|
|
14
|
+
export class ValidationError extends AppError {
|
|
19
15
|
constructor(message, _field) {
|
|
20
16
|
super(message, 'VALIDATION_ERROR', 400);
|
|
21
17
|
this.name = 'ValidationError';
|
|
22
18
|
}
|
|
23
19
|
}
|
|
24
|
-
|
|
25
|
-
class NetworkError extends AppError {
|
|
20
|
+
export class NetworkError extends AppError {
|
|
26
21
|
constructor(message = 'Chyba připojení k serveru') {
|
|
27
22
|
super(message, 'NETWORK_ERROR', 503);
|
|
28
23
|
this.name = 'NetworkError';
|
|
29
24
|
}
|
|
30
25
|
}
|
|
31
|
-
|
|
32
|
-
class AuthenticationError extends AppError {
|
|
26
|
+
export class AuthenticationError extends AppError {
|
|
33
27
|
constructor(message = 'Neplatné přihlašovací údaje') {
|
|
34
28
|
super(message, 'AUTH_ERROR', 401);
|
|
35
29
|
this.name = 'AuthenticationError';
|
|
36
30
|
}
|
|
37
31
|
}
|
|
38
|
-
|
|
39
|
-
class NotFoundError extends AppError {
|
|
32
|
+
export class NotFoundError extends AppError {
|
|
40
33
|
constructor(resource = 'Zdroj') {
|
|
41
34
|
super(`${resource} nebyl nalezen`, 'NOT_FOUND', 404);
|
|
42
35
|
this.name = 'NotFoundError';
|
|
43
36
|
}
|
|
44
37
|
}
|
|
45
|
-
|
|
46
|
-
const formatError = (error, details) => {
|
|
38
|
+
export const formatError = (error, details) => {
|
|
47
39
|
const isAppError = error instanceof AppError;
|
|
48
40
|
return {
|
|
49
41
|
success: false,
|
|
@@ -55,9 +47,8 @@ const formatError = (error, details) => {
|
|
|
55
47
|
}
|
|
56
48
|
};
|
|
57
49
|
};
|
|
58
|
-
exports.formatError = formatError;
|
|
59
50
|
// Error handler hook for React components
|
|
60
|
-
const getErrorMessage = (error) => {
|
|
51
|
+
export const getErrorMessage = (error) => {
|
|
61
52
|
if (error instanceof NetworkError) {
|
|
62
53
|
return 'Problém s připojením. Zkuste to znovu.';
|
|
63
54
|
}
|
|
@@ -80,4 +71,3 @@ const getErrorMessage = (error) => {
|
|
|
80
71
|
// Return original message if available, otherwise generic
|
|
81
72
|
return error.message || 'Něco se pokazilo. Zkuste to znovu.';
|
|
82
73
|
};
|
|
83
|
-
exports.getErrorMessage = getErrorMessage;
|
package/dist/hooks/useApi.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const errors_1 = require("../errors");
|
|
6
|
-
function useApi(_apiClient) {
|
|
7
|
-
const [state, setState] = (0, react_1.useState)({
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { NetworkError, AppError } from '../errors';
|
|
3
|
+
export function useApi(_apiClient) {
|
|
4
|
+
const [state, setState] = useState({
|
|
8
5
|
data: null,
|
|
9
6
|
loading: false,
|
|
10
7
|
error: null
|
|
11
8
|
});
|
|
12
|
-
const execute =
|
|
9
|
+
const execute = useCallback(async (apiCall, options) => {
|
|
13
10
|
try {
|
|
14
11
|
if (!options?.skipLoading) {
|
|
15
12
|
setState(prev => ({ ...prev, loading: true, error: null }));
|
|
@@ -24,9 +21,9 @@ function useApi(_apiClient) {
|
|
|
24
21
|
return data;
|
|
25
22
|
}
|
|
26
23
|
catch (error) {
|
|
27
|
-
const appError = error instanceof
|
|
24
|
+
const appError = error instanceof AppError
|
|
28
25
|
? error
|
|
29
|
-
: new
|
|
26
|
+
: new NetworkError('Chyba při komunikaci se serverem');
|
|
30
27
|
setState(prev => ({
|
|
31
28
|
...prev,
|
|
32
29
|
loading: false,
|
|
@@ -36,7 +33,7 @@ function useApi(_apiClient) {
|
|
|
36
33
|
throw appError;
|
|
37
34
|
}
|
|
38
35
|
}, []);
|
|
39
|
-
const reset =
|
|
36
|
+
const reset = useCallback(() => {
|
|
40
37
|
setState({
|
|
41
38
|
data: null,
|
|
42
39
|
loading: false,
|
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.useAsyncOperation = useAsyncOperation;
|
|
4
|
-
const react_1 = require("react");
|
|
5
|
-
const useErrorHandler_1 = require("./useErrorHandler");
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useErrorHandler } from './useErrorHandler';
|
|
6
3
|
/**
|
|
7
4
|
* Consistent hook for handling async operations with loading states and error handling
|
|
8
5
|
*/
|
|
9
|
-
function useAsyncOperation(options = {}) {
|
|
10
|
-
const [state, setState] =
|
|
6
|
+
export function useAsyncOperation(options = {}) {
|
|
7
|
+
const [state, setState] = useState({
|
|
11
8
|
data: options.initialData || null,
|
|
12
9
|
loading: false,
|
|
13
10
|
error: null
|
|
14
11
|
});
|
|
15
|
-
const { handleError } =
|
|
16
|
-
const execute =
|
|
12
|
+
const { handleError } = useErrorHandler();
|
|
13
|
+
const execute = useCallback(async (operation, context) => {
|
|
17
14
|
setState(prev => ({ ...prev, loading: true, error: null }));
|
|
18
15
|
try {
|
|
19
16
|
const result = await operation();
|
|
@@ -37,14 +34,14 @@ function useAsyncOperation(options = {}) {
|
|
|
37
34
|
return null;
|
|
38
35
|
}
|
|
39
36
|
}, [handleError, options]);
|
|
40
|
-
const reset =
|
|
37
|
+
const reset = useCallback(() => {
|
|
41
38
|
setState({
|
|
42
39
|
data: options.initialData || null,
|
|
43
40
|
loading: false,
|
|
44
41
|
error: null
|
|
45
42
|
});
|
|
46
43
|
}, [options.initialData]);
|
|
47
|
-
const setData =
|
|
44
|
+
const setData = useCallback((data) => {
|
|
48
45
|
setState(prev => ({ ...prev, data }));
|
|
49
46
|
}, []);
|
|
50
47
|
return {
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const errors_1 = require("../errors");
|
|
6
|
-
function useErrorHandler() {
|
|
7
|
-
const [errorState, setErrorState] = (0, react_1.useState)({
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { getErrorMessage, formatError } from '../errors';
|
|
3
|
+
export function useErrorHandler() {
|
|
4
|
+
const [errorState, setErrorState] = useState({
|
|
8
5
|
error: null,
|
|
9
6
|
message: '',
|
|
10
7
|
isVisible: false
|
|
11
8
|
});
|
|
12
|
-
const handleError =
|
|
9
|
+
const handleError = useCallback((error, context) => {
|
|
13
10
|
console.error(`Error in ${context || 'unknown context'}:`, error);
|
|
14
|
-
const userMessage =
|
|
11
|
+
const userMessage = getErrorMessage(error);
|
|
15
12
|
setErrorState({
|
|
16
13
|
error,
|
|
17
14
|
message: userMessage,
|
|
@@ -24,17 +21,17 @@ function useErrorHandler() {
|
|
|
24
21
|
// Send to monitoring service in production
|
|
25
22
|
if (process.env.NODE_ENV === 'production') {
|
|
26
23
|
// TODO: Integrate with monitoring service (Sentry, LogRocket, etc.)
|
|
27
|
-
console.warn('Production error logged:',
|
|
24
|
+
console.warn('Production error logged:', formatError(error));
|
|
28
25
|
}
|
|
29
26
|
}, []);
|
|
30
|
-
const clearError =
|
|
27
|
+
const clearError = useCallback(() => {
|
|
31
28
|
setErrorState({
|
|
32
29
|
error: null,
|
|
33
30
|
message: '',
|
|
34
31
|
isVisible: false
|
|
35
32
|
});
|
|
36
33
|
}, []);
|
|
37
|
-
const retryAction =
|
|
34
|
+
const retryAction = useCallback((action) => {
|
|
38
35
|
clearError();
|
|
39
36
|
try {
|
|
40
37
|
const result = action();
|
package/dist/index.js
CHANGED
|
@@ -1,31 +1,15 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Shared package entry point
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
15
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
16
|
-
};
|
|
17
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
__exportStar(require("./types"), exports);
|
|
19
|
-
__exportStar(require("./api"), exports);
|
|
20
|
-
__exportStar(require("./constants"), exports);
|
|
21
|
-
__exportStar(require("./validation"), exports);
|
|
22
|
-
__exportStar(require("./errors"), exports);
|
|
23
|
-
__exportStar(require("./utils"), exports);
|
|
2
|
+
export * from './types';
|
|
3
|
+
export * from './api';
|
|
4
|
+
export * from './constants';
|
|
5
|
+
export * from './validation';
|
|
6
|
+
export * from './errors';
|
|
7
|
+
export * from './utils';
|
|
24
8
|
// Note: ./utils exports getErrorMessage which shadows the one from ./errors
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
9
|
+
export * from './hooks/useErrorHandler';
|
|
10
|
+
export * from './hooks/useApi';
|
|
11
|
+
export * from './hooks/useAsyncOperation';
|
|
12
|
+
export * from './components/LoadingSpinner';
|
|
13
|
+
export * from './components/ErrorDisplay';
|
|
14
|
+
export * from './config/environments';
|
|
15
|
+
export * from './config/logger';
|
package/dist/types.js
CHANGED
package/dist/utils.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export declare const getKioskSecretFromUrl: () => string | undefined;
|
|
|
5
5
|
export declare const formatDate: (date: Date | string) => string;
|
|
6
6
|
export declare const debounce: <T extends (...args: any[]) => any>(func: T, delay: number) => ((...args: Parameters<T>) => void);
|
|
7
7
|
export declare const generatePaymentId: () => string;
|
|
8
|
+
export declare const validateKioskId: (kioskId: number) => boolean;
|
package/dist/utils.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Shared utility functions - consolidated from utils/simple.ts
|
|
3
|
-
|
|
4
|
-
exports.generatePaymentId = exports.debounce = exports.formatDate = exports.getKioskSecretFromUrl = exports.getKioskIdFromUrl = exports.formatPrice = exports.validateEmail = void 0;
|
|
5
|
-
const validateEmail = (email) => {
|
|
2
|
+
export const validateEmail = (email) => {
|
|
6
3
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
7
4
|
// Additional check for consecutive dots which are invalid
|
|
8
5
|
if (email.includes('..'))
|
|
9
6
|
return false;
|
|
10
7
|
return emailRegex.test(email);
|
|
11
8
|
};
|
|
12
|
-
|
|
13
|
-
const formatPrice = (price) => {
|
|
9
|
+
export const formatPrice = (price) => {
|
|
14
10
|
return `${price} Kč`;
|
|
15
11
|
};
|
|
16
|
-
|
|
17
|
-
const getKioskIdFromUrl = () => {
|
|
12
|
+
export const getKioskIdFromUrl = () => {
|
|
18
13
|
const urlParams = new URLSearchParams(window.location.search);
|
|
19
|
-
|
|
14
|
+
const kioskId = urlParams.get('kioskId');
|
|
15
|
+
if (!kioskId) {
|
|
16
|
+
throw new Error('Kiosk ID is required. Please add ?kioskId=X to the URL where X is your kiosk number.');
|
|
17
|
+
}
|
|
18
|
+
const parsedId = parseInt(kioskId);
|
|
19
|
+
if (isNaN(parsedId) || parsedId <= 0) {
|
|
20
|
+
throw new Error(`Invalid kiosk ID: ${kioskId}. Kiosk ID must be a positive number.`);
|
|
21
|
+
}
|
|
22
|
+
return parsedId;
|
|
20
23
|
};
|
|
21
|
-
|
|
22
|
-
const getKioskSecretFromUrl = () => {
|
|
24
|
+
export const getKioskSecretFromUrl = () => {
|
|
23
25
|
const urlParams = new URLSearchParams(window.location.search);
|
|
24
26
|
return urlParams.get('secret') || undefined;
|
|
25
27
|
};
|
|
26
|
-
|
|
27
|
-
const formatDate = (date) => {
|
|
28
|
+
export const formatDate = (date) => {
|
|
28
29
|
const d = typeof date === 'string' ? new Date(date) : date;
|
|
29
30
|
return d.toLocaleDateString('cs-CZ', {
|
|
30
31
|
year: 'numeric',
|
|
@@ -34,17 +35,17 @@ const formatDate = (date) => {
|
|
|
34
35
|
minute: '2-digit'
|
|
35
36
|
});
|
|
36
37
|
};
|
|
37
|
-
|
|
38
|
-
const debounce = (func, delay) => {
|
|
38
|
+
export const debounce = (func, delay) => {
|
|
39
39
|
let timeoutId;
|
|
40
40
|
return (...args) => {
|
|
41
41
|
clearTimeout(timeoutId);
|
|
42
42
|
timeoutId = setTimeout(() => func(...args), delay);
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
|
-
|
|
46
|
-
const generatePaymentId = () => {
|
|
45
|
+
export const generatePaymentId = () => {
|
|
47
46
|
return `pay-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
48
47
|
};
|
|
49
|
-
|
|
48
|
+
export const validateKioskId = (kioskId) => {
|
|
49
|
+
return Number.isInteger(kioskId) && kioskId > 0;
|
|
50
|
+
};
|
|
50
51
|
// Note: getErrorMessage is now exported from ./errors.ts instead
|
package/dist/validation.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Shared validation utilities
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.validationSchemas = exports.validateSchema = exports.validators = void 0;
|
|
5
2
|
// ValidationError is imported but not used directly in this file
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { APP_CONFIG, UI_MESSAGES } from './constants';
|
|
4
|
+
export const validators = {
|
|
8
5
|
required: (value) => {
|
|
9
6
|
if (value === null || value === undefined || value === '') {
|
|
10
|
-
return
|
|
7
|
+
return UI_MESSAGES.REQUIRED_FIELD;
|
|
11
8
|
}
|
|
12
9
|
return null;
|
|
13
10
|
},
|
|
@@ -16,7 +13,7 @@ exports.validators = {
|
|
|
16
13
|
return null; // Let required validator handle empty values
|
|
17
14
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
18
15
|
if (!emailRegex.test(value)) {
|
|
19
|
-
return
|
|
16
|
+
return UI_MESSAGES.INVALID_EMAIL;
|
|
20
17
|
}
|
|
21
18
|
return null;
|
|
22
19
|
},
|
|
@@ -47,8 +44,8 @@ exports.validators = {
|
|
|
47
44
|
username: (value) => {
|
|
48
45
|
if (!value)
|
|
49
46
|
return null;
|
|
50
|
-
if (value.length <
|
|
51
|
-
return `Uživatelské jméno musí mít alespoň ${
|
|
47
|
+
if (value.length < APP_CONFIG.MIN_USERNAME_LENGTH) {
|
|
48
|
+
return `Uživatelské jméno musí mít alespoň ${APP_CONFIG.MIN_USERNAME_LENGTH} znaky`;
|
|
52
49
|
}
|
|
53
50
|
if (!/^[a-zA-Z0-9_]+$/.test(value)) {
|
|
54
51
|
return 'Uživatelské jméno může obsahovat pouze písmena, číslice a podtržítka';
|
|
@@ -58,13 +55,13 @@ exports.validators = {
|
|
|
58
55
|
password: (value) => {
|
|
59
56
|
if (!value)
|
|
60
57
|
return null;
|
|
61
|
-
if (value.length <
|
|
62
|
-
return `Heslo musí mít alespoň ${
|
|
58
|
+
if (value.length < APP_CONFIG.MIN_PASSWORD_LENGTH) {
|
|
59
|
+
return `Heslo musí mít alespoň ${APP_CONFIG.MIN_PASSWORD_LENGTH} znaků`;
|
|
63
60
|
}
|
|
64
61
|
return null;
|
|
65
62
|
}
|
|
66
63
|
};
|
|
67
|
-
const validateSchema = (data, schema) => {
|
|
64
|
+
export const validateSchema = (data, schema) => {
|
|
68
65
|
const errors = {};
|
|
69
66
|
for (const [field, validatorFunctions] of Object.entries(schema)) {
|
|
70
67
|
const value = data[field];
|
|
@@ -81,37 +78,36 @@ const validateSchema = (data, schema) => {
|
|
|
81
78
|
errors
|
|
82
79
|
};
|
|
83
80
|
};
|
|
84
|
-
exports.validateSchema = validateSchema;
|
|
85
81
|
// Common validation schemas
|
|
86
|
-
|
|
82
|
+
export const validationSchemas = {
|
|
87
83
|
login: {
|
|
88
84
|
username: [
|
|
89
|
-
(value) =>
|
|
90
|
-
|
|
85
|
+
(value) => validators.required(value),
|
|
86
|
+
validators.username
|
|
91
87
|
],
|
|
92
88
|
password: [
|
|
93
|
-
(value) =>
|
|
94
|
-
|
|
89
|
+
(value) => validators.required(value),
|
|
90
|
+
validators.password
|
|
95
91
|
]
|
|
96
92
|
},
|
|
97
93
|
customerEmail: {
|
|
98
94
|
email: [
|
|
99
|
-
(value) =>
|
|
100
|
-
|
|
95
|
+
(value) => validators.required(value),
|
|
96
|
+
validators.email
|
|
101
97
|
]
|
|
102
98
|
},
|
|
103
99
|
product: {
|
|
104
100
|
name: [
|
|
105
|
-
(value) =>
|
|
106
|
-
(value) =>
|
|
107
|
-
(value) =>
|
|
101
|
+
(value) => validators.required(value),
|
|
102
|
+
(value) => validators.minLength(value, 2, 'Název produktu'),
|
|
103
|
+
(value) => validators.maxLength(value, APP_CONFIG.MAX_PRODUCT_NAME_LENGTH, 'Název produktu')
|
|
108
104
|
],
|
|
109
105
|
price: [
|
|
110
|
-
(value) =>
|
|
111
|
-
(value) =>
|
|
106
|
+
(value) => validators.required(value),
|
|
107
|
+
(value) => validators.positiveNumber(value, 'Cena')
|
|
112
108
|
],
|
|
113
109
|
description: [
|
|
114
|
-
(value) =>
|
|
110
|
+
(value) => validators.maxLength(value, APP_CONFIG.MAX_DESCRIPTION_LENGTH, 'Popis')
|
|
115
111
|
]
|
|
116
112
|
}
|
|
117
113
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-kiosk-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared components and utilities for Pi Kiosk system",
|
|
6
6
|
"keywords": [
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"test": "jest",
|
|
32
32
|
"test:watch": "jest --watch",
|
|
33
33
|
"test:coverage": "jest --coverage",
|
|
34
|
+
"clean": "rimraf dist",
|
|
35
|
+
"publish:local": "npm run build && npm pack --pack-destination ../",
|
|
34
36
|
"prepublishOnly": "npm run build",
|
|
35
37
|
"preversion": "npm run test",
|
|
36
38
|
"postversion": "git push && git push --tags"
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
"@types/node": "^20.0.0",
|
|
49
51
|
"@types/react": "^18.2.0",
|
|
50
52
|
"jest": "^29.5.0",
|
|
51
|
-
"ts-jest": "^29.1.0"
|
|
53
|
+
"ts-jest": "^29.1.0",
|
|
54
|
+
"rimraf": "^5.0.0"
|
|
52
55
|
}
|
|
53
56
|
}
|