@onexapis/cli 1.1.34 → 1.1.37

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 (45) hide show
  1. package/dist/cli.js +5 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +5 -1
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/preview/preview-app.tsx +13 -5
  6. package/package.json +1 -1
  7. package/templates/default/AUTH_AND_PROFILE.md +167 -0
  8. package/templates/default/CLAUDE.md +78 -24
  9. package/templates/default/LAYOUT.md +195 -0
  10. package/templates/default/bundle-entry.ts +5 -0
  11. package/templates/default/hooks/index.ts +26 -0
  12. package/templates/default/hooks/use-forgot-password-form.ts +90 -0
  13. package/templates/default/hooks/use-login-form.ts +102 -0
  14. package/templates/default/hooks/use-profile-form.ts +255 -0
  15. package/templates/default/hooks/use-register-form.ts +154 -0
  16. package/templates/default/hooks/use-verify-code-form.ts +224 -0
  17. package/templates/default/index.ts +21 -1
  18. package/templates/default/pages/forgot-password.ts +41 -0
  19. package/templates/default/pages/login.ts +41 -0
  20. package/templates/default/pages/profile.ts +39 -0
  21. package/templates/default/pages/register.ts +41 -0
  22. package/templates/default/pages/verify-code.ts +41 -0
  23. package/templates/default/sections/auth-forgot-password/auth-forgot-password-default.tsx +192 -0
  24. package/templates/default/sections/auth-forgot-password/auth-forgot-password.schema.ts +150 -0
  25. package/templates/default/sections/auth-forgot-password/index.ts +14 -0
  26. package/templates/default/sections/auth-login/auth-login-default.tsx +238 -0
  27. package/templates/default/sections/auth-login/auth-login.schema.ts +171 -0
  28. package/templates/default/sections/auth-login/index.ts +14 -0
  29. package/templates/default/sections/auth-register/auth-register-default.tsx +327 -0
  30. package/templates/default/sections/auth-register/auth-register.schema.ts +188 -0
  31. package/templates/default/sections/auth-register/index.ts +14 -0
  32. package/templates/default/sections/auth-verify-code/auth-verify-code-default.tsx +209 -0
  33. package/templates/default/sections/auth-verify-code/auth-verify-code.schema.ts +150 -0
  34. package/templates/default/sections/auth-verify-code/index.ts +14 -0
  35. package/templates/default/sections/footer/footer-default.tsx +214 -0
  36. package/templates/default/sections/footer/footer.schema.ts +170 -0
  37. package/templates/default/sections/footer/index.ts +14 -0
  38. package/templates/default/sections/header/header-default.tsx +322 -0
  39. package/templates/default/sections/header/header.schema.ts +168 -0
  40. package/templates/default/sections/header/index.ts +14 -0
  41. package/templates/default/sections/profile/index.ts +14 -0
  42. package/templates/default/sections/profile/profile-default.tsx +522 -0
  43. package/templates/default/sections/profile/profile.schema.ts +228 -0
  44. package/templates/default/sections-registry.ts +28 -0
  45. package/templates/default/theme.layout.ts +53 -2
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Auth Verify Code - Default Template
3
+ * OTP verification form with customizable settings
4
+ */
5
+
6
+ "use client";
7
+
8
+ import type { SectionComponentProps } from "@onexapis/core/types";
9
+ import coreUtils from "@onexapis/core/utils";
10
+ import { useVerifyCodeForm } from "../../hooks/use-verify-code-form";
11
+
12
+ const { getSectionValues } = coreUtils;
13
+
14
+ export function AuthVerifyCodeDefault({
15
+ section,
16
+ schema,
17
+ isEditing,
18
+ }: SectionComponentProps) {
19
+ const { settings } = getSectionValues(section, schema);
20
+ const {
21
+ heading,
22
+ subheading,
23
+ submitButtonText,
24
+ loadingText,
25
+ resendText,
26
+ resendCountdownText,
27
+ didNotReceiveText,
28
+ loginPromptText,
29
+ loginLinkText,
30
+ loginUrl,
31
+ backgroundColor,
32
+ cardBackground,
33
+ primaryColor,
34
+ textColor,
35
+ cardBorderRadius,
36
+ } = settings;
37
+
38
+ const {
39
+ code,
40
+ error,
41
+ countdown,
42
+ email,
43
+ isVerifying,
44
+ isResending,
45
+ isLoading,
46
+ inputRefs,
47
+ handleCodeChange,
48
+ handleKeyDown,
49
+ handlePaste,
50
+ handleSubmit,
51
+ handleResend,
52
+ } = useVerifyCodeForm();
53
+
54
+ const bgColor = String(backgroundColor || "#F9FAFB");
55
+ const cardBg = String(cardBackground || "#FFFFFF");
56
+ const btnColor = String(primaryColor || "#2563EB");
57
+ const headingColor = String(textColor || "#111827");
58
+ const radiusClass = `rounded-${String(cardBorderRadius || "2xl")}`;
59
+
60
+ return (
61
+ <section
62
+ className="min-h-screen w-full flex items-center justify-center p-4"
63
+ style={{ backgroundColor: bgColor }}
64
+ data-section-id={section.id}
65
+ data-section-type={section.type}
66
+ data-section-template="default"
67
+ >
68
+ <div
69
+ className={`w-full max-w-md shadow-lg p-8 ${radiusClass}`}
70
+ style={{ backgroundColor: cardBg }}
71
+ >
72
+ {/* Header */}
73
+ <div className="text-center mb-8">
74
+ <h1 className="text-2xl font-bold" style={{ color: headingColor }}>
75
+ {String(heading)}
76
+ </h1>
77
+ <p className="mt-2 text-sm text-gray-500">
78
+ {String(subheading)}
79
+ {email && (
80
+ <span
81
+ className="block mt-1 font-medium"
82
+ style={{ color: btnColor }}
83
+ >
84
+ {email}
85
+ </span>
86
+ )}
87
+ </p>
88
+ </div>
89
+
90
+ {/* Error Message */}
91
+ {error && (
92
+ <div className="mb-4 rounded-lg bg-red-50 border border-red-200 p-3 text-sm text-red-600 text-center">
93
+ {error}
94
+ </div>
95
+ )}
96
+
97
+ {/* OTP Form */}
98
+ <form
99
+ onSubmit={handleSubmit}
100
+ className="flex flex-col items-center gap-6"
101
+ >
102
+ {/* OTP Inputs */}
103
+ <div className="flex items-center justify-center gap-2 md:gap-3">
104
+ {code.map((digit, index) => (
105
+ <input
106
+ key={index}
107
+ ref={(el) => {
108
+ inputRefs.current[index] = el;
109
+ }}
110
+ type="text"
111
+ inputMode="numeric"
112
+ maxLength={1}
113
+ value={digit}
114
+ onChange={(e) => handleCodeChange(index, e.target.value)}
115
+ onKeyDown={(e) => handleKeyDown(index, e)}
116
+ onPaste={handlePaste}
117
+ disabled={isVerifying || isEditing}
118
+ className={`h-12 w-10 md:w-12 rounded-lg border text-center text-xl font-medium text-gray-900 outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:opacity-50 ${
119
+ error ? "border-red-400" : "border-gray-300"
120
+ }`}
121
+ aria-label={`Digit ${index + 1}`}
122
+ />
123
+ ))}
124
+ </div>
125
+
126
+ {/* Resend Code */}
127
+ <button
128
+ type="button"
129
+ disabled={isLoading || countdown > 0 || isEditing}
130
+ onClick={handleResend}
131
+ className="text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed hover:opacity-80"
132
+ style={{ color: btnColor }}
133
+ >
134
+ {countdown > 0 ? (
135
+ <span className="text-gray-400">
136
+ {String(resendCountdownText)} {countdown}s
137
+ </span>
138
+ ) : isResending ? (
139
+ "Sending..."
140
+ ) : (
141
+ <>
142
+ <span className="text-gray-500">
143
+ {String(didNotReceiveText)}{" "}
144
+ </span>
145
+ {String(resendText)}
146
+ </>
147
+ )}
148
+ </button>
149
+
150
+ {/* Submit Button */}
151
+ <button
152
+ type="submit"
153
+ disabled={isVerifying || code.join("").length !== 6 || isEditing}
154
+ className="h-11 w-full rounded-lg text-sm font-medium text-white transition-colors hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
155
+ style={{ backgroundColor: btnColor }}
156
+ >
157
+ {isVerifying && (
158
+ <svg
159
+ className="h-4 w-4 animate-spin"
160
+ fill="none"
161
+ viewBox="0 0 24 24"
162
+ >
163
+ <circle
164
+ className="opacity-25"
165
+ cx="12"
166
+ cy="12"
167
+ r="10"
168
+ stroke="currentColor"
169
+ strokeWidth="4"
170
+ />
171
+ <path
172
+ className="opacity-75"
173
+ fill="currentColor"
174
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
175
+ />
176
+ </svg>
177
+ )}
178
+ {isVerifying ? String(loadingText) : String(submitButtonText)}
179
+ </button>
180
+ </form>
181
+
182
+ {/* Login Link */}
183
+ <div className="mt-6 text-center">
184
+ <p className="text-sm text-gray-500">
185
+ {String(loginPromptText)}{" "}
186
+ {isEditing ? (
187
+ <span
188
+ className="font-medium hover:opacity-80"
189
+ style={{ color: btnColor }}
190
+ >
191
+ {String(loginLinkText)}
192
+ </span>
193
+ ) : (
194
+ <a
195
+ href={String(loginUrl)}
196
+ className="font-medium hover:opacity-80 transition-colors"
197
+ style={{ color: btnColor }}
198
+ >
199
+ {String(loginLinkText)}
200
+ </a>
201
+ )}
202
+ </p>
203
+ </div>
204
+ </div>
205
+ </section>
206
+ );
207
+ }
208
+
209
+ export default AuthVerifyCodeDefault;
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Auth Verify Code Section Schema
3
+ * OTP verification form with editable labels
4
+ */
5
+
6
+ import type { SectionSchema, FieldDefinition } from "@onexapis/core/types";
7
+
8
+ const commonSettings: FieldDefinition[] = [
9
+ {
10
+ id: "heading",
11
+ type: "text",
12
+ label: "Heading",
13
+ default: "Verify Your Account",
14
+ required: true,
15
+ },
16
+ {
17
+ id: "subheading",
18
+ type: "text",
19
+ label: "Subheading",
20
+ default: "Enter the 6-digit code sent to your email",
21
+ },
22
+ {
23
+ id: "submitButtonText",
24
+ type: "text",
25
+ label: "Submit Button Text",
26
+ default: "Verify",
27
+ },
28
+ {
29
+ id: "loadingText",
30
+ type: "text",
31
+ label: "Loading Text",
32
+ default: "Verifying...",
33
+ },
34
+ {
35
+ id: "resendText",
36
+ type: "text",
37
+ label: "Resend Text",
38
+ default: "Resend code",
39
+ },
40
+ {
41
+ id: "resendCountdownText",
42
+ type: "text",
43
+ label: "Countdown Text",
44
+ default: "Resend in",
45
+ },
46
+ {
47
+ id: "didNotReceiveText",
48
+ type: "text",
49
+ label: "Did Not Receive Text",
50
+ default: "Didn't receive a code?",
51
+ },
52
+ {
53
+ id: "loginPromptText",
54
+ type: "text",
55
+ label: "Login Prompt Text",
56
+ default: "Already have an account?",
57
+ },
58
+ {
59
+ id: "loginLinkText",
60
+ type: "text",
61
+ label: "Login Link Text",
62
+ default: "Sign in",
63
+ },
64
+ {
65
+ id: "loginUrl",
66
+ type: "url",
67
+ label: "Login URL",
68
+ default: "/login",
69
+ },
70
+ {
71
+ id: "countdownSeconds",
72
+ type: "number",
73
+ label: "Countdown Duration (seconds)",
74
+ default: 60,
75
+ },
76
+ {
77
+ id: "backgroundColor",
78
+ type: "color",
79
+ label: "Background Color",
80
+ default: "#F9FAFB",
81
+ },
82
+ {
83
+ id: "cardBackground",
84
+ type: "color",
85
+ label: "Card Background",
86
+ default: "#FFFFFF",
87
+ },
88
+ {
89
+ id: "primaryColor",
90
+ type: "color",
91
+ label: "Primary Color (buttons)",
92
+ default: "#2563EB",
93
+ },
94
+ {
95
+ id: "textColor",
96
+ type: "color",
97
+ label: "Heading Text Color",
98
+ default: "#111827",
99
+ },
100
+ {
101
+ id: "cardBorderRadius",
102
+ type: "select",
103
+ label: "Card Border Radius",
104
+ default: "2xl",
105
+ options: [
106
+ { label: "Small", value: "lg" },
107
+ { label: "Medium", value: "xl" },
108
+ { label: "Large", value: "2xl" },
109
+ { label: "Extra Large", value: "3xl" },
110
+ ],
111
+ },
112
+ ];
113
+
114
+ export const authVerifyCodeSchema: SectionSchema = {
115
+ type: "my-simple-auth-verify-code",
116
+ name: "Auth Verify Code",
117
+ description: "OTP verification form with 6-digit code input",
118
+ category: "auth",
119
+ icon: "shield-check",
120
+ settings: commonSettings,
121
+ templates: [
122
+ {
123
+ id: "default",
124
+ name: "Default Verify Code",
125
+ description: "Clean OTP verification form",
126
+ isDefault: true,
127
+ },
128
+ ],
129
+ defaults: {
130
+ settings: {
131
+ heading: "Verify Your Account",
132
+ subheading: "Enter the 6-digit code sent to your email",
133
+ submitButtonText: "Verify",
134
+ loadingText: "Verifying...",
135
+ resendText: "Resend code",
136
+ resendCountdownText: "Resend in",
137
+ didNotReceiveText: "Didn't receive a code?",
138
+ loginPromptText: "Already have an account?",
139
+ loginLinkText: "Sign in",
140
+ loginUrl: "/login",
141
+ countdownSeconds: 60,
142
+ backgroundColor: "#F9FAFB",
143
+ cardBackground: "#FFFFFF",
144
+ primaryColor: "#2563EB",
145
+ textColor: "#111827",
146
+ cardBorderRadius: "2xl",
147
+ },
148
+ },
149
+ tags: ["auth", "verify-code", "otp"],
150
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Auth Verify Code Section
3
+ * Exports schema and component templates
4
+ */
5
+
6
+ import type { SectionComponentProps } from "@onexapis/core/types";
7
+ import { AuthVerifyCodeDefault } from "./auth-verify-code-default";
8
+
9
+ export const authVerifyCodeComponents: Record<
10
+ string,
11
+ React.ComponentType<SectionComponentProps>
12
+ > = { default: AuthVerifyCodeDefault };
13
+
14
+ export { authVerifyCodeSchema } from "./auth-verify-code.schema";
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Footer - Default Template
3
+ * Clean responsive footer with company info, links, and copyright
4
+ */
5
+
6
+ "use client";
7
+
8
+ import type { SectionComponentProps } from "@onexapis/core/types";
9
+ import coreRenderers from "@onexapis/core/renderers";
10
+ import coreUtils from "@onexapis/core/utils";
11
+
12
+ const { ComponentRenderer } = coreRenderers;
13
+ const { toComponentInstance, getSectionValues, filterEnabledComponents } =
14
+ coreUtils;
15
+
16
+ export function FooterDefault({
17
+ section,
18
+ schema,
19
+ isEditing,
20
+ }: SectionComponentProps) {
21
+ const { settings } = getSectionValues(section, schema);
22
+ const {
23
+ companyName,
24
+ description,
25
+ copyrightText,
26
+ showAboutColumn,
27
+ aboutColumnTitle,
28
+ showLinksColumn,
29
+ linksColumnTitle,
30
+ backgroundColor,
31
+ textColor,
32
+ primaryColor,
33
+ } = settings;
34
+
35
+ const bgColor = String(backgroundColor || "#111827");
36
+ const txtColor = String(textColor || "#9CA3AF");
37
+ const headingColor = String(primaryColor || "#FFFFFF");
38
+
39
+ // Find components
40
+ const components = filterEnabledComponents(section.components || []);
41
+ const companyNameComp = components.find(
42
+ (c) => c.slot === "company-name" || c.id === "footer-company-name"
43
+ );
44
+ const descComp = components.find(
45
+ (c) => c.slot === "description" || c.id === "footer-description"
46
+ );
47
+ const copyrightComp = components.find(
48
+ (c) => c.slot === "copyright" || c.id === "footer-copyright"
49
+ );
50
+
51
+ // Parse link items
52
+ let aboutLinks: Array<{ label: string; href: string }> = [];
53
+ let quickLinks: Array<{ label: string; href: string }> = [];
54
+ try {
55
+ const rawAbout = settings.aboutLinks;
56
+ if (typeof rawAbout === "string") aboutLinks = JSON.parse(rawAbout);
57
+ else if (Array.isArray(rawAbout))
58
+ aboutLinks = rawAbout as Array<{ label: string; href: string }>;
59
+ } catch {
60
+ aboutLinks = [
61
+ { label: "About Us", href: "/about" },
62
+ { label: "Contact", href: "#contact" },
63
+ ];
64
+ }
65
+ try {
66
+ const rawLinks = settings.quickLinks;
67
+ if (typeof rawLinks === "string") quickLinks = JSON.parse(rawLinks);
68
+ else if (Array.isArray(rawLinks))
69
+ quickLinks = rawLinks as Array<{ label: string; href: string }>;
70
+ } catch {
71
+ quickLinks = [
72
+ { label: "Home", href: "/" },
73
+ { label: "Showcase", href: "/showcase" },
74
+ ];
75
+ }
76
+
77
+ const showAbout = Boolean(showAboutColumn) !== false;
78
+ const showLinks = Boolean(showLinksColumn) !== false;
79
+
80
+ return (
81
+ <footer
82
+ className="w-full"
83
+ style={{ backgroundColor: bgColor }}
84
+ data-section-id={section.id}
85
+ data-section-type={section.type}
86
+ data-section-template="default"
87
+ >
88
+ <div className="container mx-auto px-4 py-12">
89
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
90
+ {/* Column 1: Company Info */}
91
+ <div className="space-y-4">
92
+ {companyNameComp ? (
93
+ <ComponentRenderer
94
+ instance={toComponentInstance(companyNameComp)}
95
+ sectionId={section.id}
96
+ isEditing={isEditing}
97
+ />
98
+ ) : (
99
+ <h3 className="text-xl font-bold" style={{ color: headingColor }}>
100
+ {String(companyName)}
101
+ </h3>
102
+ )}
103
+ {descComp ? (
104
+ <ComponentRenderer
105
+ instance={toComponentInstance(descComp)}
106
+ sectionId={section.id}
107
+ isEditing={isEditing}
108
+ />
109
+ ) : description ? (
110
+ <p
111
+ className="text-sm leading-relaxed"
112
+ style={{ color: txtColor }}
113
+ >
114
+ {String(description)}
115
+ </p>
116
+ ) : null}
117
+ </div>
118
+
119
+ {/* Column 2: About Links */}
120
+ {showAbout && (
121
+ <div className="space-y-4">
122
+ <h4
123
+ className="text-base font-semibold"
124
+ style={{ color: headingColor }}
125
+ >
126
+ {String(aboutColumnTitle)}
127
+ </h4>
128
+ <nav className="flex flex-col gap-2">
129
+ {aboutLinks.map((link, i) =>
130
+ isEditing ? (
131
+ <span
132
+ key={i}
133
+ className="text-sm hover:opacity-80"
134
+ style={{ color: txtColor }}
135
+ >
136
+ {link.label}
137
+ </span>
138
+ ) : (
139
+ <a
140
+ key={i}
141
+ href={link.href}
142
+ className="text-sm hover:opacity-80 transition-opacity"
143
+ style={{ color: txtColor }}
144
+ >
145
+ {link.label}
146
+ </a>
147
+ )
148
+ )}
149
+ </nav>
150
+ </div>
151
+ )}
152
+
153
+ {/* Column 3: Quick Links */}
154
+ {showLinks && (
155
+ <div className="space-y-4">
156
+ <h4
157
+ className="text-base font-semibold"
158
+ style={{ color: headingColor }}
159
+ >
160
+ {String(linksColumnTitle)}
161
+ </h4>
162
+ <nav className="flex flex-col gap-2">
163
+ {quickLinks.map((link, i) =>
164
+ isEditing ? (
165
+ <span
166
+ key={i}
167
+ className="text-sm hover:opacity-80"
168
+ style={{ color: txtColor }}
169
+ >
170
+ {link.label}
171
+ </span>
172
+ ) : (
173
+ <a
174
+ key={i}
175
+ href={link.href}
176
+ className="text-sm hover:opacity-80 transition-opacity"
177
+ style={{ color: txtColor }}
178
+ >
179
+ {link.label}
180
+ </a>
181
+ )
182
+ )}
183
+ </nav>
184
+ </div>
185
+ )}
186
+ </div>
187
+
188
+ {/* Copyright */}
189
+ <div
190
+ className="mt-10 pt-8 text-center text-sm"
191
+ style={{
192
+ borderTopColor: `${txtColor}33`,
193
+ borderTopWidth: "1px",
194
+ color: txtColor,
195
+ }}
196
+ >
197
+ {copyrightComp ? (
198
+ <ComponentRenderer
199
+ instance={toComponentInstance(copyrightComp)}
200
+ sectionId={section.id}
201
+ isEditing={isEditing}
202
+ />
203
+ ) : (
204
+ <p>
205
+ &copy; {new Date().getFullYear()} {String(copyrightText)}
206
+ </p>
207
+ )}
208
+ </div>
209
+ </div>
210
+ </footer>
211
+ );
212
+ }
213
+
214
+ export default FooterDefault;