@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,203 @@
1
+ ---
2
+ /**
3
+ * Admin Form Page Pattern
4
+ *
5
+ * Standard pattern for admin form pages with sections and actions.
6
+ * Provides consistent structure for create/edit pages.
7
+ *
8
+ * @example
9
+ * ```astro
10
+ * <FormPage
11
+ * title="Create User"
12
+ * description="Add a new user to the system"
13
+ * user={user}
14
+ * >
15
+ * <div slot="actions">
16
+ * <button>Cancel</button>
17
+ * <button>Save</button>
18
+ * </div>
19
+ * <div slot="content">
20
+ * <UserForm />
21
+ * </div>
22
+ * </FormPage>
23
+ * ```
24
+ */
25
+
26
+ import AdminLayout from "../../layouts/AdminLayout.astro";
27
+ import PageHeader from "../../components/PageHeader.astro";
28
+ import type { AuthUser } from "@htlkg/core/types";
29
+
30
+ interface BreadcrumbItem {
31
+ label: string;
32
+ href?: string;
33
+ }
34
+
35
+ interface Props {
36
+ title: string;
37
+ description?: string;
38
+ user: AuthUser;
39
+ currentPage?: string;
40
+ breadcrumbs?: BreadcrumbItem[];
41
+ showSidebar?: boolean;
42
+ }
43
+
44
+ const {
45
+ title,
46
+ description,
47
+ user,
48
+ currentPage,
49
+ breadcrumbs = [],
50
+ showSidebar = false,
51
+ } = Astro.props;
52
+ ---
53
+
54
+ <AdminLayout
55
+ title={title}
56
+ description={description}
57
+ user={user}
58
+ currentPage={currentPage}
59
+ >
60
+ <div slot="actions">
61
+ <slot name="actions" />
62
+ </div>
63
+
64
+ <div class:list={["form-page", { "with-sidebar": showSidebar }]}>
65
+ <PageHeader
66
+ title={title}
67
+ description={description}
68
+ breadcrumbs={breadcrumbs}
69
+ />
70
+
71
+ <div class="form-container">
72
+ <div class="form-content">
73
+ <slot name="content" />
74
+ </div>
75
+
76
+ {
77
+ showSidebar && (
78
+ <aside class="form-sidebar">
79
+ <slot name="sidebar" />
80
+ </aside>
81
+ )
82
+ }
83
+ </div>
84
+
85
+ <div class="form-footer">
86
+ <slot name="footer">
87
+ <div class="footer-actions">
88
+ <button type="button" class="btn-secondary"> Cancel </button>
89
+ <button type="submit" class="btn-primary"> Save </button>
90
+ </div>
91
+ </slot>
92
+ </div>
93
+ </div>
94
+ </AdminLayout>
95
+
96
+ <style>
97
+ .form-page {
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: 1.5rem;
101
+ }
102
+
103
+ .form-container {
104
+ display: grid;
105
+ grid-template-columns: 1fr;
106
+ gap: 1.5rem;
107
+ }
108
+
109
+ .form-page.with-sidebar .form-container {
110
+ grid-template-columns: 1fr 20rem;
111
+ }
112
+
113
+ .form-content {
114
+ background: white;
115
+ border-radius: 0.5rem;
116
+ border: 1px solid #e5e7eb;
117
+ padding: 1.5rem;
118
+ }
119
+
120
+ .form-sidebar {
121
+ background: white;
122
+ border-radius: 0.5rem;
123
+ border: 1px solid #e5e7eb;
124
+ padding: 1.5rem;
125
+ }
126
+
127
+ .form-footer {
128
+ background: white;
129
+ border-radius: 0.5rem;
130
+ border: 1px solid #e5e7eb;
131
+ padding: 1.5rem;
132
+ display: flex;
133
+ justify-content: flex-end;
134
+ }
135
+
136
+ .footer-actions {
137
+ display: flex;
138
+ gap: 0.75rem;
139
+ }
140
+
141
+ .btn-secondary {
142
+ padding: 0.5rem 1rem;
143
+ border: 1px solid #d1d5db;
144
+ border-radius: 0.5rem;
145
+ background: white;
146
+ color: #374151;
147
+ font-size: 0.875rem;
148
+ font-weight: 500;
149
+ cursor: pointer;
150
+ transition: all 0.2s;
151
+ }
152
+
153
+ .btn-secondary:hover {
154
+ background: #f9fafb;
155
+ border-color: #9ca3af;
156
+ }
157
+
158
+ .btn-primary {
159
+ padding: 0.5rem 1rem;
160
+ border: none;
161
+ border-radius: 0.5rem;
162
+ background: #2563eb;
163
+ color: white;
164
+ font-size: 0.875rem;
165
+ font-weight: 500;
166
+ cursor: pointer;
167
+ transition: all 0.2s;
168
+ }
169
+
170
+ .btn-primary:hover {
171
+ background: #1d4ed8;
172
+ }
173
+
174
+ .btn-primary:disabled {
175
+ background: #9ca3af;
176
+ cursor: not-allowed;
177
+ }
178
+
179
+ /* Responsive */
180
+ @media (max-width: 1024px) {
181
+ .form-page.with-sidebar .form-container {
182
+ grid-template-columns: 1fr;
183
+ }
184
+ }
185
+
186
+ @media (max-width: 768px) {
187
+ .form-content,
188
+ .form-sidebar,
189
+ .form-footer {
190
+ padding: 1rem;
191
+ }
192
+
193
+ .footer-actions {
194
+ width: 100%;
195
+ flex-direction: column-reverse;
196
+ }
197
+
198
+ .btn-secondary,
199
+ .btn-primary {
200
+ width: 100%;
201
+ }
202
+ }
203
+ </style>
@@ -0,0 +1,178 @@
1
+ ---
2
+ /**
3
+ * Admin List Page Pattern
4
+ *
5
+ * Standard pattern for admin list pages with filters, search, and table.
6
+ * Provides consistent structure for entity listing pages.
7
+ *
8
+ * @example
9
+ * ```astro
10
+ * <ListPage
11
+ * title="Users"
12
+ * description="Manage system users"
13
+ * user={user}
14
+ * >
15
+ * <div slot="filters">
16
+ * <select>...</select>
17
+ * </div>
18
+ * <div slot="actions">
19
+ * <button>Add User</button>
20
+ * </div>
21
+ * <Table slot="content" items={users} />
22
+ * </ListPage>
23
+ * ```
24
+ */
25
+
26
+ import AdminLayout from "../../layouts/AdminLayout.astro";
27
+ import PageHeader from "../../components/PageHeader.astro";
28
+ import type { AuthUser } from "@htlkg/core/types";
29
+
30
+ interface BreadcrumbItem {
31
+ label: string;
32
+ href?: string;
33
+ }
34
+
35
+ interface Props {
36
+ title: string;
37
+ description?: string;
38
+ user: AuthUser;
39
+ currentPage?: string;
40
+ breadcrumbs?: BreadcrumbItem[];
41
+ showFilters?: boolean;
42
+ showSearch?: boolean;
43
+ }
44
+
45
+ const {
46
+ title,
47
+ description,
48
+ user,
49
+ currentPage,
50
+ breadcrumbs = [],
51
+ showFilters = true,
52
+ showSearch = true,
53
+ } = Astro.props;
54
+ ---
55
+
56
+ <AdminLayout
57
+ title={title}
58
+ description={description}
59
+ user={user}
60
+ currentPage={currentPage}
61
+ >
62
+ <div slot="actions">
63
+ <slot name="actions" />
64
+ </div>
65
+
66
+ <div class="list-page">
67
+ <PageHeader
68
+ title={title}
69
+ description={description}
70
+ breadcrumbs={breadcrumbs}
71
+ />
72
+
73
+ {
74
+ (showFilters || showSearch) && (
75
+ <div class="list-toolbar">
76
+ {showSearch && (
77
+ <div class="search-box">
78
+ <slot name="search">
79
+ <input
80
+ type="search"
81
+ placeholder="Search..."
82
+ class="search-input"
83
+ />
84
+ </slot>
85
+ </div>
86
+ )}
87
+
88
+ {showFilters && (
89
+ <div class="filters">
90
+ <slot name="filters" />
91
+ </div>
92
+ )}
93
+ </div>
94
+ )
95
+ }
96
+
97
+ <div class="list-content">
98
+ <slot name="content" />
99
+ </div>
100
+
101
+ <div class="list-footer">
102
+ <slot name="footer" />
103
+ </div>
104
+ </div>
105
+ </AdminLayout>
106
+
107
+ <style>
108
+ .list-page {
109
+ display: flex;
110
+ flex-direction: column;
111
+ gap: 1.5rem;
112
+ }
113
+
114
+ .list-toolbar {
115
+ display: flex;
116
+ gap: 1rem;
117
+ align-items: center;
118
+ flex-wrap: wrap;
119
+ }
120
+
121
+ .search-box {
122
+ flex: 1;
123
+ min-width: 16rem;
124
+ }
125
+
126
+ .search-input {
127
+ width: 100%;
128
+ padding: 0.5rem 1rem;
129
+ border: 1px solid #d1d5db;
130
+ border-radius: 0.5rem;
131
+ font-size: 0.875rem;
132
+ }
133
+
134
+ .search-input:focus {
135
+ outline: none;
136
+ border-color: #2563eb;
137
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
138
+ }
139
+
140
+ .filters {
141
+ display: flex;
142
+ gap: 0.75rem;
143
+ flex-wrap: wrap;
144
+ }
145
+
146
+ .list-content {
147
+ background: white;
148
+ border-radius: 0.5rem;
149
+ border: 1px solid #e5e7eb;
150
+ overflow: hidden;
151
+ }
152
+
153
+ .list-footer {
154
+ display: flex;
155
+ justify-content: space-between;
156
+ align-items: center;
157
+ padding: 1rem;
158
+ background: white;
159
+ border-radius: 0.5rem;
160
+ border: 1px solid #e5e7eb;
161
+ }
162
+
163
+ /* Responsive */
164
+ @media (max-width: 768px) {
165
+ .list-toolbar {
166
+ flex-direction: column;
167
+ align-items: stretch;
168
+ }
169
+
170
+ .search-box {
171
+ width: 100%;
172
+ }
173
+
174
+ .filters {
175
+ width: 100%;
176
+ }
177
+ }
178
+ </style>
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @htlkg/pages - Admin Page Patterns
3
+ *
4
+ * Reusable page patterns for admin interfaces.
5
+ */
6
+
7
+ export { default as ListPage } from './ListPage.astro';
8
+ export { default as DetailPage } from './DetailPage.astro';
9
+ export { default as FormPage } from './FormPage.astro';
@@ -0,0 +1,128 @@
1
+ ---
2
+ /**
3
+ * Brand Config Page Pattern
4
+ *
5
+ * Standard pattern for brand configuration pages.
6
+ * Used for brand-specific admin pages with brand context.
7
+ *
8
+ * @example
9
+ * ```astro
10
+ * <ConfigPage
11
+ * title="WiFi Settings"
12
+ * brandId={brandId}
13
+ * brand={brand}
14
+ * user={user}
15
+ * >
16
+ * <div slot="actions">
17
+ * <button>Save</button>
18
+ * </div>
19
+ * <WiFiConfigForm />
20
+ * </ConfigPage>
21
+ * ```
22
+ */
23
+
24
+ import BrandLayout from "../../layouts/BrandLayout.astro";
25
+ import PageHeader from "../../components/PageHeader.astro";
26
+ import type { AuthUser, Brand } from "@htlkg/core/types";
27
+
28
+ interface NavItem {
29
+ label: string;
30
+ href: string;
31
+ active?: boolean;
32
+ }
33
+
34
+ interface Props {
35
+ title: string;
36
+ description?: string;
37
+ brandId: number;
38
+ brand: Brand;
39
+ user: AuthUser;
40
+ navItems?: NavItem[];
41
+ showSidebar?: boolean;
42
+ }
43
+
44
+ const {
45
+ title,
46
+ description,
47
+ brandId,
48
+ brand,
49
+ user,
50
+ navItems = [],
51
+ showSidebar = false,
52
+ } = Astro.props;
53
+ ---
54
+
55
+ <BrandLayout
56
+ title={title}
57
+ description={description}
58
+ brandId={brandId}
59
+ brand={brand}
60
+ user={user}
61
+ navItems={navItems}
62
+ >
63
+ <div slot="actions">
64
+ <slot name="actions" />
65
+ </div>
66
+
67
+ <div class:list={["config-page", { "with-sidebar": showSidebar }]}>
68
+ <div class="config-container">
69
+ <div class="config-content">
70
+ <slot />
71
+ </div>
72
+
73
+ {
74
+ showSidebar && (
75
+ <aside class="config-sidebar">
76
+ <slot name="sidebar" />
77
+ </aside>
78
+ )
79
+ }
80
+ </div>
81
+ </div>
82
+ </BrandLayout>
83
+
84
+ <style>
85
+ .config-page {
86
+ display: flex;
87
+ flex-direction: column;
88
+ gap: 1.5rem;
89
+ }
90
+
91
+ .config-container {
92
+ display: grid;
93
+ grid-template-columns: 1fr;
94
+ gap: 1.5rem;
95
+ }
96
+
97
+ .config-page.with-sidebar .config-container {
98
+ grid-template-columns: 1fr 20rem;
99
+ }
100
+
101
+ .config-content {
102
+ background: white;
103
+ border-radius: 0.5rem;
104
+ border: 1px solid #e5e7eb;
105
+ padding: 1.5rem;
106
+ }
107
+
108
+ .config-sidebar {
109
+ background: white;
110
+ border-radius: 0.5rem;
111
+ border: 1px solid #e5e7eb;
112
+ padding: 1.5rem;
113
+ }
114
+
115
+ /* Responsive */
116
+ @media (max-width: 1024px) {
117
+ .config-page.with-sidebar .config-container {
118
+ grid-template-columns: 1fr;
119
+ }
120
+ }
121
+
122
+ @media (max-width: 768px) {
123
+ .config-content,
124
+ .config-sidebar {
125
+ padding: 1rem;
126
+ }
127
+ }
128
+ </style>
@@ -0,0 +1,161 @@
1
+ ---
2
+ /**
3
+ * Brand Portal Page Pattern
4
+ *
5
+ * Standard pattern for brand portal pages with brand context.
6
+ * Used for brand-specific public-facing pages like WiFi portals.
7
+ *
8
+ * @example
9
+ * ```astro
10
+ * <PortalPage
11
+ * title="WiFi Login"
12
+ * brandId={brandId}
13
+ * brand={brand}
14
+ * >
15
+ * <WiFiLoginForm />
16
+ * </PortalPage>
17
+ * ```
18
+ */
19
+
20
+ import type { Brand } from "@htlkg/core/types";
21
+
22
+ interface Props {
23
+ title: string;
24
+ description?: string;
25
+ brandId: number;
26
+ brand: Brand;
27
+ showBranding?: boolean;
28
+ }
29
+
30
+ const { title, description, brandId, brand, showBranding = true } = Astro.props;
31
+ ---
32
+
33
+ <!doctype html>
34
+ <html lang="en">
35
+ <head>
36
+ <meta charset="UTF-8" />
37
+ <link
38
+ rel="icon"
39
+ href={brand.logo || "https://images.hotelinking.com/login/favicon.ico"}
40
+ />
41
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
42
+ <title>{title} | {brand.name}</title>
43
+ {description && <meta name="description" content={description} />}
44
+ </head>
45
+ <body>
46
+ <div id="app" class="portal-page">
47
+ <div class="portal-container">
48
+ {
49
+ showBranding && (
50
+ <div class="portal-header">
51
+ {brand.logo && (
52
+ <img src={brand.logo} alt={brand.name} class="brand-logo" />
53
+ )}
54
+ <h1 class="brand-name">{brand.name}</h1>
55
+ </div>
56
+ )
57
+ }
58
+
59
+ <div class="portal-content">
60
+ <slot />
61
+ </div>
62
+
63
+ <div class="portal-footer">
64
+ <slot name="footer">
65
+ <p class="footer-text">
66
+ Powered by <strong>Hotelinking</strong>
67
+ </p>
68
+ </slot>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </body>
73
+ </html>
74
+
75
+ <style>
76
+ .portal-page {
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
+ .portal-container {
86
+ width: 100%;
87
+ max-width: 28rem;
88
+ background: white;
89
+ border-radius: 1rem;
90
+ box-shadow:
91
+ 0 20px 25px -5px rgba(0, 0, 0, 0.1),
92
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
93
+ overflow: hidden;
94
+ }
95
+
96
+ .portal-header {
97
+ padding: 2rem 2rem 1rem;
98
+ text-align: center;
99
+ border-bottom: 1px solid #e5e7eb;
100
+ }
101
+
102
+ .brand-logo {
103
+ width: 5rem;
104
+ height: 5rem;
105
+ border-radius: 0.75rem;
106
+ object-fit: cover;
107
+ margin: 0 auto 1rem;
108
+ }
109
+
110
+ .brand-name {
111
+ font-size: 1.5rem;
112
+ font-weight: 700;
113
+ color: #111827;
114
+ margin: 0;
115
+ }
116
+
117
+ .portal-content {
118
+ padding: 2rem;
119
+ }
120
+
121
+ .portal-footer {
122
+ padding: 1.5rem 2rem;
123
+ background: #f9fafb;
124
+ border-top: 1px solid #e5e7eb;
125
+ text-align: center;
126
+ }
127
+
128
+ .footer-text {
129
+ font-size: 0.875rem;
130
+ color: #6b7280;
131
+ margin: 0;
132
+ }
133
+
134
+ /* Responsive */
135
+ @media (max-width: 640px) {
136
+ .portal-page {
137
+ padding: 1rem;
138
+ }
139
+
140
+ .portal-header {
141
+ padding: 1.5rem 1.5rem 0.75rem;
142
+ }
143
+
144
+ .portal-content {
145
+ padding: 1.5rem;
146
+ }
147
+
148
+ .portal-footer {
149
+ padding: 1rem 1.5rem;
150
+ }
151
+
152
+ .brand-logo {
153
+ width: 4rem;
154
+ height: 4rem;
155
+ }
156
+
157
+ .brand-name {
158
+ font-size: 1.25rem;
159
+ }
160
+ }
161
+ </style>
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @htlkg/pages - Brand Page Patterns
3
+ *
4
+ * Reusable page patterns for brand-specific interfaces.
5
+ */
6
+
7
+ export { default as PortalPage } from './PortalPage.astro';
8
+ export { default as ConfigPage } from './ConfigPage.astro';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @htlkg/pages - Page Patterns
3
+ *
4
+ * Reusable page patterns for different contexts.
5
+ */
6
+
7
+ export * from './admin';
8
+ export * from './brand';