@hobenakicoffee/libraries 3.4.2 → 4.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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -246
  3. package/package.json +22 -22
  4. package/src/App.tsx +192 -19
  5. package/src/index.css +0 -1
  6. package/src/components/turnstile-captcha.tsx +0 -47
  7. package/src/components/ui/button.tsx +0 -77
  8. package/src/components/ui/calendar.tsx +0 -235
  9. package/src/components/ui/spinner.tsx +0 -18
  10. package/src/constants/common.test.ts +0 -33
  11. package/src/constants/legal.test.ts +0 -72
  12. package/src/constants/payment.test.ts +0 -259
  13. package/src/constants/platforms.test.ts +0 -66
  14. package/src/constants/services.test.ts +0 -58
  15. package/src/lib/utils.ts +0 -6
  16. package/src/moderation/profanity-service.test.ts +0 -106
  17. package/src/providers/theme-provider.tsx +0 -73
  18. package/src/utils/check-moderation.test.ts +0 -321
  19. package/src/utils/format-amount.test.ts +0 -30
  20. package/src/utils/format-count.test.ts +0 -56
  21. package/src/utils/format-date.test.ts +0 -19
  22. package/src/utils/format-number.test.ts +0 -29
  23. package/src/utils/format-plain-text.test.ts +0 -36
  24. package/src/utils/get-newsletter-post-link.test.ts +0 -27
  25. package/src/utils/get-product-link.test.ts +0 -34
  26. package/src/utils/get-social-handle.test.ts +0 -32
  27. package/src/utils/get-social-link.test.ts +0 -63
  28. package/src/utils/get-user-name-initials.test.ts +0 -34
  29. package/src/utils/get-user-page-link.test.ts +0 -9
  30. package/src/utils/open-to-new-window.test.ts +0 -34
  31. package/src/utils/post-to-facebook.test.ts +0 -43
  32. package/src/utils/post-to-instagram.test.ts +0 -56
  33. package/src/utils/post-to-linkedin.test.ts +0 -43
  34. package/src/utils/post-to-x.test.ts +0 -45
  35. package/src/utils/qr-svg-utils.test.ts +0 -104
  36. package/src/utils/to-human-readable.test.ts +0 -25
  37. package/src/utils/validate-phone-number.test.ts +0 -28
