@softure-ai/billing 0.1.7 → 0.1.8

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 (97) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +75 -16
  3. package/dist/contract.d.ts +19 -0
  4. package/dist/contract.d.ts.map +1 -1
  5. package/dist/contract.js +1 -0
  6. package/dist/contract.js.map +1 -1
  7. package/dist/fields.d.ts +2 -0
  8. package/dist/fields.d.ts.map +1 -1
  9. package/dist/fields.js +2 -0
  10. package/dist/fields.js.map +1 -1
  11. package/dist/index.d.ts +19 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +4 -4
  14. package/dist/index.js.map +1 -1
  15. package/dist/messages/en.d.ts +17 -0
  16. package/dist/messages/en.d.ts.map +1 -1
  17. package/dist/messages/en.js +17 -0
  18. package/dist/messages/en.js.map +1 -1
  19. package/dist/messages/index.d.ts +36 -2
  20. package/dist/messages/index.d.ts.map +1 -1
  21. package/dist/messages/index.js.map +1 -1
  22. package/dist/messages/pl.d.ts.map +1 -1
  23. package/dist/messages/pl.js +17 -0
  24. package/dist/messages/pl.js.map +1 -1
  25. package/dist/next/actions.d.ts +8 -1
  26. package/dist/next/actions.d.ts.map +1 -1
  27. package/dist/next/actions.js +37 -1
  28. package/dist/next/actions.js.map +1 -1
  29. package/dist/next/index.d.ts +1 -1
  30. package/dist/next/index.d.ts.map +1 -1
  31. package/dist/next/index.js +1 -1
  32. package/dist/next/index.js.map +1 -1
  33. package/dist/next/pages.d.ts +13 -3
  34. package/dist/next/pages.d.ts.map +1 -1
  35. package/dist/next/pages.js +25 -8
  36. package/dist/next/pages.js.map +1 -1
  37. package/dist/schema.d.ts +109 -0
  38. package/dist/schema.d.ts.map +1 -1
  39. package/dist/schema.js +10 -2
  40. package/dist/schema.js.map +1 -1
  41. package/dist/scripts/entitlement-scripts.d.ts +2 -0
  42. package/dist/scripts/entitlement-scripts.d.ts.map +1 -1
  43. package/dist/scripts/entitlement-scripts.js +17 -5
  44. package/dist/scripts/entitlement-scripts.js.map +1 -1
  45. package/dist/server/entitlements.d.ts +23 -6
  46. package/dist/server/entitlements.d.ts.map +1 -1
  47. package/dist/server/entitlements.js +37 -6
  48. package/dist/server/entitlements.js.map +1 -1
  49. package/dist/server/grants.d.ts +13 -2
  50. package/dist/server/grants.d.ts.map +1 -1
  51. package/dist/server/grants.js +11 -2
  52. package/dist/server/grants.js.map +1 -1
  53. package/dist/server/health.d.ts.map +1 -1
  54. package/dist/server/health.js +2 -1
  55. package/dist/server/health.js.map +1 -1
  56. package/dist/server/index.d.ts +3 -2
  57. package/dist/server/index.d.ts.map +1 -1
  58. package/dist/server/index.js +1 -0
  59. package/dist/server/index.js.map +1 -1
  60. package/dist/server/privacy.d.ts +9 -0
  61. package/dist/server/privacy.d.ts.map +1 -1
  62. package/dist/server/privacy.js +19 -6
  63. package/dist/server/privacy.js.map +1 -1
  64. package/dist/server/trials.d.ts +22 -0
  65. package/dist/server/trials.d.ts.map +1 -0
  66. package/dist/server/trials.js +60 -0
  67. package/dist/server/trials.js.map +1 -0
  68. package/dist/ui/index.d.ts +1 -0
  69. package/dist/ui/index.d.ts.map +1 -1
  70. package/dist/ui/index.js +1 -0
  71. package/dist/ui/index.js.map +1 -1
  72. package/dist/ui/trial-form.d.ts +16 -0
  73. package/dist/ui/trial-form.d.ts.map +1 -0
  74. package/dist/ui/trial-form.js +25 -0
  75. package/dist/ui/trial-form.js.map +1 -0
  76. package/migrations/0010_record_trial_extensions_and_index_foreign_keys.sql +27 -0
  77. package/module.json +2 -2
  78. package/package.json +1 -1
  79. package/src/contract.ts +28 -0
  80. package/src/fields.ts +2 -0
  81. package/src/index.ts +7 -3
  82. package/src/messages/en.ts +17 -0
  83. package/src/messages/index.ts +2 -2
  84. package/src/messages/pl.ts +17 -0
  85. package/src/next/actions.ts +35 -2
  86. package/src/next/index.ts +9 -1
  87. package/src/next/pages.tsx +41 -6
  88. package/src/schema.ts +11 -2
  89. package/src/scripts/entitlement-scripts.ts +18 -5
  90. package/src/server/entitlements.ts +60 -6
  91. package/src/server/grants.ts +24 -3
  92. package/src/server/health.ts +2 -1
  93. package/src/server/index.ts +3 -0
  94. package/src/server/privacy.ts +29 -6
  95. package/src/server/trials.ts +70 -0
  96. package/src/ui/index.ts +1 -0
  97. package/src/ui/trial-form.tsx +80 -0
