@codaijs/keel 0.2.3 → 0.2.4

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 (80) hide show
  1. package/dist/__tests__/sail-installer.test.js +25 -25
  2. package/dist/sail-installer.js +174 -174
  3. package/dist/scaffold.js +68 -68
  4. package/package.json +58 -58
  5. package/sails/_template/addon.json +20 -20
  6. package/sails/_template/install.ts +402 -402
  7. package/sails/admin-dashboard/README.md +117 -117
  8. package/sails/admin-dashboard/addon.json +28 -28
  9. package/sails/admin-dashboard/files/backend/middleware/admin.ts +34 -34
  10. package/sails/admin-dashboard/files/backend/routes/admin.ts +243 -243
  11. package/sails/admin-dashboard/files/frontend/components/admin/StatsCard.tsx +40 -40
  12. package/sails/admin-dashboard/files/frontend/components/admin/UsersTable.tsx +240 -240
  13. package/sails/admin-dashboard/files/frontend/hooks/useAdmin.ts +149 -149
  14. package/sails/admin-dashboard/files/frontend/pages/admin/Dashboard.tsx +173 -173
  15. package/sails/admin-dashboard/files/frontend/pages/admin/UserDetail.tsx +203 -203
  16. package/sails/admin-dashboard/install.ts +305 -305
  17. package/sails/analytics/README.md +178 -178
  18. package/sails/analytics/addon.json +27 -27
  19. package/sails/analytics/files/frontend/components/AnalyticsProvider.tsx +58 -58
  20. package/sails/analytics/files/frontend/hooks/useAnalytics.ts +64 -64
  21. package/sails/analytics/files/frontend/lib/analytics.ts +103 -103
  22. package/sails/analytics/install.ts +297 -297
  23. package/sails/file-uploads/addon.json +30 -30
  24. package/sails/file-uploads/files/backend/routes/files.ts +198 -198
  25. package/sails/file-uploads/files/backend/schema/files.ts +36 -36
  26. package/sails/file-uploads/files/backend/services/file-storage.ts +128 -128
  27. package/sails/file-uploads/files/frontend/components/FileList.tsx +248 -248
  28. package/sails/file-uploads/files/frontend/components/FileUploadButton.tsx +147 -147
  29. package/sails/file-uploads/files/frontend/hooks/useFileUpload.ts +106 -106
  30. package/sails/file-uploads/files/frontend/hooks/useFiles.ts +118 -118
  31. package/sails/file-uploads/files/frontend/pages/Files.tsx +37 -37
  32. package/sails/file-uploads/install.ts +466 -466
  33. package/sails/gdpr/README.md +174 -174
  34. package/sails/gdpr/addon.json +27 -27
  35. package/sails/gdpr/files/backend/routes/gdpr.ts +140 -140
  36. package/sails/gdpr/files/backend/services/gdpr.ts +293 -293
  37. package/sails/gdpr/files/frontend/components/auth/ConsentCheckboxes.tsx +97 -97
  38. package/sails/gdpr/files/frontend/components/gdpr/AccountDeletionRequest.tsx +192 -192
  39. package/sails/gdpr/files/frontend/components/gdpr/DataExportButton.tsx +75 -75
  40. package/sails/gdpr/files/frontend/pages/PrivacyPolicy.tsx +186 -186
  41. package/sails/gdpr/install.ts +756 -756
  42. package/sails/google-oauth/README.md +121 -121
  43. package/sails/google-oauth/addon.json +22 -22
  44. package/sails/google-oauth/files/GoogleButton.tsx +50 -50
  45. package/sails/google-oauth/install.ts +252 -252
  46. package/sails/i18n/README.md +193 -193
  47. package/sails/i18n/addon.json +30 -30
  48. package/sails/i18n/files/frontend/components/LanguageSwitcher.tsx +108 -108
  49. package/sails/i18n/files/frontend/hooks/useLanguage.ts +31 -31
  50. package/sails/i18n/files/frontend/lib/i18n.ts +32 -32
  51. package/sails/i18n/files/frontend/locales/de/common.json +44 -44
  52. package/sails/i18n/files/frontend/locales/en/common.json +44 -44
  53. package/sails/i18n/install.ts +407 -407
  54. package/sails/push-notifications/README.md +163 -163
  55. package/sails/push-notifications/addon.json +31 -31
  56. package/sails/push-notifications/files/backend/routes/notifications.ts +153 -153
  57. package/sails/push-notifications/files/backend/schema/notifications.ts +31 -31
  58. package/sails/push-notifications/files/backend/services/notifications.ts +117 -117
  59. package/sails/push-notifications/files/frontend/components/PushNotificationInit.tsx +12 -12
  60. package/sails/push-notifications/files/frontend/hooks/usePushNotifications.ts +154 -154
  61. package/sails/push-notifications/install.ts +384 -384
  62. package/sails/r2-storage/addon.json +29 -29
  63. package/sails/r2-storage/files/backend/services/storage.ts +71 -71
  64. package/sails/r2-storage/files/frontend/components/ProfilePictureUpload.tsx +167 -167
  65. package/sails/r2-storage/install.ts +412 -412
  66. package/sails/rate-limiting/addon.json +20 -20
  67. package/sails/rate-limiting/files/backend/middleware/rate-limit-store.ts +104 -104
  68. package/sails/rate-limiting/files/backend/middleware/rate-limit.ts +137 -137
  69. package/sails/rate-limiting/install.ts +300 -300
  70. package/sails/registry.json +107 -107
  71. package/sails/stripe/README.md +214 -214
  72. package/sails/stripe/addon.json +24 -24
  73. package/sails/stripe/files/backend/routes/stripe.ts +154 -154
  74. package/sails/stripe/files/backend/schema/stripe.ts +74 -74
  75. package/sails/stripe/files/backend/services/stripe.ts +224 -224
  76. package/sails/stripe/files/frontend/components/SubscriptionStatus.tsx +135 -135
  77. package/sails/stripe/files/frontend/hooks/useSubscription.ts +86 -86
  78. package/sails/stripe/files/frontend/pages/Checkout.tsx +116 -116
  79. package/sails/stripe/files/frontend/pages/Pricing.tsx +226 -226
  80. package/sails/stripe/install.ts +378 -378
