@htlkg/astro 0.0.1

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 (70) hide show
  1. package/README.md +265 -0
  2. package/dist/chunk-33R4URZV.js +59 -0
  3. package/dist/chunk-33R4URZV.js.map +1 -0
  4. package/dist/chunk-64USRLVP.js +85 -0
  5. package/dist/chunk-64USRLVP.js.map +1 -0
  6. package/dist/chunk-WLOFOVCL.js +210 -0
  7. package/dist/chunk-WLOFOVCL.js.map +1 -0
  8. package/dist/chunk-WNMPTDCR.js +73 -0
  9. package/dist/chunk-WNMPTDCR.js.map +1 -0
  10. package/dist/chunk-Z2ZAL7KX.js +9 -0
  11. package/dist/chunk-Z2ZAL7KX.js.map +1 -0
  12. package/dist/chunk-ZQ4XMJH7.js +1 -0
  13. package/dist/chunk-ZQ4XMJH7.js.map +1 -0
  14. package/dist/htlkg/config.js +7 -0
  15. package/dist/htlkg/config.js.map +1 -0
  16. package/dist/htlkg/index.js +7 -0
  17. package/dist/htlkg/index.js.map +1 -0
  18. package/dist/index.js +64 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/middleware/index.js +168 -0
  21. package/dist/middleware/index.js.map +1 -0
  22. package/dist/utils/hydration.js +21 -0
  23. package/dist/utils/hydration.js.map +1 -0
  24. package/dist/utils/index.js +56 -0
  25. package/dist/utils/index.js.map +1 -0
  26. package/dist/utils/ssr.js +21 -0
  27. package/dist/utils/ssr.js.map +1 -0
  28. package/dist/utils/static.js +19 -0
  29. package/dist/utils/static.js.map +1 -0
  30. package/package.json +53 -0
  31. package/src/__mocks__/astro-middleware.ts +19 -0
  32. package/src/__mocks__/virtual-htlkg-config.ts +14 -0
  33. package/src/auth/LoginForm.vue +482 -0
  34. package/src/auth/LoginPage.astro +70 -0
  35. package/src/components/PageHeader.astro +145 -0
  36. package/src/components/Sidebar.astro +157 -0
  37. package/src/components/Topbar.astro +167 -0
  38. package/src/components/index.ts +9 -0
  39. package/src/htlkg/config.test.ts +165 -0
  40. package/src/htlkg/config.ts +242 -0
  41. package/src/htlkg/index.ts +245 -0
  42. package/src/htlkg/virtual-modules.test.ts +158 -0
  43. package/src/htlkg/virtual-modules.ts +81 -0
  44. package/src/index.ts +37 -0
  45. package/src/layouts/AdminLayout.astro +184 -0
  46. package/src/layouts/AuthLayout.astro +164 -0
  47. package/src/layouts/BrandLayout.astro +309 -0
  48. package/src/layouts/DefaultLayout.astro +25 -0
  49. package/src/layouts/PublicLayout.astro +153 -0
  50. package/src/layouts/index.ts +10 -0
  51. package/src/middleware/auth.ts +53 -0
  52. package/src/middleware/index.ts +31 -0
  53. package/src/middleware/route-guards.test.ts +182 -0
  54. package/src/middleware/route-guards.ts +218 -0
  55. package/src/patterns/admin/DetailPage.astro +195 -0
  56. package/src/patterns/admin/FormPage.astro +203 -0
  57. package/src/patterns/admin/ListPage.astro +178 -0
  58. package/src/patterns/admin/index.ts +9 -0
  59. package/src/patterns/brand/ConfigPage.astro +128 -0
  60. package/src/patterns/brand/PortalPage.astro +161 -0
  61. package/src/patterns/brand/index.ts +8 -0
  62. package/src/patterns/index.ts +8 -0
  63. package/src/utils/hydration.test.ts +154 -0
  64. package/src/utils/hydration.ts +151 -0
  65. package/src/utils/index.ts +9 -0
  66. package/src/utils/ssr.test.ts +235 -0
  67. package/src/utils/ssr.ts +139 -0
  68. package/src/utils/static.test.ts +144 -0
  69. package/src/utils/static.ts +144 -0
  70. package/src/vue-app-setup.ts +88 -0
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Virtual module setup for htlkg integration
3
+ * Creates Vite virtual modules to pass configuration to middleware and pages
4
+ */
5
+
6
+ import type { RouteGuardConfig, LoginPageConfig, RoutePattern } from './config.js';
7
+
8
+ /**
9
+ * Serialize a route pattern (RegExp or string) to JavaScript code
10
+ */
11
+ function serializePattern(pattern: RegExp | string): string {
12
+ if (pattern instanceof RegExp) {
13
+ return `new RegExp(${JSON.stringify(pattern.source)}, ${JSON.stringify(pattern.flags)})`;
14
+ }
15
+ return JSON.stringify(pattern);
16
+ }
17
+
18
+ /**
19
+ * Serialize an array of route patterns to JavaScript code
20
+ */
21
+ function serializePatterns(patterns: RoutePattern[]): string {
22
+ if (!patterns || patterns.length === 0) return '[]';
23
+ return `[${patterns.map(serializePattern).join(', ')}]`;
24
+ }
25
+
26
+ /**
27
+ * Create the virtual module plugin for Vite
28
+ * This plugin provides configuration to middleware and pages at runtime
29
+ */
30
+ export function createVirtualModulePlugin(
31
+ authConfig: RouteGuardConfig,
32
+ loginPageConfig: LoginPageConfig | false | null,
33
+ amplifyConfig: Record<string, unknown> | null
34
+ ) {
35
+ return {
36
+ name: 'htlkg-config',
37
+ resolveId(id: string) {
38
+ if (id === 'virtual:htlkg-config') {
39
+ return '\0virtual:htlkg-config';
40
+ }
41
+ },
42
+ load(id: string) {
43
+ if (id === '\0virtual:htlkg-config') {
44
+ // Serialize auth configuration with RegExp support
45
+ const serializedAuthConfig = `{
46
+ publicRoutes: ${serializePatterns(authConfig.publicRoutes || [])},
47
+ authenticatedRoutes: ${serializePatterns(authConfig.authenticatedRoutes || [])},
48
+ adminRoutes: ${serializePatterns(authConfig.adminRoutes || [])},
49
+ brandRoutes: ${JSON.stringify(authConfig.brandRoutes || [])},
50
+ loginUrl: ${JSON.stringify(authConfig.loginUrl || '/login')}
51
+ }`;
52
+
53
+ const serializedAmplifyConfig = amplifyConfig
54
+ ? JSON.stringify(amplifyConfig)
55
+ : 'null';
56
+
57
+ const serializedLoginPageConfig = loginPageConfig !== false && loginPageConfig !== null
58
+ ? JSON.stringify(loginPageConfig)
59
+ : 'null';
60
+
61
+ return `export const routeGuardConfig = ${serializedAuthConfig};
62
+ export const loginPageConfig = ${serializedLoginPageConfig};
63
+ export const amplifyConfig = ${serializedAmplifyConfig};`;
64
+ }
65
+ },
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Type definitions for the virtual module
71
+ * This should be injected into the project's type definitions
72
+ */
73
+ export const virtualModuleTypes = `
74
+ declare module 'virtual:htlkg-config' {
75
+ import type { RouteGuardConfig, LoginPageConfig } from '@htlkg/astro/htlkg/config';
76
+
77
+ export const routeGuardConfig: RouteGuardConfig;
78
+ export const loginPageConfig: LoginPageConfig | null;
79
+ export const amplifyConfig: Record<string, unknown> | null;
80
+ }
81
+ `;
package/src/index.ts ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @htlkg/astro
3
+ *
4
+ * Astro integration, layouts, page patterns, middleware, and utilities for Hotelinking applications.
5
+ *
6
+ * @module @htlkg/astro
7
+ */
8
+
9
+ // Main htlkg integration
10
+ export { htlkg } from './htlkg/index.js';
11
+
12
+ // Configuration types
13
+ export type {
14
+ HtlkgIntegrationOptions,
15
+ RouteGuardConfig,
16
+ LoginPageConfig,
17
+ BrandRouteConfig,
18
+ RoutePattern,
19
+ AuthUser,
20
+ } from './htlkg/config.js';
21
+
22
+ export { isAuthenticatedUser } from './htlkg/config.js';
23
+
24
+ // Layouts, Components, and Patterns are not exported from main index
25
+ // They contain .astro files which cannot be bundled
26
+ // Import them directly from their subpaths:
27
+ // - '@htlkg/astro/layouts'
28
+ // - '@htlkg/astro/components'
29
+ // - '@htlkg/astro/patterns/admin'
30
+ // - '@htlkg/astro/patterns/brand'
31
+
32
+ // Utils
33
+ export * from './utils';
34
+
35
+ // Middleware is not exported from main index to avoid bundling astro:middleware
36
+ // It's loaded by Astro at runtime via the entrypoint specified in addMiddleware
37
+ // Import from '@htlkg/astro/middleware' if needed
@@ -0,0 +1,184 @@
1
+ ---
2
+ /**
3
+ * Admin Layout
4
+ *
5
+ * Admin layout using uiWrapper from @hotelinking/ui with sidebar, topbar, and content area.
6
+ * Implements the same logic as the old AdminWrapper.vue but directly in the layout.
7
+ *
8
+ * This layout will be fully implemented in a future task.
9
+ * For now, it provides a basic structure.
10
+ */
11
+
12
+ interface User {
13
+ username?: string;
14
+ email?: string;
15
+ isAdmin?: boolean;
16
+ attributes?: {
17
+ email?: string;
18
+ };
19
+ }
20
+
21
+ interface BreadcrumbItem {
22
+ label: string;
23
+ routeName: string;
24
+ current?: boolean;
25
+ }
26
+
27
+ interface SidebarItem {
28
+ name: string;
29
+ routeName: string;
30
+ icon?: string;
31
+ id: string;
32
+ }
33
+
34
+ interface TopbarAction {
35
+ name: string;
36
+ event: string;
37
+ icon: string;
38
+ }
39
+
40
+ interface SelectItem {
41
+ name: string;
42
+ id: string;
43
+ }
44
+
45
+ interface Props {
46
+ title: string;
47
+ description?: string;
48
+ currentPage?: string;
49
+ user?: User;
50
+ showBreadcrumbs?: boolean;
51
+ breadcrumbs?: BreadcrumbItem[];
52
+ sidebarLogo?: string;
53
+ sidebarTitle?: string;
54
+ sidebarItems?: SidebarItem[];
55
+ topbarActions?: TopbarAction[];
56
+ selectItems?: SelectItem[];
57
+ selectedItem?: SelectItem;
58
+ sidebarOpenByDefault?: boolean;
59
+ }
60
+
61
+ const {
62
+ title,
63
+ description,
64
+ currentPage = "dashboard",
65
+ user,
66
+ showBreadcrumbs = false,
67
+ breadcrumbs = [],
68
+ sidebarLogo = "https://images.hotelinking.com/login/logo.png",
69
+ sidebarTitle = "Admin",
70
+ sidebarItems = [],
71
+ topbarActions = [],
72
+ selectItems = [],
73
+ selectedItem = { name: "", id: "" },
74
+ sidebarOpenByDefault = true,
75
+ } = Astro.props;
76
+
77
+ // Format user data for display
78
+ const userData = user
79
+ ? {
80
+ username: user.username || "Admin",
81
+ email: user.attributes?.email || user.email || "admin@example.com",
82
+ avatar: `https://ui-avatars.com/api/?name=${encodeURIComponent(user.username || "Admin")}&background=3B82F6&color=fff`,
83
+ }
84
+ : {
85
+ username: "Admin",
86
+ email: "admin@example.com",
87
+ avatar:
88
+ "https://ui-avatars.com/api/?name=Admin&background=3B82F6&color=fff",
89
+ };
90
+
91
+ // Build products sidebar based on user admin status
92
+ const baseProducts = [
93
+ {
94
+ name: "DeskForce",
95
+ icon: "https://images.hotelinking.com/ui/ImagoDeskForce.svg",
96
+ },
97
+ {
98
+ name: "GuestMaker",
99
+ icon: "https://images.hotelinking.com/ui/ImagoGuestMaker.svg",
100
+ },
101
+ {
102
+ name: "WifiBot",
103
+ icon: "https://images.hotelinking.com/ui/ImagoWiFiBot.svg",
104
+ },
105
+ ];
106
+
107
+ const productsSidebar = user?.isAdmin
108
+ ? [
109
+ {
110
+ name: "Admin",
111
+ icon: "https://images.hotelinking.com/login/logo.png",
112
+ active: true,
113
+ },
114
+ ...baseProducts,
115
+ ]
116
+ : baseProducts;
117
+
118
+ import { ClientRouter } from "astro:transitions";
119
+ ---
120
+
121
+ <!doctype html>
122
+ <html lang="en">
123
+ <head>
124
+ <meta charset="UTF-8" />
125
+ <link rel="icon" href="https://images.hotelinking.com/login/favicon.ico" />
126
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
127
+ <title>{title}</title>
128
+ {description && <meta name="description" content={description} />}
129
+ <ClientRouter />
130
+ </head>
131
+ <body>
132
+ <div id="app">
133
+ <!-- TODO: Implement uiWrapper integration -->
134
+ <!-- This will be implemented in task 7.1 -->
135
+
136
+ <!-- Breadcrumbs Section (if enabled) -->
137
+ {
138
+ showBreadcrumbs && breadcrumbs.length > 0 && (
139
+ <div class="breadcrumbs-section mb-6 px-6 pt-6">
140
+ <nav class="flex" aria-label="Breadcrumb">
141
+ {breadcrumbs.map((crumb, index) => (
142
+ <>
143
+ {crumb.routeName ? (
144
+ <a
145
+ href={crumb.routeName}
146
+ class="text-gray-500 hover:text-gray-700"
147
+ >
148
+ {crumb.label}
149
+ </a>
150
+ ) : (
151
+ <span class="text-gray-900">{crumb.label}</span>
152
+ )}
153
+ {index < breadcrumbs.length - 1 && (
154
+ <span class="mx-2 text-gray-400">/</span>
155
+ )}
156
+ </>
157
+ ))}
158
+ </nav>
159
+ </div>
160
+ )
161
+ }
162
+
163
+ <!-- Page Header -->
164
+ <div class="px-6 mb-6">
165
+ <div class="flex items-center justify-between">
166
+ <div>
167
+ <h1 class="text-2xl font-bold leading-7 sm:text-4xl sm:truncate">
168
+ {title}
169
+ </h1>
170
+ {description && <p class="text-gray-600 mt-2">{description}</p>}
171
+ </div>
172
+ <div class="flex items-center gap-3">
173
+ <slot name="actions" />
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ <!-- Page Content -->
179
+ <div class="px-6">
180
+ <slot />
181
+ </div>
182
+ </div>
183
+ </body>
184
+ </html>
@@ -0,0 +1,164 @@
1
+ ---
2
+ /**
3
+ * Auth Layout
4
+ *
5
+ * Centered layout for authentication pages (login, register, forgot password).
6
+ * Clean design with centered card and optional branding.
7
+ *
8
+ * @example
9
+ * ```astro
10
+ * <AuthLayout
11
+ * title="Login"
12
+ * description="Sign in to your account"
13
+ * >
14
+ * <LoginForm />
15
+ * </AuthLayout>
16
+ * ```
17
+ */
18
+
19
+ interface Props {
20
+ title: string;
21
+ description?: string;
22
+ showLogo?: boolean;
23
+ }
24
+
25
+ const { title, description, showLogo = true } = Astro.props;
26
+ ---
27
+
28
+ <!doctype html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="UTF-8" />
32
+ <link rel="icon" href="https://images.hotelinking.com/login/favicon.ico" />
33
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
34
+ <title>{title} | Hotelinking</title>
35
+ {description && <meta name="description" content={description} />}
36
+ </head>
37
+ <body>
38
+ <div id="app" class="auth-layout">
39
+ <div class="auth-container">
40
+ {
41
+ showLogo && (
42
+ <div class="auth-logo">
43
+ <img
44
+ src="https://images.hotelinking.com/login/logo.png"
45
+ alt="Hotelinking"
46
+ class="logo-image"
47
+ />
48
+ </div>
49
+ )
50
+ }
51
+
52
+ <div class="auth-card">
53
+ <div class="auth-header">
54
+ <h1 class="auth-title">{title}</h1>
55
+ {description && <p class="auth-description">{description}</p>}
56
+ </div>
57
+
58
+ <div class="auth-content">
59
+ <slot />
60
+ </div>
61
+
62
+ <div class="auth-footer">
63
+ <slot name="footer" />
64
+ </div>
65
+ </div>
66
+
67
+ <div class="auth-links">
68
+ <slot name="links" />
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </body>
73
+ </html>
74
+
75
+ <style>
76
+ .auth-layout {
77
+ min-height: 100vh;
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
82
+ padding: 2rem;
83
+ }
84
+
85
+ .auth-container {
86
+ width: 100%;
87
+ max-width: 28rem;
88
+ }
89
+
90
+ .auth-logo {
91
+ text-align: center;
92
+ margin-bottom: 2rem;
93
+ }
94
+
95
+ .logo-image {
96
+ height: 3rem;
97
+ filter: brightness(0) invert(1);
98
+ }
99
+
100
+ .auth-card {
101
+ background: white;
102
+ border-radius: 1rem;
103
+ box-shadow:
104
+ 0 20px 25px -5px rgba(0, 0, 0, 0.1),
105
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
106
+ overflow: hidden;
107
+ }
108
+
109
+ .auth-header {
110
+ padding: 2rem 2rem 1rem;
111
+ text-align: center;
112
+ }
113
+
114
+ .auth-title {
115
+ font-size: 1.875rem;
116
+ font-weight: 700;
117
+ color: #111827;
118
+ margin: 0;
119
+ }
120
+
121
+ .auth-description {
122
+ margin-top: 0.5rem;
123
+ color: #6b7280;
124
+ font-size: 0.875rem;
125
+ }
126
+
127
+ .auth-content {
128
+ padding: 0 2rem 2rem;
129
+ }
130
+
131
+ .auth-footer {
132
+ padding: 1.5rem 2rem;
133
+ background: #f9fafb;
134
+ border-top: 1px solid #e5e7eb;
135
+ }
136
+
137
+ .auth-links {
138
+ margin-top: 1.5rem;
139
+ text-align: center;
140
+ }
141
+
142
+ /* Responsive */
143
+ @media (max-width: 640px) {
144
+ .auth-layout {
145
+ padding: 1rem;
146
+ }
147
+
148
+ .auth-header {
149
+ padding: 1.5rem 1.5rem 0.75rem;
150
+ }
151
+
152
+ .auth-content {
153
+ padding: 0 1.5rem 1.5rem;
154
+ }
155
+
156
+ .auth-footer {
157
+ padding: 1rem 1.5rem;
158
+ }
159
+
160
+ .auth-title {
161
+ font-size: 1.5rem;
162
+ }
163
+ }
164
+ </style>