@@ -0,0 +1,80 @@
1
+ "use client";
2
+
3
+ import type { Locale } from "@softure-ai/core";
4
+ import { Button, type ClassNames, createSlotClassGetter, FormError, TextField } from "@softure-ai/ui";
5
+ import { useActionState } from "react";
6
+ import { INITIAL_TRIAL_FORM_STATE, type TrialFormState } from "../contract.js";
7
+ import { EMAIL_FIELD, TRIAL_LAST_DAY_FIELD } from "../fields.js";
8
+ import { getBillingErrorMessage, type BillingMessages } from "../messages/index.js";
9
+
10
+ // The admin form that extends an account's trial for free: the account's email and the trial's
11
+ // new last day. It submits straight to its server action, which checks the admin role first; after
12
+ // an extension it says until when the trial lasts and stays ready for the next.
13
+
14
+ export type TrialFormAction = (previous: TrialFormState, formData: FormData) => Promise<TrialFormState>;
15
+
16
+ export type TrialFormSlot = "root" | "form" | "notice";
17
+
18
+ export interface TrialFormProps {
19
+ readonly action: TrialFormAction;
20
+ readonly messages: BillingMessages;
21
+ /** Locale of the built-in copy of the ui primitives. */
22
+ readonly locale?: Locale;
23
+ readonly classNames?: ClassNames<TrialFormSlot>;
24
+ readonly unstyled?: boolean;
25
+ }
26
+
27
+ const DEFAULT_CLASSES: Readonly<Record<TrialFormSlot, string>> = {
28
+ root: "sft:flex sft:flex-col sft:gap-3 sft:font-sans",
29
+ form: "sft:flex sft:flex-col sft:gap-3",
30
+ notice: "sft:m-0 sft:text-sm sft:text-success",
31
+ };
32
+
33
+ export function TrialForm({ action, messages, locale, classNames, unstyled }: TrialFormProps) {
34
+ const [state, formAction, isPending] = useActionState(action, INITIAL_TRIAL_FORM_STATE);
35
+ const slot = createSlotClassGetter({ defaults: DEFAULT_CLASSES, classNames, unstyled });
36
+ const copy = messages.admin.trial;
37
+ const error = state.error === undefined ? undefined : getBillingErrorMessage(messages, state.error);
38
+ const isEmailError = state.error === "billing.account_unknown";
39
+ const isDayError = state.error === "billing.day_invalid" || state.error === "billing.end_not_in_future" || state.error === "billing.trial_not_extended";
40
+ const isError = state.status === "error";
41
+ // A fresh, empty form after each extension; the typed values after an error.
42
+ const key = `${state.status}:${state.email ?? ""}:${state.lastDay ?? ""}`;
43
+ return (
44
+ <div className={slot("root")}>
45
+ <form action={formAction} className={slot("form")}>
46
+ <TextField
47
+ key={`email:${key}`}
48
+ name={EMAIL_FIELD}
49
+ type="email"
50
+ label={copy.email}
51
+ required
52
+ defaultValue={isError ? (state.email ?? "") : ""}
53
+ error={isEmailError ? error : undefined}
54
+ locale={locale}
55
+ unstyled={unstyled}
56
+ />
57
+ <TextField
58
+ key={`day:${key}`}
59
+ name={TRIAL_LAST_DAY_FIELD}
60
+ type="date"
61
+ label={copy.lastDay}
62
+ required
63
+ defaultValue={isError ? (state.lastDay ?? "") : ""}
64
+ error={isDayError ? error : undefined}
65
+ locale={locale}
66
+ unstyled={unstyled}
67
+ />
68
+ <FormError message={isEmailError || isDayError ? undefined : error} unstyled={unstyled} />
69
+ <Button type="submit" variant="primary" pending={isPending} unstyled={unstyled}>
70
+ {isPending ? copy.pending : copy.submit}
71
+ </Button>
72
+ </form>
73
+ {state.status === "extended" ? (
74
+ <p role="status" className={slot("notice")}>
75
+ {state.notice}
76
+ </p>
77
+ ) : null}
78
+ </div>
79
+ );
80
+ }