@codaijs/keel 0.1.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 (116) hide show
  1. package/dist/__tests__/cli.test.d.ts +2 -0
  2. package/dist/__tests__/cli.test.d.ts.map +1 -0
  3. package/dist/__tests__/cli.test.js +173 -0
  4. package/dist/__tests__/cli.test.js.map +1 -0
  5. package/dist/__tests__/registry.test.d.ts +2 -0
  6. package/dist/__tests__/registry.test.d.ts.map +1 -0
  7. package/dist/__tests__/registry.test.js +86 -0
  8. package/dist/__tests__/registry.test.js.map +1 -0
  9. package/dist/__tests__/sail-installer.test.d.ts +2 -0
  10. package/dist/__tests__/sail-installer.test.d.ts.map +1 -0
  11. package/dist/__tests__/sail-installer.test.js +158 -0
  12. package/dist/__tests__/sail-installer.test.js.map +1 -0
  13. package/dist/create-runner.d.ts +11 -0
  14. package/dist/create-runner.d.ts.map +1 -0
  15. package/dist/create-runner.js +63 -0
  16. package/dist/create-runner.js.map +1 -0
  17. package/dist/create.d.ts +10 -0
  18. package/dist/create.d.ts.map +1 -0
  19. package/dist/create.js +15 -0
  20. package/dist/create.js.map +1 -0
  21. package/dist/manage.d.ts +24 -0
  22. package/dist/manage.d.ts.map +1 -0
  23. package/dist/manage.js +1461 -0
  24. package/dist/manage.js.map +1 -0
  25. package/dist/prompts.d.ts +36 -0
  26. package/dist/prompts.d.ts.map +1 -0
  27. package/dist/prompts.js +208 -0
  28. package/dist/prompts.js.map +1 -0
  29. package/dist/sail-installer.d.ts +37 -0
  30. package/dist/sail-installer.d.ts.map +1 -0
  31. package/dist/sail-installer.js +935 -0
  32. package/dist/sail-installer.js.map +1 -0
  33. package/dist/scaffold.d.ts +10 -0
  34. package/dist/scaffold.d.ts.map +1 -0
  35. package/dist/scaffold.js +297 -0
  36. package/dist/scaffold.js.map +1 -0
  37. package/package.json +57 -0
  38. package/sails/_template/addon.json +20 -0
  39. package/sails/_template/install.ts +402 -0
  40. package/sails/admin-dashboard/README.md +117 -0
  41. package/sails/admin-dashboard/addon.json +28 -0
  42. package/sails/admin-dashboard/files/backend/middleware/admin.ts +34 -0
  43. package/sails/admin-dashboard/files/backend/routes/admin.ts +243 -0
  44. package/sails/admin-dashboard/files/frontend/components/admin/StatsCard.tsx +40 -0
  45. package/sails/admin-dashboard/files/frontend/components/admin/UsersTable.tsx +240 -0
  46. package/sails/admin-dashboard/files/frontend/hooks/useAdmin.ts +149 -0
  47. package/sails/admin-dashboard/files/frontend/pages/admin/Dashboard.tsx +173 -0
  48. package/sails/admin-dashboard/files/frontend/pages/admin/UserDetail.tsx +203 -0
  49. package/sails/admin-dashboard/install.ts +305 -0
  50. package/sails/analytics/README.md +178 -0
  51. package/sails/analytics/addon.json +27 -0
  52. package/sails/analytics/files/frontend/components/AnalyticsProvider.tsx +58 -0
  53. package/sails/analytics/files/frontend/hooks/useAnalytics.ts +64 -0
  54. package/sails/analytics/files/frontend/lib/analytics.ts +103 -0
  55. package/sails/analytics/install.ts +297 -0
  56. package/sails/file-uploads/README.md +191 -0
  57. package/sails/file-uploads/addon.json +30 -0
  58. package/sails/file-uploads/files/backend/routes/files.ts +198 -0
  59. package/sails/file-uploads/files/backend/schema/files.ts +36 -0
  60. package/sails/file-uploads/files/backend/services/file-storage.ts +128 -0
  61. package/sails/file-uploads/files/frontend/components/FileList.tsx +248 -0
  62. package/sails/file-uploads/files/frontend/components/FileUploadButton.tsx +147 -0
  63. package/sails/file-uploads/files/frontend/hooks/useFileUpload.ts +106 -0
  64. package/sails/file-uploads/files/frontend/hooks/useFiles.ts +118 -0
  65. package/sails/file-uploads/files/frontend/pages/Files.tsx +37 -0
  66. package/sails/file-uploads/install.ts +466 -0
  67. package/sails/gdpr/README.md +174 -0
  68. package/sails/gdpr/addon.json +27 -0
  69. package/sails/gdpr/files/backend/routes/gdpr.ts +140 -0
  70. package/sails/gdpr/files/backend/services/gdpr.ts +293 -0
  71. package/sails/gdpr/files/frontend/components/auth/ConsentCheckboxes.tsx +97 -0
  72. package/sails/gdpr/files/frontend/components/gdpr/AccountDeletionRequest.tsx +192 -0
  73. package/sails/gdpr/files/frontend/components/gdpr/DataExportButton.tsx +75 -0
  74. package/sails/gdpr/files/frontend/pages/PrivacyPolicy.tsx +186 -0
  75. package/sails/gdpr/install.ts +756 -0
  76. package/sails/google-oauth/README.md +121 -0
  77. package/sails/google-oauth/addon.json +22 -0
  78. package/sails/google-oauth/files/GoogleButton.tsx +50 -0
  79. package/sails/google-oauth/install.ts +252 -0
  80. package/sails/i18n/README.md +193 -0
  81. package/sails/i18n/addon.json +30 -0
  82. package/sails/i18n/files/frontend/components/LanguageSwitcher.tsx +108 -0
  83. package/sails/i18n/files/frontend/hooks/useLanguage.ts +31 -0
  84. package/sails/i18n/files/frontend/lib/i18n.ts +32 -0
  85. package/sails/i18n/files/frontend/locales/de/common.json +44 -0
  86. package/sails/i18n/files/frontend/locales/en/common.json +44 -0
  87. package/sails/i18n/install.ts +407 -0
  88. package/sails/push-notifications/README.md +163 -0
  89. package/sails/push-notifications/addon.json +31 -0
  90. package/sails/push-notifications/files/backend/routes/notifications.ts +153 -0
  91. package/sails/push-notifications/files/backend/schema/notifications.ts +31 -0
  92. package/sails/push-notifications/files/backend/services/notifications.ts +117 -0
  93. package/sails/push-notifications/files/frontend/components/PushNotificationInit.tsx +12 -0
  94. package/sails/push-notifications/files/frontend/hooks/usePushNotifications.ts +154 -0
  95. package/sails/push-notifications/install.ts +384 -0
  96. package/sails/r2-storage/README.md +101 -0
  97. package/sails/r2-storage/addon.json +29 -0
  98. package/sails/r2-storage/files/backend/services/storage.ts +71 -0
  99. package/sails/r2-storage/files/frontend/components/ProfilePictureUpload.tsx +167 -0
  100. package/sails/r2-storage/install.ts +412 -0
  101. package/sails/rate-limiting/README.md +145 -0
  102. package/sails/rate-limiting/addon.json +20 -0
  103. package/sails/rate-limiting/files/backend/middleware/rate-limit-store.ts +104 -0
  104. package/sails/rate-limiting/files/backend/middleware/rate-limit.ts +137 -0
  105. package/sails/rate-limiting/install.ts +300 -0
  106. package/sails/registry.json +107 -0
  107. package/sails/stripe/README.md +214 -0
  108. package/sails/stripe/addon.json +24 -0
  109. package/sails/stripe/files/backend/routes/stripe.ts +154 -0
  110. package/sails/stripe/files/backend/schema/stripe.ts +74 -0
  111. package/sails/stripe/files/backend/services/stripe.ts +224 -0
  112. package/sails/stripe/files/frontend/components/SubscriptionStatus.tsx +135 -0
  113. package/sails/stripe/files/frontend/hooks/useSubscription.ts +86 -0
  114. package/sails/stripe/files/frontend/pages/Checkout.tsx +116 -0
  115. package/sails/stripe/files/frontend/pages/Pricing.tsx +226 -0
  116. package/sails/stripe/install.ts +378 -0
