@dssp/dkpi 1.0.0-alpha.71 → 1.0.0-alpha.74

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.
@@ -1,5 +1,6 @@
1
1
  module.exports = {
2
- subdomain: 'system',
2
+ subdomain: null,
3
+ domainTypes: ['domain', 'project'],
3
4
  applianceJwtExpiresIn: '10y',
4
5
  /* database field encryption key : 32 bytes - must be changed by every installation */
5
6
  dataEncryptionKey: 'V6g5oHJZb7KcazJyL6cM95XvIDouon5b',
@@ -1,5 +1,6 @@
1
1
  module.exports = {
2
- subdomain: 'system',
2
+ subdomain: null,
3
+ domainTypes: ['domain', 'project'],
3
4
  applianceJwtExpiresIn: '10y',
4
5
  /* database field encryption key : 32 bytes - must be changed by every installation */
5
6
  dataEncryptionKey: 'V6g5oHJZb7KcazJyL6cM95XvIDouon5b',
@@ -0,0 +1,38 @@
1
+ import '@material/web/icon/icon.js';
2
+ import '@material/web/button/elevated-button.js';
3
+ import '@operato/lottie-player';
4
+ import { LitElement } from 'lit';
5
+ declare const AuthCheckIn_base: (new (...args: any[]) => LitElement) & typeof LitElement;
6
+ export declare class AuthCheckIn extends AuthCheckIn_base {
7
+ static styles: import("lit").CSSResult[];
8
+ data: any;
9
+ domains: any[];
10
+ domainTypes?: string[];
11
+ user: any;
12
+ searchValue: string;
13
+ collapsedSections: Record<string, boolean>;
14
+ private _applicationMeta?;
15
+ private redirectTo?;
16
+ render(): import("lit-html").TemplateResult<1>;
17
+ private _renderCategory;
18
+ /**
19
+ * groups 를 CATEGORY_ORDER 에 따라 정렬한 [category, items][] 로 반환.
20
+ * 정의된 순서대로 먼저 나열, 정의 안 된 카테고리는 마지막에 insertion 순으로 붙는다.
21
+ */
22
+ private _orderedEntries;
23
+ /**
24
+ * 도메인들을 extType 별로 그룹핑하고 검색어로 필터링한다.
25
+ * 매치 0건인 카테고리는 결과에서 자동 제외 — 빈 섹션은 화면에 노출되지 않는다.
26
+ */
27
+ private _groupAndFilter;
28
+ private _select;
29
+ private _toggleSection;
30
+ private _categoryLabel;
31
+ updated(changed: Map<string, unknown>): void;
32
+ get applicationMeta(): {
33
+ icon?: string;
34
+ title?: string;
35
+ description?: string;
36
+ };
37
+ }
38
+ export {};
@@ -0,0 +1,546 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import '@material/web/button/elevated-button.js';
4
+ import '@operato/lottie-player';
5
+ import { css, html, LitElement } from 'lit';
6
+ import { customElement, property, state } from 'lit/decorators.js';
7
+ import { i18next, localize } from '@operato/i18n';
8
+ import { ScrollbarStyles } from '@operato/styles';
9
+ import { isSafari } from '@operato/utils';
10
+ import { AUTH_STYLE_SIGN } from '@things-factory/auth-ui/dist-client/auth-style-sign.js';
11
+ const CATEGORY_ICON = {
12
+ domain: 'business',
13
+ project: 'assignment',
14
+ company: 'corporate_fare',
15
+ equipment: 'precision_manufacturing'
16
+ };
17
+ const CATEGORY_FALLBACK_ICON = 'folder';
18
+ /** 카테고리 표시 순서. 여기 없는 카테고리는 뒤에 insertion 순으로 붙는다. */
19
+ const CATEGORY_ORDER = ['domain', 'project', 'company', 'equipment'];
20
+ let AuthCheckIn = class AuthCheckIn extends localize(i18next)(LitElement) {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.domains = [];
24
+ this.searchValue = '';
25
+ this.collapsedSections = {};
26
+ }
27
+ render() {
28
+ var _a;
29
+ const { icon, title, description } = this.applicationMeta;
30
+ const groups = this._groupAndFilter();
31
+ const totalShown = Object.values(groups).reduce((sum, g) => sum + g.length, 0);
32
+ const hasAnyDomain = (((_a = this.domains) === null || _a === void 0 ? void 0 : _a.length) || 0) > 0;
33
+ const groupEntries = this._orderedEntries(groups);
34
+ return html `
35
+ <div class="content md-typescale-display-medium">
36
+ <div class="wrap">
37
+ <div class="auth-brand">
38
+ <img src=${icon || ''} />
39
+ <strong class="name">${title}</strong>
40
+ <span class="welcome-msg">${description}</span>
41
+ </div>
42
+
43
+ <h3>${i18next.t('label.select_domain')}</h3>
44
+
45
+ ${hasAnyDomain
46
+ ? html `
47
+ <div class="search-box">
48
+ <md-icon>search</md-icon>
49
+ <input
50
+ type="text"
51
+ placeholder=${i18next.t('label.search_domain')}
52
+ .value=${this.searchValue}
53
+ @input=${(e) => (this.searchValue = e.target.value)}
54
+ />
55
+ </div>
56
+ `
57
+ : ''}
58
+
59
+ ${!hasAnyDomain
60
+ ? html `
61
+ <div class="empty-search">
62
+ <md-icon>inbox</md-icon>
63
+ <div class="text">${i18next.t('text.no domain available')}</div>
64
+ </div>
65
+ `
66
+ : totalShown === 0
67
+ ? html `
68
+ <div class="empty-search">
69
+ <md-icon>search_off</md-icon>
70
+ <div class="text">
71
+ ${i18next.t('text.no_matching_domain', { defaultValue: '검색 결과가 없습니다.' })}
72
+ </div>
73
+ </div>
74
+ `
75
+ : html `
76
+ <div class="domain-groups">
77
+ ${groupEntries.map(([category, items]) => this._renderCategory(category, items))}
78
+ </div>
79
+ `}
80
+
81
+ <div class="button-container">
82
+ <md-elevated-button @click=${() => (location.pathname = '/auth/signout')}
83
+ >${String(i18next.t('button.logout'))}</md-elevated-button
84
+ >
85
+ </div>
86
+
87
+ ${isSafari()
88
+ ? html ``
89
+ : html `
90
+ <div class="lottie-container">
91
+ <lottie-player autoplay loop src="../../assets/images/background-animation.json"></lottie-player>
92
+ </div>
93
+ `}
94
+ </div>
95
+ </div>
96
+ `;
97
+ }
98
+ _renderCategory(category, items) {
99
+ const collapsed = !!this.collapsedSections[category];
100
+ return html `
101
+ <section class="category">
102
+ <header @click=${() => this._toggleSection(category)}>
103
+ <span class="category-icon">
104
+ <md-icon>${CATEGORY_ICON[category] || CATEGORY_FALLBACK_ICON}</md-icon>
105
+ </span>
106
+ <span class="label">${this._categoryLabel(category)}</span>
107
+ <span class="count">${items.length}</span>
108
+ <md-icon class="chevron ${collapsed ? 'collapsed' : ''}">expand_more</md-icon>
109
+ </header>
110
+ ${collapsed
111
+ ? ''
112
+ : html `
113
+ <ul class="domains">
114
+ ${items.map(domain => html `
115
+ <li
116
+ @click=${() => this._select(domain)}
117
+ title=${domain.description ? `${domain.name}\n${domain.description}` : domain.name}
118
+ >
119
+ <div class="info">
120
+ <div class="primary">
121
+ <strong>${domain.name}</strong>
122
+ <span class="subdomain">${domain.subdomain}</span>
123
+ </div>
124
+ ${domain.description ? html `<div class="desc">${domain.description}</div>` : ''}
125
+ </div>
126
+ <md-icon class="arrow">arrow_forward</md-icon>
127
+ </li>
128
+ `)}
129
+ </ul>
130
+ `}
131
+ </section>
132
+ `;
133
+ }
134
+ /**
135
+ * groups 를 CATEGORY_ORDER 에 따라 정렬한 [category, items][] 로 반환.
136
+ * 정의된 순서대로 먼저 나열, 정의 안 된 카테고리는 마지막에 insertion 순으로 붙는다.
137
+ */
138
+ _orderedEntries(groups) {
139
+ var _a;
140
+ const ordered = [];
141
+ const seen = new Set();
142
+ for (const cat of CATEGORY_ORDER) {
143
+ if ((_a = groups[cat]) === null || _a === void 0 ? void 0 : _a.length) {
144
+ ordered.push([cat, groups[cat]]);
145
+ seen.add(cat);
146
+ }
147
+ }
148
+ for (const [cat, items] of Object.entries(groups)) {
149
+ if (!seen.has(cat))
150
+ ordered.push([cat, items]);
151
+ }
152
+ return ordered;
153
+ }
154
+ /**
155
+ * 도메인들을 extType 별로 그룹핑하고 검색어로 필터링한다.
156
+ * 매치 0건인 카테고리는 결과에서 자동 제외 — 빈 섹션은 화면에 노출되지 않는다.
157
+ */
158
+ _groupAndFilter() {
159
+ var _a, _b, _c, _d, _e, _f;
160
+ const keyword = this.searchValue.toLowerCase().trim();
161
+ const groups = {};
162
+ for (const domain of this.domains || []) {
163
+ if (keyword) {
164
+ const nameMatch = (_b = (_a = domain.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(keyword)) !== null && _b !== void 0 ? _b : false;
165
+ const descMatch = (_d = (_c = domain.description) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes(keyword)) !== null && _d !== void 0 ? _d : false;
166
+ const subMatch = (_f = (_e = domain.subdomain) === null || _e === void 0 ? void 0 : _e.toLowerCase().includes(keyword)) !== null && _f !== void 0 ? _f : false;
167
+ if (!nameMatch && !descMatch && !subMatch)
168
+ continue;
169
+ }
170
+ const category = domain.extType || 'domain';
171
+ (groups[category] || (groups[category] = [])).push(domain);
172
+ }
173
+ return groups;
174
+ }
175
+ _select(domain) {
176
+ location.href = `/auth/checkin/${domain.subdomain}?redirect_to=${encodeURIComponent(this.redirectTo || '/')}`;
177
+ }
178
+ _toggleSection(category) {
179
+ this.collapsedSections = Object.assign(Object.assign({}, this.collapsedSections), { [category]: !this.collapsedSections[category] });
180
+ }
181
+ _categoryLabel(category) {
182
+ return i18next.t(`label.domain_type.${category}`, { defaultValue: category });
183
+ }
184
+ updated(changed) {
185
+ var _a, _b, _c, _d;
186
+ if (changed.has('data')) {
187
+ this.domains = ((_a = this.data) === null || _a === void 0 ? void 0 : _a.domains) || [];
188
+ this.user = (_b = this.data) === null || _b === void 0 ? void 0 : _b.user;
189
+ this.domainTypes = (_c = this.data) === null || _c === void 0 ? void 0 : _c.domainTypes;
190
+ this.redirectTo = (_d = this.data) === null || _d === void 0 ? void 0 : _d.redirectTo;
191
+ this.requestUpdate();
192
+ }
193
+ }
194
+ get applicationMeta() {
195
+ if (!this._applicationMeta) {
196
+ const iconLink = document.querySelector('link[rel="application-icon"]');
197
+ const titleMeta = document.querySelector('meta[name="application-name"]');
198
+ const descriptionMeta = document.querySelector('meta[name="application-description"]');
199
+ this._applicationMeta = {
200
+ icon: iconLink === null || iconLink === void 0 ? void 0 : iconLink.href,
201
+ title: titleMeta ? titleMeta.content : 'Things Factory',
202
+ description: descriptionMeta ? descriptionMeta.content : 'Reimagining Software'
203
+ };
204
+ }
205
+ return this._applicationMeta;
206
+ }
207
+ };
208
+ AuthCheckIn.styles = [
209
+ ScrollbarStyles,
210
+ css `
211
+ :host {
212
+ display: flex;
213
+ flex-direction: column;
214
+ margin: auto;
215
+ background: linear-gradient(
216
+ 135deg,
217
+ var(--md-sys-color-primary) 0%,
218
+ color-mix(in srgb, var(--md-sys-color-primary) 70%, #000) 100%
219
+ );
220
+ color: var(--md-sys-color-on-primary);
221
+ height: 100vh;
222
+ height: 100dvh;
223
+ }
224
+
225
+ .content {
226
+ flex: 1;
227
+ overflow: auto;
228
+ }
229
+
230
+ h3 {
231
+ margin: 6px 0 14px;
232
+ font-size: 13px;
233
+ font-weight: 500;
234
+ letter-spacing: 0.08em;
235
+ text-transform: uppercase;
236
+ opacity: 0.75;
237
+ }
238
+
239
+ /* ─── Search ─── */
240
+ .search-box {
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 10px;
244
+ margin: 0 0 20px;
245
+ padding: 12px 18px;
246
+ background: rgba(255, 255, 255, 0.08);
247
+ color: var(--md-sys-color-on-primary);
248
+ border-radius: 14px;
249
+ border: 1px solid rgba(255, 255, 255, 0.12);
250
+ backdrop-filter: blur(10px);
251
+ -webkit-backdrop-filter: blur(10px);
252
+ transition: all 0.18s ease;
253
+ }
254
+
255
+ .search-box:focus-within {
256
+ background: rgba(255, 255, 255, 0.14);
257
+ border-color: rgba(255, 255, 255, 0.3);
258
+ box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.06);
259
+ }
260
+
261
+ .search-box md-icon {
262
+ --md-icon-size: 20px;
263
+ opacity: 0.8;
264
+ }
265
+
266
+ .search-box input {
267
+ flex: 1;
268
+ border: none;
269
+ outline: none;
270
+ background: transparent;
271
+ font-size: 15px;
272
+ color: var(--md-sys-color-on-primary);
273
+ }
274
+
275
+ .search-box input::placeholder {
276
+ color: rgba(255, 255, 255, 0.5);
277
+ }
278
+
279
+ /* ─── Group of categories ─── */
280
+ .domain-groups {
281
+ display: flex;
282
+ flex-direction: column;
283
+ gap: 14px;
284
+ }
285
+
286
+ /* ─── Category section ─── */
287
+ section.category {
288
+ background: rgba(255, 255, 255, 0.96);
289
+ color: #1d2125;
290
+ border-radius: 16px;
291
+ overflow: hidden;
292
+ box-shadow:
293
+ 0 10px 30px -8px rgba(0, 0, 0, 0.25),
294
+ 0 2px 6px -2px rgba(0, 0, 0, 0.15);
295
+ animation: slide-up 0.22s ease both;
296
+ }
297
+
298
+ @keyframes slide-up {
299
+ from {
300
+ opacity: 0;
301
+ transform: translateY(6px);
302
+ }
303
+ to {
304
+ opacity: 1;
305
+ transform: translateY(0);
306
+ }
307
+ }
308
+
309
+ section.category > header {
310
+ display: flex;
311
+ align-items: center;
312
+ gap: 10px;
313
+ padding: 12px 18px;
314
+ cursor: pointer;
315
+ user-select: none;
316
+ transition: background 0.15s ease;
317
+ }
318
+
319
+ section.category > header:hover {
320
+ background: rgba(0, 0, 0, 0.03);
321
+ }
322
+
323
+ section.category > header .category-icon {
324
+ display: inline-flex;
325
+ align-items: center;
326
+ justify-content: center;
327
+ width: 32px;
328
+ height: 32px;
329
+ border-radius: 10px;
330
+ background: color-mix(in srgb, var(--md-sys-color-primary) 14%, transparent);
331
+ color: var(--md-sys-color-primary);
332
+ flex-shrink: 0;
333
+ }
334
+
335
+ section.category > header .category-icon md-icon {
336
+ --md-icon-size: 18px;
337
+ }
338
+
339
+ section.category > header .label {
340
+ font-weight: 600;
341
+ font-size: 14px;
342
+ flex: 1;
343
+ text-align: left;
344
+ letter-spacing: 0.01em;
345
+ }
346
+
347
+ section.category > header .count {
348
+ background: color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent);
349
+ color: var(--md-sys-color-primary);
350
+ border-radius: 999px;
351
+ padding: 2px 10px;
352
+ font-size: 12px;
353
+ font-weight: 600;
354
+ min-width: 26px;
355
+ text-align: center;
356
+ }
357
+
358
+ section.category > header .chevron {
359
+ --md-icon-size: 20px;
360
+ opacity: 0.5;
361
+ transition: transform 0.18s ease;
362
+ }
363
+
364
+ section.category > header .chevron.collapsed {
365
+ transform: rotate(-90deg);
366
+ }
367
+
368
+ /* ─── Domain list ─── */
369
+ ul.domains {
370
+ margin: 0;
371
+ padding: 4px 0 8px;
372
+ list-style: none;
373
+ }
374
+
375
+ ul.domains li {
376
+ display: flex;
377
+ align-items: center;
378
+ gap: 12px;
379
+ margin: 0 8px;
380
+ padding: 8px 14px;
381
+ cursor: pointer;
382
+ text-align: left;
383
+ border-radius: 10px;
384
+ transition: all 0.15s ease;
385
+ position: relative;
386
+ }
387
+
388
+ ul.domains li::before {
389
+ content: '';
390
+ position: absolute;
391
+ left: -8px;
392
+ right: -8px;
393
+ top: 0;
394
+ bottom: 0;
395
+ border-radius: 10px;
396
+ background: linear-gradient(
397
+ 90deg,
398
+ color-mix(in srgb, var(--md-sys-color-primary) 8%, transparent),
399
+ transparent
400
+ );
401
+ opacity: 0;
402
+ transition: opacity 0.15s ease;
403
+ pointer-events: none;
404
+ }
405
+
406
+ ul.domains li:hover {
407
+ transform: translateX(3px);
408
+ }
409
+
410
+ ul.domains li:hover::before {
411
+ opacity: 1;
412
+ }
413
+
414
+ ul.domains li:hover md-icon.arrow {
415
+ opacity: 1;
416
+ transform: translateX(3px);
417
+ color: var(--md-sys-color-primary);
418
+ }
419
+
420
+ ul.domains li .info {
421
+ flex: 1;
422
+ min-width: 0;
423
+ display: flex;
424
+ flex-direction: column;
425
+ gap: 2px;
426
+ z-index: 1;
427
+ }
428
+
429
+ ul.domains li .primary {
430
+ display: flex;
431
+ align-items: baseline;
432
+ justify-content: space-between;
433
+ gap: 12px;
434
+ min-width: 0;
435
+ }
436
+
437
+ ul.domains li .primary strong {
438
+ font-size: 15px;
439
+ font-weight: 600;
440
+ color: #1d2125;
441
+ overflow: hidden;
442
+ text-overflow: ellipsis;
443
+ white-space: nowrap;
444
+ min-width: 0;
445
+ }
446
+
447
+ ul.domains li .subdomain {
448
+ font-size: 11px;
449
+ color: var(--md-sys-color-primary);
450
+ font-family: var(--theme-font-mono, ui-monospace, monospace);
451
+ font-weight: 600;
452
+ flex-shrink: 0;
453
+ }
454
+
455
+ ul.domains li .desc {
456
+ font-size: 12px;
457
+ color: #5a6168;
458
+ overflow: hidden;
459
+ text-overflow: ellipsis;
460
+ white-space: nowrap;
461
+ }
462
+
463
+ ul.domains li md-icon.arrow {
464
+ opacity: 0.3;
465
+ --md-icon-size: 22px;
466
+ transition: all 0.18s ease;
467
+ z-index: 1;
468
+ flex-shrink: 0;
469
+ }
470
+
471
+ /* ─── Empty state ─── */
472
+ .empty-search {
473
+ display: flex;
474
+ flex-direction: column;
475
+ align-items: center;
476
+ justify-content: center;
477
+ gap: 8px;
478
+ margin: 24px 0;
479
+ padding: 32px 16px;
480
+ background: rgba(255, 255, 255, 0.05);
481
+ border-radius: 14px;
482
+ border: 1px dashed rgba(255, 255, 255, 0.18);
483
+ opacity: 0.85;
484
+ }
485
+
486
+ .empty-search md-icon {
487
+ --md-icon-size: 32px;
488
+ opacity: 0.5;
489
+ }
490
+
491
+ .empty-search .text {
492
+ font-size: 14px;
493
+ opacity: 0.8;
494
+ }
495
+
496
+ /* ─── Footer button ─── */
497
+ .button-container {
498
+ text-align: center;
499
+ margin-top: 24px;
500
+ }
501
+
502
+ .button-container md-elevated-button {
503
+ margin-left: var(--spacing-small);
504
+ --md-elevated-button-container-color: rgba(255, 255, 255, 0.12);
505
+ --md-elevated-button-label-text-color: var(--md-sys-color-on-primary);
506
+ --md-elevated-button-container-shadow-color: transparent;
507
+ }
508
+
509
+ @media (max-width: 450px) {
510
+ .button-container md-elevated-button {
511
+ width: 100%;
512
+ margin: var(--spacing-medium) 0 0 0;
513
+ }
514
+ }
515
+ `,
516
+ AUTH_STYLE_SIGN
517
+ ];
518
+ __decorate([
519
+ property({ type: Object }),
520
+ __metadata("design:type", Object)
521
+ ], AuthCheckIn.prototype, "data", void 0);
522
+ __decorate([
523
+ property({ type: Array }),
524
+ __metadata("design:type", Array)
525
+ ], AuthCheckIn.prototype, "domains", void 0);
526
+ __decorate([
527
+ property({ type: Array }),
528
+ __metadata("design:type", Array)
529
+ ], AuthCheckIn.prototype, "domainTypes", void 0);
530
+ __decorate([
531
+ property({ type: Object }),
532
+ __metadata("design:type", Object)
533
+ ], AuthCheckIn.prototype, "user", void 0);
534
+ __decorate([
535
+ state(),
536
+ __metadata("design:type", String)
537
+ ], AuthCheckIn.prototype, "searchValue", void 0);
538
+ __decorate([
539
+ state(),
540
+ __metadata("design:type", Object)
541
+ ], AuthCheckIn.prototype, "collapsedSections", void 0);
542
+ AuthCheckIn = __decorate([
543
+ customElement('auth-checkin')
544
+ ], AuthCheckIn);
545
+ export { AuthCheckIn };
546
+ //# sourceMappingURL=checkin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkin.js","sourceRoot":"","sources":["../../../client/entries/auth/checkin.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\n\nimport '@operato/lottie-player'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { isSafari } from '@operato/utils'\n\nimport { AUTH_STYLE_SIGN } from '@things-factory/auth-ui/dist-client/auth-style-sign.js'\n\nconst CATEGORY_ICON: Record<string, string> = {\n domain: 'business',\n project: 'assignment',\n company: 'corporate_fare',\n equipment: 'precision_manufacturing'\n}\n\nconst CATEGORY_FALLBACK_ICON = 'folder'\n\n/** 카테고리 표시 순서. 여기 없는 카테고리는 뒤에 insertion 순으로 붙는다. */\nconst CATEGORY_ORDER = ['domain', 'project', 'company', 'equipment']\n\n@customElement('auth-checkin')\nexport class AuthCheckIn extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n margin: auto;\n background: linear-gradient(\n 135deg,\n var(--md-sys-color-primary) 0%,\n color-mix(in srgb, var(--md-sys-color-primary) 70%, #000) 100%\n );\n color: var(--md-sys-color-on-primary);\n height: 100vh;\n height: 100dvh;\n }\n\n .content {\n flex: 1;\n overflow: auto;\n }\n\n h3 {\n margin: 6px 0 14px;\n font-size: 13px;\n font-weight: 500;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n opacity: 0.75;\n }\n\n /* ─── Search ─── */\n .search-box {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0 0 20px;\n padding: 12px 18px;\n background: rgba(255, 255, 255, 0.08);\n color: var(--md-sys-color-on-primary);\n border-radius: 14px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n transition: all 0.18s ease;\n }\n\n .search-box:focus-within {\n background: rgba(255, 255, 255, 0.14);\n border-color: rgba(255, 255, 255, 0.3);\n box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.06);\n }\n\n .search-box md-icon {\n --md-icon-size: 20px;\n opacity: 0.8;\n }\n\n .search-box input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n font-size: 15px;\n color: var(--md-sys-color-on-primary);\n }\n\n .search-box input::placeholder {\n color: rgba(255, 255, 255, 0.5);\n }\n\n /* ─── Group of categories ─── */\n .domain-groups {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n\n /* ─── Category section ─── */\n section.category {\n background: rgba(255, 255, 255, 0.96);\n color: #1d2125;\n border-radius: 16px;\n overflow: hidden;\n box-shadow:\n 0 10px 30px -8px rgba(0, 0, 0, 0.25),\n 0 2px 6px -2px rgba(0, 0, 0, 0.15);\n animation: slide-up 0.22s ease both;\n }\n\n @keyframes slide-up {\n from {\n opacity: 0;\n transform: translateY(6px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n section.category > header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 18px;\n cursor: pointer;\n user-select: none;\n transition: background 0.15s ease;\n }\n\n section.category > header:hover {\n background: rgba(0, 0, 0, 0.03);\n }\n\n section.category > header .category-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 10px;\n background: color-mix(in srgb, var(--md-sys-color-primary) 14%, transparent);\n color: var(--md-sys-color-primary);\n flex-shrink: 0;\n }\n\n section.category > header .category-icon md-icon {\n --md-icon-size: 18px;\n }\n\n section.category > header .label {\n font-weight: 600;\n font-size: 14px;\n flex: 1;\n text-align: left;\n letter-spacing: 0.01em;\n }\n\n section.category > header .count {\n background: color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent);\n color: var(--md-sys-color-primary);\n border-radius: 999px;\n padding: 2px 10px;\n font-size: 12px;\n font-weight: 600;\n min-width: 26px;\n text-align: center;\n }\n\n section.category > header .chevron {\n --md-icon-size: 20px;\n opacity: 0.5;\n transition: transform 0.18s ease;\n }\n\n section.category > header .chevron.collapsed {\n transform: rotate(-90deg);\n }\n\n /* ─── Domain list ─── */\n ul.domains {\n margin: 0;\n padding: 4px 0 8px;\n list-style: none;\n }\n\n ul.domains li {\n display: flex;\n align-items: center;\n gap: 12px;\n margin: 0 8px;\n padding: 8px 14px;\n cursor: pointer;\n text-align: left;\n border-radius: 10px;\n transition: all 0.15s ease;\n position: relative;\n }\n\n ul.domains li::before {\n content: '';\n position: absolute;\n left: -8px;\n right: -8px;\n top: 0;\n bottom: 0;\n border-radius: 10px;\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--md-sys-color-primary) 8%, transparent),\n transparent\n );\n opacity: 0;\n transition: opacity 0.15s ease;\n pointer-events: none;\n }\n\n ul.domains li:hover {\n transform: translateX(3px);\n }\n\n ul.domains li:hover::before {\n opacity: 1;\n }\n\n ul.domains li:hover md-icon.arrow {\n opacity: 1;\n transform: translateX(3px);\n color: var(--md-sys-color-primary);\n }\n\n ul.domains li .info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n z-index: 1;\n }\n\n ul.domains li .primary {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n min-width: 0;\n }\n\n ul.domains li .primary strong {\n font-size: 15px;\n font-weight: 600;\n color: #1d2125;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n }\n\n ul.domains li .subdomain {\n font-size: 11px;\n color: var(--md-sys-color-primary);\n font-family: var(--theme-font-mono, ui-monospace, monospace);\n font-weight: 600;\n flex-shrink: 0;\n }\n\n ul.domains li .desc {\n font-size: 12px;\n color: #5a6168;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n ul.domains li md-icon.arrow {\n opacity: 0.3;\n --md-icon-size: 22px;\n transition: all 0.18s ease;\n z-index: 1;\n flex-shrink: 0;\n }\n\n /* ─── Empty state ─── */\n .empty-search {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin: 24px 0;\n padding: 32px 16px;\n background: rgba(255, 255, 255, 0.05);\n border-radius: 14px;\n border: 1px dashed rgba(255, 255, 255, 0.18);\n opacity: 0.85;\n }\n\n .empty-search md-icon {\n --md-icon-size: 32px;\n opacity: 0.5;\n }\n\n .empty-search .text {\n font-size: 14px;\n opacity: 0.8;\n }\n\n /* ─── Footer button ─── */\n .button-container {\n text-align: center;\n margin-top: 24px;\n }\n\n .button-container md-elevated-button {\n margin-left: var(--spacing-small);\n --md-elevated-button-container-color: rgba(255, 255, 255, 0.12);\n --md-elevated-button-label-text-color: var(--md-sys-color-on-primary);\n --md-elevated-button-container-shadow-color: transparent;\n }\n\n @media (max-width: 450px) {\n .button-container md-elevated-button {\n width: 100%;\n margin: var(--spacing-medium) 0 0 0;\n }\n }\n `,\n AUTH_STYLE_SIGN\n ]\n\n @property({ type: Object }) data: any\n @property({ type: Array }) domains: any[] = []\n @property({ type: Array }) domainTypes?: string[]\n @property({ type: Object }) user: any\n\n @state() searchValue: string = ''\n @state() collapsedSections: Record<string, boolean> = {}\n\n private _applicationMeta?: { icon?: string; title?: string; description?: string }\n private redirectTo?: string\n\n render() {\n const { icon, title, description } = this.applicationMeta\n const groups = this._groupAndFilter()\n const totalShown = Object.values(groups).reduce((sum, g) => sum + g.length, 0)\n const hasAnyDomain = (this.domains?.length || 0) > 0\n const groupEntries = this._orderedEntries(groups)\n\n return html`\n <div class=\"content md-typescale-display-medium\">\n <div class=\"wrap\">\n <div class=\"auth-brand\">\n <img src=${icon || ''} />\n <strong class=\"name\">${title}</strong>\n <span class=\"welcome-msg\">${description}</span>\n </div>\n\n <h3>${i18next.t('label.select_domain')}</h3>\n\n ${hasAnyDomain\n ? html`\n <div class=\"search-box\">\n <md-icon>search</md-icon>\n <input\n type=\"text\"\n placeholder=${i18next.t('label.search_domain')}\n .value=${this.searchValue}\n @input=${(e: InputEvent) => (this.searchValue = (e.target as HTMLInputElement).value)}\n />\n </div>\n `\n : ''}\n\n ${!hasAnyDomain\n ? html`\n <div class=\"empty-search\">\n <md-icon>inbox</md-icon>\n <div class=\"text\">${i18next.t('text.no domain available')}</div>\n </div>\n `\n : totalShown === 0\n ? html`\n <div class=\"empty-search\">\n <md-icon>search_off</md-icon>\n <div class=\"text\">\n ${i18next.t('text.no_matching_domain', { defaultValue: '검색 결과가 없습니다.' })}\n </div>\n </div>\n `\n : html`\n <div class=\"domain-groups\">\n ${groupEntries.map(([category, items]) => this._renderCategory(category, items))}\n </div>\n `}\n\n <div class=\"button-container\">\n <md-elevated-button @click=${() => (location.pathname = '/auth/signout')}\n >${String(i18next.t('button.logout'))}</md-elevated-button\n >\n </div>\n\n ${isSafari()\n ? html``\n : html`\n <div class=\"lottie-container\">\n <lottie-player autoplay loop src=\"../../assets/images/background-animation.json\"></lottie-player>\n </div>\n `}\n </div>\n </div>\n `\n }\n\n private _renderCategory(category: string, items: any[]) {\n const collapsed = !!this.collapsedSections[category]\n return html`\n <section class=\"category\">\n <header @click=${() => this._toggleSection(category)}>\n <span class=\"category-icon\">\n <md-icon>${CATEGORY_ICON[category] || CATEGORY_FALLBACK_ICON}</md-icon>\n </span>\n <span class=\"label\">${this._categoryLabel(category)}</span>\n <span class=\"count\">${items.length}</span>\n <md-icon class=\"chevron ${collapsed ? 'collapsed' : ''}\">expand_more</md-icon>\n </header>\n ${collapsed\n ? ''\n : html`\n <ul class=\"domains\">\n ${items.map(\n domain => html`\n <li\n @click=${() => this._select(domain)}\n title=${domain.description ? `${domain.name}\\n${domain.description}` : domain.name}\n >\n <div class=\"info\">\n <div class=\"primary\">\n <strong>${domain.name}</strong>\n <span class=\"subdomain\">${domain.subdomain}</span>\n </div>\n ${domain.description ? html`<div class=\"desc\">${domain.description}</div>` : ''}\n </div>\n <md-icon class=\"arrow\">arrow_forward</md-icon>\n </li>\n `\n )}\n </ul>\n `}\n </section>\n `\n }\n\n /**\n * groups 를 CATEGORY_ORDER 에 따라 정렬한 [category, items][] 로 반환.\n * 정의된 순서대로 먼저 나열, 정의 안 된 카테고리는 마지막에 insertion 순으로 붙는다.\n */\n private _orderedEntries(groups: Record<string, any[]>): [string, any[]][] {\n const ordered: [string, any[]][] = []\n const seen = new Set<string>()\n\n for (const cat of CATEGORY_ORDER) {\n if (groups[cat]?.length) {\n ordered.push([cat, groups[cat]])\n seen.add(cat)\n }\n }\n for (const [cat, items] of Object.entries(groups)) {\n if (!seen.has(cat)) ordered.push([cat, items])\n }\n return ordered\n }\n\n /**\n * 도메인들을 extType 별로 그룹핑하고 검색어로 필터링한다.\n * 매치 0건인 카테고리는 결과에서 자동 제외 — 빈 섹션은 화면에 노출되지 않는다.\n */\n private _groupAndFilter(): Record<string, any[]> {\n const keyword = this.searchValue.toLowerCase().trim()\n const groups: Record<string, any[]> = {}\n\n for (const domain of this.domains || []) {\n if (keyword) {\n const nameMatch = domain.name?.toLowerCase().includes(keyword) ?? false\n const descMatch = domain.description?.toLowerCase().includes(keyword) ?? false\n const subMatch = domain.subdomain?.toLowerCase().includes(keyword) ?? false\n if (!nameMatch && !descMatch && !subMatch) continue\n }\n const category = domain.extType || 'domain'\n ;(groups[category] ||= []).push(domain)\n }\n\n return groups\n }\n\n private _select(domain: any) {\n location.href = `/auth/checkin/${domain.subdomain}?redirect_to=${encodeURIComponent(this.redirectTo || '/')}`\n }\n\n private _toggleSection(category: string) {\n this.collapsedSections = {\n ...this.collapsedSections,\n [category]: !this.collapsedSections[category]\n }\n }\n\n private _categoryLabel(category: string): string {\n return i18next.t(`label.domain_type.${category}`, { defaultValue: category })\n }\n\n updated(changed: Map<string, unknown>) {\n if (changed.has('data')) {\n this.domains = this.data?.domains || []\n this.user = this.data?.user\n this.domainTypes = this.data?.domainTypes\n this.redirectTo = this.data?.redirectTo\n\n this.requestUpdate()\n }\n }\n\n get applicationMeta() {\n if (!this._applicationMeta) {\n const iconLink: HTMLLinkElement | null = document.querySelector('link[rel=\"application-icon\"]')\n const titleMeta: HTMLMetaElement | null = document.querySelector('meta[name=\"application-name\"]')\n const descriptionMeta: HTMLMetaElement | null = document.querySelector('meta[name=\"application-description\"]')\n\n this._applicationMeta = {\n icon: iconLink?.href,\n title: titleMeta ? titleMeta.content : 'Things Factory',\n description: descriptionMeta ? descriptionMeta.content : 'Reimagining Software'\n }\n }\n\n return this._applicationMeta\n }\n}\n"]}
@@ -15,6 +15,7 @@ export declare class SvProjectCompletePage extends SvProjectCompletePage_base {
15
15
  private project;
16
16
  render(): import("lit-html").TemplateResult<1>;
17
17
  pageUpdated(changes: any, lifecycle: any): Promise<void>;
18
+ private _resolveProjectId;
18
19
  private _onTabClick;
19
20
  private _onComplete;
20
21
  }