@@ -1,226 +1,226 @@
1
- import { useState } from "react";
2
- import { useNavigate } from "react-router-dom";
3
- import { useSubscription } from "@/hooks/useSubscription";
4
-
5
- /**
6
- * Pricing plans configuration.
7
- *
8
- * Replace the priceId values with your actual Stripe Price IDs from the
9
- * Stripe Dashboard (https://dashboard.stripe.com/test/products).
10
- */
11
- const PLANS = [
12
- {
13
- name: "Free",
14
- description: "Get started with the basics",
15
- price: "$0",
16
- interval: "forever",
17
- priceId: null,
18
- features: [
19
- "Up to 3 projects",
20
- "Basic analytics",
21
- "Community support",
22
- "1 GB storage",
23
- ],
24
- },
25
- {
26
- name: "Pro",
27
- description: "Everything you need to grow",
28
- price: "$19",
29
- interval: "month",
30
- priceId: "price_REPLACE_WITH_PRO_PRICE_ID",
31
- popular: true,
32
- features: [
33
- "Unlimited projects",
34
- "Advanced analytics",
35
- "Priority support",
36
- "50 GB storage",
37
- "Custom domain",
38
- "Team collaboration",
39
- ],
40
- },
41
- {
42
- name: "Enterprise",
43
- description: "For large teams and organizations",
44
- price: "$49",
45
- interval: "month",
46
- priceId: "price_REPLACE_WITH_ENTERPRISE_PRICE_ID",
47
- features: [
48
- "Everything in Pro",
49
- "Unlimited storage",
50
- "SSO / SAML",
51
- "Dedicated support",
52
- "SLA guarantee",
53
- "Custom integrations",
54
- "Audit logs",
55
- ],
56
- },
57
- ];
58
-
59
- export function PricingPage() {
60
- const navigate = useNavigate();
61
- const { subscription, isLoading: subLoading } = useSubscription();
62
- const [loadingPriceId, setLoadingPriceId] = useState<string | null>(null);
63
-
64
- const handleSubscribe = async (priceId: string | null) => {
65
- if (!priceId) return;
66
-
67
- setLoadingPriceId(priceId);
68
- try {
69
- const response = await fetch("/api/stripe/create-checkout-session", {
70
- method: "POST",
71
- headers: { "Content-Type": "application/json" },
72
- credentials: "include",
73
- body: JSON.stringify({ priceId }),
74
- });
75
-
76
- const data = await response.json();
77
-
78
- if (data.url) {
79
- window.location.href = data.url;
80
- } else {
81
- console.error("No checkout URL returned");
82
- }
83
- } catch (error) {
84
- console.error("Failed to create checkout session:", error);
85
- } finally {
86
- setLoadingPriceId(null);
87
- }
88
- };
89
-
90
- const handleManage = async () => {
91
- try {
92
- const response = await fetch("/api/stripe/create-portal-session", {
93
- method: "POST",
94
- headers: { "Content-Type": "application/json" },
95
- credentials: "include",
96
- });
97
-
98
- const data = await response.json();
99
-
100
- if (data.url) {
101
- window.location.href = data.url;
102
- }
103
- } catch (error) {
104
- console.error("Failed to create portal session:", error);
105
- }
106
- };
107
-
108
- return (
109
- <div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
110
- {/* Header */}
111
- <div className="text-center">
112
- <h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl">
113
- Simple, transparent pricing
114
- </h1>
115
- <p className="mt-4 text-lg text-gray-500 dark:text-gray-400">
116
- Choose the plan that works best for you. Upgrade or downgrade anytime.
117
- </p>
118
- </div>
119
-
120
- {/* Plan Cards */}
121
- <div className="mt-16 grid gap-8 lg:grid-cols-3">
122
- {PLANS.map((plan) => {
123
- const isCurrentPlan =
124
- subscription?.priceId === plan.priceId && subscription?.status === "active";
125
-
126
- return (
127
- <div
128
- key={plan.name}
129
- className={`relative flex flex-col rounded-2xl border p-8 shadow-sm ${
130
- plan.popular
131
- ? "border-blue-500 ring-2 ring-blue-500"
132
- : "border-gray-200 dark:border-gray-700"
133
- }`}
134
- >
135
- {plan.popular && (
136
- <span className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-blue-500 px-4 py-1 text-xs font-semibold text-white">
137
- Most Popular
138
- </span>
139
- )}
140
-
141
- <h2 className="text-xl font-semibold text-gray-900 dark:text-white">
142
- {plan.name}
143
- </h2>
144
- <p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
145
- {plan.description}
146
- </p>
147
-
148
- <div className="mt-6 flex items-baseline gap-1">
149
- <span className="text-4xl font-bold tracking-tight text-gray-900 dark:text-white">
150
- {plan.price}
151
- </span>
152
- <span className="text-sm text-gray-500 dark:text-gray-400">
153
- /{plan.interval}
154
- </span>
155
- </div>
156
-
157
- <ul className="mt-8 flex-1 space-y-3">
158
- {plan.features.map((feature) => (
159
- <li key={feature} className="flex items-start gap-2">
160
- <svg
161
- className="mt-0.5 h-5 w-5 flex-shrink-0 text-green-500"
162
- fill="none"
163
- viewBox="0 0 24 24"
164
- stroke="currentColor"
165
- strokeWidth={2}
166
- >
167
- <path
168
- strokeLinecap="round"
169
- strokeLinejoin="round"
170
- d="M5 13l4 4L19 7"
171
- />
172
- </svg>
173
- <span className="text-sm text-gray-700 dark:text-gray-300">
174
- {feature}
175
- </span>
176
- </li>
177
- ))}
178
- </ul>
179
-
180
- <div className="mt-8">
181
- {isCurrentPlan ? (
182
- <button
183
- onClick={handleManage}
184
- className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm font-semibold text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
185
- >
186
- Manage Subscription
187
- </button>
188
- ) : plan.priceId ? (
189
- <button
190
- onClick={() => handleSubscribe(plan.priceId)}
191
- disabled={loadingPriceId === plan.priceId}
192
- className={`w-full rounded-lg px-4 py-2.5 text-sm font-semibold text-white transition-colors ${
193
- plan.popular
194
- ? "bg-blue-600 hover:bg-blue-700"
195
- : "bg-gray-900 hover:bg-gray-800 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200"
196
- } disabled:cursor-not-allowed disabled:opacity-50`}
197
- >
198
- {loadingPriceId === plan.priceId
199
- ? "Redirecting..."
200
- : "Subscribe"}
201
- </button>
202
- ) : (
203
- <button
204
- onClick={() => navigate("/signup")}
205
- className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm font-semibold text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
206
- >
207
- Get Started
208
- </button>
209
- )}
210
- </div>
211
- </div>
212
- );
213
- })}
214
- </div>
215
-
216
- {/* FAQ or trust badges can go here */}
217
- <div className="mt-16 text-center">
218
- <p className="text-sm text-gray-500 dark:text-gray-400">
219
- All plans include a 14-day free trial. No credit card required to start.
220
- <br />
221
- Secure payment processing by Stripe.
222
- </p>
223
- </div>
224
- </div>
225
- );
226
- }
1
+ import { useState } from "react";
2
+ import { useNavigate } from "react-router-dom";
3
+ import { useSubscription } from "@/hooks/useSubscription";
4
+
5
+ /**
6
+ * Pricing plans configuration.
7
+ *
8
+ * Replace the priceId values with your actual Stripe Price IDs from the
9
+ * Stripe Dashboard (https://dashboard.stripe.com/test/products).
10
+ */
11
+ const PLANS = [
12
+ {
13
+ name: "Free",
14
+ description: "Get started with the basics",
15
+ price: "$0",
16
+ interval: "forever",
17
+ priceId: null,
18
+ features: [
19
+ "Up to 3 projects",
20
+ "Basic analytics",
21
+ "Community support",
22
+ "1 GB storage",
23
+ ],
24
+ },
25
+ {
26
+ name: "Pro",
27
+ description: "Everything you need to grow",
28
+ price: "$19",
29
+ interval: "month",
30
+ priceId: "price_REPLACE_WITH_PRO_PRICE_ID",
31
+ popular: true,
32
+ features: [
33
+ "Unlimited projects",
34
+ "Advanced analytics",
35
+ "Priority support",
36
+ "50 GB storage",
37
+ "Custom domain",
38
+ "Team collaboration",
39
+ ],
40
+ },
41
+ {
42
+ name: "Enterprise",
43
+ description: "For large teams and organizations",
44
+ price: "$49",
45
+ interval: "month",
46
+ priceId: "price_REPLACE_WITH_ENTERPRISE_PRICE_ID",
47
+ features: [
48
+ "Everything in Pro",
49
+ "Unlimited storage",
50
+ "SSO / SAML",
51
+ "Dedicated support",
52
+ "SLA guarantee",
53
+ "Custom integrations",
54
+ "Audit logs",
55
+ ],
56
+ },
57
+ ];
58
+
59
+ export function PricingPage() {
60
+ const navigate = useNavigate();
61
+ const { subscription, isLoading: subLoading } = useSubscription();
62
+ const [loadingPriceId, setLoadingPriceId] = useState<string | null>(null);
63
+
64
+ const handleSubscribe = async (priceId: string | null) => {
65
+ if (!priceId) return;
66
+
67
+ setLoadingPriceId(priceId);
68
+ try {
69
+ const response = await fetch("/api/stripe/create-checkout-session", {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ credentials: "include",
73
+ body: JSON.stringify({ priceId }),
74
+ });
75
+
76
+ const data = await response.json();
77
+
78
+ if (data.url) {
79
+ window.location.href = data.url;
80
+ } else {
81
+ console.error("No checkout URL returned");
82
+ }
83
+ } catch (error) {
84
+ console.error("Failed to create checkout session:", error);
85
+ } finally {
86
+ setLoadingPriceId(null);
87
+ }
88
+ };
89
+
90
+ const handleManage = async () => {
91
+ try {
92
+ const response = await fetch("/api/stripe/create-portal-session", {
93
+ method: "POST",
94
+ headers: { "Content-Type": "application/json" },
95
+ credentials: "include",
96
+ });
97
+
98
+ const data = await response.json();
99
+
100
+ if (data.url) {
101
+ window.location.href = data.url;
102
+ }
103
+ } catch (error) {
104
+ console.error("Failed to create portal session:", error);
105
+ }
106
+ };
107
+
108
+ return (
109
+ <div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
110
+ {/* Header */}
111
+ <div className="text-center">
112
+ <h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl">
113
+ Simple, transparent pricing
114
+ </h1>
115
+ <p className="mt-4 text-lg text-gray-500 dark:text-gray-400">
116
+ Choose the plan that works best for you. Upgrade or downgrade anytime.
117
+ </p>
118
+ </div>
119
+
120
+ {/* Plan Cards */}
121
+ <div className="mt-16 grid gap-8 lg:grid-cols-3">
122
+ {PLANS.map((plan) => {
123
+ const isCurrentPlan =
124
+ subscription?.priceId === plan.priceId && subscription?.status === "active";
125
+
126
+ return (
127
+ <div
128
+ key={plan.name}
129
+ className={`relative flex flex-col rounded-2xl border p-8 shadow-sm ${
130
+ plan.popular
131
+ ? "border-blue-500 ring-2 ring-blue-500"
132
+ : "border-gray-200 dark:border-gray-700"
133
+ }`}
134
+ >
135
+ {plan.popular && (
136
+ <span className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-blue-500 px-4 py-1 text-xs font-semibold text-white">
137
+ Most Popular
138
+ </span>
139
+ )}
140
+
141
+ <h2 className="text-xl font-semibold text-gray-900 dark:text-white">
142
+ {plan.name}
143
+ </h2>
144
+ <p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
145
+ {plan.description}
146
+ </p>
147
+
148
+ <div className="mt-6 flex items-baseline gap-1">
149
+ <span className="text-4xl font-bold tracking-tight text-gray-900 dark:text-white">
150
+ {plan.price}
151
+ </span>
152
+ <span className="text-sm text-gray-500 dark:text-gray-400">
153
+ /{plan.interval}
154
+ </span>
155
+ </div>
156
+
157
+ <ul className="mt-8 flex-1 space-y-3">
158
+ {plan.features.map((feature) => (
159
+ <li key={feature} className="flex items-start gap-2">
160
+ <svg
161
+ className="mt-0.5 h-5 w-5 flex-shrink-0 text-green-500"
162
+ fill="none"
163
+ viewBox="0 0 24 24"
164
+ stroke="currentColor"
165
+ strokeWidth={2}
166
+ >
167
+ <path
168
+ strokeLinecap="round"
169
+ strokeLinejoin="round"
170
+ d="M5 13l4 4L19 7"
171
+ />
172
+ </svg>
173
+ <span className="text-sm text-gray-700 dark:text-gray-300">
174
+ {feature}
175
+ </span>
176
+ </li>
177
+ ))}
178
+ </ul>
179
+
180
+ <div className="mt-8">
181
+ {isCurrentPlan ? (
182
+ <button
183
+ onClick={handleManage}
184
+ className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm font-semibold text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
185
+ >
186
+ Manage Subscription
187
+ </button>
188
+ ) : plan.priceId ? (
189
+ <button
190
+ onClick={() => handleSubscribe(plan.priceId)}
191
+ disabled={loadingPriceId === plan.priceId}
192
+ className={`w-full rounded-lg px-4 py-2.5 text-sm font-semibold text-white transition-colors ${
193
+ plan.popular
194
+ ? "bg-blue-600 hover:bg-blue-700"
195
+ : "bg-gray-900 hover:bg-gray-800 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200"
196
+ } disabled:cursor-not-allowed disabled:opacity-50`}
197
+ >
198
+ {loadingPriceId === plan.priceId
199
+ ? "Redirecting..."
200
+ : "Subscribe"}
201
+ </button>
202
+ ) : (
203
+ <button
204
+ onClick={() => navigate("/signup")}
205
+ className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm font-semibold text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
206
+ >
207
+ Get Started
208
+ </button>
209
+ )}
210
+ </div>
211
+ </div>
212
+ );
213
+ })}
214
+ </div>
215
+
216
+ {/* FAQ or trust badges can go here */}
217
+ <div className="mt-16 text-center">
218
+ <p className="text-sm text-gray-500 dark:text-gray-400">
219
+ All plans include a 14-day free trial. No credit card required to start.
220
+ <br />
221
+ Secure payment processing by Stripe.
222
+ </p>
223
+ </div>
224
+ </div>
225
+ );
226
+ }