subos-frontend 1.0.95 → 1.0.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -174,8 +174,14 @@ var plansApi = {
174
174
  }
175
175
  };
176
176
  var subscriptionApi = {
177
- getActiveSubscription: async (externalId) => {
178
- return apiRequest(`${ENDPOINTS.SUBSCRIPTION}/${externalId}`, "GET", void 0, void 0, true);
177
+ getActiveSubscription: async (externalId, options) => {
178
+ let endpoint = `${ENDPOINTS.SUBSCRIPTION}/${externalId}`;
179
+ if (options == null ? void 0 : options.currency) {
180
+ const params = new URLSearchParams();
181
+ params.append("currency", options.currency);
182
+ endpoint += `?${params.toString()}`;
183
+ }
184
+ return apiRequest(endpoint, "GET", void 0, void 0, true);
179
185
  },
180
186
  cancelSubscription: async (externalId, options) => {
181
187
  let endpoint = `${ENDPOINTS.SUBSCRIPTION}/${externalId}`;
@@ -231,184 +237,6 @@ var transactionApi = {
231
237
  },
232
238
  getInvoice: (transactionId) => apiRequest(`${ENDPOINTS.TRANSACTIONS}/${transactionId}/invoice`)
233
239
  };
234
- var useSubscription = () => {
235
- const [subscription, setSubscription] = React10.useState(null);
236
- const [loading, setLoading] = React10.useState(false);
237
- const [error, setError] = React10.useState(null);
238
- const fetchSubscription = React10.useCallback(async (externalId) => {
239
- if (!externalId || externalId === "undefined" || externalId.trim() === "") {
240
- setError("Invalid user ID. Please ensure you are properly authenticated.");
241
- setLoading(false);
242
- return;
243
- }
244
- if (loading) return;
245
- setLoading(true);
246
- setError(null);
247
- try {
248
- const response = await subscriptionApi.getActiveSubscription(externalId);
249
- if (response.success && response.data) {
250
- setSubscription(response.data);
251
- } else {
252
- setError(response.error || "Failed to load subscription information");
253
- }
254
- } catch (err) {
255
- if (err instanceof Error && err.name !== "AbortError") {
256
- console.error("Error fetching subscription:", err);
257
- setError("Failed to load subscription information. Please try again later.");
258
- }
259
- } finally {
260
- setLoading(false);
261
- }
262
- }, [loading]);
263
- const clearSubscription = React10.useCallback(() => {
264
- setSubscription(null);
265
- setError(null);
266
- }, []);
267
- return {
268
- subscription,
269
- loading,
270
- error,
271
- fetchSubscription,
272
- clearSubscription
273
- };
274
- };
275
-
276
- // src/utils/planUtils.ts
277
- var getPlanFeatures = (plan) => {
278
- const features = [];
279
- if (plan.charges && plan.charges.length > 0) {
280
- plan.charges.forEach((charge) => {
281
- var _a;
282
- if ((_a = charge.billableMetric) == null ? void 0 : _a.name) {
283
- features.push(charge.billableMetric.name);
284
- }
285
- });
286
- }
287
- return features.length > 0 ? features : ["Core features included"];
288
- };
289
- var getCandidateUnits = (plan) => {
290
- var _a;
291
- if (plan.charges && plan.charges.length > 0) {
292
- const candidateCharge = plan.charges.find(
293
- (charge) => charge.billableMetric && charge.billableMetric.code === "CANDIDATES"
294
- );
295
- const units = (_a = candidateCharge == null ? void 0 : candidateCharge.properties) == null ? void 0 : _a.units;
296
- if (typeof units === "number" && Number.isFinite(units)) {
297
- return units;
298
- }
299
- }
300
- return 0;
301
- };
302
- var getUniqueCandidateUnits = (plans) => {
303
- const unitsSet = /* @__PURE__ */ new Set();
304
- plans.forEach((plan) => {
305
- plan.charges.forEach((charge) => {
306
- var _a, _b;
307
- if (((_a = charge.billableMetric) == null ? void 0 : _a.code) === "CANDIDATES") {
308
- const units = (_b = charge.properties) == null ? void 0 : _b.units;
309
- if (typeof units === "number" && Number.isFinite(units)) {
310
- unitsSet.add(units);
311
- }
312
- }
313
- });
314
- });
315
- return Array.from(unitsSet).sort((a, b) => a - b);
316
- };
317
- var getDropdownOptions = (plans) => {
318
- const options = [];
319
- const uniqueUnits = getUniqueCandidateUnits(plans);
320
- uniqueUnits.forEach((units) => {
321
- options.push({
322
- value: units.toString(),
323
- label: `Up to ${units.toLocaleString()} Candidates`,
324
- candidates: units
325
- });
326
- });
327
- return options;
328
- };
329
- var getCurrentSelectionText = (tierFilter, plans) => {
330
- const options = getDropdownOptions(plans);
331
- const currentOption = options.find((option) => option.value === tierFilter);
332
- if (!currentOption) {
333
- const units = parseInt(tierFilter);
334
- if (!isNaN(units)) {
335
- return `Up to ${units.toLocaleString()} Candidates`;
336
- }
337
- return options.length > 0 ? options[0].label : "";
338
- }
339
- return currentOption.label;
340
- };
341
- var filterPlansByBillingCycle = (plans, billingCycle) => {
342
- return plans.filter((plan) => {
343
- const planInterval = plan.interval.toLowerCase();
344
- if (billingCycle === "monthly") {
345
- return planInterval === "month" || planInterval === "monthly";
346
- } else {
347
- return planInterval === "year" || planInterval === "yearly";
348
- }
349
- });
350
- };
351
- var filterPlansByTier = (plans, tierFilter) => {
352
- if (tierFilter === "all") {
353
- return plans;
354
- }
355
- const targetUnits = parseInt(tierFilter);
356
- return plans.filter((plan) => {
357
- var _a, _b;
358
- const planNameLc = ((_b = (_a = plan.name) == null ? void 0 : _a.toLowerCase) == null ? void 0 : _b.call(_a)) || "";
359
- if (planNameLc.includes("starter")) return true;
360
- const planCandidateUnits = getCandidateUnits(plan);
361
- return planCandidateUnits === targetUnits;
362
- });
363
- };
364
- var isPlanPopular = (plan) => {
365
- const planName = plan.name.toLowerCase();
366
- return planName.includes("scale");
367
- };
368
- var sortPlansForDisplay = (plans) => {
369
- const priorityOf = (name) => {
370
- const n = (name || "").toLowerCase();
371
- if (n.includes("starter") || n.includes("basic")) return 0;
372
- if (n.includes("growth") || n.includes("pro")) return 1;
373
- if (n.includes("scale") || n.includes("premium")) return 2;
374
- return 99;
375
- };
376
- return [...plans].sort((a, b) => {
377
- const pa = priorityOf(a.name);
378
- const pb = priorityOf(b.name);
379
- if (pa !== pb) return pa - pb;
380
- return (a.name || "").localeCompare(b.name || "");
381
- });
382
- };
383
- var getPlanDescription = (plan) => {
384
- const planName = plan.name.toLowerCase();
385
- if (planName.includes("starter") || planName.includes("basic")) {
386
- return "Ideal for solopreneurs looking to start their niche job board journey.";
387
- } else if (planName.includes("growth") || planName.includes("pro")) {
388
- return "Ideal for creators launching their job board with powerful features and built-in scalability.";
389
- } else {
390
- return "Perfect for creators ready to run a full-scale, smart hiring engine with AI and advanced capabilities.";
391
- }
392
- };
393
- var formatDate = (dateString, format = "date") => {
394
- const date = new Date(dateString);
395
- if (format === "datetime") {
396
- return new Intl.DateTimeFormat("en-US", {
397
- year: "numeric",
398
- month: "short",
399
- day: "numeric",
400
- hour: "2-digit",
401
- minute: "2-digit",
402
- hour12: false
403
- // Military time format
404
- }).format(date);
405
- }
406
- return new Intl.DateTimeFormat("en-US", {
407
- year: "numeric",
408
- month: "short",
409
- day: "numeric"
410
- }).format(date);
411
- };
412
240
  var UpgradeContext = React10.createContext(void 0);
413
241
  var UpgradeProvider = ({ children }) => {
414
242
  const [isUpgradeSummaryVisible, setIsUpgradeSummaryVisible] = React10.useState(false);
@@ -524,6 +352,19 @@ function mapCountryToCurrency(countryCode) {
524
352
  };
525
353
  return countryToCurrency[countryCode] || "USD";
526
354
  }
355
+ function formatCurrencyAmount(amount, currencyCode) {
356
+ const currency = (currencyCode == null ? void 0 : currencyCode.toUpperCase()) || "USD";
357
+ if (currency === "INR") {
358
+ const amountStr = Math.round(amount).toString();
359
+ const lastThree = amountStr.substring(amountStr.length - 3);
360
+ const otherNumbers = amountStr.substring(0, amountStr.length - 3);
361
+ if (otherNumbers !== "") {
362
+ return otherNumbers.replace(/\B(?=(\d{2})+(?!\d))/g, ",") + "," + lastThree;
363
+ }
364
+ return lastThree;
365
+ }
366
+ return Math.round(amount).toLocaleString("en-US");
367
+ }
527
368
 
528
369
  // src/hooks/useCountryAndCurrency.ts
529
370
  var useCountryAndCurrency = ({
@@ -596,6 +437,187 @@ var CurrencyProvider = ({
596
437
  return /* @__PURE__ */ jsxRuntime.jsx(CurrencyContext.Provider, { value: { country, currency, isLoading, error, setCurrency }, children });
597
438
  };
598
439
 
440
+ // src/hooks/useSubscription.ts
441
+ var useSubscription = () => {
442
+ const { currency } = useCurrency();
443
+ const [subscription, setSubscription] = React10.useState(null);
444
+ const [loading, setLoading] = React10.useState(false);
445
+ const [error, setError] = React10.useState(null);
446
+ const fetchSubscription = React10.useCallback(async (externalId) => {
447
+ if (!externalId || externalId === "undefined" || externalId.trim() === "") {
448
+ setError("Invalid user ID. Please ensure you are properly authenticated.");
449
+ setLoading(false);
450
+ return;
451
+ }
452
+ if (loading) return;
453
+ setLoading(true);
454
+ setError(null);
455
+ try {
456
+ const response = await subscriptionApi.getActiveSubscription(externalId, { currency });
457
+ if (response.success && response.data) {
458
+ setSubscription(response.data);
459
+ } else {
460
+ setError(response.error || "Failed to load subscription information");
461
+ }
462
+ } catch (err) {
463
+ if (err instanceof Error && err.name !== "AbortError") {
464
+ console.error("Error fetching subscription:", err);
465
+ setError("Failed to load subscription information. Please try again later.");
466
+ }
467
+ } finally {
468
+ setLoading(false);
469
+ }
470
+ }, [loading, currency]);
471
+ const clearSubscription = React10.useCallback(() => {
472
+ setSubscription(null);
473
+ setError(null);
474
+ }, []);
475
+ return {
476
+ subscription,
477
+ loading,
478
+ error,
479
+ fetchSubscription,
480
+ clearSubscription
481
+ };
482
+ };
483
+
484
+ // src/utils/planUtils.ts
485
+ var getPlanFeatures = (plan) => {
486
+ const features = [];
487
+ if (plan.charges && plan.charges.length > 0) {
488
+ plan.charges.forEach((charge) => {
489
+ var _a;
490
+ if ((_a = charge.billableMetric) == null ? void 0 : _a.name) {
491
+ features.push(charge.billableMetric.name);
492
+ }
493
+ });
494
+ }
495
+ return features.length > 0 ? features : ["Core features included"];
496
+ };
497
+ var getCandidateUnits = (plan) => {
498
+ var _a;
499
+ if (plan.charges && plan.charges.length > 0) {
500
+ const candidateCharge = plan.charges.find(
501
+ (charge) => charge.billableMetric && charge.billableMetric.code === "CANDIDATES"
502
+ );
503
+ const units = (_a = candidateCharge == null ? void 0 : candidateCharge.properties) == null ? void 0 : _a.units;
504
+ if (typeof units === "number" && Number.isFinite(units)) {
505
+ return units;
506
+ }
507
+ }
508
+ return 0;
509
+ };
510
+ var getUniqueCandidateUnits = (plans) => {
511
+ const unitsSet = /* @__PURE__ */ new Set();
512
+ plans.forEach((plan) => {
513
+ plan.charges.forEach((charge) => {
514
+ var _a, _b;
515
+ if (((_a = charge.billableMetric) == null ? void 0 : _a.code) === "CANDIDATES") {
516
+ const units = (_b = charge.properties) == null ? void 0 : _b.units;
517
+ if (typeof units === "number" && Number.isFinite(units)) {
518
+ unitsSet.add(units);
519
+ }
520
+ }
521
+ });
522
+ });
523
+ return Array.from(unitsSet).sort((a, b) => a - b);
524
+ };
525
+ var getDropdownOptions = (plans) => {
526
+ const options = [];
527
+ const uniqueUnits = getUniqueCandidateUnits(plans);
528
+ uniqueUnits.forEach((units) => {
529
+ options.push({
530
+ value: units.toString(),
531
+ label: `Up to ${units.toLocaleString()} Candidates`,
532
+ candidates: units
533
+ });
534
+ });
535
+ return options;
536
+ };
537
+ var getCurrentSelectionText = (tierFilter, plans) => {
538
+ const options = getDropdownOptions(plans);
539
+ const currentOption = options.find((option) => option.value === tierFilter);
540
+ if (!currentOption) {
541
+ const units = parseInt(tierFilter);
542
+ if (!isNaN(units)) {
543
+ return `Up to ${units.toLocaleString()} Candidates`;
544
+ }
545
+ return options.length > 0 ? options[0].label : "";
546
+ }
547
+ return currentOption.label;
548
+ };
549
+ var filterPlansByBillingCycle = (plans, billingCycle) => {
550
+ return plans.filter((plan) => {
551
+ const planInterval = plan.interval.toLowerCase();
552
+ if (billingCycle === "monthly") {
553
+ return planInterval === "month" || planInterval === "monthly";
554
+ } else {
555
+ return planInterval === "year" || planInterval === "yearly";
556
+ }
557
+ });
558
+ };
559
+ var filterPlansByTier = (plans, tierFilter) => {
560
+ if (tierFilter === "all") {
561
+ return plans;
562
+ }
563
+ const targetUnits = parseInt(tierFilter);
564
+ return plans.filter((plan) => {
565
+ var _a, _b;
566
+ const planNameLc = ((_b = (_a = plan.name) == null ? void 0 : _a.toLowerCase) == null ? void 0 : _b.call(_a)) || "";
567
+ if (planNameLc.includes("starter")) return true;
568
+ const planCandidateUnits = getCandidateUnits(plan);
569
+ return planCandidateUnits === targetUnits;
570
+ });
571
+ };
572
+ var isPlanPopular = (plan) => {
573
+ const planName = plan.name.toLowerCase();
574
+ return planName.includes("scale");
575
+ };
576
+ var sortPlansForDisplay = (plans) => {
577
+ const priorityOf = (name) => {
578
+ const n = (name || "").toLowerCase();
579
+ if (n.includes("starter") || n.includes("basic")) return 0;
580
+ if (n.includes("growth") || n.includes("pro")) return 1;
581
+ if (n.includes("scale") || n.includes("premium")) return 2;
582
+ return 99;
583
+ };
584
+ return [...plans].sort((a, b) => {
585
+ const pa = priorityOf(a.name);
586
+ const pb = priorityOf(b.name);
587
+ if (pa !== pb) return pa - pb;
588
+ return (a.name || "").localeCompare(b.name || "");
589
+ });
590
+ };
591
+ var getPlanDescription = (plan) => {
592
+ const planName = plan.name.toLowerCase();
593
+ if (planName.includes("starter") || planName.includes("basic")) {
594
+ return "Ideal for solopreneurs looking to start their niche job board journey.";
595
+ } else if (planName.includes("growth") || planName.includes("pro")) {
596
+ return "Ideal for creators launching their job board with powerful features and built-in scalability.";
597
+ } else {
598
+ return "Perfect for creators ready to run a full-scale, smart hiring engine with AI and advanced capabilities.";
599
+ }
600
+ };
601
+ var formatDate = (dateString, format = "date") => {
602
+ const date = new Date(dateString);
603
+ if (format === "datetime") {
604
+ return new Intl.DateTimeFormat("en-US", {
605
+ year: "numeric",
606
+ month: "short",
607
+ day: "numeric",
608
+ hour: "2-digit",
609
+ minute: "2-digit",
610
+ hour12: false
611
+ // Military time format
612
+ }).format(date);
613
+ }
614
+ return new Intl.DateTimeFormat("en-US", {
615
+ year: "numeric",
616
+ month: "short",
617
+ day: "numeric"
618
+ }).format(date);
619
+ };
620
+
599
621
  // src/hooks/usePlans.ts
600
622
  var usePlans = () => {
601
623
  const { currency } = useCurrency();
@@ -3180,19 +3202,29 @@ var UpgradeSummary = ({
3180
3202
  )
3181
3203
  ] }),
3182
3204
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-6", children: [
3183
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs(
3184
- "h3",
3185
- {
3186
- className: "text-2xl font-semibold",
3187
- style: { color: "var(--subos-foreground, #1e293b)" },
3188
- children: [
3189
- currencySymbol,
3190
- displayInfo.planPrice,
3191
- "/",
3192
- intervalDisplay
3193
- ]
3194
- }
3195
- ) }),
3205
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
3206
+ /* @__PURE__ */ jsxRuntime.jsxs(
3207
+ "h3",
3208
+ {
3209
+ className: "text-2xl font-semibold",
3210
+ style: { color: "var(--subos-foreground, #1e293b)" },
3211
+ children: [
3212
+ currencySymbol,
3213
+ formatCurrencyAmount(displayInfo.planPrice, currencyCode),
3214
+ "/",
3215
+ intervalDisplay
3216
+ ]
3217
+ }
3218
+ ),
3219
+ /* @__PURE__ */ jsxRuntime.jsx(
3220
+ "p",
3221
+ {
3222
+ className: "text-sm font-semibold mt-0.5",
3223
+ style: { color: "var(--subos-foreground, #1e293b)" },
3224
+ children: "(Inclusive Tax)"
3225
+ }
3226
+ )
3227
+ ] }),
3196
3228
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
3197
3229
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
3198
3230
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3210,7 +3242,7 @@ var UpgradeSummary = ({
3210
3242
  style: { color: "var(--subos-foreground, #1e293b)" },
3211
3243
  children: [
3212
3244
  currencySymbol,
3213
- dueToday
3245
+ formatCurrencyAmount(dueToday, currencyCode)
3214
3246
  ]
3215
3247
  }
3216
3248
  ),
@@ -3442,14 +3474,17 @@ var DashboardPage = ({
3442
3474
  var _a, _b, _c, _d;
3443
3475
  const subscription = useSubscription();
3444
3476
  const plans = usePlans();
3477
+ const { currency, isLoading: isCurrencyLoading } = useCurrency();
3445
3478
  const lastLoadedIdRef = React10.useRef(null);
3446
3479
  React10.useEffect(() => {
3447
- if (lastLoadedIdRef.current !== externalId) {
3480
+ if (isCurrencyLoading) return;
3481
+ const cacheKey = `${externalId}-${currency}`;
3482
+ if (lastLoadedIdRef.current !== cacheKey) {
3448
3483
  subscription.fetchSubscription(externalId);
3449
3484
  plans.fetchPlans();
3450
- lastLoadedIdRef.current = externalId;
3485
+ lastLoadedIdRef.current = cacheKey;
3451
3486
  }
3452
- }, [externalId]);
3487
+ }, [externalId, currency, isCurrencyLoading]);
3453
3488
  React10.useEffect(() => {
3454
3489
  registerClearSelectedPlan(plans.clearSelectedPlan);
3455
3490
  return () => {