@@ -1,72 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { companyInfo, productInfo } from "./legal";
3
-
4
- describe("productInfo", () => {
5
- test("should have all required fields", () => {
6
- expect(productInfo).toHaveProperty("name");
7
- expect(productInfo).toHaveProperty("domain");
8
- expect(productInfo).toHaveProperty("twitterHandle");
9
- expect(productInfo).toHaveProperty("title");
10
- expect(productInfo).toHaveProperty("description");
11
- expect(productInfo).toHaveProperty("keywords");
12
- });
13
-
14
- test("should match exact values", () => {
15
- expect(productInfo).toEqual({
16
- name: "হবে নাকি Coffee?",
17
- domain: "https://www.hobenakicoffee.com",
18
- twitterHandle: "@hobenakicoffee",
19
- title: "চিন্তা ছাড়া আয়, শুরু করো তাই — হবে নাকি Coffee, ঝামেলা সব ছাই!",
20
- description:
21
- "‘হবে নাকি Coffee?’ দিয়ে যে কেউ ঝামেলা ছাড়াই নিজের অনলাইন ব্যবসা বা আয় শুরু করতে পারে। লিগ্যাল, কোম্পানি ফরমেশন বা কাগজপত্র নিয়ে ভাবতে হবে না—সবকিছু আমরা দেখি। শুধু একটি লিংক শেয়ার করুন, আর যে কেউ Coffee/Tip দিয়ে আপনাকে সাপোর্ট করতে পারবে। চাইলে এক্সক্লুসিভ কনটেন্ট, ডিজিটাল পণ্য বা মার্চ, গিফট, পার্সোনাল শাউটআউট কিংবা সার্ভিসও অফার করা যায়। কনটেন্ট ক্রিয়েটর, ফ্রিল্যান্সার, ছোট ব্যবসা, কমিউনিটি বা ইভেন্ট অর্গানাইজার, চ্যারিটি উদ্যোগ বা ব্যক্তিগত লক্ষ্য—সবার জন্যই HobeNakiCoffee হলো অনলাইনে আয় ও সাপোর্ট নেওয়ার সহজ, ঝামেলামুক্ত প্ল্যাটফর্ম।",
22
- keywords:
23
- "হবে নাকি Coffee, buy me a coffee, tip jar, support link, support me, donate, online income bangla, extra income, monetization, payment link, audience support, exclusive content, digital product, merch, shoutout, fundraising, charity, small business support, freelancer support, Bangladesh",
24
- socials: {
25
- facebook: "https://www.facebook.com/hobenakicoffee",
26
- youtube: "https://www.youtube.com/@hobenakicoffee",
27
- x: "https://x.com/hobenakicoffee",
28
- instagram: "https://www.instagram.com/hobenakicoffee",
29
- tiktok: "https://www.tiktok.com/@hobenakicoffee",
30
- },
31
- });
32
- });
33
- });
34
-
35
- describe("companyInfo", () => {
36
- test("should have all required fields", () => {
37
- expect(companyInfo).toHaveProperty("name");
38
- expect(companyInfo).toHaveProperty("contactEmail");
39
- expect(companyInfo).toHaveProperty("contactPhone");
40
- expect(companyInfo).toHaveProperty("contactLocation");
41
- expect(companyInfo).toHaveProperty("contactWhatsapp");
42
- expect(companyInfo).toHaveProperty("contactX");
43
- expect(companyInfo).toHaveProperty("contactFacebook");
44
- expect(companyInfo).toHaveProperty("contactYoutube");
45
- expect(companyInfo).toHaveProperty("contactLinkedin");
46
- expect(companyInfo).toHaveProperty("domain");
47
- expect(companyInfo).toHaveProperty("postalAddress");
48
- });
49
-
50
- test("should match exact values", () => {
51
- expect(companyInfo).toEqual({
52
- name: "Shamscorner LLC",
53
- contactEmail: "mail@shamscorner.com",
54
- contactPhone: "+1(817) 973-7285",
55
- contactLocation:
56
- "7320, 1021 E Lincolnway, Cheyenne, WY, Laramie, US, 82001",
57
- contactWhatsapp: "https://wa.me/13072212615",
58
- contactX: "https://www.x.com/shamscorner_llc",
59
- contactFacebook: "https://www.facebook.com/shamscorner.llc",
60
- contactYoutube: "https://www.youtube.com/@shamscorner",
61
- contactLinkedin: "https://www.linkedin.com/company/shamscorner",
62
- domain: "https://www.shamscorner.com",
63
- postalAddress: {
64
- streetAddress: "1021 E Lincolnway Suite 7320",
65
- addressLocality: "Cheyenne",
66
- addressRegion: "WY",
67
- postalCode: "82001",
68
- addressCountry: "US",
69
- },
70
- });
71
- });
72
- });
@@ -1,259 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import type {
3
- PaymentDirection,
4
- PaymentProvider,
5
- PaymentStatus,
6
- PaymentType,
7
- PayoutProvider,
8
- WithdrawalStatus,
9
- } from "./payment";
10
- import {
11
- PaymentDirections,
12
- PaymentProviders,
13
- PaymentStatuses,
14
- PaymentTypes,
15
- PayoutProviders,
16
- WithdrawalStatuses,
17
- } from "./payment";
18
-
19
- describe("PaymentStatuses", () => {
20
- test("should contain all expected status keys", () => {
21
- const expectedKeys = [
22
- "PENDING",
23
- "PROCESSING",
24
- "COMPLETED",
25
- "FAILED",
26
- "REVERSED",
27
- "CANCELLED",
28
- "REFUNDED",
29
- "REVIEWING",
30
- ];
31
- expect(Object.keys(PaymentStatuses)).toEqual(expectedKeys);
32
- });
33
-
34
- test("should have correct values for each status", () => {
35
- expect(PaymentStatuses.PENDING).toBe("pending");
36
- expect(PaymentStatuses.PROCESSING).toBe("processing");
37
- expect(PaymentStatuses.COMPLETED).toBe("completed");
38
- expect(PaymentStatuses.FAILED).toBe("failed");
39
- expect(PaymentStatuses.REVERSED).toBe("reversed");
40
- expect(PaymentStatuses.CANCELLED).toBe("cancelled");
41
- expect(PaymentStatuses.REFUNDED).toBe("refunded");
42
- expect(PaymentStatuses.REVIEWING).toBe("reviewing");
43
- });
44
-
45
- test("should be read-only at compile time", () => {
46
- // TypeScript prevents modification at compile time with 'as const'
47
- // This test verifies the structure is correct
48
- expect(Object.isFrozen(PaymentStatuses)).toBe(false);
49
- expect(typeof PaymentStatuses).toBe("object");
50
- });
51
-
52
- test("PaymentStatus type should accept valid status values", () => {
53
- const validStatus: PaymentStatus = "completed";
54
- expect(validStatus).toBe("completed");
55
- });
56
-
57
- test("should have 8 payment statuses", () => {
58
- expect(Object.keys(PaymentStatuses).length).toBe(8);
59
- });
60
-
61
- test("all values should be lowercase strings", () => {
62
- Object.values(PaymentStatuses).forEach((status) => {
63
- expect(status).toBe(status.toLowerCase() as PaymentStatus);
64
- expect(typeof status).toBe("string");
65
- });
66
- });
67
- });
68
-
69
- describe("PaymentTypes", () => {
70
- test("should contain all expected keys", () => {
71
- const expectedKeys = [
72
- "SUBSCRIPTION",
73
- "ONE_TIME",
74
- "PAYOUT",
75
- "WITHDRAW_LOCK",
76
- "WITHDRAW_RELEASE",
77
- "WITHDRAW_COMPLETE",
78
- "MANUAL_ADJUSTMENT",
79
- ];
80
- expect(Object.keys(PaymentTypes)).toEqual(expectedKeys);
81
- });
82
-
83
- test("should have correct values for each type", () => {
84
- expect(PaymentTypes.SUBSCRIPTION).toBe("subscription");
85
- expect(PaymentTypes.ONE_TIME).toBe("one-time");
86
- expect(PaymentTypes.PAYOUT).toBe("payout");
87
- expect(PaymentTypes.WITHDRAW_LOCK).toBe("withdraw_lock");
88
- expect(PaymentTypes.WITHDRAW_RELEASE).toBe("withdraw_release");
89
- expect(PaymentTypes.WITHDRAW_COMPLETE).toBe("withdraw_complete");
90
- expect(PaymentTypes.MANUAL_ADJUSTMENT).toBe("manual_adjustment");
91
- });
92
-
93
- test("should be read-only at compile time", () => {
94
- // TypeScript prevents modification at compile time with 'as const'
95
- // This test verifies the structure is correct
96
- expect(Object.isFrozen(PaymentTypes)).toBe(false);
97
- expect(typeof PaymentTypes).toBe("object");
98
- });
99
-
100
- test("PaymentType type should accept valid type values", () => {
101
- const validType: PaymentType = "subscription";
102
- expect(validType).toBe("subscription");
103
- });
104
-
105
- test("should have 7 payment types", () => {
106
- expect(Object.keys(PaymentTypes).length).toBe(7);
107
- });
108
-
109
- test("all values should be lowercase or kebab-case strings", () => {
110
- Object.values(PaymentTypes).forEach((type) => {
111
- expect(typeof type).toBe("string");
112
- // Check that it contains lowercase letters, hyphens, or underscores
113
- expect(type).toMatch(/^[a-z_-]+$/);
114
- });
115
- });
116
- });
117
-
118
- describe("PayoutProviders", () => {
119
- test("should contain all expected provider keys", () => {
120
- const expectedKeys = ["BKASH", "NAGAD", "ROCKET", "BANK"];
121
- expect(Object.keys(PayoutProviders)).toEqual(expectedKeys);
122
- });
123
-
124
- test("should have correct values for each provider", () => {
125
- expect(PayoutProviders.BKASH).toBe("bkash");
126
- expect(PayoutProviders.NAGAD).toBe("nagad");
127
- expect(PayoutProviders.ROCKET).toBe("rocket");
128
- expect(PayoutProviders.BANK).toBe("bank");
129
- });
130
-
131
- test("PayoutProvider type should accept valid provider values", () => {
132
- const validProvider: PayoutProvider = "bkash";
133
- expect(validProvider).toBe("bkash");
134
- });
135
-
136
- test("should have 4 payout providers", () => {
137
- expect(Object.keys(PayoutProviders).length).toBe(4);
138
- });
139
- });
140
-
141
- describe("WithdrawalStatuses", () => {
142
- test("should contain all expected status keys", () => {
143
- const expectedKeys = [
144
- "REQUESTED",
145
- "APPROVED",
146
- "PROCESSING",
147
- "PAID",
148
- "REJECTED",
149
- "FAILED",
150
- ];
151
- expect(Object.keys(WithdrawalStatuses)).toEqual(expectedKeys);
152
- });
153
-
154
- test("should have correct values for each status", () => {
155
- expect(WithdrawalStatuses.REQUESTED).toBe("requested");
156
- expect(WithdrawalStatuses.APPROVED).toBe("approved");
157
- expect(WithdrawalStatuses.PROCESSING).toBe("processing");
158
- expect(WithdrawalStatuses.PAID).toBe("paid");
159
- expect(WithdrawalStatuses.REJECTED).toBe("rejected");
160
- expect(WithdrawalStatuses.FAILED).toBe("failed");
161
- });
162
-
163
- test("WithdrawalStatus type should accept valid status values", () => {
164
- const validStatus: WithdrawalStatus = "processing";
165
- expect(validStatus).toBe("processing");
166
- });
167
-
168
- test("should have 6 withdrawal statuses", () => {
169
- expect(Object.keys(WithdrawalStatuses).length).toBe(6);
170
- });
171
- });
172
-
173
- describe("PaymentProviders", () => {
174
- test("should contain all expected provider keys", () => {
175
- const expectedKeys = [
176
- "HOBENAKICOFFEE",
177
- "BKASH",
178
- "NAGAD",
179
- "ROCKET",
180
- "UPAY",
181
- "SSLCOMMERZ",
182
- "AAMARPAY",
183
- "PORTWALLET",
184
- "TAP",
185
- "OTHER",
186
- ];
187
- expect(Object.keys(PaymentProviders)).toEqual(expectedKeys);
188
- });
189
-
190
- test("should have correct values for each provider", () => {
191
- expect(PaymentProviders.HOBENAKICOFFEE).toBe("HobeNakiCoffee");
192
- expect(PaymentProviders.BKASH).toBe("Bkash");
193
- expect(PaymentProviders.NAGAD).toBe("Nagad");
194
- expect(PaymentProviders.ROCKET).toBe("Rocket");
195
- expect(PaymentProviders.UPAY).toBe("Upay");
196
- expect(PaymentProviders.SSLCOMMERZ).toBe("SSLCommerz");
197
- expect(PaymentProviders.AAMARPAY).toBe("Aamarpay");
198
- expect(PaymentProviders.PORTWALLET).toBe("Portwallet");
199
- expect(PaymentProviders.TAP).toBe("Tap");
200
- expect(PaymentProviders.OTHER).toBe("Other");
201
- });
202
- test("should allow HOBENAKICOFFEE as PaymentProvider type", () => {
203
- const provider: PaymentProvider = PaymentProviders.HOBENAKICOFFEE;
204
- expect(provider).toBe("HobeNakiCoffee");
205
- });
206
-
207
- test("should be read-only at compile time", () => {
208
- // TypeScript prevents modification at compile time with 'as const'
209
- // This test verifies the structure is correct
210
- expect(Object.isFrozen(PaymentProviders)).toBe(false);
211
- expect(typeof PaymentProviders).toBe("object");
212
- });
213
-
214
- test("PaymentProvider type should accept valid provider values", () => {
215
- const validProvider: PaymentProvider = "Bkash";
216
- expect(validProvider).toBe("Bkash");
217
- });
218
-
219
- test("should have 10 payment providers", () => {
220
- expect(Object.keys(PaymentProviders).length).toBe(10);
221
- });
222
-
223
- test("all values should be lowercase strings", () => {
224
- Object.values(PaymentProviders).forEach((provider) => {
225
- expect(provider).toBe(provider as PaymentProvider);
226
- expect(typeof provider).toBe("string");
227
- });
228
- });
229
- });
230
-
231
- describe("PaymentDirections", () => {
232
- test("should contain all expected direction keys", () => {
233
- const expectedKeys = ["DEBIT", "CREDIT"];
234
- expect(Object.keys(PaymentDirections)).toEqual(expectedKeys);
235
- });
236
-
237
- test("should have correct values for each direction", () => {
238
- expect(PaymentDirections.DEBIT).toBe("debit");
239
- expect(PaymentDirections.CREDIT).toBe("credit");
240
- });
241
-
242
- test("should be usable as PaymentDirection type", () => {
243
- const debit: PaymentDirection = "debit";
244
- const credit: PaymentDirection = "credit";
245
- expect(debit).toBe("debit");
246
- expect(credit).toBe("credit");
247
- });
248
-
249
- test("should have 2 payment directions", () => {
250
- expect(Object.keys(PaymentDirections).length).toBe(2);
251
- });
252
-
253
- test("all values should be lowercase strings", () => {
254
- Object.values(PaymentDirections).forEach((direction) => {
255
- expect(direction).toBe(direction.toLowerCase() as PaymentDirection);
256
- expect(typeof direction).toBe("string");
257
- });
258
- });
259
- });
@@ -1,66 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import type { SupporterPlatform } from "./platforms";
3
- import { SupporterPlatforms } from "./platforms";
4
-
5
- describe("SupporterPlatforms", () => {
6
- test("should contain all expected platform keys", () => {
7
- const expectedKeys = [
8
- "FACEBOOK",
9
- "X",
10
- "INSTAGRAM",
11
- "YOUTUBE",
12
- "GITHUB",
13
- "LINKEDIN",
14
- "TWITCH",
15
- "TIKTOK",
16
- "THREADS",
17
- "WHATSAPP",
18
- "TELEGRAM",
19
- "DISCORD",
20
- "REDDIT",
21
- "PINTEREST",
22
- "MEDIUM",
23
- "DEVTO",
24
- "BEHANCE",
25
- "DRIBBBLE",
26
- ];
27
- expect(Object.keys(SupporterPlatforms)).toEqual(expectedKeys);
28
- });
29
-
30
- test("should have correct values for each platform", () => {
31
- expect(SupporterPlatforms.FACEBOOK).toBe("facebook");
32
- expect(SupporterPlatforms.X).toBe("x");
33
- expect(SupporterPlatforms.INSTAGRAM).toBe("instagram");
34
- expect(SupporterPlatforms.YOUTUBE).toBe("youtube");
35
- expect(SupporterPlatforms.GITHUB).toBe("github");
36
- expect(SupporterPlatforms.LINKEDIN).toBe("linkedin");
37
- expect(SupporterPlatforms.TWITCH).toBe("twitch");
38
- expect(SupporterPlatforms.TIKTOK).toBe("tiktok");
39
- expect(SupporterPlatforms.THREADS).toBe("threads");
40
- expect(SupporterPlatforms.WHATSAPP).toBe("whatsapp");
41
- expect(SupporterPlatforms.TELEGRAM).toBe("telegram");
42
- expect(SupporterPlatforms.DISCORD).toBe("discord");
43
- expect(SupporterPlatforms.REDDIT).toBe("reddit");
44
- expect(SupporterPlatforms.PINTEREST).toBe("pinterest");
45
- expect(SupporterPlatforms.MEDIUM).toBe("medium");
46
- expect(SupporterPlatforms.DEVTO).toBe("devto");
47
- expect(SupporterPlatforms.BEHANCE).toBe("behance");
48
- expect(SupporterPlatforms.DRIBBBLE).toBe("dribbble");
49
- });
50
-
51
- test("should be read-only at compile time", () => {
52
- // TypeScript prevents modification at compile time with 'as const'
53
- // This test verifies the structure is correct
54
- expect(Object.isFrozen(SupporterPlatforms)).toBe(false);
55
- expect(typeof SupporterPlatforms).toBe("object");
56
- });
57
-
58
- test("SupporterPlatform type should accept valid platform values", () => {
59
- const validPlatform: SupporterPlatform = "facebook";
60
- expect(validPlatform).toBe("facebook");
61
- });
62
-
63
- test("should have 18 platforms", () => {
64
- expect(Object.keys(SupporterPlatforms).length).toBe(18);
65
- });
66
- });
@@ -1,58 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import type { ServiceType } from "./services";
3
- import { ServiceTypes } from "./services";
4
-
5
- describe("ServiceTypes", () => {
6
- test("should contain all expected service type keys", () => {
7
- const expectedKeys = [
8
- "GIFT",
9
- "DIGITAL_CONTENT",
10
- "MY_SHOP",
11
- "CONSULTANCY_1ON1",
12
- "HIRE_ME",
13
- "COURSES",
14
- "LIVE_STREAMS",
15
- "NEWSLETTER",
16
- "WITHDRAWAL",
17
- "FOLLOW",
18
- ];
19
- expect(Object.keys(ServiceTypes)).toEqual(expectedKeys);
20
- });
21
-
22
- test("should have correct values for each service type", () => {
23
- expect(ServiceTypes.GIFT).toBe("gift");
24
- expect(ServiceTypes.DIGITAL_CONTENT).toBe("digital_content");
25
- expect(ServiceTypes.MY_SHOP).toBe("my_shop");
26
- expect(ServiceTypes.CONSULTANCY_1ON1).toBe("consultancy_1on1");
27
- expect(ServiceTypes.HIRE_ME).toBe("hire_me");
28
- expect(ServiceTypes.COURSES).toBe("courses");
29
- expect(ServiceTypes.LIVE_STREAMS).toBe("live_streams");
30
- expect(ServiceTypes.NEWSLETTER).toBe("newsletter");
31
- expect(ServiceTypes.WITHDRAWAL).toBe("withdrawal");
32
- expect(ServiceTypes.FOLLOW).toBe("follow");
33
- });
34
-
35
- test("should be read-only at compile time", () => {
36
- // TypeScript prevents modification at compile time with 'as const'
37
- // This test verifies the structure is correct
38
- expect(Object.isFrozen(ServiceTypes)).toBe(false);
39
- expect(typeof ServiceTypes).toBe("object");
40
- });
41
-
42
- test("ServiceType type should accept valid service type values", () => {
43
- const validType: ServiceType = "gift";
44
- expect(validType).toBe("gift");
45
- });
46
-
47
- test("should have 10 service types", () => {
48
- expect(Object.keys(ServiceTypes).length).toBe(10);
49
- });
50
-
51
- test("all values should be lowercase or snake_case strings", () => {
52
- for (const type of Object.values(ServiceTypes)) {
53
- expect(typeof type).toBe("string");
54
- // Check that it only contains lowercase letters, numbers, and underscores
55
- expect(type).toMatch(/^[a-z0-9_]+$/);
56
- }
57
- });
58
- });
package/src/lib/utils.ts DELETED
@@ -1,6 +0,0 @@
1
- import { type ClassValue, clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
6
- }
@@ -1,106 +0,0 @@
1
- import { describe, expect, mock, test } from "bun:test";
2
- import {
3
- badwordsMatcher,
4
- containsBanglaSwear,
5
- containsProfanity,
6
- } from "./profanity-service";
7
-
8
- mock.module("obscenity", () => ({
9
- englishDataset: { build: () => ({ patterns: [], masks: [] }) },
10
- englishRecommendedTransformers: {},
11
- RegExpMatcher: class {
12
- hasMatch() {
13
- return false;
14
- }
15
- },
16
- }));
17
-
18
- describe("containsBanglaSwear", () => {
19
- test("returns false when no bad words found", () => {
20
- const result = containsBanglaSwear("ভালো কথা");
21
- expect(result).toBe(false);
22
- });
23
-
24
- test("returns true when Bangla bad word is found", () => {
25
- const result = containsBanglaSwear("খানকির ছেলে");
26
- expect(result).toBe(true);
27
- });
28
-
29
- test("matches bad words in mixed text", () => {
30
- const result = containsBanglaSwear("এটি খানকির ছেলে একটি বাক্য");
31
- expect(result).toBe(true);
32
- });
33
-
34
- test("handles normalized unicode", () => {
35
- const result = containsBanglaSwear("খানকির ছেলে");
36
- expect(result).toBe(true);
37
- });
38
-
39
- test("returns false for empty string", () => {
40
- const result = containsBanglaSwear("");
41
- expect(result).toBe(false);
42
- });
43
-
44
- test("handles multiple bad words", () => {
45
- const result = containsBanglaSwear("খানকির ছেলে এবং চোদানীর পোলা");
46
- expect(result).toBe(true);
47
- });
48
-
49
- test("handles leetspeak variations", () => {
50
- const result = containsBanglaSwear("খানকির ছেলে");
51
- expect(typeof result).toBe("boolean");
52
- });
53
- });
54
-
55
- describe("containsProfanity", () => {
56
- test("returns false for clean text", () => {
57
- const result = containsProfanity("এটি একটি পরিষ্কার বাক্য");
58
- expect(result).toBe(false);
59
- });
60
-
61
- test("throws error for undefined input", () => {
62
- expect(() => containsProfanity(undefined as unknown as string)).toThrow();
63
- });
64
-
65
- test("returns false for empty string", () => {
66
- const result = containsProfanity("");
67
- expect(result).toBe(false);
68
- });
69
-
70
- test("returns proper boolean structure", () => {
71
- const result = containsProfanity("clean text");
72
- expect(typeof result).toBe("boolean");
73
- });
74
-
75
- test("handles mixed English and Bangla text", () => {
76
- const result = containsProfanity("hello world খানকির ছেলে content");
77
- expect(result).toBe(true);
78
- });
79
-
80
- test("handles whitespace and punctuation", () => {
81
- const result = containsProfanity("clean text!!!");
82
- expect(result).toBe(false);
83
- });
84
-
85
- test("handles very long text", () => {
86
- const longText = `clean text ${"word ".repeat(1000)}`;
87
- const result = containsProfanity(longText);
88
- expect(typeof result).toBe("boolean");
89
- });
90
-
91
- test("throws error for null input", () => {
92
- expect(() => containsProfanity(null as unknown as string)).toThrow();
93
- });
94
- });
95
-
96
- describe("badwordsMatcher", () => {
97
- test("is a RegExpMatcher instance", () => {
98
- expect(badwordsMatcher).toBeDefined();
99
- expect(typeof badwordsMatcher.hasMatch).toBe("function");
100
- });
101
-
102
- test("hasMatch returns boolean", () => {
103
- const result = badwordsMatcher.hasMatch("clean text");
104
- expect(typeof result).toBe("boolean");
105
- });
106
- });
@@ -1,73 +0,0 @@
1
- import { createContext, useContext, useEffect, useState } from "react";
2
-
3
- type Theme = "dark" | "light" | "system";
4
-
5
- type ThemeProviderProps = {
6
- children: React.ReactNode;
7
- defaultTheme?: Theme;
8
- storageKey?: string;
9
- };
10
-
11
- type ThemeProviderState = {
12
- theme: Theme;
13
- setTheme: (theme: Theme) => void;
14
- };
15
-
16
- const initialState: ThemeProviderState = {
17
- theme: "system",
18
- setTheme: () => null,
19
- };
20
-
21
- const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
22
-
23
- export function ThemeProvider({
24
- children,
25
- defaultTheme = "system",
26
- storageKey = "hobenakicoffee-app-ui-themes",
27
- ...props
28
- }: ThemeProviderProps) {
29
- const [theme, setTheme] = useState<Theme>(
30
- () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
31
- );
32
-
33
- useEffect(() => {
34
- const root = window.document.documentElement;
35
-
36
- root.classList.remove("light", "dark");
37
-
38
- if (theme === "system") {
39
- const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
40
- .matches
41
- ? "dark"
42
- : "light";
43
-
44
- root.classList.add(systemTheme);
45
- return;
46
- }
47
-
48
- root.classList.add(theme);
49
- }, [theme]);
50
-
51
- const value = {
52
- theme,
53
- setTheme: (theme: Theme) => {
54
- localStorage.setItem(storageKey, theme);
55
- setTheme(theme);
56
- },
57
- };
58
-
59
- return (
60
- <ThemeProviderContext.Provider {...props} value={value}>
61
- {children}
62
- </ThemeProviderContext.Provider>
63
- );
64
- }
65
-
66
- export const useTheme = () => {
67
- const context = useContext(ThemeProviderContext);
68
-
69
- if (context === undefined)
70
- throw new Error("useTheme must be used within a ThemeProvider");
71
-
72
- return context;
73
- };