@hobenakicoffee/libraries 1.17.0 → 1.18.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 +847 -71
- package/package.json +1 -1
- package/src/types/supabase.ts +38 -0
- package/src/utils/get-user-page-link.test.ts +0 -1
- package/src/utils/get-user-page-link.ts +5 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @hobenakicoffee/libraries
|
|
2
2
|
|
|
3
|
-
Framework-agnostic shared constants and
|
|
3
|
+
Framework-agnostic shared constants, utilities, types, UI components, and moderation tools for "হবে নাকি Coffee?" projects.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -16,42 +16,736 @@ bun add @hobenakicoffee/libraries
|
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
18
18
|
|
|
19
|
-
This package exposes
|
|
19
|
+
This package exposes multiple entry points:
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
21
|
+
```ts
|
|
22
|
+
// Main entry - constants and types
|
|
23
|
+
import { PaymentStatuses, ServiceTypes, Visibility } from "@hobenakicoffee/libraries";
|
|
24
|
+
|
|
25
|
+
// Constants only
|
|
26
|
+
import { SupporterPlatforms } from "@hobenakicoffee/libraries/constants";
|
|
27
|
+
|
|
28
|
+
// Utilities only
|
|
29
|
+
import { formatAmount, formatDate, getUserPageLink } from "@hobenakicoffee/libraries/utils";
|
|
30
|
+
|
|
31
|
+
// Types only
|
|
32
|
+
import type { Database, Tables } from "@hobenakicoffee/libraries/types";
|
|
33
|
+
|
|
34
|
+
// Moderation tools
|
|
35
|
+
import { moderateText } from "@hobenakicoffee/libraries/moderation";
|
|
36
|
+
|
|
37
|
+
// UI components
|
|
38
|
+
import { Button, Card, Dialog } from "@hobenakicoffee/libraries/components/ui/button";
|
|
39
|
+
import { ThemeProvider, useTheme } from "@hobenakicoffee/libraries/providers/theme-provider";
|
|
40
|
+
|
|
41
|
+
// Class merging utility
|
|
42
|
+
import { cn } from "@hobenakicoffee/libraries/lib/utils";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Entry Points Overview
|
|
46
|
+
|
|
47
|
+
| Entrypoint | Description |
|
|
48
|
+
| ---------- | ----------- |
|
|
49
|
+
| `@hobenakicoffee/libraries` | Main entry - re-exports constants and types |
|
|
50
|
+
| `@hobenakicoffee/libraries/constants` | Constants and types |
|
|
51
|
+
| `@hobenakicoffee/libraries/utils` | Utility functions |
|
|
52
|
+
| `@hobenakicoffee/libraries/types` | TypeScript types (Supabase) |
|
|
53
|
+
| `@hobenakicoffee/libraries/moderation` | Content moderation tools |
|
|
54
|
+
| `@hobenakicoffee/libraries/providers/theme-provider` | Theme provider component |
|
|
55
|
+
| `@hobenakicoffee/libraries/lib/utils` | Class merging utility (`cn()`) |
|
|
56
|
+
| `@hobenakicoffee/libraries/components/ui/*` | UI components |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Constants (`@hobenakicoffee/libraries/constants`)
|
|
24
61
|
|
|
25
|
-
|
|
62
|
+
### Visibility
|
|
26
63
|
|
|
27
64
|
```ts
|
|
28
|
-
import {
|
|
65
|
+
import { Visibility } from "@hobenakicoffee/libraries";
|
|
29
66
|
|
|
30
|
-
|
|
67
|
+
Visibility.PUBLIC // "public"
|
|
68
|
+
Visibility.PRIVATE // "private"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### productInfo
|
|
72
|
+
|
|
73
|
+
Product metadata for "হবে নাকি Coffee?":
|
|
31
74
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
75
|
+
```ts
|
|
76
|
+
import { productInfo } from "@hobenakicoffee/libraries";
|
|
77
|
+
|
|
78
|
+
productInfo.name // "হবে নাকি Coffee?"
|
|
79
|
+
productInfo.domain // "https://www.hobenakicoffee.com"
|
|
80
|
+
productInfo.twitterHandle // "@hobenakicoffee"
|
|
81
|
+
productInfo.title // Platform tagline
|
|
82
|
+
productInfo.description // Full description
|
|
83
|
+
productInfo.keywords // SEO keywords
|
|
84
|
+
productInfo.socials // Social media links object
|
|
37
85
|
```
|
|
38
86
|
|
|
39
|
-
|
|
87
|
+
### companyInfo
|
|
40
88
|
|
|
41
|
-
|
|
89
|
+
Company contact and legal information:
|
|
42
90
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
91
|
+
```ts
|
|
92
|
+
import { companyInfo } from "@hobenakicoffee/libraries";
|
|
93
|
+
|
|
94
|
+
companyInfo.name // "Shamscorner LLC"
|
|
95
|
+
companyInfo.contactEmail // "mail@shamscorner.com"
|
|
96
|
+
companyInfo.contactPhone // "+1(817) 973-7285"
|
|
97
|
+
companyInfo.contactLocation // Full address
|
|
98
|
+
companyInfo.domain // "https://www.shamscorner.com"
|
|
99
|
+
companyInfo.postalAddress // Postal address object
|
|
100
|
+
```
|
|
47
101
|
|
|
48
|
-
###
|
|
102
|
+
### Payment Constants
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { PaymentTypes, PaymentStatuses, PaymentProviders, PaymentDirections, PayoutProviders, WithdrawalStatuses } from "@hobenakicoffee/libraries";
|
|
106
|
+
|
|
107
|
+
// Payment Types
|
|
108
|
+
PaymentTypes.SUBSCRIPTION // "subscription"
|
|
109
|
+
PaymentTypes.ONE_TIME // "one-time"
|
|
110
|
+
PaymentTypes.PAYOUT // "payout"
|
|
111
|
+
PaymentTypes.WITHDRAW_LOCK // "withdraw_lock"
|
|
112
|
+
PaymentTypes.WITHDRAW_RELEASE // "withdraw_release"
|
|
113
|
+
PaymentTypes.WITHDRAW_COMPLETE // "withdraw_complete"
|
|
114
|
+
PaymentTypes.MANUAL_ADJUSTMENT // "manual_adjustment"
|
|
115
|
+
|
|
116
|
+
// Payment Statuses
|
|
117
|
+
PaymentStatuses.PENDING // "pending"
|
|
118
|
+
PaymentStatuses.PROCESSING // "processing"
|
|
119
|
+
PaymentStatuses.COMPLETED // "completed"
|
|
120
|
+
PaymentStatuses.FAILED // "failed"
|
|
121
|
+
PaymentStatuses.REVERSED // "reversed"
|
|
122
|
+
PaymentStatuses.CANCELLED // "cancelled"
|
|
123
|
+
PaymentStatuses.REFUNDED // "refunded"
|
|
124
|
+
PaymentStatuses.REVIEWING // "reviewing"
|
|
125
|
+
|
|
126
|
+
// Payment Providers
|
|
127
|
+
PaymentProviders.HOBENAKICOFFEE // "HobeNakiCoffee"
|
|
128
|
+
PaymentProviders.BKASH // "Bkash"
|
|
129
|
+
PaymentProviders.NAGAD // "Nagad"
|
|
130
|
+
PaymentProviders.ROCKET // "Rocket"
|
|
131
|
+
PaymentProviders.UPAY // "Upay"
|
|
132
|
+
PaymentProviders.SSLCOMMERZ // "SSLCommerz"
|
|
133
|
+
PaymentProviders.AAMARPAY // "Aamarpay"
|
|
134
|
+
PaymentProviders.PORTWALLET // "Portwallet"
|
|
135
|
+
PaymentProviders.TAP // "Tap"
|
|
136
|
+
PaymentProviders.OTHER // "Other"
|
|
137
|
+
|
|
138
|
+
// Payment Directions
|
|
139
|
+
PaymentDirections.DEBIT // "debit"
|
|
140
|
+
PaymentDirections.CREDIT // "credit"
|
|
141
|
+
|
|
142
|
+
// Payout Providers
|
|
143
|
+
PayoutProviders.BKASH // "bkash"
|
|
144
|
+
PayoutProviders.NAGAD // "nagad"
|
|
145
|
+
PayoutProviders.ROCKET // "rocket"
|
|
146
|
+
PayoutProviders.BANK // "bank"
|
|
147
|
+
|
|
148
|
+
// Withdrawal Statuses
|
|
149
|
+
WithdrawalStatuses.REQUESTED // "requested"
|
|
150
|
+
WithdrawalStatuses.APPROVED // "approved"
|
|
151
|
+
WithdrawalStatuses.PROCESSING // "processing"
|
|
152
|
+
WithdrawalStatuses.PAID // "paid"
|
|
153
|
+
WithdrawalStatuses.REJECTED // "rejected"
|
|
154
|
+
WithdrawalStatuses.FAILED // "failed"
|
|
155
|
+
```
|
|
49
156
|
|
|
50
|
-
|
|
51
|
-
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
-
| `@hobenakicoffee/libraries/utils` | `checkModeration`, `formatAmount`, `formatSignedAmount`, `formatDate`, `formatNumber`, `formatToPlainText`, `getSocialHandle`, `getSocialUrl`, `getUserNameInitials`, `getUserPageLink`, `openInNewWindow`, `shareToFacebook`, `shareToInstagram`, `shareToLinkedIn`, `shareToX`, `printQrSvg`, `toHumanReadable`, `validatePhoneNumber` |
|
|
157
|
+
### Supporter Platforms
|
|
53
158
|
|
|
54
|
-
|
|
159
|
+
```ts
|
|
160
|
+
import { SupporterPlatforms } from "@hobenakicoffee/libraries";
|
|
161
|
+
|
|
162
|
+
SupporterPlatforms.FACEBOOK // "facebook"
|
|
163
|
+
SupporterPlatforms.X // "x"
|
|
164
|
+
SupporterPlatforms.INSTAGRAM // "instagram"
|
|
165
|
+
SupporterPlatforms.YOUTUBE // "youtube"
|
|
166
|
+
SupporterPlatforms.GITHUB // "github"
|
|
167
|
+
SupporterPlatforms.LINKEDIN // "linkedin"
|
|
168
|
+
SupporterPlatforms.TWITCH // "twitch"
|
|
169
|
+
SupporterPlatforms.TIKTOK // "tiktok"
|
|
170
|
+
SupporterPlatforms.THREADS // "threads"
|
|
171
|
+
SupporterPlatforms.WHATSAPP // "whatsapp"
|
|
172
|
+
SupporterPlatforms.TELEGRAM // "telegram"
|
|
173
|
+
SupporterPlatforms.DISCORD // "discord"
|
|
174
|
+
SupporterPlatforms.REDDIT // "reddit"
|
|
175
|
+
SupporterPlatforms.PINTEREST // "pinterest"
|
|
176
|
+
SupporterPlatformS.MEDIUM // "medium"
|
|
177
|
+
SupporterPlatforms.DEVTO // "devto"
|
|
178
|
+
SupporterPlatforms.BEHANCE // "behance"
|
|
179
|
+
SupporterPlatforms.DRIBBBLE // "dribbble"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Service Types
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { ServiceTypes } from "@hobenakicoffee/libraries";
|
|
186
|
+
|
|
187
|
+
ServiceTypes.GIFT // "gift"
|
|
188
|
+
ServiceTypes.EXCLUSIVE_CONTENT // "exclusive_content"
|
|
189
|
+
ServiceTypes.WITHDRAWAL // "withdrawal"
|
|
190
|
+
ServiceTypes.FOLLOW // "follow"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Utilities (`@hobenakicoffee/libraries/utils`)
|
|
196
|
+
|
|
197
|
+
### formatAmount
|
|
198
|
+
|
|
199
|
+
Formats a number as Bangladeshi Taka (৳).
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { formatAmount, formatSignedAmount } from "@hobenakicoffee/libraries/utils";
|
|
203
|
+
|
|
204
|
+
formatAmount(1000); // "৳1,000"
|
|
205
|
+
formatAmount(-500); // "৳500" (absolute value)
|
|
206
|
+
|
|
207
|
+
// With direction sign
|
|
208
|
+
formatSignedAmount(1000, "credit"); // "+ ৳1,000"
|
|
209
|
+
formatSignedAmount(500, "debit"); // "- ৳500"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### formatDate
|
|
213
|
+
|
|
214
|
+
Formats a date string to a readable format.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { formatDate } from "@hobenakicoffee/libraries/utils";
|
|
218
|
+
|
|
219
|
+
formatDate("2024-01-15T00:00:00Z"); // "Jan 15, 2024"
|
|
220
|
+
formatDate("invalid"); // "-"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### formatNumber
|
|
224
|
+
|
|
225
|
+
Formats a number with thousand separators.
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import { formatNumber } from "@hobenakicoffee/libraries/utils";
|
|
229
|
+
|
|
230
|
+
formatNumber(1000000); // "1,000,000"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### formatToPlainText
|
|
234
|
+
|
|
235
|
+
Converts various data types to plain text.
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
import { formatToPlainText, formatMetadataKey } from "@hobenakicoffee/libraries/utils";
|
|
239
|
+
|
|
240
|
+
// Basic usage
|
|
241
|
+
formatToPlainText("hello"); // "hello"
|
|
242
|
+
formatToPlainText(123); // "123"
|
|
243
|
+
formatToPlainText(true); // "Yes"
|
|
244
|
+
formatToPlainText(false); // "No"
|
|
245
|
+
|
|
246
|
+
// With options
|
|
247
|
+
formatToPlainText("some long text...", { maxStringLength: 10 });
|
|
248
|
+
// "some lo..."
|
|
249
|
+
|
|
250
|
+
formatToPlainText({ key: "value" });
|
|
251
|
+
// JSON stringified
|
|
252
|
+
|
|
253
|
+
formatMetadataKey("supporterName"); // "Supporter Name"
|
|
254
|
+
formatMetadataKey("is_monthly"); // "Is monthly"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### getUserPageLink
|
|
258
|
+
|
|
259
|
+
Generates a user profile page URL.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { getUserPageLink } from "@hobenakicoffee/libraries/utils";
|
|
263
|
+
|
|
264
|
+
getUserPageLink("johndoe");
|
|
265
|
+
// "https://hobenakicoffee.com/@johndoe"
|
|
266
|
+
|
|
267
|
+
getUserPageLink("johndoe", "https://custom.com");
|
|
268
|
+
// "https://custom.com/@johndoe"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### getUserNameInitials
|
|
272
|
+
|
|
273
|
+
Extracts initials from a name.
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
import { getInitials } from "@hobenakicoffee/libraries/utils";
|
|
277
|
+
|
|
278
|
+
getInitials("John Doe"); // "JD"
|
|
279
|
+
getInitials("John"); // "J"
|
|
280
|
+
getInitials("John Michael"); // "JM"
|
|
281
|
+
getInitials(null); // "?"
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### getSocialLink
|
|
285
|
+
|
|
286
|
+
Generates social media profile URLs.
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import { getSocialLink, SupporterPlatforms } from "@hobenakicoffee/libraries";
|
|
290
|
+
|
|
291
|
+
getSocialLink("johndoe", SupporterPlatforms.FACEBOOK);
|
|
292
|
+
// "https://facebook.com/johndoe"
|
|
293
|
+
|
|
294
|
+
getSocialLink("johndoe", SupporterPlatforms.INSTAGRAM);
|
|
295
|
+
// "https://instagram.com/johndoe"
|
|
296
|
+
|
|
297
|
+
getSocialLink("johndoe", SupporterPlatforms.GITHUB);
|
|
298
|
+
// "https://github.com/johndoe"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### getSocialUrl
|
|
302
|
+
|
|
303
|
+
Generates social sharing URLs with support for platform usernames.
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
import { getSocialUrl } from "@hobenakicoffee/libraries/utils";
|
|
307
|
+
|
|
308
|
+
// With our platform username
|
|
309
|
+
getSocialUrl("johndoe");
|
|
310
|
+
// "https://hobenakicoffee.com/@johndoe"
|
|
311
|
+
|
|
312
|
+
// With platform and supporter name
|
|
313
|
+
getSocialUrl(null, "instagram", "johndoe");
|
|
314
|
+
// "https://instagram.com/johndoe"
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### openInNewWindow
|
|
318
|
+
|
|
319
|
+
Opens a URL in a new tab safely.
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
import { openInNewWindow } from "@hobenakicoffee/libraries/utils";
|
|
323
|
+
|
|
324
|
+
openInNewWindow("https://example.com");
|
|
325
|
+
// Opens in new tab with rel="noopener noreferrer"
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Social Sharing Functions
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
import { shareToFacebook, shareToInstagram, shareToLinkedIn, shareToX } from "@hobenakicoffee/libraries/utils";
|
|
332
|
+
|
|
333
|
+
// Facebook
|
|
334
|
+
shareToFacebook({
|
|
335
|
+
url: "https://example.com",
|
|
336
|
+
quote: "Check this out!",
|
|
337
|
+
hashtag: "coffee",
|
|
338
|
+
ref: "campaign123"
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Instagram (uses Web Share API or copies to clipboard)
|
|
342
|
+
shareToInstagram({
|
|
343
|
+
url: "https://example.com",
|
|
344
|
+
text: "Check this out!"
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// LinkedIn
|
|
348
|
+
shareToLinkedIn({
|
|
349
|
+
url: "https://example.com",
|
|
350
|
+
title: "My Title",
|
|
351
|
+
summary: "Description here",
|
|
352
|
+
source: "HobeNakiCoffee"
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// X (Twitter)
|
|
356
|
+
shareToX({
|
|
357
|
+
text: "Hello from HobeNakiCoffee!",
|
|
358
|
+
url: "https://example.com",
|
|
359
|
+
hashtags: "coffee,support",
|
|
360
|
+
via: "hobenakicoffee"
|
|
361
|
+
});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### QR Code Utilities
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import { downloadQrSvgAsPng, printQrSvg } from "@hobenakicoffee/libraries/utils";
|
|
368
|
+
|
|
369
|
+
// Download QR as PNG
|
|
370
|
+
await downloadQrSvgAsPng(
|
|
371
|
+
'<svg>...</svg>',
|
|
372
|
+
"qr-code.png",
|
|
373
|
+
() => console.log("Success"),
|
|
374
|
+
() => console.log("Error")
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// Print QR
|
|
378
|
+
printQrSvg(
|
|
379
|
+
'<svg>...</svg>',
|
|
380
|
+
"QR Code Print",
|
|
381
|
+
() => console.log("Error")
|
|
382
|
+
);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### toHumanReadable
|
|
386
|
+
|
|
387
|
+
Converts camelCase or snake_case strings to human-readable format.
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
import { toHumanReadable } from "@hobenakicoffee/libraries/utils";
|
|
391
|
+
|
|
392
|
+
toHumanReadable("camelCase"); // "Camel Case"
|
|
393
|
+
toHumanReadable("snake_case"); // "Snake Case"
|
|
394
|
+
toHumanReadable("CONSTANT_VALUE"); // "CONSTANT VALUE"
|
|
395
|
+
toHumanReadable("HTTPResponseCode"); // "HTTP Response Code"
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### validatePhoneNumber
|
|
399
|
+
|
|
400
|
+
Validates Bangladeshi mobile phone numbers.
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
import { validatePhoneNumber } from "@hobenakicoffee/libraries/utils";
|
|
404
|
+
|
|
405
|
+
validatePhoneNumber("01712345678"); // true
|
|
406
|
+
validatePhoneNumber("+8801712345678"); // true
|
|
407
|
+
validatePhoneNumber("8801712345678"); // true
|
|
408
|
+
validatePhoneNumber("01512345678"); // false (invalid prefix)
|
|
409
|
+
validatePhoneNumber("1234567890"); // false
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### checkModeration
|
|
413
|
+
|
|
414
|
+
Checks text for profanity using both local Bangla word lists and OpenAI moderation API.
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
import { checkModeration } from "@hobenakicoffee/libraries/utils";
|
|
418
|
+
import OpenAI from "openai";
|
|
419
|
+
|
|
420
|
+
const openai = new OpenAI();
|
|
421
|
+
|
|
422
|
+
const result = await checkModeration(openai, "some text to check");
|
|
423
|
+
|
|
424
|
+
result.flagged // boolean - true if content is flagged
|
|
425
|
+
result.categories // OpenAI categories if flagged
|
|
426
|
+
result.source // "profanity" | "openai" | null
|
|
427
|
+
result.profaneWords // Array of matched profanity words (if from profanity check)
|
|
428
|
+
result.error // Error if any
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Moderation (`@hobenakicoffee/libraries/moderation`)
|
|
434
|
+
|
|
435
|
+
### moderateText
|
|
436
|
+
|
|
437
|
+
Local profanity detection for English and Bangla.
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { moderateText } from "@hobenakicoffee/libraries/moderation";
|
|
441
|
+
|
|
442
|
+
const result = moderateText("some bad word here");
|
|
443
|
+
|
|
444
|
+
result.isAllowed // boolean
|
|
445
|
+
result.matched // Array of matched words
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### normalizeLeetspeak
|
|
449
|
+
|
|
450
|
+
Converts leetspeak to normal text (e.g., "h4x0r" -> "haxor").
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
import { normalizeLeetspeak } from "@hobenakicoffee/libraries/moderation";
|
|
454
|
+
|
|
455
|
+
normalizeLeetspeak("h4x0r"); // "haxor"
|
|
456
|
+
normalizeLeetspeak("p@ssw0rd"); // "password"
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### normalizeUnicode
|
|
460
|
+
|
|
461
|
+
Normalizes Unicode characters (removes diacritics).
|
|
462
|
+
|
|
463
|
+
```ts
|
|
464
|
+
import { normalizeUnicode } from "@hobenakicoffee/libraries/moderation";
|
|
465
|
+
|
|
466
|
+
normalizeUnicode("café"); // "cafe"
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### banglaBadWords
|
|
470
|
+
|
|
471
|
+
Array of Bangla profanity words (710+ words) for content moderation.
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
import { banglaBadWords } from "@hobenakicoffee/libraries/moderation";
|
|
475
|
+
|
|
476
|
+
console.log(banglaBadWords.length); // 710+
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Types (`@hobenakicoffee/libraries/types`)
|
|
482
|
+
|
|
483
|
+
Full Supabase database types with Row, Insert, and Update types for each table.
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
import type { Database, Tables, TablesInsert, TablesUpdate, Enums } from "@hobenakicoffee/libraries/types";
|
|
487
|
+
|
|
488
|
+
// Database type
|
|
489
|
+
type DB = Database;
|
|
490
|
+
|
|
491
|
+
// Table row types
|
|
492
|
+
type Profile = Tables<"profiles">;
|
|
493
|
+
type Transaction = Tables<"transactions">;
|
|
494
|
+
type Supporter = Tables<"supporters">;
|
|
495
|
+
|
|
496
|
+
// Insert types
|
|
497
|
+
type NewProfile = TablesInsert<"profiles">;
|
|
498
|
+
type NewTransaction = TablesInsert<"transactions">;
|
|
499
|
+
|
|
500
|
+
// Update types
|
|
501
|
+
type ProfileUpdate = TablesUpdate<"profiles">;
|
|
502
|
+
|
|
503
|
+
// Enum types
|
|
504
|
+
type PaymentStatus = Enums<"payment_status_enum">;
|
|
505
|
+
type SupporterPlatform = Enums<"supporter_platform_enum">;
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Database Tables
|
|
509
|
+
|
|
510
|
+
- `activities` - User activity tracking
|
|
511
|
+
- `coffee_gifts` - Coffee gift transactions
|
|
512
|
+
- `conversation_participants` - Chat participants
|
|
513
|
+
- `conversations` - Chat conversations
|
|
514
|
+
- `follows` - User follow relationships
|
|
515
|
+
- `manager_role_permissions` - Manager role permissions
|
|
516
|
+
- `manager_user_roles` - Manager user role assignments
|
|
517
|
+
- `managers` - Manager profiles
|
|
518
|
+
- `messages` - Chat messages
|
|
519
|
+
- `messages_*` - Monthly partitioned message tables
|
|
520
|
+
- `payout_methods` - User payout methods
|
|
521
|
+
- `profiles` - User profiles
|
|
522
|
+
- `supporters` - Supporter records
|
|
523
|
+
- `transactions` - Payment transactions
|
|
524
|
+
- `wallets` - User wallets
|
|
525
|
+
- `withdrawal_requests` - Withdrawal requests
|
|
526
|
+
|
|
527
|
+
### Database Enums
|
|
528
|
+
|
|
529
|
+
- `manager_permission` - Manager permissions
|
|
530
|
+
- `manager_role` - Manager roles (super_admin, content_manager, etc.)
|
|
531
|
+
- `manager_status` - Manager account status
|
|
532
|
+
- `payment_status_enum` - Payment statuses
|
|
533
|
+
- `payout_provider` - Payout provider types
|
|
534
|
+
- `provider_enum` - Payment providers
|
|
535
|
+
- `reference_type_enum` - Transaction reference types
|
|
536
|
+
- `supporter_platform_enum` - Supporter platform types
|
|
537
|
+
- `transaction_direction_enum` - Debit/credit
|
|
538
|
+
- `user_role` - User roles
|
|
539
|
+
- `visibility_enum` - Public/private visibility
|
|
540
|
+
- `withdrawal_status` - Withdrawal request statuses
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## UI Components
|
|
545
|
+
|
|
546
|
+
The library includes 30+ accessible UI components built on Radix UI.
|
|
547
|
+
|
|
548
|
+
### Importing Components
|
|
549
|
+
|
|
550
|
+
```ts
|
|
551
|
+
// Individual imports (recommended for tree-shaking)
|
|
552
|
+
import { Button } from "@hobenakicoffee/libraries/components/ui/button";
|
|
553
|
+
import { Card } from "@hobenakicoffee/libraries/components/ui/card";
|
|
554
|
+
import { Dialog } from "@hobenakicoffee/libraries/components/ui/dialog";
|
|
555
|
+
import { Input } from "@hobenakicoffee/libraries/components/ui/input";
|
|
556
|
+
|
|
557
|
+
// All components available:
|
|
558
|
+
import { Alert } from "@hobenakicoffee/libraries/components/ui/alert";
|
|
559
|
+
import { AlertDialog } from "@hobenakicoffee/libraries/components/ui/alert-dialog";
|
|
560
|
+
import { Avatar } from "@hobenakicoffee/libraries/components/ui/avatar";
|
|
561
|
+
import { Badge } from "@hobenakicoffee/libraries/components/ui/badge";
|
|
562
|
+
import { Breadcrumb } from "@hobenakicoffee/libraries/components/ui/breadcrumb";
|
|
563
|
+
import { Button, ButtonGroup } from "@hobenakicoffee/libraries/components/ui/button";
|
|
564
|
+
import { Calendar } from "@hobenakicoffee/libraries/components/ui/calendar";
|
|
565
|
+
import { Card, Chart } from "@hobenakicoffee/libraries/components/ui/card";
|
|
566
|
+
import { Checkbox } from "@hobenakicoffee/libraries/components/ui/checkbox";
|
|
567
|
+
import { Dialog } from "@hobenakicoffee/libraries/components/ui/dialog";
|
|
568
|
+
import { Drawer } from "@hobenakicoffee/libraries/components/ui/drawer";
|
|
569
|
+
import { DropdownMenu } from "@hobenakicoffee/libraries/components/ui/dropdown-menu";
|
|
570
|
+
import { Empty, EmptyMinimal } from "@hobenakicoffee/libraries/components/ui/empty";
|
|
571
|
+
import { Field, Input, InputGroup, InputOtp } from "@hobenakicoffee/libraries/components/ui/input";
|
|
572
|
+
import { Item } from "@hobenakicoffee/libraries/components/ui/item";
|
|
573
|
+
import { Label } from "@hobenakicoffee/libraries/components/ui/label";
|
|
574
|
+
import { Popover } from "@hobenakicoffee/libraries/components/ui/popover";
|
|
575
|
+
import { RadioGroup } from "@hobenakicoffee/libraries/components/ui/radio-group";
|
|
576
|
+
import { Select } from "@hobenakicoffee/libraries/components/ui/select";
|
|
577
|
+
import { Separator } from "@hobenakicoffee/libraries/components/ui/separator";
|
|
578
|
+
import { Sheet } from "@hobenakicoffee/libraries/components/ui/sheet";
|
|
579
|
+
import { Sidebar } from "@hobenakicoffee/libraries/components/ui/sidebar";
|
|
580
|
+
import { Skeleton } from "@hobenakicoffee/libraries/components/ui/skeleton";
|
|
581
|
+
import { Sonner } from "@hobenakicoffee/libraries/components/ui/sonner";
|
|
582
|
+
import { Spinner } from "@hobenakicoffee/libraries/components/ui/spinner";
|
|
583
|
+
import { Table } from "@hobenakicoffee/libraries/components/ui/table";
|
|
584
|
+
import { Tabs } from "@hobenakicoffee/libraries/components/ui/tabs";
|
|
585
|
+
import { Textarea } from "@hobenakicoffee/libraries/components/ui/textarea";
|
|
586
|
+
import { Toggle, ToggleGroup } from "@hobenakicoffee/libraries/components/ui/toggle";
|
|
587
|
+
import { Tooltip } from "@hobenakicoffee/libraries/components/ui/tooltip";
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Component Variants
|
|
591
|
+
|
|
592
|
+
Components use `cva` (class-variance-authority) for variants:
|
|
593
|
+
|
|
594
|
+
```ts
|
|
595
|
+
import { Button } from "@hobenakicoffee/libraries/components/ui/button";
|
|
596
|
+
import { Badge } from "@hobenakicoffee/libraries/components/ui/badge";
|
|
597
|
+
|
|
598
|
+
// Button variants
|
|
599
|
+
<Button variant="default" />
|
|
600
|
+
<Button variant="destructive" />
|
|
601
|
+
<Button variant="outline" />
|
|
602
|
+
<Button variant="secondary" />
|
|
603
|
+
<Button variant="ghost" />
|
|
604
|
+
<Button variant="link" />
|
|
605
|
+
|
|
606
|
+
// Button sizes
|
|
607
|
+
<Button size="default" />
|
|
608
|
+
<Button size="sm" />
|
|
609
|
+
<Button size="lg" />
|
|
610
|
+
<Button size="icon" />
|
|
611
|
+
|
|
612
|
+
// Badge variants
|
|
613
|
+
<Badge variant="default" />
|
|
614
|
+
<Badge variant="secondary" />
|
|
615
|
+
<Badge variant="destructive" />
|
|
616
|
+
<Badge variant="outline" />
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Turnstile Captcha
|
|
620
|
+
|
|
621
|
+
```ts
|
|
622
|
+
import { TurnstileCaptcha } from "@hobenakicoffee/libraries/components/turnstile-captcha";
|
|
623
|
+
|
|
624
|
+
<TurnstileCaptcha
|
|
625
|
+
siteKey="your-site-key"
|
|
626
|
+
onSuccess={(token) => console.log(token)}
|
|
627
|
+
onError={() => console.error("Error")}
|
|
628
|
+
theme="auto"
|
|
629
|
+
/>
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## Theme Provider (`@hobenakicoffee/libraries/providers/theme-provider`)
|
|
635
|
+
|
|
636
|
+
Dark/light mode theming with system preference support.
|
|
637
|
+
|
|
638
|
+
```tsx
|
|
639
|
+
import { ThemeProvider, useTheme } from "@hobenakicoffee/libraries/providers/theme-provider";
|
|
640
|
+
|
|
641
|
+
// Wrap your app
|
|
642
|
+
function App({ children }) {
|
|
643
|
+
return (
|
|
644
|
+
<ThemeProvider defaultTheme="system" storageKey="my-app-theme">
|
|
645
|
+
{children}
|
|
646
|
+
</ThemeProvider>
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Use in components
|
|
651
|
+
function MyComponent() {
|
|
652
|
+
const { theme, setTheme } = useTheme();
|
|
653
|
+
|
|
654
|
+
return (
|
|
655
|
+
<button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
|
|
656
|
+
Toggle Theme
|
|
657
|
+
</button>
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
Props:
|
|
663
|
+
- `children` - React nodes
|
|
664
|
+
- `defaultTheme` - "light" | "dark" | "system" (default: "system")
|
|
665
|
+
- `storageKey` - localStorage key (default: "hobenakicoffee-app-ui-themes")
|
|
666
|
+
|
|
667
|
+
---
|
|
668
|
+
|
|
669
|
+
## Utils (`@hobenakicoffee/libraries/lib/utils`)
|
|
670
|
+
|
|
671
|
+
### cn
|
|
672
|
+
|
|
673
|
+
Class name merging utility combining `clsx` and `tailwind-merge`.
|
|
674
|
+
|
|
675
|
+
```ts
|
|
676
|
+
import { cn } from "@hobenakicoffee/libraries/lib/utils";
|
|
677
|
+
|
|
678
|
+
cn("px-2 py-1", "bg-red-500", condition && "text-white");
|
|
679
|
+
// Returns merged class string
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## API Reference
|
|
685
|
+
|
|
686
|
+
### Constants and Types
|
|
687
|
+
|
|
688
|
+
| Export | Type | Description |
|
|
689
|
+
|--------|------|-------------|
|
|
690
|
+
| `Visibility` | `object` | PUBLIC, PRIVATE constants |
|
|
691
|
+
| `Visibility` | `type` | Type for visibility values |
|
|
692
|
+
| `productInfo` | `object` | Product metadata |
|
|
693
|
+
| `companyInfo` | `object` | Company information |
|
|
694
|
+
| `PaymentTypes` | `object` | Payment type constants |
|
|
695
|
+
| `PaymentType` | `type` | Type for payment types |
|
|
696
|
+
| `PaymentStatuses` | `object` | Payment status constants |
|
|
697
|
+
| `PaymentStatus` | `type` | Type for payment statuses |
|
|
698
|
+
| `PaymentProviders` | `object` | Payment provider constants |
|
|
699
|
+
| `PaymentProvider` | `type` | Type for payment providers |
|
|
700
|
+
| `PaymentDirections` | `object` | Payment direction constants |
|
|
701
|
+
| `PaymentDirection` | `type` | Type for payment directions |
|
|
702
|
+
| `PayoutProviders` | `object` | Payout provider constants |
|
|
703
|
+
| `PayoutProvider` | `type` | Type for payout providers |
|
|
704
|
+
| `WithdrawalStatuses` | `object` | Withdrawal status constants |
|
|
705
|
+
| `WithdrawalStatus` | `type` | Type for withdrawal statuses |
|
|
706
|
+
| `SupporterPlatforms` | `object` | Social platform constants |
|
|
707
|
+
| `SupporterPlatform` | `type` | Type for supporter platforms |
|
|
708
|
+
| `ServiceTypes` | `object` | Service type constants |
|
|
709
|
+
| `ServiceType` | `type` | Type for service types |
|
|
710
|
+
|
|
711
|
+
### Utilities
|
|
712
|
+
|
|
713
|
+
| Function | Description |
|
|
714
|
+
|----------|-------------|
|
|
715
|
+
| `formatAmount` | Format number as ৳ currency |
|
|
716
|
+
| `formatSignedAmount` | Format with + or - sign |
|
|
717
|
+
| `formatDate` | Format date string |
|
|
718
|
+
| `formatNumber` | Format with thousand separators |
|
|
719
|
+
| `formatToPlainText` | Convert value to plain text |
|
|
720
|
+
| `formatMetadataKey` | Format metadata key to readable text |
|
|
721
|
+
| `getUserPageLink` | Generate user profile URL |
|
|
722
|
+
| `getInitials` | Get name initials |
|
|
723
|
+
| `getSocialLink` | Generate social profile URL |
|
|
724
|
+
| `getSocialUrl` | Generate social sharing URL |
|
|
725
|
+
| `openInNewWindow` | Open URL in new tab |
|
|
726
|
+
| `shareToFacebook` | Share to Facebook |
|
|
727
|
+
| `shareToInstagram` | Share to Instagram |
|
|
728
|
+
| `shareToLinkedIn` | Share to LinkedIn |
|
|
729
|
+
| `shareToX` | Share to X (Twitter) |
|
|
730
|
+
| `downloadQrSvgAsPng` | Download QR as PNG |
|
|
731
|
+
| `printQrSvg` | Print QR code |
|
|
732
|
+
| `toHumanReadable` | Convert camelCase/snake_case to readable |
|
|
733
|
+
| `validatePhoneNumber` | Validate Bangladeshi phone |
|
|
734
|
+
| `checkModeration` | Check text for profanity |
|
|
735
|
+
|
|
736
|
+
### Moderation
|
|
737
|
+
|
|
738
|
+
| Function | Description |
|
|
739
|
+
|----------|-------------|
|
|
740
|
+
| `moderateText` | Check text for profanity |
|
|
741
|
+
| `checkBanglaWords` | Check for Bangla bad words |
|
|
742
|
+
| `normalizeLeetspeak` | Convert leetspeak to normal |
|
|
743
|
+
| `normalizeUnicode` | Remove Unicode diacritics |
|
|
744
|
+
| `banglaBadWords` | Bangla profanity word list |
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
## Local Development
|
|
55
749
|
|
|
56
750
|
Install dependencies:
|
|
57
751
|
|
|
@@ -80,59 +774,141 @@ bun run typecheck
|
|
|
80
774
|
# Alias for typecheck
|
|
81
775
|
bun run lint
|
|
82
776
|
|
|
777
|
+
# Format code
|
|
778
|
+
bun run format
|
|
779
|
+
|
|
780
|
+
# Check formatting
|
|
781
|
+
bun run format:check
|
|
782
|
+
|
|
83
783
|
# Clean build artifacts
|
|
84
784
|
bun run clean
|
|
85
785
|
```
|
|
86
786
|
|
|
87
|
-
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## Project Structure
|
|
88
790
|
|
|
89
|
-
```
|
|
791
|
+
```
|
|
90
792
|
src/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
793
|
+
├── index.ts # Main entry
|
|
794
|
+
├── constants/
|
|
795
|
+
│ ├── common.ts # Visibility
|
|
796
|
+
│ ├── legal.ts # productInfo, companyInfo
|
|
797
|
+
│ ├── payment.ts # Payment constants
|
|
798
|
+
│ ├── platforms.ts # SupporterPlatforms
|
|
799
|
+
│ ├── services.ts # ServiceTypes
|
|
800
|
+
│ └── index.ts # Exports
|
|
801
|
+
├── utils/
|
|
802
|
+
│ ├── check-moderation.ts
|
|
803
|
+
│ ├── format-amount.ts
|
|
804
|
+
│ ├── format-date.ts
|
|
805
|
+
│ ├── format-number.ts
|
|
806
|
+
│ ├── format-plain-text.ts
|
|
807
|
+
│ ├── get-social-handle.ts
|
|
808
|
+
│ ├── get-social-link.ts
|
|
809
|
+
│ ├── get-user-name-initials.ts
|
|
810
|
+
│ ├── get-user-page-link.ts
|
|
811
|
+
│ ├── open-to-new-window.ts
|
|
812
|
+
│ ├── post-to-facebook.ts
|
|
813
|
+
│ ├── post-to-instagram.ts
|
|
814
|
+
│ ├── post-to-linkedin.ts
|
|
815
|
+
│ ├── post-to-x.ts
|
|
816
|
+
│ ├── qr-svg-utils.ts
|
|
817
|
+
│ ├── to-human-readable.ts
|
|
818
|
+
│ ├── validate-phone-number.ts
|
|
819
|
+
│ └── index.ts # Exports
|
|
820
|
+
├── moderation/
|
|
821
|
+
│ ├── datasets/
|
|
822
|
+
│ │ ├── bn.ts # Bangla bad words (710+)
|
|
823
|
+
│ │ └── index.ts
|
|
824
|
+
│ ├── normalizer.ts
|
|
825
|
+
│ ├── profanity-service.ts
|
|
826
|
+
│ └── index.ts # Exports
|
|
827
|
+
├── types/
|
|
828
|
+
│ ├── supabase.ts # Full Supabase types
|
|
829
|
+
│ └── index.ts
|
|
830
|
+
├── lib/
|
|
831
|
+
│ └── utils.ts # cn() utility
|
|
832
|
+
├── providers/
|
|
833
|
+
│ └── theme-provider.tsx # Theme provider
|
|
834
|
+
└── components/
|
|
835
|
+
├── turnstile-captcha.tsx
|
|
836
|
+
└── ui/ # 30+ UI components
|
|
837
|
+
├── alert.tsx
|
|
838
|
+
├── alert-dialog.tsx
|
|
839
|
+
├── avatar.tsx
|
|
840
|
+
├── badge.tsx
|
|
841
|
+
├── breadcrumb.tsx
|
|
842
|
+
├── button.tsx
|
|
843
|
+
├── button-group.tsx
|
|
844
|
+
├── calendar.tsx
|
|
845
|
+
├── card.tsx
|
|
846
|
+
├── chart.tsx
|
|
847
|
+
├── checkbox.tsx
|
|
848
|
+
├── dialog.tsx
|
|
849
|
+
├── drawer.tsx
|
|
850
|
+
├── dropdown-menu.tsx
|
|
851
|
+
├── empty.tsx
|
|
852
|
+
├── empty-minimal.tsx
|
|
853
|
+
├── field.tsx
|
|
854
|
+
├── input.tsx
|
|
855
|
+
├── input-group.tsx
|
|
856
|
+
├── input-otp.tsx
|
|
857
|
+
├── item.tsx
|
|
858
|
+
├── label.tsx
|
|
859
|
+
├── popover.tsx
|
|
860
|
+
├── radio-group.tsx
|
|
861
|
+
├── select.tsx
|
|
862
|
+
├── separator.tsx
|
|
863
|
+
├── sheet.tsx
|
|
864
|
+
├── sidebar.tsx
|
|
865
|
+
├── skeleton.tsx
|
|
866
|
+
├── sonner.tsx
|
|
867
|
+
├── spinner.tsx
|
|
868
|
+
├── table.tsx
|
|
869
|
+
├── tabs.tsx
|
|
870
|
+
├── textarea.tsx
|
|
871
|
+
├── toggle.tsx
|
|
872
|
+
├── toggle-group.tsx
|
|
873
|
+
└── tooltip.tsx
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
## Dependencies
|
|
879
|
+
|
|
880
|
+
### Runtime
|
|
881
|
+
|
|
882
|
+
- `@fontsource-variable/noto-sans-bengali` - Bengali font
|
|
883
|
+
- `@hugeicons/core-free-icons` - Hugeicons
|
|
884
|
+
- `@hugeicons/react` - React icons
|
|
885
|
+
- `@marsidev/react-turnstile` - Turnstile captcha
|
|
886
|
+
- `@tailwindcss/vite` - Tailwind CSS
|
|
887
|
+
- `class-variance-authority` - Component variants
|
|
888
|
+
- `glin-profanity` - Profanity detection
|
|
889
|
+
- `input-otp` - OTP input
|
|
890
|
+
- `next-themes` - Theme provider
|
|
891
|
+
- `openai` - OpenAI API
|
|
892
|
+
- `radix-ui` - UI primitives
|
|
893
|
+
- `react` / `react-dom` - React
|
|
894
|
+
- `react-day-picker` - Calendar
|
|
895
|
+
- `recharts` - Charts
|
|
896
|
+
- `shadcn` - UI components
|
|
897
|
+
- `sonner` - Toast notifications
|
|
898
|
+
- `tailwind-merge` - Tailwind merge
|
|
899
|
+
- `tailwindcss` - Styling
|
|
900
|
+
- `tw-animate-css` - Animations
|
|
901
|
+
- `vaul` - Drawer
|
|
902
|
+
|
|
903
|
+
### Dev
|
|
904
|
+
|
|
905
|
+
- `@biomejs/biome` - Linting/formatting
|
|
906
|
+
- `ultracite` - Ultracite CLI
|
|
907
|
+
- `vite` - Build tool
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
## Release & Publish
|
|
136
912
|
|
|
137
913
|
Publishing is automated on push to the `main` branch via GitHub Actions. Ensure:
|
|
138
914
|
|
package/package.json
CHANGED
package/src/types/supabase.ts
CHANGED
|
@@ -784,6 +784,44 @@ export type Database = {
|
|
|
784
784
|
},
|
|
785
785
|
];
|
|
786
786
|
};
|
|
787
|
+
user_services: {
|
|
788
|
+
Row: {
|
|
789
|
+
config: Json | null;
|
|
790
|
+
created_at: string | null;
|
|
791
|
+
id: string;
|
|
792
|
+
is_enabled: boolean | null;
|
|
793
|
+
profile_id: string;
|
|
794
|
+
service: string;
|
|
795
|
+
updated_at: string | null;
|
|
796
|
+
};
|
|
797
|
+
Insert: {
|
|
798
|
+
config?: Json | null;
|
|
799
|
+
created_at?: string | null;
|
|
800
|
+
id?: string;
|
|
801
|
+
is_enabled?: boolean | null;
|
|
802
|
+
profile_id: string;
|
|
803
|
+
service: string;
|
|
804
|
+
updated_at?: string | null;
|
|
805
|
+
};
|
|
806
|
+
Update: {
|
|
807
|
+
config?: Json | null;
|
|
808
|
+
created_at?: string | null;
|
|
809
|
+
id?: string;
|
|
810
|
+
is_enabled?: boolean | null;
|
|
811
|
+
profile_id?: string;
|
|
812
|
+
service?: string;
|
|
813
|
+
updated_at?: string | null;
|
|
814
|
+
};
|
|
815
|
+
Relationships: [
|
|
816
|
+
{
|
|
817
|
+
foreignKeyName: "user_services_profile_id_fkey";
|
|
818
|
+
columns: ["profile_id"];
|
|
819
|
+
isOneToOne: false;
|
|
820
|
+
referencedRelation: "profiles";
|
|
821
|
+
referencedColumns: ["id"];
|
|
822
|
+
},
|
|
823
|
+
];
|
|
824
|
+
};
|
|
787
825
|
wallets: {
|
|
788
826
|
Row: {
|
|
789
827
|
balance: number;
|
|
@@ -4,7 +4,6 @@ import { getUserPageLink } from "./get-user-page-link";
|
|
|
4
4
|
describe("getUserPageLink", () => {
|
|
5
5
|
test("builds user page link with sanitized username", () => {
|
|
6
6
|
const result = getUserPageLink(" @john doe ");
|
|
7
|
-
|
|
8
7
|
expect(result.endsWith("/@%40johndoe")).toBe(true);
|
|
9
8
|
});
|
|
10
9
|
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
export function getUserPageLink(
|
|
1
|
+
export function getUserPageLink(
|
|
2
|
+
username: string,
|
|
3
|
+
baseUrl = "https://hobenakicoffee.com"
|
|
4
|
+
) {
|
|
2
5
|
const sanitizedUsername = encodeURIComponent(
|
|
3
6
|
username.trim().replace(/\s+/g, "")
|
|
4
7
|
);
|
|
5
|
-
return
|
|
8
|
+
return `${baseUrl}/@${sanitizedUsername}`;
|
|
6
9
|
}
|