@onexapis/cli 1.1.37 → 1.1.38

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 (48) hide show
  1. package/dist/cli.js +688 -454
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +681 -449
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/index.js +343 -256
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +339 -253
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/preview/preview-app.tsx +5 -13
  10. package/package.json +8 -4
  11. package/templates/default/bundle-entry.ts +0 -5
  12. package/templates/default/index.ts +1 -21
  13. package/templates/default/sections-registry.ts +0 -28
  14. package/templates/default/theme.layout.ts +2 -53
  15. package/templates/default/AUTH_AND_PROFILE.md +0 -167
  16. package/templates/default/LAYOUT.md +0 -195
  17. package/templates/default/hooks/index.ts +0 -26
  18. package/templates/default/hooks/use-forgot-password-form.ts +0 -90
  19. package/templates/default/hooks/use-login-form.ts +0 -102
  20. package/templates/default/hooks/use-profile-form.ts +0 -255
  21. package/templates/default/hooks/use-register-form.ts +0 -154
  22. package/templates/default/hooks/use-verify-code-form.ts +0 -224
  23. package/templates/default/pages/forgot-password.ts +0 -41
  24. package/templates/default/pages/login.ts +0 -41
  25. package/templates/default/pages/profile.ts +0 -39
  26. package/templates/default/pages/register.ts +0 -41
  27. package/templates/default/pages/verify-code.ts +0 -41
  28. package/templates/default/sections/auth-forgot-password/auth-forgot-password-default.tsx +0 -192
  29. package/templates/default/sections/auth-forgot-password/auth-forgot-password.schema.ts +0 -150
  30. package/templates/default/sections/auth-forgot-password/index.ts +0 -14
  31. package/templates/default/sections/auth-login/auth-login-default.tsx +0 -238
  32. package/templates/default/sections/auth-login/auth-login.schema.ts +0 -171
  33. package/templates/default/sections/auth-login/index.ts +0 -14
  34. package/templates/default/sections/auth-register/auth-register-default.tsx +0 -327
  35. package/templates/default/sections/auth-register/auth-register.schema.ts +0 -188
  36. package/templates/default/sections/auth-register/index.ts +0 -14
  37. package/templates/default/sections/auth-verify-code/auth-verify-code-default.tsx +0 -209
  38. package/templates/default/sections/auth-verify-code/auth-verify-code.schema.ts +0 -150
  39. package/templates/default/sections/auth-verify-code/index.ts +0 -14
  40. package/templates/default/sections/footer/footer-default.tsx +0 -214
  41. package/templates/default/sections/footer/footer.schema.ts +0 -170
  42. package/templates/default/sections/footer/index.ts +0 -14
  43. package/templates/default/sections/header/header-default.tsx +0 -322
  44. package/templates/default/sections/header/header.schema.ts +0 -168
  45. package/templates/default/sections/header/index.ts +0 -14
  46. package/templates/default/sections/profile/index.ts +0 -14
  47. package/templates/default/sections/profile/profile-default.tsx +0 -522
  48. package/templates/default/sections/profile/profile.schema.ts +0 -228
