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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Tests for shared utilities
|
|
2
|
+
import { validateEmail, formatPrice, generatePaymentId, getErrorMessage } from '../utils';
|
|
3
|
+
describe('Shared Utilities', () => {
|
|
4
|
+
describe('validateEmail', () => {
|
|
5
|
+
test('validates correct email addresses', () => {
|
|
6
|
+
expect(validateEmail('test@example.com')).toBe(true);
|
|
7
|
+
expect(validateEmail('user.name+tag@domain.co.uk')).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
test('rejects invalid email addresses', () => {
|
|
10
|
+
expect(validateEmail('')).toBe(false);
|
|
11
|
+
expect(validateEmail('invalid')).toBe(false);
|
|
12
|
+
expect(validateEmail('test@')).toBe(false);
|
|
13
|
+
expect(validateEmail('@domain.com')).toBe(false);
|
|
14
|
+
expect(validateEmail('test..test@domain.com')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('formatPrice', () => {
|
|
18
|
+
test('formats prices correctly', () => {
|
|
19
|
+
expect(formatPrice(100)).toBe('100 Kč');
|
|
20
|
+
expect(formatPrice(25.5)).toBe('25.5 Kč');
|
|
21
|
+
expect(formatPrice(0)).toBe('0 Kč');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('generatePaymentId', () => {
|
|
25
|
+
test('generates unique payment IDs', () => {
|
|
26
|
+
const id1 = generatePaymentId();
|
|
27
|
+
const id2 = generatePaymentId();
|
|
28
|
+
expect(id1).toMatch(/^pay-\d+$/);
|
|
29
|
+
expect(id2).toMatch(/^pay-\d+$/);
|
|
30
|
+
expect(id1).not.toBe(id2);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('getErrorMessage', () => {
|
|
34
|
+
test('returns user-friendly error messages', () => {
|
|
35
|
+
const fetchError = new Error('Failed to fetch');
|
|
36
|
+
expect(getErrorMessage(fetchError)).toBe('Problém s připojením. Zkuste to znovu.');
|
|
37
|
+
const authError = new Error('401 Unauthorized');
|
|
38
|
+
expect(getErrorMessage(authError)).toBe('Neplatné přihlašovací údaje.');
|
|
39
|
+
const genericError = new Error('Something went wrong');
|
|
40
|
+
expect(getErrorMessage(genericError)).toBe('Something went wrong');
|
|
41
|
+
const emptyError = new Error('');
|
|
42
|
+
expect(getErrorMessage(emptyError)).toBe('Něco se pokazilo. Zkuste to znovu.');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const validateEmail: (email: string) => boolean;
|
|
2
|
+
export declare const formatPrice: (price: number) => string;
|
|
3
|
+
export declare const getKioskIdFromUrl: () => number;
|
|
4
|
+
export declare const getKioskSecretFromUrl: () => string | undefined;
|
|
5
|
+
export declare const formatDate: (date: Date | string) => string;
|
|
6
|
+
export declare const debounce: <T extends (...args: any[]) => any>(func: T, delay: number) => ((...args: Parameters<T>) => void);
|
|
7
|
+
export declare const generatePaymentId: () => string;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Shared utility functions - consolidated from utils/simple.ts
|
|
2
|
+
export const validateEmail = (email) => {
|
|
3
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4
|
+
// Additional check for consecutive dots which are invalid
|
|
5
|
+
if (email.includes('..'))
|
|
6
|
+
return false;
|
|
7
|
+
return emailRegex.test(email);
|
|
8
|
+
};
|
|
9
|
+
export const formatPrice = (price) => {
|
|
10
|
+
return `${price} Kč`;
|
|
11
|
+
};
|
|
12
|
+
export const getKioskIdFromUrl = () => {
|
|
13
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
14
|
+
return parseInt(urlParams.get('kioskId') || '1');
|
|
15
|
+
};
|
|
16
|
+
export const getKioskSecretFromUrl = () => {
|
|
17
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
18
|
+
return urlParams.get('secret') || undefined;
|
|
19
|
+
};
|
|
20
|
+
export const formatDate = (date) => {
|
|
21
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
22
|
+
return d.toLocaleDateString('cs-CZ', {
|
|
23
|
+
year: 'numeric',
|
|
24
|
+
month: 'short',
|
|
25
|
+
day: 'numeric',
|
|
26
|
+
hour: '2-digit',
|
|
27
|
+
minute: '2-digit'
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
export const debounce = (func, delay) => {
|
|
31
|
+
let timeoutId;
|
|
32
|
+
return (...args) => {
|
|
33
|
+
clearTimeout(timeoutId);
|
|
34
|
+
timeoutId = setTimeout(() => func(...args), delay);
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export const generatePaymentId = () => {
|
|
38
|
+
return `pay-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
39
|
+
};
|
|
40
|
+
// Note: getErrorMessage is now exported from ./errors.ts instead
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
isValid: boolean;
|
|
3
|
+
errors: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
export declare const validators: {
|
|
6
|
+
required: (value: any, fieldName: string) => string | null;
|
|
7
|
+
email: (value: string) => string | null;
|
|
8
|
+
minLength: (value: string, min: number, fieldName: string) => string | null;
|
|
9
|
+
maxLength: (value: string, max: number, fieldName: string) => string | null;
|
|
10
|
+
positiveNumber: (value: number, fieldName: string) => string | null;
|
|
11
|
+
username: (value: string) => string | null;
|
|
12
|
+
password: (value: string) => string | null;
|
|
13
|
+
};
|
|
14
|
+
export interface ValidationSchema {
|
|
15
|
+
[field: string]: Array<(value: any) => string | null>;
|
|
16
|
+
}
|
|
17
|
+
export declare const validateSchema: (data: Record<string, any>, schema: ValidationSchema) => ValidationResult;
|
|
18
|
+
export declare const validationSchemas: {
|
|
19
|
+
login: {
|
|
20
|
+
username: ((value: string) => string | null)[];
|
|
21
|
+
password: ((value: string) => string | null)[];
|
|
22
|
+
};
|
|
23
|
+
customerEmail: {
|
|
24
|
+
email: ((value: string) => string | null)[];
|
|
25
|
+
};
|
|
26
|
+
product: {
|
|
27
|
+
name: ((value: any) => string | null)[];
|
|
28
|
+
price: ((value: any) => string | null)[];
|
|
29
|
+
description: ((value: any) => string | null)[];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Shared validation utilities
|
|
2
|
+
// ValidationError is imported but not used directly in this file
|
|
3
|
+
import { APP_CONFIG, UI_MESSAGES } from './constants';
|
|
4
|
+
export const validators = {
|
|
5
|
+
required: (value, fieldName) => {
|
|
6
|
+
if (value === null || value === undefined || value === '') {
|
|
7
|
+
return `${fieldName} ${UI_MESSAGES.REQUIRED_FIELD.toLowerCase()}`;
|
|
8
|
+
}
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
email: (value) => {
|
|
12
|
+
if (!value)
|
|
13
|
+
return null; // Let required validator handle empty values
|
|
14
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
15
|
+
if (!emailRegex.test(value)) {
|
|
16
|
+
return UI_MESSAGES.INVALID_EMAIL;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
},
|
|
20
|
+
minLength: (value, min, fieldName) => {
|
|
21
|
+
if (!value)
|
|
22
|
+
return null; // Let required validator handle empty values
|
|
23
|
+
if (value.length < min) {
|
|
24
|
+
return `${fieldName} musí mít alespoň ${min} znaků`;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
},
|
|
28
|
+
maxLength: (value, max, fieldName) => {
|
|
29
|
+
if (!value)
|
|
30
|
+
return null;
|
|
31
|
+
if (value.length > max) {
|
|
32
|
+
return `${fieldName} může mít maximálně ${max} znaků`;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
},
|
|
36
|
+
positiveNumber: (value, fieldName) => {
|
|
37
|
+
if (value === null || value === undefined)
|
|
38
|
+
return null;
|
|
39
|
+
if (value <= 0) {
|
|
40
|
+
return `${fieldName} musí být větší než 0`;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
},
|
|
44
|
+
username: (value) => {
|
|
45
|
+
if (!value)
|
|
46
|
+
return null;
|
|
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`;
|
|
49
|
+
}
|
|
50
|
+
if (!/^[a-zA-Z0-9_]+$/.test(value)) {
|
|
51
|
+
return 'Uživatelské jméno může obsahovat pouze písmena, číslice a podtržítka';
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
},
|
|
55
|
+
password: (value) => {
|
|
56
|
+
if (!value)
|
|
57
|
+
return null;
|
|
58
|
+
if (value.length < APP_CONFIG.MIN_PASSWORD_LENGTH) {
|
|
59
|
+
return `Heslo musí mít alespoň ${APP_CONFIG.MIN_PASSWORD_LENGTH} znaků`;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
export const validateSchema = (data, schema) => {
|
|
65
|
+
const errors = {};
|
|
66
|
+
for (const [field, validatorFunctions] of Object.entries(schema)) {
|
|
67
|
+
const value = data[field];
|
|
68
|
+
for (const validator of validatorFunctions) {
|
|
69
|
+
const error = validator(value);
|
|
70
|
+
if (error) {
|
|
71
|
+
errors[field] = error;
|
|
72
|
+
break; // Stop at first error for this field
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
isValid: Object.keys(errors).length === 0,
|
|
78
|
+
errors
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
// Common validation schemas
|
|
82
|
+
export const validationSchemas = {
|
|
83
|
+
login: {
|
|
84
|
+
username: [
|
|
85
|
+
(value) => validators.required(value, 'Uživatelské jméno'),
|
|
86
|
+
validators.username
|
|
87
|
+
],
|
|
88
|
+
password: [
|
|
89
|
+
(value) => validators.required(value, 'Heslo'),
|
|
90
|
+
validators.password
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
customerEmail: {
|
|
94
|
+
email: [
|
|
95
|
+
(value) => validators.required(value, 'Email'),
|
|
96
|
+
validators.email
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
product: {
|
|
100
|
+
name: [
|
|
101
|
+
(value) => validators.required(value, 'Název produktu'),
|
|
102
|
+
(value) => validators.minLength(value, 2, 'Název produktu'),
|
|
103
|
+
(value) => validators.maxLength(value, APP_CONFIG.MAX_PRODUCT_NAME_LENGTH, 'Název produktu')
|
|
104
|
+
],
|
|
105
|
+
price: [
|
|
106
|
+
(value) => validators.required(value, 'Cena'),
|
|
107
|
+
(value) => validators.positiveNumber(value, 'Cena')
|
|
108
|
+
],
|
|
109
|
+
description: [
|
|
110
|
+
(value) => validators.maxLength(value, APP_CONFIG.MAX_DESCRIPTION_LENGTH, 'Popis')
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-kiosk-shared",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Shared components and utilities for Pi Kiosk system",
|
|
6
|
+
"keywords": ["kiosk", "react", "typescript", "components"],
|
|
7
|
+
"author": "tehacko@seznam.cz",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/tehacko/pi-kiosk-shared"
|
|
12
|
+
},
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"test": "jest",
|
|
19
|
+
"test:watch": "jest --watch",
|
|
20
|
+
"test:coverage": "jest --coverage",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"preversion": "npm run test",
|
|
23
|
+
"postversion": "git push && git push --tags"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"react": "^18.2.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.0.0",
|
|
34
|
+
"@types/jest": "^29.5.0",
|
|
35
|
+
"@types/node": "^20.0.0",
|
|
36
|
+
"@types/react": "^18.2.0",
|
|
37
|
+
"jest": "^29.5.0",
|
|
38
|
+
"ts-jest": "^29.1.0"
|
|
39
|
+
}
|
|
40
|
+
}
|