@promakeai/cli 0.0.5 → 0.0.6

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 (91) hide show
  1. package/dist/index.js +214 -135
  2. package/dist/registry/about-page.json +1 -1
  3. package/dist/registry/about-section.json +1 -1
  4. package/dist/registry/api.json +55 -0
  5. package/dist/registry/auth.json +70 -0
  6. package/dist/registry/bento-grid-section.json +1 -1
  7. package/dist/registry/blog-list-page.json +1 -1
  8. package/dist/registry/blog-section.json +1 -1
  9. package/dist/registry/cart-drawer.json +1 -1
  10. package/dist/registry/cart-page.json +3 -2
  11. package/dist/registry/category-section.json +1 -1
  12. package/dist/registry/checkout-page.json +3 -2
  13. package/dist/registry/contact-info-grid.json +1 -1
  14. package/dist/registry/contact-page-centered.json +1 -1
  15. package/dist/registry/contact-page-map-overlay.json +1 -1
  16. package/dist/registry/contact-page.json +1 -1
  17. package/dist/registry/cookies-page.json +1 -1
  18. package/dist/registry/cta-section.json +1 -1
  19. package/dist/registry/db.json +129 -0
  20. package/dist/registry/docs/cart-page.md +1 -0
  21. package/dist/registry/docs/checkout-page.md +1 -0
  22. package/dist/registry/docs/forgot-password-page.md +37 -0
  23. package/dist/registry/docs/header-ecommerce.md +1 -0
  24. package/dist/registry/docs/products-page.md +1 -0
  25. package/dist/registry/docs/register-page.md +39 -0
  26. package/dist/registry/ecommerce-core.json +1 -1
  27. package/dist/registry/empty-page.json +1 -1
  28. package/dist/registry/faq-categorized.json +1 -1
  29. package/dist/registry/faq-simple.json +1 -1
  30. package/dist/registry/favorites-blog-block.json +1 -1
  31. package/dist/registry/favorites-ecommerce-block.json +1 -1
  32. package/dist/registry/feature-section.json +1 -1
  33. package/dist/registry/featured-products.json +1 -1
  34. package/dist/registry/footer-detailed.json +1 -1
  35. package/dist/registry/footer-minimal.json +3 -3
  36. package/dist/registry/footer.json +1 -1
  37. package/dist/registry/forgot-password-page.json +49 -0
  38. package/dist/registry/header-ecommerce.json +3 -2
  39. package/dist/registry/header-mega.json +1 -1
  40. package/dist/registry/header-minimal.json +1 -1
  41. package/dist/registry/header-simple.json +1 -1
  42. package/dist/registry/hero-cta.json +1 -1
  43. package/dist/registry/hero-gradient.json +1 -1
  44. package/dist/registry/hero-profile.json +1 -1
  45. package/dist/registry/hero.json +1 -1
  46. package/dist/registry/index.json +3 -0
  47. package/dist/registry/orders-list-block.json +1 -1
  48. package/dist/registry/payment-success-block.json +1 -1
  49. package/dist/registry/post-detail-block.json +1 -1
  50. package/dist/registry/pricing-section.json +1 -1
  51. package/dist/registry/privacy-page.json +1 -1
  52. package/dist/registry/products-page.json +3 -2
  53. package/dist/registry/register-page.json +49 -0
  54. package/dist/registry/related-posts-block.json +1 -1
  55. package/dist/registry/terms-page.json +1 -1
  56. package/dist/registry/testimonials-carousel.json +1 -1
  57. package/dist/registry/testimonials-grid.json +1 -1
  58. package/package.json +1 -1
  59. package/template/src/App.tsx +3 -24
  60. package/template/src/components/Layout.tsx +0 -4
  61. package/template/src/index.css +1 -0
  62. package/template/src/lang/en/index.json +1 -28
  63. package/template/src/lang/tr/index.json +1 -28
  64. package/template/src/pages/Index.tsx +1 -102
  65. package/template/src/components/Footer.tsx +0 -100
  66. package/template/src/components/Header.tsx +0 -79
  67. package/template/src/components/Hero.tsx +0 -69
  68. package/template/src/modules/api/USAGE.md +0 -515
  69. package/template/src/modules/api/customer-client.ts +0 -20
  70. package/template/src/modules/api/get-error-message.ts +0 -18
  71. package/template/src/modules/api/validation/en.json +0 -29
  72. package/template/src/modules/api/validation/tr.json +0 -29
  73. package/template/src/modules/auth/USAGE.md +0 -248
  74. package/template/src/modules/auth/auth-header-menu.tsx +0 -123
  75. package/template/src/modules/auth/auth-store.ts +0 -57
  76. package/template/src/modules/auth/forgot-password-page.tsx +0 -371
  77. package/template/src/modules/auth/login-page.tsx +0 -183
  78. package/template/src/modules/auth/register-page.tsx +0 -252
  79. package/template/src/modules/auth/use-auth.ts +0 -273
  80. package/template/src/modules/db/adapters/IDataAdapter.ts +0 -26
  81. package/template/src/modules/db/adapters/SqliteAdapter.ts +0 -364
  82. package/template/src/modules/db/adapters/index.ts +0 -2
  83. package/template/src/modules/db/config.ts +0 -59
  84. package/template/src/modules/db/core/DataManager.ts +0 -125
  85. package/template/src/modules/db/core/types.ts +0 -101
  86. package/template/src/modules/db/index.ts +0 -42
  87. package/template/src/modules/db/react/QueryProvider.tsx +0 -16
  88. package/template/src/modules/db/react/index.ts +0 -23
  89. package/template/src/modules/db/react/queryClient.ts +0 -64
  90. package/template/src/modules/db/react/useRepository.ts +0 -400
  91. package/template/src/modules/db/utils/parsers.ts +0 -96
