hazo_auth 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 (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/components.json +22 -0
  4. package/hazo_auth_config.example.ini +414 -0
  5. package/hazo_notify_config.example.ini +159 -0
  6. package/instrumentation.ts +32 -0
  7. package/migrations/001_add_token_type_to_refresh_tokens.sql +14 -0
  8. package/migrations/002_add_name_to_hazo_users.sql +7 -0
  9. package/next.config.mjs +55 -0
  10. package/package.json +114 -0
  11. package/postcss.config.mjs +8 -0
  12. package/public/file.svg +1 -0
  13. package/public/globe.svg +1 -0
  14. package/public/next.svg +1 -0
  15. package/public/vercel.svg +1 -0
  16. package/public/window.svg +1 -0
  17. package/scripts/apply_migration.ts +118 -0
  18. package/src/app/api/auth/change_password/route.ts +109 -0
  19. package/src/app/api/auth/forgot_password/route.ts +107 -0
  20. package/src/app/api/auth/library_photos/route.ts +70 -0
  21. package/src/app/api/auth/login/route.ts +155 -0
  22. package/src/app/api/auth/logout/route.ts +62 -0
  23. package/src/app/api/auth/me/route.ts +47 -0
  24. package/src/app/api/auth/profile_picture/[filename]/route.ts +67 -0
  25. package/src/app/api/auth/register/route.ts +106 -0
  26. package/src/app/api/auth/remove_profile_picture/route.ts +86 -0
  27. package/src/app/api/auth/resend_verification/route.ts +107 -0
  28. package/src/app/api/auth/reset_password/route.ts +107 -0
  29. package/src/app/api/auth/update_user/route.ts +126 -0
  30. package/src/app/api/auth/upload_profile_picture/route.ts +268 -0
  31. package/src/app/api/auth/validate_reset_token/route.ts +80 -0
  32. package/src/app/api/auth/verify_email/route.ts +85 -0
  33. package/src/app/api/migrations/apply/route.ts +91 -0
  34. package/src/app/favicon.ico +0 -0
  35. package/src/app/fonts/GeistMonoVF.woff +0 -0
  36. package/src/app/fonts/GeistVF.woff +0 -0
  37. package/src/app/forgot_password/forgot_password_page_client.tsx +60 -0
  38. package/src/app/forgot_password/page.tsx +24 -0
  39. package/src/app/globals.css +89 -0
  40. package/src/app/hazo_connect/api/sqlite/data/route.ts +197 -0
  41. package/src/app/hazo_connect/api/sqlite/schema/route.ts +35 -0
  42. package/src/app/hazo_connect/api/sqlite/tables/route.ts +26 -0
  43. package/src/app/hazo_connect/sqlite_admin/page.tsx +51 -0
  44. package/src/app/hazo_connect/sqlite_admin/sqlite-admin-client.tsx +947 -0
  45. package/src/app/layout.tsx +43 -0
  46. package/src/app/login/login_page_client.tsx +71 -0
  47. package/src/app/login/page.tsx +26 -0
  48. package/src/app/my_settings/my_settings_page_client.tsx +120 -0
  49. package/src/app/my_settings/page.tsx +40 -0
  50. package/src/app/page.tsx +170 -0
  51. package/src/app/register/page.tsx +26 -0
  52. package/src/app/register/register_page_client.tsx +72 -0
  53. package/src/app/reset_password/page.tsx +29 -0
  54. package/src/app/reset_password/reset_password_page_client.tsx +81 -0
  55. package/src/app/verify_email/page.tsx +24 -0
  56. package/src/app/verify_email/verify_email_page_client.tsx +60 -0
  57. package/src/components/layouts/email_verification/config/email_verification_field_config.ts +86 -0
  58. package/src/components/layouts/email_verification/hooks/use_email_verification.ts +291 -0
  59. package/src/components/layouts/email_verification/index.tsx +297 -0
  60. package/src/components/layouts/forgot_password/config/forgot_password_field_config.ts +58 -0
  61. package/src/components/layouts/forgot_password/hooks/use_forgot_password_form.ts +179 -0
  62. package/src/components/layouts/forgot_password/index.tsx +168 -0
  63. package/src/components/layouts/login/config/login_field_config.ts +67 -0
  64. package/src/components/layouts/login/hooks/use_login_form.ts +281 -0
  65. package/src/components/layouts/login/index.tsx +224 -0
  66. package/src/components/layouts/my_settings/components/editable_field.tsx +177 -0
  67. package/src/components/layouts/my_settings/components/password_change_dialog.tsx +301 -0
  68. package/src/components/layouts/my_settings/components/profile_picture_dialog.tsx +385 -0
  69. package/src/components/layouts/my_settings/components/profile_picture_display.tsx +66 -0
  70. package/src/components/layouts/my_settings/components/profile_picture_gravatar_tab.tsx +143 -0
  71. package/src/components/layouts/my_settings/components/profile_picture_library_tab.tsx +282 -0
  72. package/src/components/layouts/my_settings/components/profile_picture_upload_tab.tsx +341 -0
  73. package/src/components/layouts/my_settings/config/my_settings_field_config.ts +61 -0
  74. package/src/components/layouts/my_settings/hooks/use_my_settings.ts +458 -0
  75. package/src/components/layouts/my_settings/index.tsx +351 -0
  76. package/src/components/layouts/register/config/register_field_config.ts +101 -0
  77. package/src/components/layouts/register/hooks/use_register_form.ts +272 -0
  78. package/src/components/layouts/register/index.tsx +208 -0
  79. package/src/components/layouts/reset_password/config/reset_password_field_config.ts +86 -0
  80. package/src/components/layouts/reset_password/hooks/use_reset_password_form.ts +276 -0
  81. package/src/components/layouts/reset_password/index.tsx +294 -0
  82. package/src/components/layouts/shared/components/already_logged_in_guard.tsx +95 -0
  83. package/src/components/layouts/shared/components/field_error_message.tsx +29 -0
  84. package/src/components/layouts/shared/components/form_action_buttons.tsx +64 -0
  85. package/src/components/layouts/shared/components/form_field_wrapper.tsx +44 -0
  86. package/src/components/layouts/shared/components/form_header.tsx +36 -0
  87. package/src/components/layouts/shared/components/logout_button.tsx +76 -0
  88. package/src/components/layouts/shared/components/password_field.tsx +72 -0
  89. package/src/components/layouts/shared/components/sidebar_layout_wrapper.tsx +264 -0
  90. package/src/components/layouts/shared/components/two_column_auth_layout.tsx +44 -0
  91. package/src/components/layouts/shared/components/unauthorized_guard.tsx +78 -0
  92. package/src/components/layouts/shared/components/visual_panel.tsx +41 -0
  93. package/src/components/layouts/shared/config/layout_customization.ts +95 -0
  94. package/src/components/layouts/shared/data/layout_data_client.ts +19 -0
  95. package/src/components/layouts/shared/hooks/use_auth_status.ts +103 -0
  96. package/src/components/layouts/shared/utils/ip_address.ts +37 -0
  97. package/src/components/layouts/shared/utils/validation.ts +66 -0
  98. package/src/components/ui/avatar.tsx +50 -0
  99. package/src/components/ui/button.tsx +57 -0
  100. package/src/components/ui/dialog.tsx +122 -0
  101. package/src/components/ui/hazo_ui_tooltip.tsx +67 -0
  102. package/src/components/ui/input.tsx +22 -0
  103. package/src/components/ui/label.tsx +26 -0
  104. package/src/components/ui/separator.tsx +31 -0
  105. package/src/components/ui/sheet.tsx +139 -0
  106. package/src/components/ui/sidebar.tsx +773 -0
  107. package/src/components/ui/skeleton.tsx +15 -0
  108. package/src/components/ui/sonner.tsx +31 -0
  109. package/src/components/ui/switch.tsx +29 -0
  110. package/src/components/ui/tabs.tsx +55 -0
  111. package/src/components/ui/tooltip.tsx +32 -0
  112. package/src/components/ui/vertical-tabs.tsx +59 -0
  113. package/src/hooks/use-mobile.tsx +19 -0
  114. package/src/lib/already_logged_in_config.server.ts +46 -0
  115. package/src/lib/app_logger.ts +24 -0
  116. package/src/lib/auth/auth_utils.server.ts +196 -0
  117. package/src/lib/auth/server_auth.ts +88 -0
  118. package/src/lib/config/config_loader.server.ts +149 -0
  119. package/src/lib/email_verification_config.server.ts +32 -0
  120. package/src/lib/file_types_config.server.ts +25 -0
  121. package/src/lib/forgot_password_config.server.ts +32 -0
  122. package/src/lib/hazo_connect_instance.server.ts +77 -0
  123. package/src/lib/hazo_connect_setup.server.ts +181 -0
  124. package/src/lib/hazo_connect_setup.ts +54 -0
  125. package/src/lib/login_config.server.ts +46 -0
  126. package/src/lib/messages_config.server.ts +45 -0
  127. package/src/lib/migrations/apply_migration.ts +105 -0
  128. package/src/lib/my_settings_config.server.ts +135 -0
  129. package/src/lib/password_requirements_config.server.ts +39 -0
  130. package/src/lib/profile_picture_config.server.ts +56 -0
  131. package/src/lib/register_config.server.ts +57 -0
  132. package/src/lib/reset_password_config.server.ts +75 -0
  133. package/src/lib/services/email_service.ts +581 -0
  134. package/src/lib/services/email_verification_service.ts +264 -0
  135. package/src/lib/services/login_service.ts +118 -0
  136. package/src/lib/services/password_change_service.ts +154 -0
  137. package/src/lib/services/password_reset_service.ts +405 -0
  138. package/src/lib/services/profile_picture_remove_service.ts +120 -0
  139. package/src/lib/services/profile_picture_service.ts +215 -0
  140. package/src/lib/services/profile_picture_source_mapper.ts +62 -0
  141. package/src/lib/services/registration_service.ts +163 -0
  142. package/src/lib/services/token_service.ts +240 -0
  143. package/src/lib/services/user_update_service.ts +128 -0
  144. package/src/lib/ui_sizes_config.server.ts +37 -0
  145. package/src/lib/user_fields_config.server.ts +31 -0
  146. package/src/lib/utils/api_route_helpers.ts +60 -0
  147. package/src/lib/utils.ts +11 -0
  148. package/src/middleware.ts +91 -0
  149. package/src/server/config/config_loader.ts +496 -0
  150. package/src/server/index.ts +38 -0
  151. package/src/server/logging/logger_service.ts +56 -0
  152. package/src/server/routes/root_router.ts +16 -0
  153. package/src/server/server.ts +28 -0
  154. package/src/server/types/app_types.ts +74 -0
  155. package/src/server/types/express.d.ts +15 -0
  156. package/src/stories/email_verification_layout.stories.tsx +137 -0
  157. package/src/stories/forgot_password_layout.stories.tsx +85 -0
  158. package/src/stories/login_layout.stories.tsx +85 -0
  159. package/src/stories/project_overview.stories.tsx +33 -0
  160. package/src/stories/register_layout.stories.tsx +107 -0
  161. package/tailwind.config.ts +77 -0
  162. package/tsconfig.json +27 -0
@@ -0,0 +1,43 @@
1
+ // file_description: define the root layout wrapper for the ui_component project
2
+ import type { Metadata } from "next";
3
+ import local_font from "next/font/local";
4
+ import { Toaster } from "@/components/ui/sonner";
5
+ import "./globals.css";
6
+
7
+ // section: typography_setup
8
+ const geist_sans = local_font({
9
+ src: "./fonts/GeistVF.woff",
10
+ variable: "--font-geist-sans",
11
+ weight: "100 900",
12
+ });
13
+
14
+ const geist_mono = local_font({
15
+ src: "./fonts/GeistMonoVF.woff",
16
+ variable: "--font-geist-mono",
17
+ weight: "100 900",
18
+ });
19
+
20
+ // section: metadata_definition
21
+ export const metadata: Metadata = {
22
+ title: "hazo reusable ui components",
23
+ description:
24
+ "Storybook-ready Next.js workspace for building reusable components powered by shadcn.",
25
+ };
26
+
27
+ // section: root_layout_component
28
+ export default function root_layout({
29
+ children,
30
+ }: Readonly<{
31
+ children: React.ReactNode;
32
+ }>) {
33
+ return (
34
+ <html lang="en">
35
+ <body
36
+ className={`${geist_sans.variable} ${geist_mono.variable} antialiased`}
37
+ >
38
+ {children}
39
+ <Toaster />
40
+ </body>
41
+ </html>
42
+ );
43
+ }
@@ -0,0 +1,71 @@
1
+ // file_description: client component for login page that initializes hazo_connect and renders login layout
2
+ // section: client_directive
3
+ "use client";
4
+
5
+ // section: imports
6
+ import { useEffect, useState } from "react";
7
+ import login_layout from "@/components/layouts/login";
8
+ import { createLayoutDataClient } from "@/components/layouts/shared/data/layout_data_client";
9
+ import { create_sqlite_hazo_connect } from "@/lib/hazo_connect_setup";
10
+ import { create_app_logger } from "@/lib/app_logger";
11
+ import type { LayoutDataClient } from "@/components/layouts/shared/data/layout_data_client";
12
+
13
+ // section: types
14
+ type LoginPageClientProps = {
15
+ redirectRoute?: string;
16
+ successMessage?: string;
17
+ alreadyLoggedInMessage?: string;
18
+ showLogoutButton?: boolean;
19
+ showReturnHomeButton?: boolean;
20
+ returnHomeButtonLabel?: string;
21
+ returnHomePath?: string;
22
+ };
23
+
24
+ // section: component
25
+ export function LoginPageClient({
26
+ redirectRoute,
27
+ successMessage,
28
+ alreadyLoggedInMessage,
29
+ showLogoutButton,
30
+ showReturnHomeButton,
31
+ returnHomeButtonLabel,
32
+ returnHomePath,
33
+ }: LoginPageClientProps) {
34
+ const [dataClient, setDataClient] = useState<LayoutDataClient<unknown> | null>(null);
35
+ const [logger, setLogger] = useState<ReturnType<typeof create_app_logger> | null>(null);
36
+
37
+ useEffect(() => {
38
+ // Initialize hazo_connect and logger on client side
39
+ const hazoConnect = create_sqlite_hazo_connect();
40
+ const client = createLayoutDataClient(hazoConnect);
41
+ const appLogger = create_app_logger();
42
+
43
+ setDataClient(client);
44
+ setLogger(appLogger);
45
+ }, []);
46
+
47
+ // Show loading state while initializing
48
+ if (!dataClient || !logger) {
49
+ return <div className="cls_login_page_loading text-slate-600">Loading...</div>;
50
+ }
51
+
52
+ const LoginLayout = login_layout;
53
+
54
+ return (
55
+ <LoginLayout
56
+ image_src="/globe.svg"
57
+ image_alt="Illustration of a globe representing secure authentication workflows"
58
+ image_background_color="#e2e8f0"
59
+ data_client={dataClient}
60
+ logger={logger}
61
+ redirectRoute={redirectRoute}
62
+ successMessage={successMessage}
63
+ alreadyLoggedInMessage={alreadyLoggedInMessage}
64
+ showLogoutButton={showLogoutButton}
65
+ showReturnHomeButton={showReturnHomeButton}
66
+ returnHomeButtonLabel={returnHomeButtonLabel}
67
+ returnHomePath={returnHomePath}
68
+ />
69
+ );
70
+ }
71
+
@@ -0,0 +1,26 @@
1
+ // file_description: render the login page shell and mount the login layout component within sidebar
2
+ // section: imports
3
+ import { SidebarLayoutWrapper } from "@/components/layouts/shared/components/sidebar_layout_wrapper";
4
+ import { LoginPageClient } from "./login_page_client";
5
+ import { get_login_config } from "@/lib/login_config.server";
6
+
7
+ // section: component
8
+ export default function login_page() {
9
+ // Read login configuration from hazo_auth_config.ini (server-side)
10
+ const loginConfig = get_login_config();
11
+
12
+ return (
13
+ <SidebarLayoutWrapper>
14
+ <LoginPageClient
15
+ redirectRoute={loginConfig.redirectRoute}
16
+ successMessage={loginConfig.successMessage}
17
+ alreadyLoggedInMessage={loginConfig.alreadyLoggedInMessage}
18
+ showLogoutButton={loginConfig.showLogoutButton}
19
+ showReturnHomeButton={loginConfig.showReturnHomeButton}
20
+ returnHomeButtonLabel={loginConfig.returnHomeButtonLabel}
21
+ returnHomePath={loginConfig.returnHomePath}
22
+ />
23
+ </SidebarLayoutWrapper>
24
+ );
25
+ }
26
+
@@ -0,0 +1,120 @@
1
+ // file_description: client component for my settings page that initializes hazo_connect and renders my settings layout
2
+ // section: client_directive
3
+ "use client";
4
+
5
+ // section: imports
6
+ import { useEffect, useState } from "react";
7
+ import my_settings_layout from "@/components/layouts/my_settings";
8
+ import type { LayoutDataClient } from "@/components/layouts/shared/data/layout_data_client";
9
+ import type { PasswordRequirementOptions } from "@/components/layouts/shared/config/layout_customization";
10
+
11
+ // section: types
12
+ type MySettingsPageClientProps = {
13
+ userFields: {
14
+ show_name_field: boolean;
15
+ show_email_field: boolean;
16
+ show_password_field: boolean;
17
+ };
18
+ passwordRequirements: PasswordRequirementOptions;
19
+ profilePicture: {
20
+ allow_photo_upload: boolean;
21
+ upload_photo_path?: string;
22
+ max_photo_size: number;
23
+ user_photo_default: boolean;
24
+ user_photo_default_priority1: "gravatar" | "library";
25
+ user_photo_default_priority2?: "library" | "gravatar";
26
+ library_photo_path: string;
27
+ };
28
+ heading?: string;
29
+ subHeading?: string;
30
+ profilePhotoLabel?: string;
31
+ profilePhotoRecommendation?: string;
32
+ uploadPhotoButtonLabel?: string;
33
+ removePhotoButtonLabel?: string;
34
+ profileInformationLabel?: string;
35
+ passwordLabel?: string;
36
+ currentPasswordLabel?: string;
37
+ newPasswordLabel?: string;
38
+ confirmPasswordLabel?: string;
39
+ savePasswordButtonLabel?: string;
40
+ unauthorizedMessage?: string;
41
+ loginButtonLabel?: string;
42
+ loginPath?: string;
43
+ messages: {
44
+ photo_upload_disabled_message: string;
45
+ gravatar_setup_message: string;
46
+ gravatar_no_account_message: string;
47
+ library_tooltip_message: string;
48
+ };
49
+ uiSizes: {
50
+ gravatar_size: number;
51
+ profile_picture_size: number;
52
+ tooltip_icon_size_default: number;
53
+ tooltip_icon_size_small: number;
54
+ library_photo_grid_columns: number;
55
+ library_photo_preview_size: number;
56
+ image_compression_max_dimension: number;
57
+ upload_file_hard_limit_bytes: number;
58
+ };
59
+ fileTypes: {
60
+ allowed_image_extensions: string[];
61
+ allowed_image_mime_types: string[];
62
+ };
63
+ };
64
+
65
+ // section: component
66
+ export function MySettingsPageClient({
67
+ userFields,
68
+ passwordRequirements,
69
+ profilePicture,
70
+ heading,
71
+ subHeading,
72
+ profilePhotoLabel,
73
+ profilePhotoRecommendation,
74
+ uploadPhotoButtonLabel,
75
+ removePhotoButtonLabel,
76
+ profileInformationLabel,
77
+ passwordLabel,
78
+ currentPasswordLabel,
79
+ newPasswordLabel,
80
+ confirmPasswordLabel,
81
+ savePasswordButtonLabel,
82
+ unauthorizedMessage,
83
+ loginButtonLabel,
84
+ loginPath,
85
+ messages,
86
+ uiSizes,
87
+ fileTypes,
88
+ }: MySettingsPageClientProps) {
89
+ // Note: hazo_connect is not needed for this component as it's a self-service feature
90
+ // All data is fetched via API routes using cookies for authentication
91
+ // The layout component uses use_auth_status hook which calls /api/auth/me
92
+
93
+ const MySettingsLayout = my_settings_layout;
94
+
95
+ return (
96
+ <MySettingsLayout
97
+ userFields={userFields}
98
+ password_requirements={passwordRequirements}
99
+ profilePicture={profilePicture}
100
+ heading={heading}
101
+ subHeading={subHeading}
102
+ profilePhotoLabel={profilePhotoLabel}
103
+ profilePhotoRecommendation={profilePhotoRecommendation}
104
+ uploadPhotoButtonLabel={uploadPhotoButtonLabel}
105
+ removePhotoButtonLabel={removePhotoButtonLabel}
106
+ profileInformationLabel={profileInformationLabel}
107
+ passwordLabel={passwordLabel}
108
+ currentPasswordLabel={currentPasswordLabel}
109
+ newPasswordLabel={newPasswordLabel}
110
+ confirmPasswordLabel={confirmPasswordLabel}
111
+ unauthorizedMessage={unauthorizedMessage}
112
+ loginButtonLabel={loginButtonLabel}
113
+ loginPath={loginPath}
114
+ messages={messages}
115
+ uiSizes={uiSizes}
116
+ fileTypes={fileTypes}
117
+ />
118
+ );
119
+ }
120
+
@@ -0,0 +1,40 @@
1
+ // file_description: render the my settings page shell and mount the my settings layout component within sidebar
2
+ // section: imports
3
+ import { SidebarLayoutWrapper } from "@/components/layouts/shared/components/sidebar_layout_wrapper";
4
+ import { MySettingsPageClient } from "./my_settings_page_client";
5
+ import { get_my_settings_config } from "@/lib/my_settings_config.server";
6
+
7
+ // section: component
8
+ export default function my_settings_page() {
9
+ // Read my settings configuration from hazo_auth_config.ini (server-side)
10
+ const mySettingsConfig = get_my_settings_config();
11
+
12
+ return (
13
+ <SidebarLayoutWrapper>
14
+ <MySettingsPageClient
15
+ userFields={mySettingsConfig.userFields}
16
+ passwordRequirements={mySettingsConfig.passwordRequirements}
17
+ profilePicture={mySettingsConfig.profilePicture}
18
+ heading={mySettingsConfig.heading}
19
+ subHeading={mySettingsConfig.subHeading}
20
+ profilePhotoLabel={mySettingsConfig.profilePhotoLabel}
21
+ profilePhotoRecommendation={mySettingsConfig.profilePhotoRecommendation}
22
+ uploadPhotoButtonLabel={mySettingsConfig.uploadPhotoButtonLabel}
23
+ removePhotoButtonLabel={mySettingsConfig.removePhotoButtonLabel}
24
+ profileInformationLabel={mySettingsConfig.profileInformationLabel}
25
+ passwordLabel={mySettingsConfig.passwordLabel}
26
+ currentPasswordLabel={mySettingsConfig.currentPasswordLabel}
27
+ newPasswordLabel={mySettingsConfig.newPasswordLabel}
28
+ confirmPasswordLabel={mySettingsConfig.confirmPasswordLabel}
29
+ savePasswordButtonLabel={mySettingsConfig.savePasswordButtonLabel}
30
+ unauthorizedMessage={mySettingsConfig.unauthorizedMessage}
31
+ loginButtonLabel={mySettingsConfig.loginButtonLabel}
32
+ loginPath={mySettingsConfig.loginPath}
33
+ messages={mySettingsConfig.messages}
34
+ uiSizes={mySettingsConfig.uiSizes}
35
+ fileTypes={mySettingsConfig.fileTypes}
36
+ />
37
+ </SidebarLayoutWrapper>
38
+ );
39
+ }
40
+
@@ -0,0 +1,170 @@
1
+ // file_description: render the default landing page with sidebar navigation to test components
2
+ // section: client_directive
3
+ "use client";
4
+
5
+ // section: imports
6
+ import Link from "next/link";
7
+ import {
8
+ Sidebar,
9
+ SidebarContent,
10
+ SidebarGroup,
11
+ SidebarGroupLabel,
12
+ SidebarHeader,
13
+ SidebarMenu,
14
+ SidebarMenuButton,
15
+ SidebarMenuItem,
16
+ SidebarProvider,
17
+ SidebarTrigger,
18
+ SidebarInset,
19
+ } from "@/components/ui/sidebar";
20
+ import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck } from "lucide-react";
21
+
22
+ // section: page_component
23
+ export default function home_page() {
24
+ return (
25
+ <SidebarProvider>
26
+ <div className="cls_home_wrapper flex min-h-screen w-full">
27
+ <Sidebar>
28
+ <SidebarHeader className="cls_home_sidebar_header">
29
+ <div className="cls_home_sidebar_title flex items-center gap-2 px-2 py-4">
30
+ <h1 className="cls_home_sidebar_title_text text-lg font-semibold text-sidebar-foreground">
31
+ hazo auth
32
+ </h1>
33
+ </div>
34
+ </SidebarHeader>
35
+ <SidebarContent className="cls_home_sidebar_content">
36
+ <SidebarGroup className="cls_home_sidebar_test_group">
37
+ <SidebarGroupLabel className="cls_home_sidebar_group_label">
38
+ Test components
39
+ </SidebarGroupLabel>
40
+ <SidebarMenu className="cls_home_sidebar_test_menu">
41
+ <SidebarMenuItem className="cls_home_sidebar_test_login_item">
42
+ <SidebarMenuButton asChild>
43
+ <Link
44
+ href="/login"
45
+ className="cls_home_sidebar_test_login_link flex items-center gap-2"
46
+ aria-label="Test login layout component"
47
+ >
48
+ <LogIn className="h-4 w-4" aria-hidden="true" />
49
+ <span>Test login</span>
50
+ </Link>
51
+ </SidebarMenuButton>
52
+ </SidebarMenuItem>
53
+ <SidebarMenuItem className="cls_home_sidebar_test_register_item">
54
+ <SidebarMenuButton asChild>
55
+ <Link
56
+ href="/register"
57
+ className="cls_home_sidebar_test_register_link flex items-center gap-2"
58
+ aria-label="Test register layout component"
59
+ >
60
+ <UserPlus className="h-4 w-4" aria-hidden="true" />
61
+ <span>Test register</span>
62
+ </Link>
63
+ </SidebarMenuButton>
64
+ </SidebarMenuItem>
65
+ <SidebarMenuItem className="cls_home_sidebar_test_forgot_password_item">
66
+ <SidebarMenuButton asChild>
67
+ <Link
68
+ href="/forgot_password"
69
+ className="cls_home_sidebar_test_forgot_password_link flex items-center gap-2"
70
+ aria-label="Test forgot password layout component"
71
+ >
72
+ <KeyRound className="h-4 w-4" aria-hidden="true" />
73
+ <span>Test forgot password</span>
74
+ </Link>
75
+ </SidebarMenuButton>
76
+ </SidebarMenuItem>
77
+ <SidebarMenuItem className="cls_home_sidebar_test_email_verification_item">
78
+ <SidebarMenuButton asChild>
79
+ <Link
80
+ href="/verify_email"
81
+ className="cls_home_sidebar_test_email_verification_link flex items-center gap-2"
82
+ aria-label="Test email verification layout component"
83
+ >
84
+ <MailCheck className="h-4 w-4" aria-hidden="true" />
85
+ <span>Test email verification</span>
86
+ </Link>
87
+ </SidebarMenuButton>
88
+ </SidebarMenuItem>
89
+ <SidebarMenuItem className="cls_home_sidebar_sqlite_admin_item">
90
+ <SidebarMenuButton asChild>
91
+ <Link
92
+ href="/hazo_connect/sqlite_admin"
93
+ className="cls_home_sidebar_sqlite_admin_link flex items-center gap-2"
94
+ aria-label="Open SQLite admin UI to browse and edit database"
95
+ >
96
+ <Database className="h-4 w-4" aria-hidden="true" />
97
+ <span>SQLite Admin</span>
98
+ </Link>
99
+ </SidebarMenuButton>
100
+ </SidebarMenuItem>
101
+ </SidebarMenu>
102
+ </SidebarGroup>
103
+ <SidebarGroup className="cls_home_sidebar_resources_group">
104
+ <SidebarGroupLabel className="cls_home_sidebar_group_label">
105
+ Resources
106
+ </SidebarGroupLabel>
107
+ <SidebarMenu className="cls_home_sidebar_resources_menu">
108
+ <SidebarMenuItem className="cls_home_sidebar_storybook_item">
109
+ <SidebarMenuButton asChild>
110
+ <a
111
+ href="http://localhost:6006"
112
+ target="_blank"
113
+ rel="noopener noreferrer"
114
+ className="cls_home_sidebar_storybook_link flex items-center gap-2"
115
+ aria-label="Open Storybook preview for reusable components"
116
+ >
117
+ <BookOpen className="h-4 w-4" aria-hidden="true" />
118
+ <span>Storybook</span>
119
+ <ExternalLink className="ml-auto h-3 w-3" aria-hidden="true" />
120
+ </a>
121
+ </SidebarMenuButton>
122
+ </SidebarMenuItem>
123
+ <SidebarMenuItem className="cls_home_sidebar_docs_item">
124
+ <SidebarMenuButton asChild>
125
+ <a
126
+ href="https://ui.shadcn.com/docs"
127
+ target="_blank"
128
+ rel="noopener noreferrer"
129
+ className="cls_home_sidebar_docs_link flex items-center gap-2"
130
+ aria-label="Review shadcn documentation for styling guidance"
131
+ >
132
+ <BookOpen className="h-4 w-4" aria-hidden="true" />
133
+ <span>Shadcn docs</span>
134
+ <ExternalLink className="ml-auto h-3 w-3" aria-hidden="true" />
135
+ </a>
136
+ </SidebarMenuButton>
137
+ </SidebarMenuItem>
138
+ </SidebarMenu>
139
+ </SidebarGroup>
140
+ </SidebarContent>
141
+ </Sidebar>
142
+ <SidebarInset className="cls_home_sidebar_inset">
143
+ <header className="cls_home_main_header flex h-16 shrink-0 items-center gap-2 border-b px-4">
144
+ <SidebarTrigger className="cls_home_sidebar_trigger" />
145
+ <div className="cls_home_main_header_content flex flex-1 items-center gap-2">
146
+ <h2 className="cls_home_main_title text-lg font-semibold text-foreground">
147
+ hazo reusable ui library workspace
148
+ </h2>
149
+ </div>
150
+ </header>
151
+ <main className="cls_home_main_content flex flex-1 flex-col items-center justify-center gap-6 p-8 text-center">
152
+ <div className="cls_home_welcome_content flex w-full max-w-3xl flex-col items-center justify-center gap-6">
153
+ <h1 className="cls_home_welcome_title text-3xl font-semibold tracking-tight text-foreground md:text-4xl">
154
+ Welcome to hazo auth
155
+ </h1>
156
+ <p className="cls_home_welcome_summary text-base leading-relaxed text-muted-foreground md:text-lg">
157
+ Start shaping accessible, shadcn-powered components with integrated
158
+ hazo_config and hazo_connect support. Storybook is pre-installed so
159
+ you can document and iterate on each piece with confidence.
160
+ </p>
161
+ <p className="cls_home_welcome_instruction text-sm text-muted-foreground">
162
+ Use the sidebar to navigate to test components or access resources.
163
+ </p>
164
+ </div>
165
+ </main>
166
+ </SidebarInset>
167
+ </div>
168
+ </SidebarProvider>
169
+ );
170
+ }
@@ -0,0 +1,26 @@
1
+ // file_description: render the register page shell and mount the register layout component within sidebar
2
+ // section: imports
3
+ import { SidebarLayoutWrapper } from "@/components/layouts/shared/components/sidebar_layout_wrapper";
4
+ import { RegisterPageClient } from "./register_page_client";
5
+ import { get_register_config } from "@/lib/register_config.server";
6
+
7
+ // section: component
8
+ export default function register_page() {
9
+ // Read register configuration from hazo_auth_config.ini (server-side)
10
+ const registerConfig = get_register_config();
11
+
12
+ return (
13
+ <SidebarLayoutWrapper>
14
+ <RegisterPageClient
15
+ showNameField={registerConfig.showNameField}
16
+ passwordRequirements={registerConfig.passwordRequirements}
17
+ alreadyLoggedInMessage={registerConfig.alreadyLoggedInMessage}
18
+ showLogoutButton={registerConfig.showLogoutButton}
19
+ showReturnHomeButton={registerConfig.showReturnHomeButton}
20
+ returnHomeButtonLabel={registerConfig.returnHomeButtonLabel}
21
+ returnHomePath={registerConfig.returnHomePath}
22
+ />
23
+ </SidebarLayoutWrapper>
24
+ );
25
+ }
26
+
@@ -0,0 +1,72 @@
1
+ // file_description: client component for register page that initializes hazo_connect and renders register layout
2
+ // section: client_directive
3
+ "use client";
4
+
5
+ // section: imports
6
+ import { useEffect, useState } from "react";
7
+ import register_layout from "@/components/layouts/register";
8
+ import { createLayoutDataClient } from "@/components/layouts/shared/data/layout_data_client";
9
+ import { create_sqlite_hazo_connect } from "@/lib/hazo_connect_setup";
10
+ import type { LayoutDataClient } from "@/components/layouts/shared/data/layout_data_client";
11
+
12
+ // section: types
13
+ type RegisterPageClientProps = {
14
+ showNameField?: boolean;
15
+ passwordRequirements?: {
16
+ minimum_length: number;
17
+ require_uppercase: boolean;
18
+ require_lowercase: boolean;
19
+ require_number: boolean;
20
+ require_special: boolean;
21
+ };
22
+ alreadyLoggedInMessage?: string;
23
+ showLogoutButton?: boolean;
24
+ showReturnHomeButton?: boolean;
25
+ returnHomeButtonLabel?: string;
26
+ returnHomePath?: string;
27
+ };
28
+
29
+ // section: component
30
+ export function RegisterPageClient({
31
+ showNameField,
32
+ passwordRequirements,
33
+ alreadyLoggedInMessage,
34
+ showLogoutButton,
35
+ showReturnHomeButton,
36
+ returnHomeButtonLabel,
37
+ returnHomePath,
38
+ }: RegisterPageClientProps) {
39
+ const [dataClient, setDataClient] = useState<LayoutDataClient<unknown> | null>(null);
40
+
41
+ useEffect(() => {
42
+ // Initialize hazo_connect on client side
43
+ const hazoConnect = create_sqlite_hazo_connect();
44
+ const client = createLayoutDataClient(hazoConnect);
45
+
46
+ setDataClient(client);
47
+ }, []);
48
+
49
+ // Show loading state while initializing
50
+ if (!dataClient) {
51
+ return <div className="cls_register_page_loading text-slate-600">Loading...</div>;
52
+ }
53
+
54
+ const RegisterLayout = register_layout;
55
+
56
+ return (
57
+ <RegisterLayout
58
+ image_src="/globe.svg"
59
+ image_alt="Illustration of a globe representing secure authentication workflows"
60
+ image_background_color="#e2e8f0"
61
+ password_requirements={passwordRequirements}
62
+ show_name_field={showNameField}
63
+ data_client={dataClient}
64
+ alreadyLoggedInMessage={alreadyLoggedInMessage}
65
+ showLogoutButton={showLogoutButton}
66
+ showReturnHomeButton={showReturnHomeButton}
67
+ returnHomeButtonLabel={returnHomeButtonLabel}
68
+ returnHomePath={returnHomePath}
69
+ />
70
+ );
71
+ }
72
+
@@ -0,0 +1,29 @@
1
+ // file_description: render the reset password page shell and mount the reset password layout component within sidebar
2
+ // section: imports
3
+ import { SidebarLayoutWrapper } from "@/components/layouts/shared/components/sidebar_layout_wrapper";
4
+ import { ResetPasswordPageClient } from "./reset_password_page_client";
5
+ import { get_reset_password_config } from "@/lib/reset_password_config.server";
6
+
7
+ // section: component
8
+ export default function reset_password_page() {
9
+ // Read reset password configuration from hazo_auth_config.ini (server-side)
10
+ const resetPasswordConfig = get_reset_password_config();
11
+
12
+ return (
13
+ <SidebarLayoutWrapper>
14
+ <ResetPasswordPageClient
15
+ errorMessage={resetPasswordConfig.errorMessage}
16
+ successMessage={resetPasswordConfig.successMessage}
17
+ loginPath={resetPasswordConfig.loginPath}
18
+ forgotPasswordPath={resetPasswordConfig.forgotPasswordPath}
19
+ alreadyLoggedInMessage={resetPasswordConfig.alreadyLoggedInMessage}
20
+ showLogoutButton={resetPasswordConfig.showLogoutButton}
21
+ showReturnHomeButton={resetPasswordConfig.showReturnHomeButton}
22
+ returnHomeButtonLabel={resetPasswordConfig.returnHomeButtonLabel}
23
+ returnHomePath={resetPasswordConfig.returnHomePath}
24
+ passwordRequirements={resetPasswordConfig.passwordRequirements}
25
+ />
26
+ </SidebarLayoutWrapper>
27
+ );
28
+ }
29
+