pi-kiosk-shared 1.0.17 → 1.0.19
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.d.ts +6 -0
- package/dist/api.js +7 -0
- package/dist/config/environments.js +21 -28
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +18 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +24 -0
- package/dist/types.d.ts +184 -17
- package/dist/types.js +17 -1
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +29 -4
- package/dist/validation.d.ts +15 -1
- package/dist/validation.js +60 -1
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -5,8 +5,13 @@ export declare const API_ENDPOINTS: {
|
|
|
5
5
|
readonly PAYMENT_CREATE_MULTI_QR: "/api/payments/create-multi-qr";
|
|
6
6
|
readonly PAYMENT_CHECK_STATUS: "/api/payments/check-status/:paymentId";
|
|
7
7
|
readonly PAYMENT_COMPLETE: "/api/payments/complete";
|
|
8
|
+
readonly PAYMENT_COMPLETE_MULTI: "/api/payments/complete-multi";
|
|
8
9
|
readonly PAYMENT_CANCEL: "/api/payments/cancel";
|
|
9
10
|
readonly PAYMENT_START_MONITORING: "/api/payments/start-monitoring";
|
|
11
|
+
readonly PAYMENT_THEPAY_CREATE: "/api/payments/create-thepay";
|
|
12
|
+
readonly PAYMENT_THEPAY_STATUS: "/api/payments/thepay-status/:paymentId";
|
|
13
|
+
readonly PAYMENT_THEPAY_CANCEL: "/api/payments/thepay-cancel";
|
|
14
|
+
readonly PAYMENT_THEPAY_METHODS: "/api/payments/thepay-methods";
|
|
10
15
|
readonly ADMIN_LOGIN: "/admin/login";
|
|
11
16
|
readonly ADMIN_PRODUCTS: "/admin/products";
|
|
12
17
|
readonly ADMIN_PRODUCTS_INVENTORY: "/admin/products/inventory/:kioskId";
|
|
@@ -15,6 +20,7 @@ export declare const API_ENDPOINTS: {
|
|
|
15
20
|
readonly ADMIN_PRODUCT_KIOSK_VISIBILITY: "/admin/products/:productId/kiosk/:kioskId";
|
|
16
21
|
readonly ADMIN_KIOSKS: "/admin/kiosks";
|
|
17
22
|
readonly ADMIN_KIOSK_DETAILS: "/admin/kiosks/:id";
|
|
23
|
+
readonly ADMIN_LOGS: "/admin/logs";
|
|
18
24
|
readonly HEALTH: "/health";
|
|
19
25
|
readonly CHECK_TRANSACTIONS: "/api/check-new-transactions";
|
|
20
26
|
readonly EVENTS: "/events/:kioskId";
|
package/dist/api.js
CHANGED
|
@@ -8,8 +8,14 @@ export const API_ENDPOINTS = {
|
|
|
8
8
|
PAYMENT_CREATE_MULTI_QR: '/api/payments/create-multi-qr',
|
|
9
9
|
PAYMENT_CHECK_STATUS: '/api/payments/check-status/:paymentId',
|
|
10
10
|
PAYMENT_COMPLETE: '/api/payments/complete',
|
|
11
|
+
PAYMENT_COMPLETE_MULTI: '/api/payments/complete-multi',
|
|
11
12
|
PAYMENT_CANCEL: '/api/payments/cancel',
|
|
12
13
|
PAYMENT_START_MONITORING: '/api/payments/start-monitoring',
|
|
14
|
+
// ThePay.eu payment endpoints
|
|
15
|
+
PAYMENT_THEPAY_CREATE: '/api/payments/create-thepay',
|
|
16
|
+
PAYMENT_THEPAY_STATUS: '/api/payments/thepay-status/:paymentId',
|
|
17
|
+
PAYMENT_THEPAY_CANCEL: '/api/payments/thepay-cancel',
|
|
18
|
+
PAYMENT_THEPAY_METHODS: '/api/payments/thepay-methods',
|
|
13
19
|
// Admin endpoints
|
|
14
20
|
ADMIN_LOGIN: '/admin/login',
|
|
15
21
|
ADMIN_PRODUCTS: '/admin/products',
|
|
@@ -19,6 +25,7 @@ export const API_ENDPOINTS = {
|
|
|
19
25
|
ADMIN_PRODUCT_KIOSK_VISIBILITY: '/admin/products/:productId/kiosk/:kioskId',
|
|
20
26
|
ADMIN_KIOSKS: '/admin/kiosks',
|
|
21
27
|
ADMIN_KIOSK_DETAILS: '/admin/kiosks/:id',
|
|
28
|
+
ADMIN_LOGS: '/admin/logs',
|
|
22
29
|
// System endpoints
|
|
23
30
|
HEALTH: '/health',
|
|
24
31
|
CHECK_TRANSACTIONS: '/api/check-new-transactions',
|
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
// Centralized environment configuration for all services
|
|
2
2
|
// This ensures consistency across backend, kiosk, and admin apps
|
|
3
|
-
//
|
|
3
|
+
// Simplified environment variable helper
|
|
4
4
|
function getEnvVar(key, defaultValue) {
|
|
5
|
-
// Try to get from process.env (Node.js) or import.meta.env (Vite)
|
|
6
|
-
let value;
|
|
7
5
|
// Check for process.env (Node.js environment)
|
|
8
6
|
if (typeof process !== 'undefined' && process.env) {
|
|
9
|
-
|
|
7
|
+
return process.env[key] || defaultValue;
|
|
10
8
|
}
|
|
11
|
-
//
|
|
12
|
-
if (
|
|
9
|
+
// Check for import.meta.env (Vite environment)
|
|
10
|
+
if (typeof window !== 'undefined') {
|
|
13
11
|
try {
|
|
14
|
-
//
|
|
15
|
-
// @ts-ignore
|
|
12
|
+
// @ts-ignore - Vite environment
|
|
16
13
|
const meta = globalThis.import?.meta;
|
|
17
14
|
if (meta && meta.env) {
|
|
18
|
-
|
|
15
|
+
return meta.env[key] || defaultValue;
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
18
|
catch (e) {
|
|
22
|
-
//
|
|
19
|
+
// Fallback for test environments
|
|
23
20
|
}
|
|
24
21
|
}
|
|
25
|
-
return
|
|
22
|
+
return defaultValue;
|
|
26
23
|
}
|
|
27
24
|
function getEnvBool(key, defaultValue) {
|
|
28
25
|
const value = getEnvVar(key, defaultValue.toString());
|
|
@@ -81,35 +78,31 @@ function getConfigForEnvironment(env) {
|
|
|
81
78
|
};
|
|
82
79
|
}
|
|
83
80
|
}
|
|
84
|
-
//
|
|
81
|
+
// Simplified environment detection
|
|
85
82
|
export const getCurrentEnvironment = () => {
|
|
86
|
-
let nodeEnv;
|
|
87
83
|
// Check for process.env (Node.js environment)
|
|
88
84
|
if (typeof process !== 'undefined' && process.env) {
|
|
89
|
-
|
|
85
|
+
return process.env.NODE_ENV === 'production' ? 'production' : 'development';
|
|
90
86
|
}
|
|
91
|
-
//
|
|
92
|
-
if (
|
|
87
|
+
// Check for import.meta.env (Vite environment)
|
|
88
|
+
if (typeof window !== 'undefined') {
|
|
93
89
|
try {
|
|
94
|
-
//
|
|
95
|
-
// @ts-ignore
|
|
90
|
+
// @ts-ignore - Vite environment
|
|
96
91
|
const meta = globalThis.import?.meta;
|
|
97
92
|
if (meta && meta.env) {
|
|
98
|
-
|
|
93
|
+
return meta.env.MODE === 'production' ? 'production' : 'development';
|
|
99
94
|
}
|
|
100
95
|
}
|
|
101
96
|
catch (e) {
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
window.location.hostname.includes('up.railway.app')) {
|
|
109
|
-
return 'production';
|
|
97
|
+
// Fallback: check if we're on Railway domain
|
|
98
|
+
if (window.location &&
|
|
99
|
+
(window.location.hostname.includes('railway.app') ||
|
|
100
|
+
window.location.hostname.includes('up.railway.app'))) {
|
|
101
|
+
return 'production';
|
|
102
|
+
}
|
|
110
103
|
}
|
|
111
104
|
}
|
|
112
|
-
return
|
|
105
|
+
return 'development';
|
|
113
106
|
};
|
|
114
107
|
// Get current environment configuration
|
|
115
108
|
export const getEnvironmentConfig = () => {
|
package/dist/constants.d.ts
CHANGED
|
@@ -25,11 +25,13 @@ export declare const UI_MESSAGES: {
|
|
|
25
25
|
readonly LOADING_PRODUCTS: "Načítání produktů...";
|
|
26
26
|
readonly LOADING_PAYMENT: "Zpracovávám platbu...";
|
|
27
27
|
readonly GENERATING_QR: "Generuji QR kód...";
|
|
28
|
+
readonly PAYMENT_INITIALIZING: "Inicializuji platbu...";
|
|
28
29
|
readonly PAYMENT_SUCCESS: "Platba byla úspěšně zpracována!";
|
|
29
30
|
readonly PRODUCT_SAVED: "Produkt byl úspěšně uložen!";
|
|
30
31
|
readonly NETWORK_ERROR: "Problém s připojením. Zkuste to znovu.";
|
|
31
32
|
readonly VALIDATION_ERROR: "Zkontrolujte zadané údaje.";
|
|
32
33
|
readonly UNKNOWN_ERROR: "Něco se pokazilo. Zkuste to znovu.";
|
|
34
|
+
readonly PAYMENT_ERROR: "Chyba při zpracování platby";
|
|
33
35
|
readonly NO_PRODUCTS: "Žádné produkty nejsou k dispozici";
|
|
34
36
|
readonly NO_TRANSACTIONS: "Žádné transakce";
|
|
35
37
|
readonly COMING_SOON: "Připravujeme pro vás...";
|
|
@@ -42,6 +44,8 @@ export declare const UI_MESSAGES: {
|
|
|
42
44
|
readonly PAYMENT_CONFIRMED: "Platba potvrzena!";
|
|
43
45
|
readonly CONTINUE_SHOPPING: "Pokračovat v nákupu";
|
|
44
46
|
readonly BACK_TO_PRODUCTS: "Zpět na produkty";
|
|
47
|
+
readonly RETRY: "Zkusit znovu";
|
|
48
|
+
readonly CANCEL: "Zrušit";
|
|
45
49
|
};
|
|
46
50
|
export declare const CSS_CLASSES: {
|
|
47
51
|
readonly CONTAINER: "container";
|
|
@@ -56,8 +60,20 @@ export declare const CSS_CLASSES: {
|
|
|
56
60
|
readonly ACTIVE: "active";
|
|
57
61
|
readonly BUTTON_PRIMARY: "btn-primary";
|
|
58
62
|
readonly BUTTON_SECONDARY: "btn-secondary";
|
|
63
|
+
readonly BUTTON_SUCCESS: "btn-success";
|
|
59
64
|
readonly INPUT: "input";
|
|
60
65
|
readonly CARD: "card";
|
|
66
|
+
readonly PAYMENT_CONTAINER: "payment-container";
|
|
67
|
+
readonly PAYMENT_HEADER: "payment-header";
|
|
68
|
+
readonly PAYMENT_METHODS: "payment-methods";
|
|
69
|
+
readonly PAYMENT_METHOD: "payment-method";
|
|
70
|
+
readonly PAYMENT_DETAILS: "payment-details";
|
|
71
|
+
readonly PAYMENT_ACTIONS: "payment-actions";
|
|
72
|
+
readonly LOADING_CONTAINER: "loading-container";
|
|
73
|
+
readonly LOADING_SPINNER: "loading-spinner";
|
|
74
|
+
readonly ERROR_CONTAINER: "error-container";
|
|
75
|
+
readonly BUTTON_GROUP: "button-group";
|
|
76
|
+
readonly BADGE_SUCCESS: "badge-success";
|
|
61
77
|
readonly ONLINE: "online";
|
|
62
78
|
readonly OFFLINE: "offline";
|
|
63
79
|
readonly CONNECTED: "connected";
|
package/dist/constants.js
CHANGED
|
@@ -34,6 +34,7 @@ export const UI_MESSAGES = {
|
|
|
34
34
|
LOADING_PRODUCTS: 'Načítání produktů...',
|
|
35
35
|
LOADING_PAYMENT: 'Zpracovávám platbu...',
|
|
36
36
|
GENERATING_QR: 'Generuji QR kód...',
|
|
37
|
+
PAYMENT_INITIALIZING: 'Inicializuji platbu...',
|
|
37
38
|
// Success messages
|
|
38
39
|
PAYMENT_SUCCESS: 'Platba byla úspěšně zpracována!',
|
|
39
40
|
PRODUCT_SAVED: 'Produkt byl úspěšně uložen!',
|
|
@@ -41,6 +42,7 @@ export const UI_MESSAGES = {
|
|
|
41
42
|
NETWORK_ERROR: 'Problém s připojením. Zkuste to znovu.',
|
|
42
43
|
VALIDATION_ERROR: 'Zkontrolujte zadané údaje.',
|
|
43
44
|
UNKNOWN_ERROR: 'Něco se pokazilo. Zkuste to znovu.',
|
|
45
|
+
PAYMENT_ERROR: 'Chyba při zpracování platby',
|
|
44
46
|
// Empty states
|
|
45
47
|
NO_PRODUCTS: 'Žádné produkty nejsou k dispozici',
|
|
46
48
|
NO_TRANSACTIONS: 'Žádné transakce',
|
|
@@ -56,6 +58,9 @@ export const UI_MESSAGES = {
|
|
|
56
58
|
PAYMENT_CONFIRMED: 'Platba potvrzena!',
|
|
57
59
|
CONTINUE_SHOPPING: 'Pokračovat v nákupu',
|
|
58
60
|
BACK_TO_PRODUCTS: 'Zpět na produkty',
|
|
61
|
+
// Action buttons
|
|
62
|
+
RETRY: 'Zkusit znovu',
|
|
63
|
+
CANCEL: 'Zrušit',
|
|
59
64
|
};
|
|
60
65
|
export const CSS_CLASSES = {
|
|
61
66
|
// Layout
|
|
@@ -73,8 +78,21 @@ export const CSS_CLASSES = {
|
|
|
73
78
|
// Components
|
|
74
79
|
BUTTON_PRIMARY: 'btn-primary',
|
|
75
80
|
BUTTON_SECONDARY: 'btn-secondary',
|
|
81
|
+
BUTTON_SUCCESS: 'btn-success',
|
|
76
82
|
INPUT: 'input',
|
|
77
83
|
CARD: 'card',
|
|
84
|
+
// Payment specific
|
|
85
|
+
PAYMENT_CONTAINER: 'payment-container',
|
|
86
|
+
PAYMENT_HEADER: 'payment-header',
|
|
87
|
+
PAYMENT_METHODS: 'payment-methods',
|
|
88
|
+
PAYMENT_METHOD: 'payment-method',
|
|
89
|
+
PAYMENT_DETAILS: 'payment-details',
|
|
90
|
+
PAYMENT_ACTIONS: 'payment-actions',
|
|
91
|
+
LOADING_CONTAINER: 'loading-container',
|
|
92
|
+
LOADING_SPINNER: 'loading-spinner',
|
|
93
|
+
ERROR_CONTAINER: 'error-container',
|
|
94
|
+
BUTTON_GROUP: 'button-group',
|
|
95
|
+
BADGE_SUCCESS: 'badge-success',
|
|
78
96
|
// Status indicators
|
|
79
97
|
ONLINE: 'online',
|
|
80
98
|
OFFLINE: 'offline',
|
package/dist/errors.d.ts
CHANGED
|
@@ -16,6 +16,18 @@ export declare class AuthenticationError extends AppError {
|
|
|
16
16
|
export declare class NotFoundError extends AppError {
|
|
17
17
|
constructor(resource?: string);
|
|
18
18
|
}
|
|
19
|
+
export declare class PaymentError extends AppError {
|
|
20
|
+
constructor(message?: string);
|
|
21
|
+
}
|
|
22
|
+
export declare class InventoryError extends AppError {
|
|
23
|
+
constructor(message?: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class KioskError extends AppError {
|
|
26
|
+
constructor(message?: string);
|
|
27
|
+
}
|
|
28
|
+
export declare class DatabaseError extends AppError {
|
|
29
|
+
constructor(message?: string);
|
|
30
|
+
}
|
|
19
31
|
export interface ErrorResponse {
|
|
20
32
|
success: false;
|
|
21
33
|
error: {
|
package/dist/errors.js
CHANGED
|
@@ -35,6 +35,30 @@ export class NotFoundError extends AppError {
|
|
|
35
35
|
this.name = 'NotFoundError';
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
export class PaymentError extends AppError {
|
|
39
|
+
constructor(message = 'Chyba při zpracování platby') {
|
|
40
|
+
super(message, 'PAYMENT_ERROR', 400);
|
|
41
|
+
this.name = 'PaymentError';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class InventoryError extends AppError {
|
|
45
|
+
constructor(message = 'Chyba při správě zásob') {
|
|
46
|
+
super(message, 'INVENTORY_ERROR', 400);
|
|
47
|
+
this.name = 'InventoryError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export class KioskError extends AppError {
|
|
51
|
+
constructor(message = 'Chyba konfigurace kiosku') {
|
|
52
|
+
super(message, 'KIOSK_ERROR', 400);
|
|
53
|
+
this.name = 'KioskError';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export class DatabaseError extends AppError {
|
|
57
|
+
constructor(message = 'Chyba databáze') {
|
|
58
|
+
super(message, 'DATABASE_ERROR', 503);
|
|
59
|
+
this.name = 'DatabaseError';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
38
62
|
export const formatError = (error, details) => {
|
|
39
63
|
const isAppError = error instanceof AppError;
|
|
40
64
|
return {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
export declare enum TransactionStatus {
|
|
2
|
+
INITIATED = "INITIATED",
|
|
3
|
+
PENDING = "PENDING",
|
|
4
|
+
PROCESSING = "PROCESSING",
|
|
5
|
+
COMPLETED = "COMPLETED",
|
|
6
|
+
FAILED = "FAILED",
|
|
7
|
+
CANCELLED = "CANCELLED",
|
|
8
|
+
TIMEOUT = "TIMEOUT"
|
|
9
|
+
}
|
|
10
|
+
export declare enum ReceiptType {
|
|
11
|
+
PLAIN = "PLAIN",
|
|
12
|
+
INVOICE = "INVOICE",
|
|
13
|
+
PROFORMA = "PROFORMA"
|
|
14
|
+
}
|
|
1
15
|
export interface Product {
|
|
2
16
|
id: number;
|
|
3
17
|
name: string;
|
|
@@ -5,12 +19,69 @@ export interface Product {
|
|
|
5
19
|
description: string;
|
|
6
20
|
image?: string;
|
|
7
21
|
imageUrl?: string;
|
|
22
|
+
clickedOn: number;
|
|
23
|
+
qrCodesGenerated: number;
|
|
24
|
+
numberOfPurchases: number;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
updatedAt: string;
|
|
27
|
+
}
|
|
28
|
+
export interface KioskProduct extends Product {
|
|
29
|
+
quantityInStock: number;
|
|
30
|
+
kioskClickedOn: number;
|
|
31
|
+
kioskNumberOfPurchases: number;
|
|
32
|
+
}
|
|
33
|
+
export interface Kiosk {
|
|
34
|
+
id: number;
|
|
35
|
+
name: string;
|
|
36
|
+
location: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
isActive: boolean;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
}
|
|
42
|
+
export interface Customer {
|
|
43
|
+
id: number;
|
|
44
|
+
email: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
firstContactAt: string;
|
|
47
|
+
purchasesRequested: number;
|
|
48
|
+
purchasesCompleted: number;
|
|
49
|
+
totalSpent: number;
|
|
50
|
+
}
|
|
51
|
+
export interface KioskInventory {
|
|
52
|
+
id: number;
|
|
53
|
+
kioskId: number;
|
|
54
|
+
productId: number;
|
|
8
55
|
quantityInStock: number;
|
|
56
|
+
active: boolean;
|
|
9
57
|
clickedOn: number;
|
|
58
|
+
qrCodesGenerated: number;
|
|
10
59
|
numberOfPurchases: number;
|
|
60
|
+
lastRestocked?: string;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
updatedAt: string;
|
|
63
|
+
}
|
|
64
|
+
export interface Transaction {
|
|
65
|
+
id: number;
|
|
66
|
+
kioskId: number;
|
|
67
|
+
customerId: number;
|
|
68
|
+
productId?: number;
|
|
69
|
+
requestedAt: string;
|
|
70
|
+
completedAt?: string;
|
|
71
|
+
status: TransactionStatus;
|
|
72
|
+
amount: number;
|
|
73
|
+
paymentId?: string;
|
|
74
|
+
qrCodeData?: string;
|
|
75
|
+
variableSymbol?: string;
|
|
76
|
+
receiptType: ReceiptType;
|
|
77
|
+
fioTransactionId?: string;
|
|
78
|
+
lastFioCheckAt?: string;
|
|
79
|
+
fioCheckCount: number;
|
|
80
|
+
createdAt: string;
|
|
81
|
+
updatedAt: string;
|
|
11
82
|
}
|
|
12
83
|
export interface CartItem {
|
|
13
|
-
product:
|
|
84
|
+
product: KioskProduct;
|
|
14
85
|
quantity: number;
|
|
15
86
|
}
|
|
16
87
|
export interface Cart {
|
|
@@ -25,6 +96,7 @@ export interface PaymentData {
|
|
|
25
96
|
customerEmail: string;
|
|
26
97
|
qrCode: string;
|
|
27
98
|
paymentId: string;
|
|
99
|
+
status?: TransactionStatus;
|
|
28
100
|
}
|
|
29
101
|
export interface MultiProductPaymentData {
|
|
30
102
|
items: CartItem[];
|
|
@@ -32,19 +104,9 @@ export interface MultiProductPaymentData {
|
|
|
32
104
|
customerEmail: string;
|
|
33
105
|
qrCode: string;
|
|
34
106
|
paymentId: string;
|
|
107
|
+
status?: TransactionStatus;
|
|
35
108
|
}
|
|
36
|
-
export interface AdminProduct {
|
|
37
|
-
id: number;
|
|
38
|
-
name: string;
|
|
39
|
-
price: number;
|
|
40
|
-
description: string;
|
|
41
|
-
image: string;
|
|
42
|
-
imageUrl?: string;
|
|
43
|
-
clickedOn: number;
|
|
44
|
-
qrCodesGenerated: number;
|
|
45
|
-
numberOfPurchases: number;
|
|
46
|
-
createdAt: string;
|
|
47
|
-
updatedAt: string;
|
|
109
|
+
export interface AdminProduct extends Product {
|
|
48
110
|
quantityInStock?: number;
|
|
49
111
|
active?: boolean;
|
|
50
112
|
}
|
|
@@ -62,11 +124,116 @@ export interface KioskStatus {
|
|
|
62
124
|
lastSeen: Date;
|
|
63
125
|
salesToday: number;
|
|
64
126
|
}
|
|
65
|
-
export
|
|
66
|
-
|
|
67
|
-
updateType?: string;
|
|
68
|
-
data?: any;
|
|
127
|
+
export type WebSocketMessage = ProductUpdateMessage | PaymentStatusMessage | SystemMessage;
|
|
128
|
+
export interface BaseWebSocketMessage {
|
|
69
129
|
kioskId?: number;
|
|
70
130
|
timestamp?: string;
|
|
71
131
|
}
|
|
132
|
+
export interface ProductUpdateMessage extends BaseWebSocketMessage {
|
|
133
|
+
type: 'product_update';
|
|
134
|
+
updateType: 'product_created' | 'product_updated' | 'product_deleted' | 'inventory_updated' | 'payment_completed' | 'payment_timeout' | 'payment_failed';
|
|
135
|
+
data?: {
|
|
136
|
+
productId?: number;
|
|
137
|
+
name?: string;
|
|
138
|
+
price?: number;
|
|
139
|
+
paymentId?: string;
|
|
140
|
+
amount?: number;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export interface PaymentStatusMessage extends BaseWebSocketMessage {
|
|
144
|
+
type: 'payment_status';
|
|
145
|
+
paymentId: string;
|
|
146
|
+
status: TransactionStatus;
|
|
147
|
+
data?: {
|
|
148
|
+
amount?: number;
|
|
149
|
+
customerEmail?: string;
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
export interface SystemMessage extends BaseWebSocketMessage {
|
|
153
|
+
type: 'system';
|
|
154
|
+
message: string;
|
|
155
|
+
level: 'info' | 'warning' | 'error';
|
|
156
|
+
}
|
|
72
157
|
export type ScreenType = 'products' | 'payment' | 'confirmation' | 'admin-login' | 'admin-dashboard';
|
|
158
|
+
export interface CreateQRPaymentRequest {
|
|
159
|
+
productId: number;
|
|
160
|
+
customerEmail: string;
|
|
161
|
+
kioskId: number;
|
|
162
|
+
}
|
|
163
|
+
export interface CreateQRPaymentResponse {
|
|
164
|
+
paymentId: string;
|
|
165
|
+
qrCodeData: string;
|
|
166
|
+
amount: number;
|
|
167
|
+
customerEmail: string;
|
|
168
|
+
variableSymbol: string;
|
|
169
|
+
}
|
|
170
|
+
export interface CreateMultiQRPaymentRequest {
|
|
171
|
+
items: Array<{
|
|
172
|
+
productId: number;
|
|
173
|
+
quantity: number;
|
|
174
|
+
}>;
|
|
175
|
+
totalAmount: number;
|
|
176
|
+
customerEmail: string;
|
|
177
|
+
kioskId: number;
|
|
178
|
+
}
|
|
179
|
+
export interface CreateMultiQRPaymentResponse {
|
|
180
|
+
paymentId: string;
|
|
181
|
+
qrCodeData: string;
|
|
182
|
+
amount: number;
|
|
183
|
+
customerEmail: string;
|
|
184
|
+
variableSymbol: string;
|
|
185
|
+
}
|
|
186
|
+
export interface PaymentStatusResponse {
|
|
187
|
+
paymentId: string;
|
|
188
|
+
status: TransactionStatus;
|
|
189
|
+
amount: number;
|
|
190
|
+
customerEmail: string;
|
|
191
|
+
requestedAt: string;
|
|
192
|
+
completedAt?: string;
|
|
193
|
+
}
|
|
194
|
+
export interface StartMonitoringRequest {
|
|
195
|
+
paymentId: string;
|
|
196
|
+
}
|
|
197
|
+
export interface StartMonitoringResponse {
|
|
198
|
+
success: boolean;
|
|
199
|
+
message: string;
|
|
200
|
+
}
|
|
201
|
+
export interface ThePayCreateRequest {
|
|
202
|
+
items: Array<{
|
|
203
|
+
productId: number;
|
|
204
|
+
quantity: number;
|
|
205
|
+
price: number;
|
|
206
|
+
}>;
|
|
207
|
+
totalAmount: number;
|
|
208
|
+
customerEmail: string;
|
|
209
|
+
kioskId: number;
|
|
210
|
+
}
|
|
211
|
+
export interface ThePayCreateResponse {
|
|
212
|
+
paymentId: string;
|
|
213
|
+
thepayPaymentId: string;
|
|
214
|
+
paymentUrl: string;
|
|
215
|
+
amount: number;
|
|
216
|
+
customerEmail: string;
|
|
217
|
+
kioskId: number;
|
|
218
|
+
}
|
|
219
|
+
export interface ThePayStatusResponse {
|
|
220
|
+
paymentId: string;
|
|
221
|
+
status: string;
|
|
222
|
+
amount: number;
|
|
223
|
+
customerEmail: string;
|
|
224
|
+
}
|
|
225
|
+
export interface ThePayMethodsResponse {
|
|
226
|
+
methods: Array<{
|
|
227
|
+
name: string;
|
|
228
|
+
enabled: boolean;
|
|
229
|
+
}>;
|
|
230
|
+
}
|
|
231
|
+
export interface InventoryUpdateRequest {
|
|
232
|
+
quantityInStock: number;
|
|
233
|
+
}
|
|
234
|
+
export interface VisibilityToggleRequest {
|
|
235
|
+
visible: boolean;
|
|
236
|
+
}
|
|
237
|
+
export interface ProductClickRequest {
|
|
238
|
+
kioskId: number;
|
|
239
|
+
}
|
package/dist/types.js
CHANGED
|
@@ -1,2 +1,18 @@
|
|
|
1
1
|
// Shared types across all packages
|
|
2
|
-
|
|
2
|
+
// Enums matching backend Prisma schema
|
|
3
|
+
export var TransactionStatus;
|
|
4
|
+
(function (TransactionStatus) {
|
|
5
|
+
TransactionStatus["INITIATED"] = "INITIATED";
|
|
6
|
+
TransactionStatus["PENDING"] = "PENDING";
|
|
7
|
+
TransactionStatus["PROCESSING"] = "PROCESSING";
|
|
8
|
+
TransactionStatus["COMPLETED"] = "COMPLETED";
|
|
9
|
+
TransactionStatus["FAILED"] = "FAILED";
|
|
10
|
+
TransactionStatus["CANCELLED"] = "CANCELLED";
|
|
11
|
+
TransactionStatus["TIMEOUT"] = "TIMEOUT";
|
|
12
|
+
})(TransactionStatus || (TransactionStatus = {}));
|
|
13
|
+
export var ReceiptType;
|
|
14
|
+
(function (ReceiptType) {
|
|
15
|
+
ReceiptType["PLAIN"] = "PLAIN";
|
|
16
|
+
ReceiptType["INVOICE"] = "INVOICE";
|
|
17
|
+
ReceiptType["PROFORMA"] = "PROFORMA";
|
|
18
|
+
})(ReceiptType || (ReceiptType = {}));
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare const validateEmail: (email: string) => boolean;
|
|
2
2
|
export declare const formatPrice: (price: number) => string;
|
|
3
|
+
export declare const PRICE_PRECISION = 2;
|
|
4
|
+
export declare const roundPrice: (price: number) => number;
|
|
5
|
+
export declare const parsePrice: (value: string | number) => number;
|
|
6
|
+
export declare const validatePrice: (price: number) => boolean;
|
|
7
|
+
export declare const priceToString: (price: number) => string;
|
|
8
|
+
export declare const stringToPrice: (priceStr: string) => number;
|
|
3
9
|
export declare const getKioskIdFromUrl: () => number;
|
|
4
10
|
export declare const getKioskSecretFromUrl: () => string | undefined;
|
|
5
11
|
export declare const formatDate: (date: Date | string) => string;
|
package/dist/utils.js
CHANGED
|
@@ -7,7 +7,32 @@ export const validateEmail = (email) => {
|
|
|
7
7
|
return emailRegex.test(email);
|
|
8
8
|
};
|
|
9
9
|
export const formatPrice = (price) => {
|
|
10
|
-
return
|
|
10
|
+
return new Intl.NumberFormat('cs-CZ', {
|
|
11
|
+
style: 'currency',
|
|
12
|
+
currency: 'CZK',
|
|
13
|
+
minimumFractionDigits: 2,
|
|
14
|
+
maximumFractionDigits: 2
|
|
15
|
+
}).format(price);
|
|
16
|
+
};
|
|
17
|
+
// Price precision handling utilities
|
|
18
|
+
export const PRICE_PRECISION = 2; // 2 decimal places for CZK
|
|
19
|
+
export const roundPrice = (price) => {
|
|
20
|
+
return Math.round(price * 100) / 100;
|
|
21
|
+
};
|
|
22
|
+
export const parsePrice = (value) => {
|
|
23
|
+
const num = typeof value === 'string' ? parseFloat(value) : value;
|
|
24
|
+
return roundPrice(num);
|
|
25
|
+
};
|
|
26
|
+
export const validatePrice = (price) => {
|
|
27
|
+
return !isNaN(price) && price > 0 && price <= 999999.99;
|
|
28
|
+
};
|
|
29
|
+
// Convert decimal to string for API calls to avoid precision issues
|
|
30
|
+
export const priceToString = (price) => {
|
|
31
|
+
return price.toFixed(PRICE_PRECISION);
|
|
32
|
+
};
|
|
33
|
+
// Convert string back to number for calculations
|
|
34
|
+
export const stringToPrice = (priceStr) => {
|
|
35
|
+
return parseFloat(priceStr);
|
|
11
36
|
};
|
|
12
37
|
export const getKioskIdFromUrl = () => {
|
|
13
38
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -62,13 +87,13 @@ export const addToCart = (cart, product, quantity = 1) => {
|
|
|
62
87
|
else {
|
|
63
88
|
cart.items.push({ product, quantity });
|
|
64
89
|
}
|
|
65
|
-
cart.totalAmount = cart.items.reduce((sum, item) => sum + (item.product.price * item.quantity), 0);
|
|
90
|
+
cart.totalAmount = roundPrice(cart.items.reduce((sum, item) => sum + (item.product.price * item.quantity), 0));
|
|
66
91
|
cart.totalItems = cart.items.reduce((sum, item) => sum + item.quantity, 0);
|
|
67
92
|
return cart;
|
|
68
93
|
};
|
|
69
94
|
export const removeFromCart = (cart, productId) => {
|
|
70
95
|
cart.items = cart.items.filter((item) => item.product.id !== productId);
|
|
71
|
-
cart.totalAmount = cart.items.reduce((sum, item) => sum + (item.product.price * item.quantity), 0);
|
|
96
|
+
cart.totalAmount = roundPrice(cart.items.reduce((sum, item) => sum + (item.product.price * item.quantity), 0));
|
|
72
97
|
cart.totalItems = cart.items.reduce((sum, item) => sum + item.quantity, 0);
|
|
73
98
|
return cart;
|
|
74
99
|
};
|
|
@@ -79,7 +104,7 @@ export const updateCartItemQuantity = (cart, productId, quantity) => {
|
|
|
79
104
|
return removeFromCart(cart, productId);
|
|
80
105
|
}
|
|
81
106
|
item.quantity = quantity;
|
|
82
|
-
cart.totalAmount = cart.items.reduce((sum, item) => sum + (item.product.price * item.quantity), 0);
|
|
107
|
+
cart.totalAmount = roundPrice(cart.items.reduce((sum, item) => sum + (item.product.price * item.quantity), 0));
|
|
83
108
|
cart.totalItems = cart.items.reduce((sum, item) => sum + item.quantity, 0);
|
|
84
109
|
}
|
|
85
110
|
return cart;
|
package/dist/validation.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export declare const validators: {
|
|
|
10
10
|
positiveNumber: (value: number, fieldName: string) => string | null;
|
|
11
11
|
username: (value: string) => string | null;
|
|
12
12
|
password: (value: string) => string | null;
|
|
13
|
+
kioskId: (value: number) => string | null;
|
|
14
|
+
quantity: (value: number, fieldName?: string) => string | null;
|
|
15
|
+
price: (value: number, fieldName?: string) => string | null;
|
|
13
16
|
};
|
|
14
17
|
export interface ValidationSchema {
|
|
15
18
|
[field: string]: Array<(value: any) => string | null>;
|
|
@@ -25,7 +28,18 @@ export declare const validationSchemas: {
|
|
|
25
28
|
};
|
|
26
29
|
product: {
|
|
27
30
|
name: ((value: any) => string | null)[];
|
|
28
|
-
price: ((value:
|
|
31
|
+
price: ((value: number, fieldName?: string) => string | null)[];
|
|
29
32
|
description: ((value: any) => string | null)[];
|
|
30
33
|
};
|
|
34
|
+
kioskId: {
|
|
35
|
+
kioskId: ((value: number) => string | null)[];
|
|
36
|
+
};
|
|
37
|
+
inventory: {
|
|
38
|
+
quantityInStock: ((value: number, fieldName?: string) => string | null)[];
|
|
39
|
+
};
|
|
40
|
+
payment: {
|
|
41
|
+
productId: ((value: any) => string | null)[];
|
|
42
|
+
kioskId: ((value: number) => string | null)[];
|
|
43
|
+
amount: ((value: number, fieldName?: string) => string | null)[];
|
|
44
|
+
};
|
|
31
45
|
};
|
package/dist/validation.js
CHANGED
|
@@ -59,6 +59,39 @@ export const validators = {
|
|
|
59
59
|
return `Heslo musí mít alespoň ${APP_CONFIG.MIN_PASSWORD_LENGTH} znaků`;
|
|
60
60
|
}
|
|
61
61
|
return null;
|
|
62
|
+
},
|
|
63
|
+
kioskId: (value) => {
|
|
64
|
+
if (value === null || value === undefined)
|
|
65
|
+
return null;
|
|
66
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
67
|
+
return 'Kiosk ID musí být kladné celé číslo';
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
},
|
|
71
|
+
quantity: (value, fieldName = 'Množství') => {
|
|
72
|
+
if (value === null || value === undefined)
|
|
73
|
+
return null;
|
|
74
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
75
|
+
return `${fieldName} musí být nezáporné celé číslo`;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
},
|
|
79
|
+
price: (value, fieldName = 'Cena') => {
|
|
80
|
+
if (value === null || value === undefined)
|
|
81
|
+
return null;
|
|
82
|
+
if (isNaN(value) || value <= 0) {
|
|
83
|
+
return `${fieldName} musí být kladné číslo`;
|
|
84
|
+
}
|
|
85
|
+
// Check for reasonable price range (0.01 to 999999.99)
|
|
86
|
+
if (value < 0.01 || value > 999999.99) {
|
|
87
|
+
return `${fieldName} musí být v rozsahu 0,01 - 999 999,99 Kč`;
|
|
88
|
+
}
|
|
89
|
+
// Check for precision (max 2 decimal places)
|
|
90
|
+
const rounded = Math.round(value * 100) / 100;
|
|
91
|
+
if (Math.abs(value - rounded) > 0.001) {
|
|
92
|
+
return `${fieldName} může mít maximálně 2 desetinná místa`;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
62
95
|
}
|
|
63
96
|
};
|
|
64
97
|
export const validateSchema = (data, schema) => {
|
|
@@ -104,10 +137,36 @@ export const validationSchemas = {
|
|
|
104
137
|
],
|
|
105
138
|
price: [
|
|
106
139
|
(value) => validators.required(value),
|
|
107
|
-
|
|
140
|
+
validators.price
|
|
108
141
|
],
|
|
109
142
|
description: [
|
|
110
143
|
(value) => validators.maxLength(value, APP_CONFIG.MAX_DESCRIPTION_LENGTH, 'Popis')
|
|
111
144
|
]
|
|
145
|
+
},
|
|
146
|
+
kioskId: {
|
|
147
|
+
kioskId: [
|
|
148
|
+
(value) => validators.required(value),
|
|
149
|
+
validators.kioskId
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
inventory: {
|
|
153
|
+
quantityInStock: [
|
|
154
|
+
(value) => validators.required(value),
|
|
155
|
+
validators.quantity
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
payment: {
|
|
159
|
+
productId: [
|
|
160
|
+
(value) => validators.required(value),
|
|
161
|
+
(value) => validators.positiveNumber(value, 'ID produktu')
|
|
162
|
+
],
|
|
163
|
+
kioskId: [
|
|
164
|
+
(value) => validators.required(value),
|
|
165
|
+
validators.kioskId
|
|
166
|
+
],
|
|
167
|
+
amount: [
|
|
168
|
+
(value) => validators.required(value),
|
|
169
|
+
validators.price
|
|
170
|
+
]
|
|
112
171
|
}
|
|
113
172
|
};
|