@smartnet360/svelte-components 0.0.124 → 0.0.125
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/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte +4 -174
- package/dist/apps/antenna-tools/components/DatabaseViewer.svelte +2 -2
- package/dist/apps/antenna-tools/components/MSIConverter.svelte +302 -43
- package/dist/apps/antenna-tools/db.js +4 -0
- package/dist/apps/antenna-tools/utils/db-utils.d.ts +19 -0
- package/dist/apps/antenna-tools/utils/db-utils.js +108 -0
- package/dist/core/Auth/LoginForm.svelte +397 -0
- package/dist/core/Auth/LoginForm.svelte.d.ts +16 -0
- package/dist/core/Auth/auth.svelte.d.ts +22 -0
- package/dist/core/Auth/auth.svelte.js +184 -0
- package/dist/core/Auth/config.d.ts +25 -0
- package/dist/core/Auth/config.js +256 -0
- package/dist/core/Auth/index.d.ts +4 -0
- package/dist/core/Auth/index.js +5 -0
- package/dist/core/Auth/types.d.ts +140 -0
- package/dist/core/Auth/types.js +2 -0
- package/dist/core/LandingPage/App.svelte +102 -0
- package/dist/core/LandingPage/App.svelte.d.ts +20 -0
- package/dist/core/LandingPage/LandingPage.svelte +480 -0
- package/dist/core/LandingPage/LandingPage.svelte.d.ts +21 -0
- package/dist/core/LandingPage/index.d.ts +2 -0
- package/dist/core/LandingPage/index.js +3 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { browser } from '$app/environment';
|
|
3
|
+
import { LoginForm } from '../Auth/index.js';
|
|
4
|
+
import { LandingPage } from '../LandingPage/index.js';
|
|
5
|
+
import { createAuthState } from '../Auth/auth.svelte.js';
|
|
6
|
+
import { getDevConfig, isDevMode, DEFAULT_CATEGORIES, DEFAULT_APPS } from '../Auth/config.js';
|
|
7
|
+
import type { CategoryInfo, AppDefinition, AuthConfig } from '../Auth/types.js';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
/** Page title */
|
|
11
|
+
title?: string;
|
|
12
|
+
/** Page subtitle */
|
|
13
|
+
subtitle?: string;
|
|
14
|
+
/** Logo URL */
|
|
15
|
+
logo?: string;
|
|
16
|
+
/** Categories to display */
|
|
17
|
+
categories?: CategoryInfo[];
|
|
18
|
+
/** Apps to display */
|
|
19
|
+
apps?: AppDefinition[];
|
|
20
|
+
/** Auth configuration */
|
|
21
|
+
authConfig?: Partial<AuthConfig>;
|
|
22
|
+
/** Force dev mode (auto-login) */
|
|
23
|
+
forceDevMode?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
title = 'RF Tool Components',
|
|
28
|
+
subtitle = 'Network Planning and Visualization Suite',
|
|
29
|
+
logo,
|
|
30
|
+
categories = DEFAULT_CATEGORIES,
|
|
31
|
+
apps = DEFAULT_APPS,
|
|
32
|
+
authConfig = {},
|
|
33
|
+
forceDevMode = false
|
|
34
|
+
}: Props = $props();
|
|
35
|
+
|
|
36
|
+
// Create auth state
|
|
37
|
+
const auth = createAuthState(authConfig);
|
|
38
|
+
|
|
39
|
+
// Auto-login in dev mode
|
|
40
|
+
const devMode = forceDevMode || isDevMode();
|
|
41
|
+
|
|
42
|
+
$effect(() => {
|
|
43
|
+
if (browser && devMode && !auth.isAuthenticated && auth.initialized) {
|
|
44
|
+
const config = getDevConfig();
|
|
45
|
+
auth.loginDev(config.devUser, config.devPermissions);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Show loading state until initialized
|
|
50
|
+
const showLoading = $derived(!auth.initialized);
|
|
51
|
+
|
|
52
|
+
// Show login form if not authenticated and not in dev mode
|
|
53
|
+
const showLogin = $derived(auth.initialized && !auth.isAuthenticated && !devMode);
|
|
54
|
+
|
|
55
|
+
// Show landing page if authenticated
|
|
56
|
+
const showLandingPage = $derived(auth.initialized && auth.isAuthenticated);
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
{#if showLoading}
|
|
60
|
+
<!-- Loading State -->
|
|
61
|
+
<div class="loading-container d-flex justify-content-center align-items-center vh-100">
|
|
62
|
+
<div class="text-center">
|
|
63
|
+
<div class="spinner-border text-primary mb-3" role="status">
|
|
64
|
+
<span class="visually-hidden">Loading...</span>
|
|
65
|
+
</div>
|
|
66
|
+
<p class="text-muted">Loading application...</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
{:else if showLogin}
|
|
70
|
+
<!-- Login Form -->
|
|
71
|
+
<LoginForm
|
|
72
|
+
{auth}
|
|
73
|
+
{title}
|
|
74
|
+
{subtitle}
|
|
75
|
+
{logo}
|
|
76
|
+
/>
|
|
77
|
+
{:else if showLandingPage}
|
|
78
|
+
<!-- Landing Page -->
|
|
79
|
+
<LandingPage
|
|
80
|
+
{auth}
|
|
81
|
+
{title}
|
|
82
|
+
{subtitle}
|
|
83
|
+
{logo}
|
|
84
|
+
{categories}
|
|
85
|
+
{apps}
|
|
86
|
+
/>
|
|
87
|
+
{/if}
|
|
88
|
+
|
|
89
|
+
<style>
|
|
90
|
+
.loading-container {
|
|
91
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.loading-container .spinner-border {
|
|
95
|
+
width: 3rem;
|
|
96
|
+
height: 3rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.loading-container p {
|
|
100
|
+
color: white !important;
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CategoryInfo, AppDefinition, AuthConfig } from '../Auth/types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Page title */
|
|
4
|
+
title?: string;
|
|
5
|
+
/** Page subtitle */
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
/** Logo URL */
|
|
8
|
+
logo?: string;
|
|
9
|
+
/** Categories to display */
|
|
10
|
+
categories?: CategoryInfo[];
|
|
11
|
+
/** Apps to display */
|
|
12
|
+
apps?: AppDefinition[];
|
|
13
|
+
/** Auth configuration */
|
|
14
|
+
authConfig?: Partial<AuthConfig>;
|
|
15
|
+
/** Force dev mode (auto-login) */
|
|
16
|
+
forceDevMode?: boolean;
|
|
17
|
+
}
|
|
18
|
+
declare const App: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type App = ReturnType<typeof App>;
|
|
20
|
+
export default App;
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { AppDefinition, CategoryInfo } from '../Auth/types.js';
|
|
3
|
+
import type { AuthState } from '../Auth/auth.svelte.js';
|
|
4
|
+
import { base } from '$app/paths';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Auth state instance */
|
|
8
|
+
auth: AuthState;
|
|
9
|
+
/** Page title */
|
|
10
|
+
title?: string;
|
|
11
|
+
/** Page subtitle */
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
/** Logo URL */
|
|
14
|
+
logo?: string;
|
|
15
|
+
/** Categories to display */
|
|
16
|
+
categories: CategoryInfo[];
|
|
17
|
+
/** Apps to display */
|
|
18
|
+
apps: AppDefinition[];
|
|
19
|
+
/** Show only accessible apps (based on permissions) */
|
|
20
|
+
filterByPermissions?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
auth,
|
|
25
|
+
title = 'RF Tool Components',
|
|
26
|
+
subtitle = 'Network Planning and Visualization Suite',
|
|
27
|
+
logo,
|
|
28
|
+
categories,
|
|
29
|
+
apps,
|
|
30
|
+
filterByPermissions = true
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
|
|
33
|
+
// Filter apps based on permissions if enabled
|
|
34
|
+
const accessibleApps = $derived(
|
|
35
|
+
filterByPermissions && auth.isAuthenticated
|
|
36
|
+
? apps.filter(app => {
|
|
37
|
+
if (!app.enabled) return false;
|
|
38
|
+
if (!app.requiredPermission) return true;
|
|
39
|
+
return auth.hasPermission(app.requiredPermission, 'view');
|
|
40
|
+
})
|
|
41
|
+
: apps.filter(app => app.enabled)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Group apps by category
|
|
45
|
+
const appsByCategory = $derived(
|
|
46
|
+
categories
|
|
47
|
+
.map(cat => ({
|
|
48
|
+
category: cat,
|
|
49
|
+
apps: accessibleApps.filter(app => app.category === cat.id)
|
|
50
|
+
}))
|
|
51
|
+
.filter(group => group.apps.length > 0)
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
function handleLogout() {
|
|
55
|
+
auth.logout();
|
|
56
|
+
}
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<div class="landing-page">
|
|
60
|
+
<!-- Navbar -->
|
|
61
|
+
<nav class="navbar">
|
|
62
|
+
<div class="navbar-brand">
|
|
63
|
+
{#if logo}
|
|
64
|
+
<img src={logo} alt="Logo" class="navbar-logo" />
|
|
65
|
+
{:else}
|
|
66
|
+
<i class="bi bi-broadcast-pin"></i>
|
|
67
|
+
{/if}
|
|
68
|
+
<div class="navbar-title">
|
|
69
|
+
<span class="title">{title}</span>
|
|
70
|
+
<span class="subtitle">{subtitle}</span>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{#if auth.isAuthenticated && auth.user}
|
|
75
|
+
<div class="navbar-user">
|
|
76
|
+
<div class="user-info">
|
|
77
|
+
<span class="user-name">{auth.user.displayName}</span>
|
|
78
|
+
<span class="user-email">{auth.user.email || auth.user.username}</span>
|
|
79
|
+
</div>
|
|
80
|
+
<button class="logout-btn" onclick={handleLogout} title="Sign Out" aria-label="Sign Out">
|
|
81
|
+
<i class="bi bi-box-arrow-right"></i>
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
{/if}
|
|
85
|
+
</nav>
|
|
86
|
+
|
|
87
|
+
<!-- Content -->
|
|
88
|
+
<main class="content">
|
|
89
|
+
{#each appsByCategory as { category, apps: categoryApps }, index (category.id)}
|
|
90
|
+
<!-- Category Section -->
|
|
91
|
+
<section class="category-section">
|
|
92
|
+
<div class="category-header">
|
|
93
|
+
<div class="category-icon" style="--cat-color: {category.color}">
|
|
94
|
+
<i class="bi {category.icon}"></i>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="category-info">
|
|
97
|
+
<h2>{category.title}</h2>
|
|
98
|
+
<p>{category.description}</p>
|
|
99
|
+
</div>
|
|
100
|
+
<span class="category-count">{categoryApps.length}</span>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="apps-grid">
|
|
104
|
+
{#each categoryApps as app (app.id)}
|
|
105
|
+
<a href="{base}{app.route}" class="app-card" style="--cat-color: {category.color}">
|
|
106
|
+
<div class="app-icon">
|
|
107
|
+
<i class="bi {app.icon}"></i>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="app-info">
|
|
110
|
+
<h3>
|
|
111
|
+
{app.title}
|
|
112
|
+
{#if app.badge}
|
|
113
|
+
<span class="app-badge" class:new={app.badge === 'New'} class:beta={app.badge === 'Beta'}>
|
|
114
|
+
{app.badge}
|
|
115
|
+
</span>
|
|
116
|
+
{/if}
|
|
117
|
+
</h3>
|
|
118
|
+
<p>{app.description}</p>
|
|
119
|
+
</div>
|
|
120
|
+
<i class="bi bi-chevron-right app-arrow"></i>
|
|
121
|
+
</a>
|
|
122
|
+
{/each}
|
|
123
|
+
</div>
|
|
124
|
+
</section>
|
|
125
|
+
|
|
126
|
+
{#if index < appsByCategory.length - 1}
|
|
127
|
+
<div class="section-divider"></div>
|
|
128
|
+
{/if}
|
|
129
|
+
{/each}
|
|
130
|
+
|
|
131
|
+
<!-- Empty State -->
|
|
132
|
+
{#if appsByCategory.length === 0}
|
|
133
|
+
<div class="empty-state">
|
|
134
|
+
<i class="bi bi-inbox"></i>
|
|
135
|
+
<h3>No Applications Available</h3>
|
|
136
|
+
<p>You don't have access to any applications yet.</p>
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
139
|
+
</main>
|
|
140
|
+
|
|
141
|
+
<!-- Footer -->
|
|
142
|
+
<footer class="footer">
|
|
143
|
+
<div class="footer-left">
|
|
144
|
+
<i class="bi bi-shield-check"></i>
|
|
145
|
+
{#if auth.isAuthenticated}
|
|
146
|
+
Signed in as <strong>{auth.user?.username}</strong>
|
|
147
|
+
{:else}
|
|
148
|
+
Not authenticated
|
|
149
|
+
{/if}
|
|
150
|
+
</div>
|
|
151
|
+
<div class="footer-right">
|
|
152
|
+
RF Tool Components © {new Date().getFullYear()}
|
|
153
|
+
</div>
|
|
154
|
+
</footer>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<style>
|
|
158
|
+
.landing-page {
|
|
159
|
+
min-height: 100vh;
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-direction: column;
|
|
162
|
+
background: #f1f5f9;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Navbar */
|
|
166
|
+
.navbar {
|
|
167
|
+
display: flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
justify-content: space-between;
|
|
170
|
+
padding: 0 1.5rem;
|
|
171
|
+
height: 64px;
|
|
172
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
173
|
+
color: #fff;
|
|
174
|
+
flex-shrink: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.navbar-brand {
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: 0.75rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.navbar-brand > i {
|
|
184
|
+
font-size: 1.75rem;
|
|
185
|
+
color: #3b82f6;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.navbar-logo {
|
|
189
|
+
height: 36px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.navbar-title {
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.navbar-title .title {
|
|
198
|
+
font-size: 1.125rem;
|
|
199
|
+
font-weight: 600;
|
|
200
|
+
line-height: 1.2;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.navbar-title .subtitle {
|
|
204
|
+
font-size: 0.75rem;
|
|
205
|
+
color: #94a3b8;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.navbar-user {
|
|
209
|
+
display: flex;
|
|
210
|
+
align-items: center;
|
|
211
|
+
gap: 0.75rem;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.user-info {
|
|
215
|
+
display: flex;
|
|
216
|
+
flex-direction: column;
|
|
217
|
+
align-items: flex-end;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.user-name {
|
|
221
|
+
font-size: 0.875rem;
|
|
222
|
+
font-weight: 500;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.user-email {
|
|
226
|
+
font-size: 0.75rem;
|
|
227
|
+
color: #94a3b8;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.logout-btn {
|
|
231
|
+
width: 36px;
|
|
232
|
+
height: 36px;
|
|
233
|
+
border-radius: 8px;
|
|
234
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
235
|
+
background: rgba(255, 255, 255, 0.05);
|
|
236
|
+
color: #fff;
|
|
237
|
+
display: flex;
|
|
238
|
+
align-items: center;
|
|
239
|
+
justify-content: center;
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
transition: all 0.15s;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.logout-btn:hover {
|
|
245
|
+
background: rgba(239, 68, 68, 0.2);
|
|
246
|
+
border-color: #ef4444;
|
|
247
|
+
color: #ef4444;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Content */
|
|
251
|
+
.content {
|
|
252
|
+
flex: 1;
|
|
253
|
+
overflow-y: auto;
|
|
254
|
+
padding: 2rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Category Section */
|
|
258
|
+
.category-section {
|
|
259
|
+
max-width: 1400px;
|
|
260
|
+
margin: 0 auto;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.category-header {
|
|
264
|
+
display: flex;
|
|
265
|
+
align-items: center;
|
|
266
|
+
gap: 1rem;
|
|
267
|
+
margin-bottom: 1.25rem;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.category-icon {
|
|
271
|
+
width: 48px;
|
|
272
|
+
height: 48px;
|
|
273
|
+
border-radius: 12px;
|
|
274
|
+
background: color-mix(in srgb, var(--cat-color, #3b82f6) 15%, transparent);
|
|
275
|
+
color: var(--cat-color, #3b82f6);
|
|
276
|
+
display: flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
justify-content: center;
|
|
279
|
+
font-size: 1.25rem;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.category-info h2 {
|
|
283
|
+
font-size: 1.25rem;
|
|
284
|
+
font-weight: 600;
|
|
285
|
+
color: #0f172a;
|
|
286
|
+
margin: 0;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.category-info p {
|
|
290
|
+
font-size: 0.875rem;
|
|
291
|
+
color: #64748b;
|
|
292
|
+
margin: 0.125rem 0 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.category-count {
|
|
296
|
+
margin-left: auto;
|
|
297
|
+
background: #e2e8f0;
|
|
298
|
+
color: #475569;
|
|
299
|
+
padding: 0.25rem 0.625rem;
|
|
300
|
+
border-radius: 999px;
|
|
301
|
+
font-size: 0.75rem;
|
|
302
|
+
font-weight: 500;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Apps Grid */
|
|
306
|
+
.apps-grid {
|
|
307
|
+
display: grid;
|
|
308
|
+
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
|
309
|
+
gap: 1rem;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.app-card {
|
|
313
|
+
display: flex;
|
|
314
|
+
align-items: center;
|
|
315
|
+
gap: 1rem;
|
|
316
|
+
padding: 1rem 1.25rem;
|
|
317
|
+
background: #fff;
|
|
318
|
+
border-radius: 12px;
|
|
319
|
+
border: 1px solid #e2e8f0;
|
|
320
|
+
text-decoration: none;
|
|
321
|
+
color: inherit;
|
|
322
|
+
transition: all 0.2s;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.app-card:hover {
|
|
326
|
+
border-color: var(--cat-color, #3b82f6);
|
|
327
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
328
|
+
transform: translateY(-2px);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.app-icon {
|
|
332
|
+
width: 44px;
|
|
333
|
+
height: 44px;
|
|
334
|
+
border-radius: 10px;
|
|
335
|
+
background: color-mix(in srgb, var(--cat-color, #3b82f6) 12%, transparent);
|
|
336
|
+
color: var(--cat-color, #3b82f6);
|
|
337
|
+
display: flex;
|
|
338
|
+
align-items: center;
|
|
339
|
+
justify-content: center;
|
|
340
|
+
font-size: 1.25rem;
|
|
341
|
+
flex-shrink: 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.app-info {
|
|
345
|
+
flex: 1;
|
|
346
|
+
min-width: 0;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.app-info h3 {
|
|
350
|
+
font-size: 0.9375rem;
|
|
351
|
+
font-weight: 600;
|
|
352
|
+
color: #0f172a;
|
|
353
|
+
margin: 0;
|
|
354
|
+
display: flex;
|
|
355
|
+
align-items: center;
|
|
356
|
+
gap: 0.5rem;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.app-info p {
|
|
360
|
+
font-size: 0.8125rem;
|
|
361
|
+
color: #64748b;
|
|
362
|
+
margin: 0.25rem 0 0;
|
|
363
|
+
white-space: nowrap;
|
|
364
|
+
overflow: hidden;
|
|
365
|
+
text-overflow: ellipsis;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.app-badge {
|
|
369
|
+
font-size: 0.625rem;
|
|
370
|
+
font-weight: 600;
|
|
371
|
+
text-transform: uppercase;
|
|
372
|
+
padding: 0.125rem 0.375rem;
|
|
373
|
+
border-radius: 4px;
|
|
374
|
+
background: #e2e8f0;
|
|
375
|
+
color: #475569;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.app-badge.new {
|
|
379
|
+
background: #dcfce7;
|
|
380
|
+
color: #16a34a;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.app-badge.beta {
|
|
384
|
+
background: #fef3c7;
|
|
385
|
+
color: #d97706;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.app-arrow {
|
|
389
|
+
color: #cbd5e1;
|
|
390
|
+
transition: color 0.15s, transform 0.15s;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.app-card:hover .app-arrow {
|
|
394
|
+
color: var(--cat-color, #3b82f6);
|
|
395
|
+
transform: translateX(3px);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* Section Divider */
|
|
399
|
+
.section-divider {
|
|
400
|
+
max-width: 1400px;
|
|
401
|
+
margin: 2rem auto;
|
|
402
|
+
height: 1px;
|
|
403
|
+
background: linear-gradient(90deg, transparent, #cbd5e1, transparent);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* Empty State */
|
|
407
|
+
.empty-state {
|
|
408
|
+
text-align: center;
|
|
409
|
+
padding: 4rem 2rem;
|
|
410
|
+
color: #64748b;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.empty-state i {
|
|
414
|
+
font-size: 4rem;
|
|
415
|
+
color: #cbd5e1;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.empty-state h3 {
|
|
419
|
+
font-size: 1.25rem;
|
|
420
|
+
font-weight: 600;
|
|
421
|
+
margin: 1rem 0 0.5rem;
|
|
422
|
+
color: #475569;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.empty-state p {
|
|
426
|
+
margin: 0;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* Footer */
|
|
430
|
+
.footer {
|
|
431
|
+
display: flex;
|
|
432
|
+
align-items: center;
|
|
433
|
+
justify-content: space-between;
|
|
434
|
+
padding: 1rem 2rem;
|
|
435
|
+
background: #fff;
|
|
436
|
+
border-top: 1px solid #e2e8f0;
|
|
437
|
+
font-size: 0.8125rem;
|
|
438
|
+
color: #64748b;
|
|
439
|
+
flex-shrink: 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.footer-left {
|
|
443
|
+
display: flex;
|
|
444
|
+
align-items: center;
|
|
445
|
+
gap: 0.375rem;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.footer-left strong {
|
|
449
|
+
color: #334155;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* Responsive */
|
|
453
|
+
@media (max-width: 640px) {
|
|
454
|
+
.navbar {
|
|
455
|
+
padding: 0 1rem;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.navbar-title .subtitle {
|
|
459
|
+
display: none;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.user-info {
|
|
463
|
+
display: none;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.content {
|
|
467
|
+
padding: 1.5rem 1rem;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.apps-grid {
|
|
471
|
+
grid-template-columns: 1fr;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.footer {
|
|
475
|
+
flex-direction: column;
|
|
476
|
+
gap: 0.5rem;
|
|
477
|
+
text-align: center;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AppDefinition, CategoryInfo } from '../Auth/types.js';
|
|
2
|
+
import type { AuthState } from '../Auth/auth.svelte.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Auth state instance */
|
|
5
|
+
auth: AuthState;
|
|
6
|
+
/** Page title */
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Page subtitle */
|
|
9
|
+
subtitle?: string;
|
|
10
|
+
/** Logo URL */
|
|
11
|
+
logo?: string;
|
|
12
|
+
/** Categories to display */
|
|
13
|
+
categories: CategoryInfo[];
|
|
14
|
+
/** Apps to display */
|
|
15
|
+
apps: AppDefinition[];
|
|
16
|
+
/** Show only accessible apps (based on permissions) */
|
|
17
|
+
filterByPermissions?: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare const LandingPage: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type LandingPage = ReturnType<typeof LandingPage>;
|
|
21
|
+
export default LandingPage;
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -19,3 +19,7 @@ export * from './CellTable/index.js';
|
|
|
19
19
|
export * from './FeatureRegistry/index.js';
|
|
20
20
|
// Benchmark - Performance testing for Svelte reactivity
|
|
21
21
|
export * from './Benchmark/index.js';
|
|
22
|
+
// Auth - Authentication and authorization system
|
|
23
|
+
export * from './Auth/index.js';
|
|
24
|
+
// LandingPage - Main application landing page with app grid
|
|
25
|
+
export * from './LandingPage/index.js';
|