@@ -1,522 +0,0 @@
1
- /**
2
- * Profile - Default Template
3
- * User profile page with form fields and change password
4
- */
5
-
6
- "use client";
7
-
8
- import type { SectionComponentProps } from "@onexapis/core/types";
9
- import coreUtils from "@onexapis/core/utils";
10
- import { useProfileForm } from "../../hooks/use-profile-form";
11
-
12
- const { getSectionValues } = coreUtils;
13
-
14
- export function ProfileDefault({
15
- section,
16
- schema,
17
- isEditing,
18
- }: SectionComponentProps) {
19
- const { settings } = getSectionValues(section, schema);
20
- const {
21
- heading,
22
- nameLabel,
23
- namePlaceholder,
24
- emailLabel,
25
- phoneLabel,
26
- phonePlaceholder,
27
- addressLabel,
28
- addressPlaceholder,
29
- changePasswordText,
30
- updateButtonText,
31
- updatingText,
32
- currentPasswordLabel,
33
- currentPasswordPlaceholder,
34
- newPasswordLabel,
35
- newPasswordPlaceholder,
36
- confirmPasswordLabel,
37
- confirmPasswordPlaceholder,
38
- changePasswordButtonText,
39
- changingPasswordText,
40
- cancelText,
41
- passwordRequirementText,
42
- logoutText,
43
- backgroundColor,
44
- cardBackground,
45
- primaryColor,
46
- textColor,
47
- cardBorderRadius,
48
- } = settings;
49
-
50
- const profile = useProfileForm();
51
-
52
- const bgColor = String(backgroundColor || "#F9FAFB");
53
- const cardBg = String(cardBackground || "#FFFFFF");
54
- const btnColor = String(primaryColor || "#2563EB");
55
- const headingColor = String(textColor || "#111827");
56
- const radiusClass = `rounded-${String(cardBorderRadius || "2xl")}`;
57
-
58
- const { passwordErrors } = profile;
59
-
60
- // Loading skeleton
61
- if (profile.isLoading && !isEditing) {
62
- return (
63
- <section
64
- className="py-12"
65
- style={{ backgroundColor: bgColor }}
66
- data-section-id={section.id}
67
- data-section-type={section.type}
68
- data-section-template="default"
69
- >
70
- <div className="container mx-auto px-4 max-w-2xl">
71
- <div
72
- className={`shadow-lg p-8 animate-pulse ${radiusClass}`}
73
- style={{ backgroundColor: cardBg }}
74
- >
75
- <div className="h-8 bg-gray-200 rounded w-48 mb-8" />
76
- <div className="space-y-6">
77
- {[1, 2, 3, 4].map((i) => (
78
- <div key={i}>
79
- <div className="h-4 bg-gray-200 rounded w-24 mb-2" />
80
- <div className="h-11 bg-gray-200 rounded-lg" />
81
- </div>
82
- ))}
83
- </div>
84
- </div>
85
- </div>
86
- </section>
87
- );
88
- }
89
-
90
- // Not authenticated
91
- if (!profile.isAuthenticated && !isEditing) {
92
- return (
93
- <section
94
- className="py-12"
95
- style={{ backgroundColor: bgColor }}
96
- data-section-id={section.id}
97
- data-section-type={section.type}
98
- data-section-template="default"
99
- >
100
- <div className="container mx-auto px-4 max-w-2xl">
101
- <div
102
- className={`shadow-lg p-8 text-center ${radiusClass}`}
103
- style={{ backgroundColor: cardBg }}
104
- >
105
- <h2
106
- className="text-xl font-semibold mb-4"
107
- style={{ color: headingColor }}
108
- >
109
- Please sign in to view your profile
110
- </h2>
111
- <a
112
- href="/login"
113
- className="inline-block px-6 py-2.5 text-white text-sm font-medium rounded-lg hover:opacity-90 transition-colors"
114
- style={{ backgroundColor: btnColor }}
115
- >
116
- Sign In
117
- </a>
118
- </div>
119
- </div>
120
- </section>
121
- );
122
- }
123
-
124
- return (
125
- <section
126
- className="py-12"
127
- style={{ backgroundColor: bgColor }}
128
- data-section-id={section.id}
129
- data-section-type={section.type}
130
- data-section-template="default"
131
- >
132
- <div className="container mx-auto px-4 max-w-2xl">
133
- <div
134
- className={`shadow-lg overflow-hidden ${radiusClass}`}
135
- style={{ backgroundColor: cardBg }}
136
- >
137
- {/* Header */}
138
- <div className="flex items-center justify-between px-8 pt-8 pb-2">
139
- <h1 className="text-2xl font-bold" style={{ color: headingColor }}>
140
- {String(heading)}
141
- </h1>
142
- {!isEditing && (
143
- <button
144
- type="button"
145
- onClick={profile.handleLogout}
146
- className="text-sm text-gray-500 hover:text-red-600 transition-colors"
147
- >
148
- {String(logoutText)}
149
- </button>
150
- )}
151
- </div>
152
-
153
- {/* Profile Form */}
154
- <form onSubmit={profile.handleSubmit} className="px-8 py-6 space-y-5">
155
- {/* Error */}
156
- {profile.submitError && (
157
- <div className="rounded-lg bg-red-50 border border-red-200 p-3 text-sm text-red-600">
158
- {profile.submitError}
159
- </div>
160
- )}
161
-
162
- {/* Name */}
163
- <div className="flex flex-col gap-1.5">
164
- <label
165
- htmlFor="profile-name"
166
- className="text-sm font-medium text-gray-700"
167
- >
168
- {String(nameLabel)}
169
- </label>
170
- <input
171
- id="profile-name"
172
- type="text"
173
- value={profile.formData.name}
174
- onChange={(e) =>
175
- profile.handleFieldChange("name", e.target.value)
176
- }
177
- placeholder={String(namePlaceholder)}
178
- disabled={profile.isSubmitting || isEditing}
179
- className="h-11 w-full rounded-lg border border-gray-300 px-4 text-sm text-gray-900 placeholder:text-gray-400 outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
180
- />
181
- </div>
182
-
183
- {/* Email (read-only) */}
184
- <div className="flex flex-col gap-1.5">
185
- <label
186
- htmlFor="profile-email"
187
- className="text-sm font-medium text-gray-700"
188
- >
189
- {String(emailLabel)}
190
- </label>
191
- <input
192
- id="profile-email"
193
- type="text"
194
- value={profile.formData.email}
195
- disabled
196
- className="h-11 w-full rounded-lg border border-gray-200 bg-gray-50 px-4 text-sm text-gray-500 outline-none"
197
- />
198
- </div>
199
-
200
- {/* Phone */}
201
- <div className="flex flex-col gap-1.5">
202
- <label
203
- htmlFor="profile-phone"
204
- className="text-sm font-medium text-gray-700"
205
- >
206
- {String(phoneLabel)}
207
- </label>
208
- <input
209
- id="profile-phone"
210
- type="tel"
211
- value={profile.formData.phone}
212
- onChange={(e) =>
213
- profile.handleFieldChange("phone", e.target.value)
214
- }
215
- placeholder={String(phonePlaceholder)}
216
- disabled={profile.isSubmitting || isEditing}
217
- className="h-11 w-full rounded-lg border border-gray-300 px-4 text-sm text-gray-900 placeholder:text-gray-400 outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
218
- />
219
- </div>
220
-
221
- {/* Address */}
222
- <div className="flex flex-col gap-1.5">
223
- <label
224
- htmlFor="profile-address"
225
- className="text-sm font-medium text-gray-700"
226
- >
227
- {String(addressLabel)}
228
- </label>
229
- <input
230
- id="profile-address"
231
- type="text"
232
- value={profile.formData.address}
233
- onChange={(e) =>
234
- profile.handleFieldChange("address", e.target.value)
235
- }
236
- placeholder={String(addressPlaceholder)}
237
- disabled={profile.isSubmitting || isEditing}
238
- className="h-11 w-full rounded-lg border border-gray-300 px-4 text-sm text-gray-900 placeholder:text-gray-400 outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
239
- />
240
- </div>
241
- </form>
242
-
243
- {/* Update Button Footer */}
244
- <div className="flex justify-end border-t border-gray-100 bg-gray-50/50 px-8 py-4">
245
- <button
246
- type="button"
247
- onClick={profile.handleSubmit}
248
- disabled={profile.isSubmitting || !profile.isDirty || isEditing}
249
- className="px-6 py-2.5 rounded-lg text-sm font-medium text-white transition-colors hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
250
- style={{ backgroundColor: btnColor }}
251
- >
252
- {profile.isSubmitting && (
253
- <svg
254
- className="h-4 w-4 animate-spin"
255
- fill="none"
256
- viewBox="0 0 24 24"
257
- >
258
- <circle
259
- className="opacity-25"
260
- cx="12"
261
- cy="12"
262
- r="10"
263
- stroke="currentColor"
264
- strokeWidth="4"
265
- />
266
- <path
267
- className="opacity-75"
268
- fill="currentColor"
269
- 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"
270
- />
271
- </svg>
272
- )}
273
- {profile.isSubmitting
274
- ? String(updatingText)
275
- : String(updateButtonText)}
276
- </button>
277
- </div>
278
-
279
- {/* Change Password Section */}
280
- <div className="border-t border-gray-100 px-8 py-6">
281
- {!profile.showPasswordForm ? (
282
- <button
283
- type="button"
284
- onClick={() => profile.setShowPasswordForm(true)}
285
- disabled={isEditing}
286
- className="text-sm font-medium hover:opacity-80 transition-colors disabled:opacity-50"
287
- style={{ color: btnColor }}
288
- >
289
- {String(changePasswordText)}
290
- </button>
291
- ) : (
292
- <div className="space-y-4">
293
- <h3
294
- className="text-base font-semibold"
295
- style={{ color: headingColor }}
296
- >
297
- {String(changePasswordText)}
298
- </h3>
299
-
300
- {passwordErrors.form && (
301
- <div className="rounded-lg bg-red-50 border border-red-200 p-3 text-sm text-red-600">
302
- {passwordErrors.form}
303
- </div>
304
- )}
305
-
306
- {/* Current Password */}
307
- <div className="flex flex-col gap-1.5">
308
- <label
309
- htmlFor="current-password"
310
- className="text-sm font-medium text-gray-700"
311
- >
312
- {String(currentPasswordLabel)}{" "}
313
- <span className="text-red-500">*</span>
314
- </label>
315
- <div className="relative">
316
- <input
317
- id="current-password"
318
- type={profile.showCurrentPassword ? "text" : "password"}
319
- value={profile.passwordData.currentPassword}
320
- onChange={(e) =>
321
- profile.handlePasswordFieldChange(
322
- "currentPassword",
323
- e.target.value
324
- )
325
- }
326
- placeholder={String(currentPasswordPlaceholder)}
327
- disabled={profile.isChangingPassword}
328
- className={`h-11 w-full rounded-lg border px-4 pr-10 text-sm text-gray-900 placeholder:text-gray-400 outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:opacity-50 ${
329
- passwordErrors.currentPassword
330
- ? "border-red-400"
331
- : "border-gray-300"
332
- }`}
333
- />
334
- <button
335
- type="button"
336
- onClick={profile.toggleCurrentPassword}
337
- className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
338
- tabIndex={-1}
339
- >
340
- <EyeIcon open={profile.showCurrentPassword} />
341
- </button>
342
- </div>
343
- {passwordErrors.currentPassword && (
344
- <p className="text-xs text-red-500">
345
- {passwordErrors.currentPassword}
346
- </p>
347
- )}
348
- </div>
349
-
350
- {/* New Password */}
351
- <div className="flex flex-col gap-1.5">
352
- <label
353
- htmlFor="new-password"
354
- className="text-sm font-medium text-gray-700"
355
- >
356
- {String(newPasswordLabel)}{" "}
357
- <span className="text-red-500">*</span>
358
- </label>
359
- <div className="relative">
360
- <input
361
- id="new-password"
362
- type={profile.showNewPassword ? "text" : "password"}
363
- value={profile.passwordData.newPassword}
364
- onChange={(e) =>
365
- profile.handlePasswordFieldChange(
366
- "newPassword",
367
- e.target.value
368
- )
369
- }
370
- placeholder={String(newPasswordPlaceholder)}
371
- disabled={profile.isChangingPassword}
372
- className={`h-11 w-full rounded-lg border px-4 pr-10 text-sm text-gray-900 placeholder:text-gray-400 outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:opacity-50 ${
373
- passwordErrors.newPassword
374
- ? "border-red-400"
375
- : "border-gray-300"
376
- }`}
377
- />
378
- <button
379
- type="button"
380
- onClick={profile.toggleNewPassword}
381
- className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
382
- tabIndex={-1}
383
- >
384
- <EyeIcon open={profile.showNewPassword} />
385
- </button>
386
- </div>
387
- {passwordErrors.newPassword ? (
388
- <p className="text-xs text-red-500">
389
- {passwordErrors.newPassword}
390
- </p>
391
- ) : (
392
- <p className="text-xs text-gray-400">
393
- {String(passwordRequirementText)}
394
- </p>
395
- )}
396
- </div>
397
-
398
- {/* Confirm Password */}
399
- <div className="flex flex-col gap-1.5">
400
- <label
401
- htmlFor="confirm-new-password"
402
- className="text-sm font-medium text-gray-700"
403
- >
404
- {String(confirmPasswordLabel)}{" "}
405
- <span className="text-red-500">*</span>
406
- </label>
407
- <div className="relative">
408
- <input
409
- id="confirm-new-password"
410
- type={profile.showConfirmPassword ? "text" : "password"}
411
- value={profile.passwordData.confirmPassword}
412
- onChange={(e) =>
413
- profile.handlePasswordFieldChange(
414
- "confirmPassword",
415
- e.target.value
416
- )
417
- }
418
- placeholder={String(confirmPasswordPlaceholder)}
419
- disabled={profile.isChangingPassword}
420
- className={`h-11 w-full rounded-lg border px-4 pr-10 text-sm text-gray-900 placeholder:text-gray-400 outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:opacity-50 ${
421
- passwordErrors.confirmPassword
422
- ? "border-red-400"
423
- : "border-gray-300"
424
- }`}
425
- />
426
- <button
427
- type="button"
428
- onClick={profile.toggleConfirmPassword}
429
- className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
430
- tabIndex={-1}
431
- >
432
- <EyeIcon open={profile.showConfirmPassword} />
433
- </button>
434
- </div>
435
- {passwordErrors.confirmPassword && (
436
- <p className="text-xs text-red-500">
437
- {passwordErrors.confirmPassword}
438
- </p>
439
- )}
440
- </div>
441
-
442
- {/* Buttons */}
443
- <div className="flex items-center gap-3 pt-1">
444
- <button
445
- type="button"
446
- onClick={profile.handlePasswordSubmit}
447
- disabled={profile.isChangingPassword}
448
- className="px-5 py-2.5 rounded-lg text-sm font-medium text-white transition-colors hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
449
- style={{ backgroundColor: btnColor }}
450
- >
451
- {profile.isChangingPassword && (
452
- <svg
453
- className="h-4 w-4 animate-spin"
454
- fill="none"
455
- viewBox="0 0 24 24"
456
- >
457
- <circle
458
- className="opacity-25"
459
- cx="12"
460
- cy="12"
461
- r="10"
462
- stroke="currentColor"
463
- strokeWidth="4"
464
- />
465
- <path
466
- className="opacity-75"
467
- fill="currentColor"
468
- 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"
469
- />
470
- </svg>
471
- )}
472
- {profile.isChangingPassword
473
- ? String(changingPasswordText)
474
- : String(changePasswordButtonText)}
475
- </button>
476
- <button
477
- type="button"
478
- onClick={() => profile.setShowPasswordForm(false)}
479
- className="px-5 py-2.5 rounded-lg border border-gray-300 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50"
480
- >
481
- {String(cancelText)}
482
- </button>
483
- </div>
484
- </div>
485
- )}
486
- </div>
487
- </div>
488
- </div>
489
- </section>
490
- );
491
- }
492
-
493
- /** Inline eye icon to avoid lucide-react dependency */
494
- function EyeIcon({ open }: { open: boolean }) {
495
- if (open) {
496
- return (
497
- <svg
498
- className="h-4 w-4"
499
- fill="none"
500
- viewBox="0 0 24 24"
501
- stroke="currentColor"
502
- strokeWidth={2}
503
- >
504
- <path d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
505
- </svg>
506
- );
507
- }
508
- return (
509
- <svg
510
- className="h-4 w-4"
511
- fill="none"
512
- viewBox="0 0 24 24"
513
- stroke="currentColor"
514
- strokeWidth={2}
515
- >
516
- <path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
517
- <path d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
518
- </svg>
519
- );
520
- }
521
-
522
- export default ProfileDefault;