@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.
- package/README.md +265 -0
- package/dist/chunk-33R4URZV.js +59 -0
- package/dist/chunk-33R4URZV.js.map +1 -0
- package/dist/chunk-64USRLVP.js +85 -0
- package/dist/chunk-64USRLVP.js.map +1 -0
- package/dist/chunk-WLOFOVCL.js +210 -0
- package/dist/chunk-WLOFOVCL.js.map +1 -0
- package/dist/chunk-WNMPTDCR.js +73 -0
- package/dist/chunk-WNMPTDCR.js.map +1 -0
- package/dist/chunk-Z2ZAL7KX.js +9 -0
- package/dist/chunk-Z2ZAL7KX.js.map +1 -0
- package/dist/chunk-ZQ4XMJH7.js +1 -0
- package/dist/chunk-ZQ4XMJH7.js.map +1 -0
- package/dist/htlkg/config.js +7 -0
- package/dist/htlkg/config.js.map +1 -0
- package/dist/htlkg/index.js +7 -0
- package/dist/htlkg/index.js.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.js +168 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/utils/hydration.js +21 -0
- package/dist/utils/hydration.js.map +1 -0
- package/dist/utils/index.js +56 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/ssr.js +21 -0
- package/dist/utils/ssr.js.map +1 -0
- package/dist/utils/static.js +19 -0
- package/dist/utils/static.js.map +1 -0
- package/package.json +53 -0
- package/src/__mocks__/astro-middleware.ts +19 -0
- package/src/__mocks__/virtual-htlkg-config.ts +14 -0
- package/src/auth/LoginForm.vue +482 -0
- package/src/auth/LoginPage.astro +70 -0
- package/src/components/PageHeader.astro +145 -0
- package/src/components/Sidebar.astro +157 -0
- package/src/components/Topbar.astro +167 -0
- package/src/components/index.ts +9 -0
- package/src/htlkg/config.test.ts +165 -0
- package/src/htlkg/config.ts +242 -0
- package/src/htlkg/index.ts +245 -0
- package/src/htlkg/virtual-modules.test.ts +158 -0
- package/src/htlkg/virtual-modules.ts +81 -0
- package/src/index.ts +37 -0
- package/src/layouts/AdminLayout.astro +184 -0
- package/src/layouts/AuthLayout.astro +164 -0
- package/src/layouts/BrandLayout.astro +309 -0
- package/src/layouts/DefaultLayout.astro +25 -0
- package/src/layouts/PublicLayout.astro +153 -0
- package/src/layouts/index.ts +10 -0
- package/src/middleware/auth.ts +53 -0
- package/src/middleware/index.ts +31 -0
- package/src/middleware/route-guards.test.ts +182 -0
- package/src/middleware/route-guards.ts +218 -0
- package/src/patterns/admin/DetailPage.astro +195 -0
- package/src/patterns/admin/FormPage.astro +203 -0
- package/src/patterns/admin/ListPage.astro +178 -0
- package/src/patterns/admin/index.ts +9 -0
- package/src/patterns/brand/ConfigPage.astro +128 -0
- package/src/patterns/brand/PortalPage.astro +161 -0
- package/src/patterns/brand/index.ts +8 -0
- package/src/patterns/index.ts +8 -0
- package/src/utils/hydration.test.ts +154 -0
- package/src/utils/hydration.ts +151 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/ssr.test.ts +235 -0
- package/src/utils/ssr.ts +139 -0
- package/src/utils/static.test.ts +144 -0
- package/src/utils/static.ts +144 -0
- 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>
|