@@ -0,0 +1,116 @@
1
+ import { useEffect } from "react";
2
+ import { Link, useSearchParams } from "react-router-dom";
3
+
4
+ interface CheckoutPageProps {
5
+ status: "success" | "cancel";
6
+ }
7
+
8
+ /**
9
+ * Post-checkout page shown after Stripe redirects back to the app.
10
+ *
11
+ * Rendered for both success and cancellation outcomes.
12
+ */
13
+ export function CheckoutPage({ status }: CheckoutPageProps) {
14
+ const [searchParams] = useSearchParams();
15
+ const sessionId = searchParams.get("session_id");
16
+
17
+ useEffect(() => {
18
+ if (status === "success" && sessionId) {
19
+ // Optionally verify the session on the server or trigger analytics
20
+ console.log("Checkout completed:", sessionId);
21
+ }
22
+ }, [status, sessionId]);
23
+
24
+ if (status === "success") {
25
+ return (
26
+ <div className="flex min-h-[60vh] items-center justify-center px-4">
27
+ <div className="max-w-md text-center">
28
+ <div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/30">
29
+ <svg
30
+ className="h-8 w-8 text-green-600 dark:text-green-400"
31
+ fill="none"
32
+ viewBox="0 0 24 24"
33
+ stroke="currentColor"
34
+ strokeWidth={2}
35
+ >
36
+ <path
37
+ strokeLinecap="round"
38
+ strokeLinejoin="round"
39
+ d="M5 13l4 4L19 7"
40
+ />
41
+ </svg>
42
+ </div>
43
+
44
+ <h1 className="text-2xl font-bold text-gray-900 dark:text-white">
45
+ Payment successful!
46
+ </h1>
47
+ <p className="mt-3 text-gray-500 dark:text-gray-400">
48
+ Thank you for subscribing. Your account has been upgraded and all
49
+ premium features are now available.
50
+ </p>
51
+
52
+ <div className="mt-8 flex flex-col gap-3 sm:flex-row sm:justify-center">
53
+ <Link
54
+ to="/dashboard"
55
+ className="inline-flex items-center justify-center rounded-lg bg-blue-600 px-5 py-2.5 text-sm font-semibold text-white hover:bg-blue-700"
56
+ >
57
+ Go to Dashboard
58
+ </Link>
59
+ <Link
60
+ to="/settings"
61
+ className="inline-flex items-center justify-center rounded-lg border border-gray-300 px-5 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"
62
+ >
63
+ Manage Subscription
64
+ </Link>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ );
69
+ }
70
+
71
+ // Cancellation
72
+ return (
73
+ <div className="flex min-h-[60vh] items-center justify-center px-4">
74
+ <div className="max-w-md text-center">
75
+ <div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-yellow-100 dark:bg-yellow-900/30">
76
+ <svg
77
+ className="h-8 w-8 text-yellow-600 dark:text-yellow-400"
78
+ fill="none"
79
+ viewBox="0 0 24 24"
80
+ stroke="currentColor"
81
+ strokeWidth={2}
82
+ >
83
+ <path
84
+ strokeLinecap="round"
85
+ strokeLinejoin="round"
86
+ d="M6 18L18 6M6 6l12 12"
87
+ />
88
+ </svg>
89
+ </div>
90
+
91
+ <h1 className="text-2xl font-bold text-gray-900 dark:text-white">
92
+ Checkout cancelled
93
+ </h1>
94
+ <p className="mt-3 text-gray-500 dark:text-gray-400">
95
+ No worries! Your account has not been charged. You can subscribe
96
+ anytime from the pricing page.
97
+ </p>
98
+
99
+ <div className="mt-8 flex flex-col gap-3 sm:flex-row sm:justify-center">
100
+ <Link
101
+ to="/pricing"
102
+ className="inline-flex items-center justify-center rounded-lg bg-blue-600 px-5 py-2.5 text-sm font-semibold text-white hover:bg-blue-700"
103
+ >
104
+ Back to Pricing
105
+ </Link>
106
+ <Link
107
+ to="/dashboard"
108
+ className="inline-flex items-center justify-center rounded-lg border border-gray-300 px-5 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"
109
+ >
110
+ Go to Dashboard
111
+ </Link>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ );
116
+ }
@@ -0,0 +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
+ }