@temboplus/frontend-react-core 0.1.3-beta.38 → 0.1.3-beta.39
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 +72 -338
- package/dist/index.cjs.js +15 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +4 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/timezone/index.cjs.js +16 -0
- package/dist/{notifications → timezone}/index.cjs.js.map +1 -1
- package/dist/timezone/index.d.ts +3 -0
- package/dist/timezone/index.js +5 -0
- package/dist/{notifications → timezone}/index.js.map +1 -1
- package/dist/timezone/use-format-date.d.ts +22 -0
- package/dist/timezone/use-timezone-date.d.ts +17 -0
- package/dist/timezone/use-timezone.d.ts +19 -0
- package/dist/use-format-date--9gcnP7_.js +82 -0
- package/dist/use-format-date--9gcnP7_.js.map +1 -0
- package/dist/use-format-date-BvwtJTnU.js +89 -0
- package/dist/use-format-date-BvwtJTnU.js.map +1 -0
- package/package.json +23 -51
- package/dist/InfoCircleOutlined-B4_-Z3H4.js +0 -6
- package/dist/InfoCircleOutlined-B4_-Z3H4.js.map +0 -1
- package/dist/InfoCircleOutlined-pKxRobXG.js +0 -6
- package/dist/InfoCircleOutlined-pKxRobXG.js.map +0 -1
- package/dist/alerts/index.cjs.js +0 -2
- package/dist/alerts/index.cjs.js.map +0 -1
- package/dist/alerts/index.d.ts +0 -1
- package/dist/alerts/index.js +0 -2
- package/dist/alerts/index.js.map +0 -1
- package/dist/dialogs/index.cjs.js +0 -2
- package/dist/dialogs/index.cjs.js.map +0 -1
- package/dist/dialogs/index.d.ts +0 -1
- package/dist/dialogs/index.js +0 -2
- package/dist/dialogs/index.js.map +0 -1
- package/dist/features/alerts/alert.d.ts +0 -12
- package/dist/features/alerts/alert.js +0 -95
- package/dist/features/alerts/index.d.ts +0 -1
- package/dist/features/alerts/index.js +0 -1
- package/dist/features/dialogs/index.d.ts +0 -1
- package/dist/features/dialogs/index.js +0 -1
- package/dist/features/dialogs/modal-provider.d.ts +0 -3
- package/dist/features/dialogs/modal-provider.js +0 -6
- package/dist/features/dialogs/tembo-confirm.d.ts +0 -63
- package/dist/features/dialogs/tembo-confirm.js +0 -98
- package/dist/features/input-validation/account-name-validator.d.ts +0 -13
- package/dist/features/input-validation/account-name-validator.js +0 -28
- package/dist/features/input-validation/account-number-validator.d.ts +0 -13
- package/dist/features/input-validation/account-number-validator.js +0 -65
- package/dist/features/input-validation/amount-validator.d.ts +0 -78
- package/dist/features/input-validation/amount-validator.js +0 -100
- package/dist/features/input-validation/index.d.ts +0 -5
- package/dist/features/input-validation/index.js +0 -5
- package/dist/features/input-validation/phone-number-validator.d.ts +0 -25
- package/dist/features/input-validation/phone-number-validator.js +0 -79
- package/dist/features/input-validation/swift-code-validator.d.ts +0 -13
- package/dist/features/input-validation/swift-code-validator.js +0 -38
- package/dist/features/notifications/index.d.ts +0 -3
- package/dist/features/notifications/index.js +0 -3
- package/dist/features/notifications/tembo-notify.d.ts +0 -51
- package/dist/features/notifications/tembo-notify.js +0 -140
- package/dist/features/notifications/toast-config.d.ts +0 -13
- package/dist/features/notifications/toast-config.js +0 -60
- package/dist/features/notifications/toast-container.d.ts +0 -19
- package/dist/features/notifications/toast-container.js +0 -104
- package/dist/index.js +0 -1
- package/dist/notifications/index.cjs.js +0 -2
- package/dist/notifications/index.d.ts +0 -1
- package/dist/notifications/index.js +0 -2
- package/dist/providers.d.ts +0 -7
- package/dist/providers.js +0 -11
- package/dist/tembo-notify-By9oLCSX.js +0 -3
- package/dist/tembo-notify-By9oLCSX.js.map +0 -1
- package/dist/tembo-notify-CssdTeIP.js +0 -3
- package/dist/tembo-notify-CssdTeIP.js.map +0 -1
- package/dist/theme/deprecated/colors.d.ts +0 -278
- package/dist/theme/deprecated/colors.js +0 -212
- package/dist/theme/deprecated/constants.d.ts +0 -143
- package/dist/theme/deprecated/constants.js +0 -82
- package/dist/theme/deprecated/theme-provider.d.ts +0 -100
- package/dist/theme/deprecated/theme-provider.js +0 -405
- package/dist/theme/index.cjs.js +0 -2
- package/dist/theme/index.cjs.js.map +0 -1
- package/dist/theme/index.d.ts +0 -4
- package/dist/theme/index.js +0 -2
- package/dist/theme/index.js.map +0 -1
- package/dist/theme/theme-config.d.ts +0 -15
- package/dist/theme/theme-config.js +0 -735
- package/dist/theme/theme-provider.d.ts +0 -29
- package/dist/theme/theme-provider.js +0 -32
- package/dist/theme/tokens/colors.d.ts +0 -153
- package/dist/theme/tokens/colors.js +0 -306
- package/dist/theme/tokens/constants.d.ts +0 -136
- package/dist/theme/tokens/constants.js +0 -137
- package/dist/theme-provider-XqWasApp.js +0 -11
- package/dist/theme-provider-XqWasApp.js.map +0 -1
- package/dist/theme-provider-c4R_KW4X.js +0 -2
- package/dist/theme-provider-c4R_KW4X.js.map +0 -1
- package/dist/theme-provider-slJZwhTc.js +0 -11
- package/dist/theme-provider-slJZwhTc.js.map +0 -1
- package/dist/theme-provider-slTbQLX5.js +0 -2
- package/dist/theme-provider-slTbQLX5.js.map +0 -1
- package/dist/validation/index.cjs.js +0 -2
- package/dist/validation/index.cjs.js.map +0 -1
- package/dist/validation/index.d.ts +0 -1
- package/dist/validation/index.js +0 -2
- package/dist/validation/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,385 +1,119 @@
|
|
|
1
1
|
# @temboplus/frontend-react-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React hooks that wrap pure helpers from [`@temboplus/frontend-core`](https://github.com/TemboPlus-Frontend/frontend-core-js) for use in React applications across the TemboPlus platform.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Today the package ships one feature area — timezone — with more to be added as React-flavored counterparts to frontend-core utilities are needed.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
|
|
10
|
+
bun add @temboplus/frontend-react-core
|
|
9
11
|
# or
|
|
10
|
-
|
|
11
|
-
# or
|
|
12
|
-
pnpm add @temboplus/frontend-react-core
|
|
12
|
+
npm install @temboplus/frontend-react-core
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
This package requires the following peer dependencies:
|
|
15
|
+
Peer dependency:
|
|
18
16
|
|
|
19
17
|
```bash
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## 🚀 Quick Start
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import { PHONE_NUMBER_VALIDATOR, AMOUNT_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
27
|
-
|
|
28
|
-
// Use in your Ant Design form
|
|
29
|
-
const formRules = {
|
|
30
|
-
phoneNumber: [{ required: true, validator: PHONE_NUMBER_VALIDATOR('TZ') }],
|
|
31
|
-
amount: [{ required: true, validator: AMOUNT_VALIDATOR({ currencyCode: 'TZS' }) }]
|
|
32
|
-
};
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## 📚 Features
|
|
36
|
-
|
|
37
|
-
### ✅ Form Validators (Available Now)
|
|
38
|
-
|
|
39
|
-
Type-safe, country-aware form validators for Ant Design forms with built-in internationalization and error messaging.
|
|
40
|
-
|
|
41
|
-
### 🔄 Coming Soon
|
|
42
|
-
|
|
43
|
-
- **Reusable React Components** - Pre-built UI components for financial applications
|
|
44
|
-
- **Hooks Library** - Custom React hooks for common patterns
|
|
45
|
-
- **Context Providers** - Application-wide state management
|
|
46
|
-
- **Theme System** - Consistent design tokens and styling
|
|
47
|
-
- **Utility Functions** - Helper functions for React applications
|
|
48
|
-
|
|
49
|
-
## 📋 API Reference
|
|
50
|
-
|
|
51
|
-
### Form Validators
|
|
52
|
-
|
|
53
|
-
All validators follow the Ant Design validator pattern and return Promise-based validation results.
|
|
54
|
-
|
|
55
|
-
#### `PHONE_NUMBER_VALIDATOR(countryCode?: ISO2CountryCode)`
|
|
56
|
-
|
|
57
|
-
Validates general phone number format using libphonenumber-js.
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
import { PHONE_NUMBER_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
61
|
-
|
|
62
|
-
// Basic usage
|
|
63
|
-
const rules = [{ required: true, validator: PHONE_NUMBER_VALIDATOR('TZ') }];
|
|
64
|
-
|
|
65
|
-
// Supported countries: TZ, KE, and all libphonenumber-js supported countries
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**Features:**
|
|
69
|
-
- Validates phone number format for any country
|
|
70
|
-
- Returns normalized E.164 format on success
|
|
71
|
-
- Country-specific error messages
|
|
72
|
-
- Supports international and local number formats
|
|
73
|
-
|
|
74
|
-
#### `MOBILE_PHONE_VALIDATOR(countryCode?: ISO2CountryCode)`
|
|
75
|
-
|
|
76
|
-
Validates mobile phone numbers eligible for payout operations (stricter validation).
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
import { MOBILE_PHONE_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
80
|
-
|
|
81
|
-
const rules = [{ required: true, validator: MOBILE_PHONE_VALIDATOR('TZ') }];
|
|
18
|
+
bun add react
|
|
82
19
|
```
|
|
83
20
|
|
|
84
|
-
|
|
85
|
-
- Validates mobile-specific number formats
|
|
86
|
-
- Checks payout eligibility (supports TZ and KE)
|
|
87
|
-
- MNO (Mobile Network Operator) validation
|
|
88
|
-
- Returns normalized E.164 format
|
|
21
|
+
Runtime dependency (auto-installed):
|
|
89
22
|
|
|
90
|
-
|
|
23
|
+
- `@temboplus/frontend-core` — the pure utilities and domain entities the hooks wrap.
|
|
91
24
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
```typescript
|
|
95
|
-
import { ACCOUNT_NAME_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
96
|
-
|
|
97
|
-
const rules = [{ required: true, validator: ACCOUNT_NAME_VALIDATOR }];
|
|
98
|
-
```
|
|
25
|
+
## Subpath imports
|
|
99
26
|
|
|
100
|
-
|
|
101
|
-
- 3-50 characters length
|
|
102
|
-
- Only letters, spaces, hyphens, apostrophes, commas
|
|
103
|
-
- At least two words (first name + last name)
|
|
104
|
-
- Not all uppercase
|
|
105
|
-
- No numbers or special characters
|
|
27
|
+
Import the focused surface or the root entry; both work:
|
|
106
28
|
|
|
107
|
-
|
|
29
|
+
```ts
|
|
30
|
+
// Focused (recommended — keeps your bundles honest):
|
|
31
|
+
import { useTimezone } from "@temboplus/frontend-react-core/timezone";
|
|
108
32
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
import { SWIFT_CODE_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
113
|
-
|
|
114
|
-
// Country-specific validation
|
|
115
|
-
const rules = [{ required: true, validator: SWIFT_CODE_VALIDATOR('TZ') }];
|
|
116
|
-
|
|
117
|
-
// Multi-country validation
|
|
118
|
-
const rules = [{ required: true, validator: SWIFT_CODE_VALIDATOR() }];
|
|
33
|
+
// Or the root:
|
|
34
|
+
import { useTimezone } from "@temboplus/frontend-react-core";
|
|
119
35
|
```
|
|
120
36
|
|
|
121
|
-
|
|
122
|
-
- Validates against known bank SWIFT codes
|
|
123
|
-
- Country-specific validation (TZ, KE)
|
|
124
|
-
- Automatic uppercase conversion
|
|
125
|
-
- Real bank verification
|
|
37
|
+
## Timezone hooks
|
|
126
38
|
|
|
127
|
-
|
|
39
|
+
Each app passes its own `storageKey` so timezone preferences don't collide across products sharing a browser origin. Wrap once in your app and use the bare hook everywhere:
|
|
128
40
|
|
|
129
|
-
|
|
41
|
+
```ts
|
|
42
|
+
// src/shared/lib/timezone.ts (or similar)
|
|
43
|
+
import {
|
|
44
|
+
useTimezone as useTimezoneBase,
|
|
45
|
+
useTimezoneDate as useTimezoneDateBase,
|
|
46
|
+
useFormatDate as useFormatDateBase,
|
|
47
|
+
useFormatDateTime as useFormatDateTimeBase,
|
|
48
|
+
useFormatTime as useFormatTimeBase,
|
|
49
|
+
} from "@temboplus/frontend-react-core/timezone";
|
|
130
50
|
|
|
131
|
-
|
|
132
|
-
import { ACCOUNT_NUMBER_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
51
|
+
const STORAGE_KEY = "myapp.timezone";
|
|
133
52
|
|
|
134
|
-
const
|
|
53
|
+
export const useTimezone = () => useTimezoneBase({ storageKey: STORAGE_KEY });
|
|
54
|
+
export const useTimezoneDate = () => useTimezoneDateBase({ storageKey: STORAGE_KEY });
|
|
55
|
+
export const useFormatDate = () => useFormatDateBase({ storageKey: STORAGE_KEY });
|
|
56
|
+
export const useFormatDateTime = () => useFormatDateTimeBase({ storageKey: STORAGE_KEY });
|
|
57
|
+
export const useFormatTime = () => useFormatTimeBase({ storageKey: STORAGE_KEY });
|
|
135
58
|
```
|
|
136
59
|
|
|
137
|
-
|
|
138
|
-
- **Kenya (KE)**: 10, 12, or 15 alphanumeric characters
|
|
139
|
-
- **Tanzania (TZ)**: 9-19 characters
|
|
140
|
-
- Automatic space removal and normalization
|
|
141
|
-
|
|
142
|
-
#### `AMOUNT_VALIDATOR(options?: AmountValidatorOptions)`
|
|
143
|
-
|
|
144
|
-
Validates monetary amounts with currency-specific constraints.
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
import { AMOUNT_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
148
|
-
|
|
149
|
-
// Basic usage with defaults
|
|
150
|
-
const rules = [{ required: true, validator: AMOUNT_VALIDATOR() }];
|
|
151
|
-
|
|
152
|
-
// Custom configuration
|
|
153
|
-
const rules = [{
|
|
154
|
-
required: true,
|
|
155
|
-
validator: AMOUNT_VALIDATOR({
|
|
156
|
-
currencyCode: 'KES',
|
|
157
|
-
min: 100,
|
|
158
|
-
max: 50000,
|
|
159
|
-
invalidFormatMessage: 'Please enter a valid amount',
|
|
160
|
-
minMessage: 'Minimum amount is 100 KES',
|
|
161
|
-
maxMessage: 'Maximum amount is 50,000 KES'
|
|
162
|
-
})
|
|
163
|
-
}];
|
|
164
|
-
|
|
165
|
-
// Disable constraints
|
|
166
|
-
const rules = [{
|
|
167
|
-
required: true,
|
|
168
|
-
validator: AMOUNT_VALIDATOR({
|
|
169
|
-
currencyCode: 'USD',
|
|
170
|
-
min: null, // No minimum
|
|
171
|
-
max: null, // No maximum
|
|
172
|
-
})
|
|
173
|
-
}];
|
|
174
|
-
```
|
|
60
|
+
### `useTimezone()`
|
|
175
61
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
- **KES**: Min: 40, Max: 40,000,000
|
|
179
|
-
- **Other**: Min: 1, Max: 1,000,000
|
|
180
|
-
|
|
181
|
-
**Options:**
|
|
182
|
-
```typescript
|
|
183
|
-
interface AmountValidatorOptions {
|
|
184
|
-
currencyCode?: CurrencyCode; // Default: 'TZS'
|
|
185
|
-
min?: number | null; // Default: currency-specific
|
|
186
|
-
max?: number | null; // Default: currency-specific
|
|
187
|
-
invalidFormatMessage?: string; // Custom error message
|
|
188
|
-
minMessage?: string; // Custom minimum error
|
|
189
|
-
maxMessage?: string; // Custom maximum error
|
|
190
|
-
}
|
|
62
|
+
```ts
|
|
63
|
+
const [timezone, setTimezone] = useTimezone();
|
|
191
64
|
```
|
|
192
65
|
|
|
193
|
-
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
- Rejects negative amounts
|
|
197
|
-
- Currency-specific formatting
|
|
198
|
-
- Returns normalized numeric format
|
|
66
|
+
- Reads from `localStorage[storageKey]` on mount (after first paint, to avoid SSR mismatch).
|
|
67
|
+
- Falls back to `DEFAULT_TIMEZONE` (`"Africa/Nairobi"`) when nothing's stored.
|
|
68
|
+
- `setTimezone(next)` validates against `Intl.supportedValuesOf("timeZone")` plus TemboPlus operating/anchor zones, then persists.
|
|
199
69
|
|
|
200
|
-
|
|
70
|
+
### `useTimezoneDate()`
|
|
201
71
|
|
|
202
|
-
|
|
72
|
+
Boundary helpers bound to the current timezone preference. Re-memoises when the preference changes.
|
|
203
73
|
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
import { Form, Input, Button } from 'antd';
|
|
207
|
-
import {
|
|
208
|
-
PHONE_NUMBER_VALIDATOR,
|
|
209
|
-
AMOUNT_VALIDATOR,
|
|
210
|
-
ACCOUNT_NAME_VALIDATOR,
|
|
211
|
-
SWIFT_CODE_VALIDATOR,
|
|
212
|
-
ACCOUNT_NUMBER_VALIDATOR,
|
|
213
|
-
} from '@temboplus/frontend-react-core';
|
|
214
|
-
|
|
215
|
-
const PayoutForm: React.FC = () => {
|
|
216
|
-
const [form] = Form.useForm();
|
|
217
|
-
|
|
218
|
-
const onFinish = (values: any) => {
|
|
219
|
-
console.log('Validated values:', values);
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<Form form={form} onFinish={onFinish} layout="vertical">
|
|
224
|
-
<Form.Item
|
|
225
|
-
label="Phone Number"
|
|
226
|
-
name="phoneNumber"
|
|
227
|
-
rules={[{ required: true, validator: PHONE_NUMBER_VALIDATOR('TZ') }]}
|
|
228
|
-
>
|
|
229
|
-
<Input placeholder="+255 712 345 678" />
|
|
230
|
-
</Form.Item>
|
|
231
|
-
|
|
232
|
-
<Form.Item
|
|
233
|
-
label="Amount"
|
|
234
|
-
name="amount"
|
|
235
|
-
rules={[{
|
|
236
|
-
required: true,
|
|
237
|
-
validator: AMOUNT_VALIDATOR({
|
|
238
|
-
currencyCode: 'TZS',
|
|
239
|
-
min: 1000,
|
|
240
|
-
max: 100000
|
|
241
|
-
})
|
|
242
|
-
}]}
|
|
243
|
-
>
|
|
244
|
-
<Input placeholder="10,000" />
|
|
245
|
-
</Form.Item>
|
|
246
|
-
|
|
247
|
-
<Form.Item
|
|
248
|
-
label="Account Name"
|
|
249
|
-
name="accountName"
|
|
250
|
-
rules={[{ required: true, validator: ACCOUNT_NAME_VALIDATOR }]}
|
|
251
|
-
>
|
|
252
|
-
<Input placeholder="John Smith" />
|
|
253
|
-
</Form.Item>
|
|
254
|
-
|
|
255
|
-
<Form.Item
|
|
256
|
-
label="Bank SWIFT Code"
|
|
257
|
-
name="swiftCode"
|
|
258
|
-
rules={[{ required: true, validator: SWIFT_CODE_VALIDATOR('TZ') }]}
|
|
259
|
-
>
|
|
260
|
-
<Input placeholder="CORUTZTZ" />
|
|
261
|
-
</Form.Item>
|
|
262
|
-
|
|
263
|
-
<Form.Item
|
|
264
|
-
label="Account Number"
|
|
265
|
-
name="accountNumber"
|
|
266
|
-
rules={[{ required: true, validator: ACCOUNT_NUMBER_VALIDATOR('TZ') }]}
|
|
267
|
-
>
|
|
268
|
-
<Input placeholder="123456789" />
|
|
269
|
-
</Form.Item>
|
|
270
|
-
|
|
271
|
-
<Form.Item>
|
|
272
|
-
<Button type="primary" htmlType="submit">
|
|
273
|
-
Submit
|
|
274
|
-
</Button>
|
|
275
|
-
</Form.Item>
|
|
276
|
-
</Form>
|
|
277
|
-
);
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
export default PayoutForm;
|
|
281
|
-
```
|
|
74
|
+
```ts
|
|
75
|
+
const { tz, startOfDay, endOfDay, parse, sameDay, toUtc } = useTimezoneDate();
|
|
282
76
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
import { useState } from 'react';
|
|
287
|
-
import { PHONE_NUMBER_VALIDATOR, AMOUNT_VALIDATOR } from '@temboplus/frontend-react-core';
|
|
288
|
-
|
|
289
|
-
const MultiCountryForm: React.FC = () => {
|
|
290
|
-
const [selectedCountry, setSelectedCountry] = useState<'TZ' | 'KE'>('TZ');
|
|
291
|
-
|
|
292
|
-
const getValidationRules = () => ({
|
|
293
|
-
phoneNumber: [{
|
|
294
|
-
required: true,
|
|
295
|
-
validator: PHONE_NUMBER_VALIDATOR(selectedCountry)
|
|
296
|
-
}],
|
|
297
|
-
amount: [{
|
|
298
|
-
required: true,
|
|
299
|
-
validator: AMOUNT_VALIDATOR({
|
|
300
|
-
currencyCode: selectedCountry === 'TZ' ? 'TZS' : 'KES'
|
|
301
|
-
})
|
|
302
|
-
}]
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
// Rest of component...
|
|
306
|
-
};
|
|
77
|
+
const utcStart = startOfDay(pickerDate); // UTC instant of midnight in `tz`
|
|
78
|
+
const utcEnd = endOfDay(pickerDate);
|
|
79
|
+
const zoned = parse("2026-05-15T00:00:00Z"); // TZDate; getDate() reports `tz` wall clock
|
|
307
80
|
```
|
|
308
81
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
| Country | Code | Phone | Mobile | Bank | Currency |
|
|
312
|
-
| -------- | ---- | ----- | ------ | ---- | --------------- |
|
|
313
|
-
| Tanzania | TZ | ✅ | ✅ | ✅ | TZS |
|
|
314
|
-
| Kenya | KE | ✅ | ✅ | ✅ | KES |
|
|
315
|
-
| Global | * | ✅ | ❌ | ❌ | 150+ currencies |
|
|
316
|
-
|
|
317
|
-
## 🔧 Error Handling
|
|
318
|
-
|
|
319
|
-
All validators provide clear, user-friendly error messages:
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
// Phone validation errors
|
|
323
|
-
"Invalid Tanzania phone number format. Please enter a valid Tanzania phone number."
|
|
324
|
-
|
|
325
|
-
// Amount validation errors
|
|
326
|
-
"Amount must be at least TSh 1,000. Please enter a higher amount."
|
|
327
|
-
"Negative amounts are not allowed. Please enter a positive amount."
|
|
82
|
+
### `useFormatDate*()`
|
|
328
83
|
|
|
329
|
-
|
|
330
|
-
"Invalid account name. Please enter a valid full name with at least two words."
|
|
84
|
+
Reactive formatters. Re-render automatically when the user changes their timezone preference.
|
|
331
85
|
|
|
332
|
-
|
|
333
|
-
|
|
86
|
+
```ts
|
|
87
|
+
const fmtDateTime = useFormatDateTime();
|
|
88
|
+
return <span>{fmtDateTime(row.createdAt)}</span>; // "25 MAY 2026, 02:30 PM"
|
|
334
89
|
```
|
|
335
90
|
|
|
336
|
-
|
|
91
|
+
Available variants:
|
|
337
92
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
93
|
+
| Hook | Output |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `useFormatDate` | `25 MAY 2026` |
|
|
96
|
+
| `useFormatDateTime` | `25 MAY 2026, 02:30 PM` |
|
|
97
|
+
| `useFormatDateTimeWithSeconds` | `25 MAY 2026, 02:30:45 PM` |
|
|
98
|
+
| `useFormatTime` | `02:30 PM` |
|
|
342
99
|
|
|
343
|
-
|
|
344
|
-
validator: PHONE_NUMBER_VALIDATOR()
|
|
345
|
-
```
|
|
100
|
+
For non-React call sites, the imperative siblings live in [`@temboplus/frontend-core`](https://github.com/TemboPlus-Frontend/frontend-core-js):
|
|
346
101
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
// ✅ Good - explicit required validation
|
|
350
|
-
rules: [
|
|
351
|
-
{ required: true, message: 'Phone number is required' },
|
|
352
|
-
{ validator: PHONE_NUMBER_VALIDATOR('TZ') }
|
|
353
|
-
]
|
|
102
|
+
```ts
|
|
103
|
+
import { formatDateTime } from "@temboplus/frontend-core";
|
|
354
104
|
|
|
355
|
-
//
|
|
356
|
-
rules: [{ required: true, validator: PHONE_NUMBER_VALIDATOR('TZ') }]
|
|
105
|
+
formatDateTime(date, "Africa/Nairobi"); // "25 MAY 2026, 02:30 PM"
|
|
357
106
|
```
|
|
358
107
|
|
|
359
|
-
|
|
360
|
-
```typescript
|
|
361
|
-
// ✅ Good - custom business-specific messages
|
|
362
|
-
validator: AMOUNT_VALIDATOR({
|
|
363
|
-
currencyCode: 'TZS',
|
|
364
|
-
min: 5000,
|
|
365
|
-
minMessage: 'Minimum transfer amount is TSh 5,000 for international transfers'
|
|
366
|
-
})
|
|
367
|
-
```
|
|
108
|
+
## Development
|
|
368
109
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
};
|
|
110
|
+
```bash
|
|
111
|
+
make help # list targets
|
|
112
|
+
make install # bun install
|
|
113
|
+
make build # rollup + tsc declarations
|
|
114
|
+
make test # bun test
|
|
115
|
+
make publish-dry # see what would publish
|
|
116
|
+
make publish # build, test, then publish to npm
|
|
377
117
|
```
|
|
378
118
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
Private package for TemboPlus applications.
|
|
382
|
-
|
|
383
|
-
## 🔗 Related Packages
|
|
384
|
-
|
|
385
|
-
- **[@temboplus/frontend-core](https://github.com/TemboPlus-Frontend/temboplus-frontend-core)** - Core business logic and models
|
|
119
|
+
Bun handles install, test, and publish. Rollup bundles ESM + CJS (with a `"use client"` banner per output, since every hook is client-only).
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
|
-
"use
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var useFormatDate = require('./use-format-date-BvwtJTnU.js');
|
|
5
|
+
require('react');
|
|
6
|
+
require('@temboplus/frontend-core');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.useFormatDate = useFormatDate.useFormatDate;
|
|
11
|
+
exports.useFormatDateTime = useFormatDate.useFormatDateTime;
|
|
12
|
+
exports.useFormatDateTimeWithSeconds = useFormatDate.useFormatDateTimeWithSeconds;
|
|
13
|
+
exports.useFormatTime = useFormatDate.useFormatTime;
|
|
14
|
+
exports.useTimezone = useFormatDate.useTimezone;
|
|
15
|
+
exports.useTimezoneDate = useFormatDate.useTimezoneDate;
|
|
2
16
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./timezone/index.js";
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
export { b as useFormatDate, c as useFormatDateTime, d as useFormatDateTimeWithSeconds, e as useFormatTime, u as useTimezone, a as useTimezoneDate } from './use-format-date--9gcnP7_.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import '@temboplus/frontend-core';
|
|
2
5
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var useFormatDate = require('../use-format-date-BvwtJTnU.js');
|
|
5
|
+
require('react');
|
|
6
|
+
require('@temboplus/frontend-core');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.useFormatDate = useFormatDate.useFormatDate;
|
|
11
|
+
exports.useFormatDateTime = useFormatDate.useFormatDateTime;
|
|
12
|
+
exports.useFormatDateTimeWithSeconds = useFormatDate.useFormatDateTimeWithSeconds;
|
|
13
|
+
exports.useFormatTime = useFormatDate.useFormatTime;
|
|
14
|
+
exports.useTimezone = useFormatDate.useTimezone;
|
|
15
|
+
exports.useTimezoneDate = useFormatDate.useTimezoneDate;
|
|
16
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
export { b as useFormatDate, c as useFormatDateTime, d as useFormatDateTimeWithSeconds, e as useFormatTime, u as useTimezone, a as useTimezoneDate } from '../use-format-date--9gcnP7_.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import '@temboplus/frontend-core';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralised, reactive date-formatter hooks. Each returns a function
|
|
3
|
+
* `(date: Date) => string` bound to the user's current timezone
|
|
4
|
+
* preference. Consumers re-render automatically when the preference
|
|
5
|
+
* changes in Settings.
|
|
6
|
+
*
|
|
7
|
+
* For non-React call sites, import the imperative siblings directly
|
|
8
|
+
* from `@temboplus/frontend-core`.
|
|
9
|
+
*/
|
|
10
|
+
type Options = {
|
|
11
|
+
storageKey: string;
|
|
12
|
+
defaultTimezone?: string;
|
|
13
|
+
};
|
|
14
|
+
/** `25 MAY 2026` */
|
|
15
|
+
export declare function useFormatDate(options: Options): (date: Date) => string;
|
|
16
|
+
/** `25 MAY 2026, 02:30 PM` */
|
|
17
|
+
export declare function useFormatDateTime(options: Options): (date: Date) => string;
|
|
18
|
+
/** `25 MAY 2026, 02:30:45 PM` — audit rows where seconds matter. */
|
|
19
|
+
export declare function useFormatDateTimeWithSeconds(options: Options): (date: Date) => string;
|
|
20
|
+
/** `02:30 PM` */
|
|
21
|
+
export declare function useFormatTime(options: Options): (date: Date) => string;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { toUtcInstant } from "@temboplus/frontend-core";
|
|
2
|
+
/**
|
|
3
|
+
* Boundary helpers bound to the user's current timezone preference.
|
|
4
|
+
* Re-memoises when the preference changes so date pickers / range
|
|
5
|
+
* builders pick up the new offset automatically.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useTimezoneDate(options: {
|
|
8
|
+
storageKey: string;
|
|
9
|
+
defaultTimezone?: string;
|
|
10
|
+
}): {
|
|
11
|
+
tz: string;
|
|
12
|
+
startOfDay: (date: Date) => Date;
|
|
13
|
+
endOfDay: (date: Date) => Date;
|
|
14
|
+
parse: (iso: string) => import("@date-fns/tz").TZDate;
|
|
15
|
+
sameDay: (a: Date, b: Date) => boolean;
|
|
16
|
+
toUtc: typeof toUtcInstant;
|
|
17
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook over the user's timezone preference, persisted to localStorage.
|
|
3
|
+
*
|
|
4
|
+
* Each app supplies its own `storageKey` (e.g. `"afloat.timezone"`,
|
|
5
|
+
* `"dashboard.timezone"`) so preferences don't collide across products
|
|
6
|
+
* sharing the same browser origin.
|
|
7
|
+
*
|
|
8
|
+
* Initial render returns `defaultTimezone` (or {@link DEFAULT_TIMEZONE});
|
|
9
|
+
* the persisted value is rehydrated on mount to avoid SSR mismatch.
|
|
10
|
+
*
|
|
11
|
+
* When backend-stored user preferences arrive later, the source of truth
|
|
12
|
+
* inside this hook can swap to a hybrid local+remote scheme without
|
|
13
|
+
* consumers having to touch their call sites — the `[tz, setTz]` shape
|
|
14
|
+
* stays stable.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useTimezone(options: {
|
|
17
|
+
storageKey: string;
|
|
18
|
+
defaultTimezone?: string;
|
|
19
|
+
}): readonly [string, (next: string) => void];
|