@@ -1,252 +0,0 @@
1
- import { useState, useEffect } from "react";
2
- import { Link, useNavigate } from "react-router";
3
- import { toast } from "sonner";
4
- import { Layout } from "@/components/Layout";
5
- import { usePageTitle } from "@/hooks/use-page-title";
6
- import { useTranslation } from "react-i18next";
7
- import { useAuth } from "@/modules/auth/use-auth";
8
- import { getErrorMessage } from "@/modules/api/get-error-message";
9
- import { Button } from "@/components/ui/button";
10
- import { Input } from "@/components/ui/input";
11
- import { Label } from "@/components/ui/label";
12
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
13
- import { UserPlus, Eye, EyeOff, CheckCircle } from "lucide-react";
14
-
15
- export default function RegisterPage() {
16
- const { t } = useTranslation("register");
17
- usePageTitle({ title: t("title") });
18
-
19
- const navigate = useNavigate();
20
- const { register, isAuthenticated } = useAuth();
21
-
22
- const [formData, setFormData] = useState({
23
- username: "",
24
- email: "",
25
- password: "",
26
- confirmPassword: "",
27
- });
28
- const [showPassword, setShowPassword] = useState(false);
29
- const [isSubmitting, setIsSubmitting] = useState(false);
30
- const [error, setError] = useState<string | null>(null);
31
- const [success, setSuccess] = useState(false);
32
-
33
- // Redirect if already authenticated
34
- useEffect(() => {
35
- if (isAuthenticated) {
36
- navigate("/", { replace: true });
37
- }
38
- }, [isAuthenticated, navigate]);
39
-
40
- const handleSubmit = async (e: React.FormEvent) => {
41
- e.preventDefault();
42
- setIsSubmitting(true);
43
- setError(null);
44
-
45
- // Validate passwords match
46
- if (formData.password !== formData.confirmPassword) {
47
- setError(t("passwordMismatch"));
48
- toast.error(t("toastErrorTitle", "Registration failed"), {
49
- description: t("passwordMismatch"),
50
- });
51
- setIsSubmitting(false);
52
- return;
53
- }
54
-
55
- try {
56
- await register(formData.username, formData.email, formData.password);
57
- setSuccess(true);
58
- toast.success(t("toastSuccessTitle", "Account created!"), {
59
- description: t(
60
- "toastSuccessDesc",
61
- "Please check your email to verify your account.",
62
- ),
63
- });
64
- } catch (err) {
65
- const errorMessage = getErrorMessage(err, t("errorGeneric"));
66
- setError(errorMessage);
67
- toast.error(t("toastErrorTitle", "Registration failed"), {
68
- description: errorMessage,
69
- });
70
- } finally {
71
- setIsSubmitting(false);
72
- }
73
- };
74
-
75
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
76
- setFormData((prev) => ({
77
- ...prev,
78
- [e.target.name]: e.target.value,
79
- }));
80
- };
81
-
82
- if (success) {
83
- return (
84
- <Layout>
85
- <div className="min-h-screen bg-muted/30 py-12">
86
- <div className="container mx-auto px-4">
87
- <div className="max-w-md mx-auto">
88
- <Card>
89
- <CardContent className="pt-6">
90
- <div className="text-center space-y-4">
91
- <CheckCircle className="w-16 h-16 text-green-500 mx-auto" />
92
- <h2 className="text-2xl font-bold text-foreground">
93
- {t("successTitle")}
94
- </h2>
95
- <p className="text-muted-foreground">
96
- {t("successMessage")}
97
- </p>
98
- <Button asChild className="mt-4">
99
- <Link to="/login">{t("goToLogin")}</Link>
100
- </Button>
101
- </div>
102
- </CardContent>
103
- </Card>
104
- </div>
105
- </div>
106
- </div>
107
- </Layout>
108
- );
109
- }
110
-
111
- return (
112
- <Layout>
113
- <div className="min-h-screen bg-muted/30 py-12">
114
- <div className="container mx-auto px-4">
115
- {/* Hero Section */}
116
- <div className="text-center mb-12">
117
- <h1 className="text-4xl font-bold text-foreground mb-4">
118
- {t("title")}
119
- </h1>
120
- <div className="w-16 h-1 bg-primary mx-auto mb-6"></div>
121
- <p className="text-lg text-muted-foreground max-w-xl mx-auto">
122
- {t("description")}
123
- </p>
124
- </div>
125
-
126
- <div className="max-w-md mx-auto">
127
- <Card>
128
- <CardHeader>
129
- <CardTitle className="flex items-center gap-2">
130
- <UserPlus className="w-5 h-5 text-primary" />
131
- {t("cardTitle")}
132
- </CardTitle>
133
- </CardHeader>
134
- <CardContent>
135
- <form onSubmit={handleSubmit} className="space-y-6">
136
- <div>
137
- <Label htmlFor="username">{t("username")} *</Label>
138
- <Input
139
- id="username"
140
- name="username"
141
- type="text"
142
- value={formData.username}
143
- onChange={handleChange}
144
- placeholder={t("usernamePlaceholder")}
145
- required
146
- className="mt-1"
147
- autoComplete="username"
148
- />
149
- </div>
150
-
151
- <div>
152
- <Label htmlFor="email">{t("email")} *</Label>
153
- <Input
154
- id="email"
155
- name="email"
156
- type="email"
157
- value={formData.email}
158
- onChange={handleChange}
159
- placeholder={t("emailPlaceholder")}
160
- required
161
- className="mt-1"
162
- autoComplete="email"
163
- />
164
- </div>
165
-
166
- <div>
167
- <Label htmlFor="password">{t("password")} *</Label>
168
- <div className="relative">
169
- <Input
170
- id="password"
171
- name="password"
172
- type={showPassword ? "text" : "password"}
173
- value={formData.password}
174
- onChange={handleChange}
175
- placeholder={t("passwordPlaceholder")}
176
- required
177
- className="mt-1 pr-10"
178
- autoComplete="new-password"
179
- />
180
- <button
181
- type="button"
182
- onClick={() => setShowPassword(!showPassword)}
183
- className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
184
- >
185
- {showPassword ? (
186
- <EyeOff className="w-4 h-4" />
187
- ) : (
188
- <Eye className="w-4 h-4" />
189
- )}
190
- </button>
191
- </div>
192
- </div>
193
-
194
- <div>
195
- <Label htmlFor="confirmPassword">
196
- {t("confirmPassword")} *
197
- </Label>
198
- <Input
199
- id="confirmPassword"
200
- name="confirmPassword"
201
- type={showPassword ? "text" : "password"}
202
- value={formData.confirmPassword}
203
- onChange={handleChange}
204
- placeholder={t("confirmPasswordPlaceholder")}
205
- required
206
- className="mt-1"
207
- autoComplete="new-password"
208
- />
209
- </div>
210
-
211
- {error && (
212
- <div className="p-4 bg-red-50 border border-red-200 rounded-lg">
213
- <p className="text-red-800 text-sm font-medium">
214
- {error}
215
- </p>
216
- </div>
217
- )}
218
-
219
- <Button
220
- type="submit"
221
- size="lg"
222
- className="w-full"
223
- disabled={isSubmitting}
224
- >
225
- {isSubmitting ? (
226
- <>
227
- <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" />
228
- {t("submitting")}
229
- </>
230
- ) : (
231
- t("submit")
232
- )}
233
- </Button>
234
-
235
- <div className="text-center text-sm text-muted-foreground">
236
- {t("hasAccount")}{" "}
237
- <Link
238
- to="/login"
239
- className="text-primary hover:underline font-medium"
240
- >
241
- {t("loginLink")}
242
- </Link>
243
- </div>
244
- </form>
245
- </CardContent>
246
- </Card>
247
- </div>
248
- </div>
249
- </div>
250
- </Layout>
251
- );
252
- }
@@ -1,273 +0,0 @@
1
- import { useCallback, useEffect, useRef } from "react";
2
- import {
3
- useAuthStore,
4
- type User,
5
- type AuthTokens,
6
- } from "@/modules/auth/auth-store";
7
- import { customerClient } from "@/modules/api/customer-client";
8
-
9
- // Refresh token 1 minute before expiry
10
- const REFRESH_BUFFER_MS = 60 * 1000;
11
-
12
- export function useAuth() {
13
- const {
14
- user,
15
- tokens,
16
- isAuthenticated,
17
- setAuth,
18
- updateTokens,
19
- clearAuth,
20
- isTokenExpired,
21
- getTimeUntilExpiry,
22
- } = useAuthStore();
23
-
24
- const refreshTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
25
- const isRefreshingRef = useRef(false);
26
-
27
- // Refresh token using the refresh token
28
- const refreshAccessToken = useCallback(async (): Promise<boolean> => {
29
- const currentTokens = useAuthStore.getState().tokens;
30
-
31
- // Don't refresh if no refresh token exists
32
- if (!currentTokens?.refreshToken || isRefreshingRef.current) {
33
- console.log("⚠️ No refresh token available, skipping refresh");
34
- return false;
35
- }
36
-
37
- isRefreshingRef.current = true;
38
-
39
- try {
40
- // Make a refresh request using the axios instance directly
41
- const response = await customerClient.axios.post<{
42
- accessToken: string;
43
- refreshToken?: string;
44
- expiresIn?: number;
45
- }>("/auth/refresh", {
46
- refreshToken: currentTokens.refreshToken,
47
- });
48
-
49
- const { accessToken, refreshToken, expiresIn } = response.data;
50
-
51
- // Validate response has required data
52
- if (!accessToken) {
53
- console.error("❌ Refresh response missing accessToken");
54
- return false;
55
- }
56
-
57
- const newTokens: AuthTokens = {
58
- accessToken,
59
- refreshToken: refreshToken || currentTokens.refreshToken,
60
- idToken: currentTokens.idToken, // Preserve existing idToken
61
- encryptionKey: currentTokens.encryptionKey, // Preserve existing encryptionKey
62
- expiresAt: expiresIn ? Date.now() + expiresIn * 1000 : undefined,
63
- };
64
-
65
- customerClient.setToken(accessToken);
66
- updateTokens(newTokens);
67
-
68
- console.log("✅ Token refreshed successfully");
69
- return true;
70
- } catch (error) {
71
- console.error("❌ Token refresh failed:", error);
72
- // DON'T clear auth on refresh failure - just return false
73
- // User can still use their existing token until it expires
74
- return false;
75
- } finally {
76
- isRefreshingRef.current = false;
77
- }
78
- }, [updateTokens]);
79
-
80
- // Schedule automatic token refresh
81
- const scheduleTokenRefresh = useCallback(() => {
82
- // Clear any existing timeout
83
- if (refreshTimeoutRef.current) {
84
- clearTimeout(refreshTimeoutRef.current);
85
- refreshTimeoutRef.current = null;
86
- }
87
-
88
- const timeUntilExpiry = getTimeUntilExpiry();
89
-
90
- // Only schedule if we have an expiry time and a refresh token
91
- if (timeUntilExpiry === null || !tokens?.refreshToken) {
92
- return;
93
- }
94
-
95
- // Calculate when to refresh (REFRESH_BUFFER_MS before expiry)
96
- const refreshIn = Math.max(timeUntilExpiry - REFRESH_BUFFER_MS, 0);
97
-
98
- // Don't schedule if expiry is too far in the future (> 24 hours)
99
- if (refreshIn > 24 * 60 * 60 * 1000) {
100
- return;
101
- }
102
-
103
- refreshTimeoutRef.current = setTimeout(async () => {
104
- const success = await refreshAccessToken();
105
- if (success) {
106
- // Reschedule for the new token
107
- scheduleTokenRefresh();
108
- }
109
- }, refreshIn);
110
- }, [getTimeUntilExpiry, tokens?.refreshToken, refreshAccessToken]);
111
-
112
- // Sync token with API client and set up refresh on mount and token changes
113
- useEffect(() => {
114
- if (tokens?.accessToken) {
115
- console.log("🔑 Setting token in API client");
116
- customerClient.setToken(tokens.accessToken);
117
-
118
- // Only try to refresh if we have a refresh token AND token is expired
119
- if (isTokenExpired() && tokens.refreshToken) {
120
- console.log("⏰ Token expired, attempting refresh...");
121
- refreshAccessToken().then((success) => {
122
- if (success) {
123
- scheduleTokenRefresh();
124
- } else {
125
- console.log("⚠️ Refresh failed, but keeping existing token");
126
- }
127
- });
128
- } else if (tokens.refreshToken) {
129
- // Only schedule refresh if we have a refresh token
130
- scheduleTokenRefresh();
131
- }
132
- } else if (tokens && Object.keys(tokens).length === 0) {
133
- // tokens is empty object {} - this shouldn't happen, log it
134
- console.warn("⚠️ Tokens object is empty, this may indicate a bug");
135
- } else {
136
- customerClient.setToken(null);
137
- }
138
-
139
- // Cleanup timeout on unmount
140
- return () => {
141
- if (refreshTimeoutRef.current) {
142
- clearTimeout(refreshTimeoutRef.current);
143
- }
144
- };
145
- }, [
146
- tokens?.accessToken,
147
- tokens?.refreshToken,
148
- isTokenExpired,
149
- refreshAccessToken,
150
- scheduleTokenRefresh,
151
- ]);
152
-
153
- // Set up axios interceptor for 401 responses (token expired during request)
154
- useEffect(() => {
155
- const interceptorId = customerClient.axios.interceptors.response.use(
156
- (response) => response,
157
- async (error) => {
158
- const originalRequest = error.config;
159
-
160
- // Skip refresh for auth endpoints to prevent infinite loops
161
- const isAuthEndpoint = originalRequest?.url?.includes("/auth/");
162
-
163
- // If we get a 401 and haven't retried yet, try to refresh
164
- if (
165
- error.response?.status === 401 &&
166
- !originalRequest._retry &&
167
- tokens?.refreshToken &&
168
- !isAuthEndpoint
169
- ) {
170
- originalRequest._retry = true;
171
-
172
- const success = await refreshAccessToken();
173
- if (success) {
174
- // Retry the original request with new token
175
- const newTokens = useAuthStore.getState().tokens;
176
- if (newTokens?.accessToken) {
177
- originalRequest.headers.Authorization = `Bearer ${newTokens.accessToken}`;
178
- return customerClient.axios(originalRequest);
179
- }
180
- }
181
- }
182
-
183
- return Promise.reject(error);
184
- },
185
- );
186
-
187
- return () => {
188
- customerClient.axios.interceptors.response.eject(interceptorId);
189
- };
190
- }, [tokens?.refreshToken, refreshAccessToken]);
191
-
192
- const login = useCallback(async (username: string, password: string) => {
193
- const response = await customerClient.auth.login({ username, password });
194
-
195
- console.log("🔐 Login response:", response);
196
- console.log("🔐 accessToken:", response.accessToken);
197
- console.log("🔐 refreshToken:", response.refreshToken);
198
- console.log("🔐 encryptionKey:", response.encryptionKey);
199
-
200
- const newTokens: AuthTokens = {
201
- accessToken: response.accessToken,
202
- refreshToken: response.refreshToken,
203
- idToken: response.idToken,
204
- encryptionKey: response.encryptionKey,
205
- expiresAt: response.expiresIn
206
- ? Date.now() + response.expiresIn * 1000
207
- : undefined,
208
- };
209
-
210
- console.log("🔐 newTokens object:", newTokens);
211
-
212
- const newUser: User = {
213
- username,
214
- email: (response as any).email || (response as any).user?.email,
215
- };
216
-
217
- customerClient.setToken(newTokens.accessToken);
218
- setAuth(newUser, newTokens);
219
-
220
- console.log(
221
- "🔐 Auth set complete, checking store:",
222
- useAuthStore.getState().tokens,
223
- );
224
- }, []);
225
-
226
- const register = useCallback(
227
- async (username: string, email: string, password: string) => {
228
- await customerClient.auth.register({ username, email, password });
229
- },
230
- [],
231
- );
232
-
233
- const confirmEmail = useCallback(async (username: string, code: string) => {
234
- await customerClient.auth.confirm({ username, code });
235
- }, []);
236
-
237
- const forgotPassword = useCallback(async (username: string) => {
238
- await customerClient.auth.forgotPassword({ username });
239
- }, []);
240
-
241
- const resetPassword = useCallback(
242
- async (username: string, code: string, newPassword: string) => {
243
- await customerClient.auth.resetPassword({ username, code, newPassword });
244
- },
245
- [],
246
- );
247
-
248
- const logout = useCallback(() => {
249
- // Clear any scheduled refresh
250
- if (refreshTimeoutRef.current) {
251
- clearTimeout(refreshTimeoutRef.current);
252
- refreshTimeoutRef.current = null;
253
- }
254
-
255
- customerClient.setToken(null);
256
- clearAuth();
257
- }, [clearAuth]);
258
-
259
- return {
260
- user,
261
- token: tokens?.accessToken ?? null,
262
- tokens,
263
- isAuthenticated,
264
- api: customerClient,
265
- login,
266
- register,
267
- confirmEmail,
268
- forgotPassword,
269
- resetPassword,
270
- logout,
271
- refreshAccessToken,
272
- };
273
- }
@@ -1,26 +0,0 @@
1
- import type { QueryOptions } from "../core/types";
2
-
3
- /**
4
- * Data Adapter Interface
5
- * Implement this interface to create custom data sources
6
- */
7
- export interface IDataAdapter {
8
- // Connection
9
- connect(): Promise<void>;
10
- disconnect(): Promise<void>;
11
-
12
- // CRUD operations
13
- findMany<T>(table: string, options?: QueryOptions): Promise<T[]>;
14
- findOne<T>(table: string, options: QueryOptions): Promise<T | null>;
15
- findById<T>(table: string, id: number | string): Promise<T | null>;
16
- create<T>(table: string, data: Partial<T>): Promise<T>;
17
- update<T>(table: string, id: number | string, data: Partial<T>): Promise<T>;
18
- delete(table: string, id: number | string): Promise<boolean>;
19
-
20
- // Utilities
21
- count(table: string, options?: QueryOptions): Promise<number>;
22
-
23
- // Raw SQL queries - for complex queries that can't be expressed with QueryOptions
24
- raw<T>(sql: string, params?: any[]): Promise<T[]>;
25
- rawOne<T>(sql: string, params?: any[]): Promise<T | null>;
26
- }