@serve.zone/catalog 1.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 (122) hide show
  1. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  2. package/dist_ts_web/00_commitinfo_data.js +9 -0
  3. package/dist_ts_web/elements/index.d.ts +33 -0
  4. package/dist_ts_web/elements/index.js +45 -0
  5. package/dist_ts_web/elements/sz-certificates-card.d.ts +14 -0
  6. package/dist_ts_web/elements/sz-certificates-card.js +210 -0
  7. package/dist_ts_web/elements/sz-dashboard-view.d.ts +33 -0
  8. package/dist_ts_web/elements/sz-dashboard-view.js +242 -0
  9. package/dist_ts_web/elements/sz-demo-view-dashboard.d.ts +18 -0
  10. package/dist_ts_web/elements/sz-demo-view-dashboard.js +184 -0
  11. package/dist_ts_web/elements/sz-demo-view-network.d.ts +32 -0
  12. package/dist_ts_web/elements/sz-demo-view-network.js +384 -0
  13. package/dist_ts_web/elements/sz-demo-view-registries.d.ts +22 -0
  14. package/dist_ts_web/elements/sz-demo-view-registries.js +240 -0
  15. package/dist_ts_web/elements/sz-demo-view-services.d.ts +32 -0
  16. package/dist_ts_web/elements/sz-demo-view-services.js +468 -0
  17. package/dist_ts_web/elements/sz-demo-view-settings.d.ts +19 -0
  18. package/dist_ts_web/elements/sz-demo-view-settings.js +151 -0
  19. package/dist_ts_web/elements/sz-demo-view-tokens.d.ts +20 -0
  20. package/dist_ts_web/elements/sz-demo-view-tokens.js +141 -0
  21. package/dist_ts_web/elements/sz-dns-ssl-card.d.ts +13 -0
  22. package/dist_ts_web/elements/sz-dns-ssl-card.js +180 -0
  23. package/dist_ts_web/elements/sz-domain-detail-view.d.ts +48 -0
  24. package/dist_ts_web/elements/sz-domain-detail-view.js +789 -0
  25. package/dist_ts_web/elements/sz-login-view.d.ts +18 -0
  26. package/dist_ts_web/elements/sz-login-view.js +384 -0
  27. package/dist_ts_web/elements/sz-network-dns-view.d.ts +20 -0
  28. package/dist_ts_web/elements/sz-network-dns-view.js +244 -0
  29. package/dist_ts_web/elements/sz-network-domains-view.d.ts +28 -0
  30. package/dist_ts_web/elements/sz-network-domains-view.js +312 -0
  31. package/dist_ts_web/elements/sz-network-proxy-view.d.ts +39 -0
  32. package/dist_ts_web/elements/sz-network-proxy-view.js +510 -0
  33. package/dist_ts_web/elements/sz-platform-service-detail-view.d.ts +49 -0
  34. package/dist_ts_web/elements/sz-platform-service-detail-view.js +733 -0
  35. package/dist_ts_web/elements/sz-platform-services-card.d.ts +19 -0
  36. package/dist_ts_web/elements/sz-platform-services-card.js +196 -0
  37. package/dist_ts_web/elements/sz-quick-actions-card.d.ts +19 -0
  38. package/dist_ts_web/elements/sz-quick-actions-card.js +194 -0
  39. package/dist_ts_web/elements/sz-registry-external-view.d.ts +22 -0
  40. package/dist_ts_web/elements/sz-registry-external-view.js +313 -0
  41. package/dist_ts_web/elements/sz-registry-onebox-view.d.ts +14 -0
  42. package/dist_ts_web/elements/sz-registry-onebox-view.js +307 -0
  43. package/dist_ts_web/elements/sz-resource-usage-card.d.ts +25 -0
  44. package/dist_ts_web/elements/sz-resource-usage-card.js +323 -0
  45. package/dist_ts_web/elements/sz-reverse-proxy-card.d.ts +16 -0
  46. package/dist_ts_web/elements/sz-reverse-proxy-card.js +216 -0
  47. package/dist_ts_web/elements/sz-service-create-view.d.ts +67 -0
  48. package/dist_ts_web/elements/sz-service-create-view.js +828 -0
  49. package/dist_ts_web/elements/sz-service-detail-view.d.ts +57 -0
  50. package/dist_ts_web/elements/sz-service-detail-view.js +728 -0
  51. package/dist_ts_web/elements/sz-services-backups-view.d.ts +37 -0
  52. package/dist_ts_web/elements/sz-services-backups-view.js +413 -0
  53. package/dist_ts_web/elements/sz-services-list-view.d.ts +20 -0
  54. package/dist_ts_web/elements/sz-services-list-view.js +272 -0
  55. package/dist_ts_web/elements/sz-settings-view.d.ts +30 -0
  56. package/dist_ts_web/elements/sz-settings-view.js +448 -0
  57. package/dist_ts_web/elements/sz-stat-card.d.ts +17 -0
  58. package/dist_ts_web/elements/sz-stat-card.js +249 -0
  59. package/dist_ts_web/elements/sz-status-grid-cluster.d.ts +19 -0
  60. package/dist_ts_web/elements/sz-status-grid-cluster.js +142 -0
  61. package/dist_ts_web/elements/sz-status-grid-infra.d.ts +17 -0
  62. package/dist_ts_web/elements/sz-status-grid-infra.js +140 -0
  63. package/dist_ts_web/elements/sz-status-grid-network.d.ts +30 -0
  64. package/dist_ts_web/elements/sz-status-grid-network.js +190 -0
  65. package/dist_ts_web/elements/sz-status-grid-services.d.ts +17 -0
  66. package/dist_ts_web/elements/sz-status-grid-services.js +145 -0
  67. package/dist_ts_web/elements/sz-tokens-view.d.ts +26 -0
  68. package/dist_ts_web/elements/sz-tokens-view.js +344 -0
  69. package/dist_ts_web/elements/sz-traffic-card.d.ts +24 -0
  70. package/dist_ts_web/elements/sz-traffic-card.js +255 -0
  71. package/dist_ts_web/index.d.ts +2 -0
  72. package/dist_ts_web/index.js +3 -0
  73. package/dist_ts_web/pages/index.d.ts +3 -0
  74. package/dist_ts_web/pages/index.js +4 -0
  75. package/dist_ts_web/pages/mainpage.d.ts +1 -0
  76. package/dist_ts_web/pages/mainpage.js +46 -0
  77. package/dist_ts_web/pages/sz-demo-app-shell.d.ts +13 -0
  78. package/dist_ts_web/pages/sz-demo-app-shell.js +212 -0
  79. package/dist_ts_web/pages/sz-demo-app.d.ts +2 -0
  80. package/dist_ts_web/pages/sz-demo-app.js +20 -0
  81. package/npmextra.json +24 -0
  82. package/package.json +45 -0
  83. package/ts_web/00_commitinfo_data.ts +8 -0
  84. package/ts_web/elements/index.ts +54 -0
  85. package/ts_web/elements/sz-certificates-card.ts +155 -0
  86. package/ts_web/elements/sz-dashboard-view.ts +217 -0
  87. package/ts_web/elements/sz-demo-view-dashboard.ts +150 -0
  88. package/ts_web/elements/sz-demo-view-network.ts +354 -0
  89. package/ts_web/elements/sz-demo-view-registries.ts +206 -0
  90. package/ts_web/elements/sz-demo-view-services.ts +434 -0
  91. package/ts_web/elements/sz-demo-view-settings.ts +118 -0
  92. package/ts_web/elements/sz-demo-view-tokens.ts +109 -0
  93. package/ts_web/elements/sz-dns-ssl-card.ts +130 -0
  94. package/ts_web/elements/sz-domain-detail-view.ts +766 -0
  95. package/ts_web/elements/sz-login-view.ts +329 -0
  96. package/ts_web/elements/sz-network-dns-view.ts +208 -0
  97. package/ts_web/elements/sz-network-domains-view.ts +273 -0
  98. package/ts_web/elements/sz-network-proxy-view.ts +456 -0
  99. package/ts_web/elements/sz-platform-service-detail-view.ts +714 -0
  100. package/ts_web/elements/sz-platform-services-card.ts +163 -0
  101. package/ts_web/elements/sz-quick-actions-card.ts +161 -0
  102. package/ts_web/elements/sz-registry-external-view.ts +279 -0
  103. package/ts_web/elements/sz-registry-onebox-view.ts +258 -0
  104. package/ts_web/elements/sz-resource-usage-card.ts +284 -0
  105. package/ts_web/elements/sz-reverse-proxy-card.ts +151 -0
  106. package/ts_web/elements/sz-service-create-view.ts +773 -0
  107. package/ts_web/elements/sz-service-detail-view.ts +710 -0
  108. package/ts_web/elements/sz-services-backups-view.ts +390 -0
  109. package/ts_web/elements/sz-services-list-view.ts +237 -0
  110. package/ts_web/elements/sz-settings-view.ts +417 -0
  111. package/ts_web/elements/sz-stat-card.ts +187 -0
  112. package/ts_web/elements/sz-status-grid-cluster.ts +105 -0
  113. package/ts_web/elements/sz-status-grid-infra.ts +88 -0
  114. package/ts_web/elements/sz-status-grid-network.ts +152 -0
  115. package/ts_web/elements/sz-status-grid-services.ts +99 -0
  116. package/ts_web/elements/sz-tokens-view.ts +308 -0
  117. package/ts_web/elements/sz-traffic-card.ts +222 -0
  118. package/ts_web/index.ts +2 -0
  119. package/ts_web/pages/index.ts +3 -0
  120. package/ts_web/pages/mainpage.ts +46 -0
  121. package/ts_web/pages/sz-demo-app-shell.ts +179 -0
  122. package/ts_web/pages/sz-demo-app.ts +20 -0
@@ -0,0 +1,714 @@
1
+ import {
2
+ DeesElement,
3
+ customElement,
4
+ html,
5
+ css,
6
+ cssManager,
7
+ property,
8
+ type TemplateResult,
9
+ } from '@design.estate/dees-element';
10
+
11
+ declare global {
12
+ interface HTMLElementTagNameMap {
13
+ 'sz-platform-service-detail-view': SzPlatformServiceDetailView;
14
+ }
15
+ }
16
+
17
+ export interface IPlatformServiceDetail {
18
+ id: string;
19
+ name: string;
20
+ type: 'mongodb' | 'minio' | 'clickhouse' | 'redis';
21
+ status: 'running' | 'stopped' | 'error';
22
+ version: string;
23
+ host: string;
24
+ port: number;
25
+ credentials?: {
26
+ username?: string;
27
+ password?: string;
28
+ accessKey?: string;
29
+ secretKey?: string;
30
+ };
31
+ config: Record<string, any>;
32
+ metrics?: {
33
+ cpu: number;
34
+ memory: number;
35
+ storage: number;
36
+ connections?: number;
37
+ };
38
+ }
39
+
40
+ export interface IPlatformLogEntry {
41
+ timestamp: string;
42
+ level: 'info' | 'warn' | 'error' | 'debug';
43
+ message: string;
44
+ }
45
+
46
+ @customElement('sz-platform-service-detail-view')
47
+ export class SzPlatformServiceDetailView extends DeesElement {
48
+ public static demo = () => html`
49
+ <div style="padding: 24px; max-width: 1000px;">
50
+ <sz-platform-service-detail-view
51
+ .service=${{
52
+ id: '1',
53
+ name: 'MongoDB',
54
+ type: 'mongodb',
55
+ status: 'running',
56
+ version: '7.0.4',
57
+ host: 'localhost',
58
+ port: 27017,
59
+ credentials: { username: 'admin', password: '••••••••' },
60
+ config: { replicaSet: 'rs0', authEnabled: true },
61
+ metrics: { cpu: 12, memory: 45, storage: 23, connections: 8 },
62
+ }}
63
+ .logs=${[
64
+ { timestamp: '2024-01-20 14:30:22', level: 'info', message: 'Connection accepted from 127.0.0.1:54321' },
65
+ { timestamp: '2024-01-20 14:30:20', level: 'info', message: 'Index build completed on collection users' },
66
+ { timestamp: '2024-01-20 14:30:15', level: 'warn', message: 'Slow query detected: 1.2s on collection orders' },
67
+ { timestamp: '2024-01-20 14:30:10', level: 'info', message: 'Checkpoint complete' },
68
+ ]}
69
+ ></sz-platform-service-detail-view>
70
+ </div>
71
+ `;
72
+
73
+ @property({ type: Object })
74
+ public accessor service: IPlatformServiceDetail | null = null;
75
+
76
+ @property({ type: Array })
77
+ public accessor logs: IPlatformLogEntry[] = [];
78
+
79
+ @property({ type: Boolean })
80
+ public accessor actionLoading: boolean = false;
81
+
82
+ public static styles = [
83
+ cssManager.defaultStyles,
84
+ css`
85
+ :host {
86
+ display: block;
87
+ }
88
+
89
+ .header {
90
+ display: flex;
91
+ justify-content: space-between;
92
+ align-items: flex-start;
93
+ margin-bottom: 24px;
94
+ }
95
+
96
+ .header-info {
97
+ display: flex;
98
+ align-items: center;
99
+ gap: 16px;
100
+ }
101
+
102
+ .service-icon {
103
+ width: 56px;
104
+ height: 56px;
105
+ background: ${cssManager.bdTheme('#f4f4f5', '#27272a')};
106
+ border-radius: 12px;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ }
111
+
112
+ .service-icon svg {
113
+ width: 28px;
114
+ height: 28px;
115
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
116
+ }
117
+
118
+ .service-details {
119
+ display: flex;
120
+ flex-direction: column;
121
+ gap: 4px;
122
+ }
123
+
124
+ .service-name {
125
+ font-size: 22px;
126
+ font-weight: 600;
127
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
128
+ }
129
+
130
+ .service-meta {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 12px;
134
+ font-size: 14px;
135
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
136
+ }
137
+
138
+ .status-badge {
139
+ display: inline-flex;
140
+ align-items: center;
141
+ gap: 6px;
142
+ padding: 4px 10px;
143
+ border-radius: 9999px;
144
+ font-size: 13px;
145
+ font-weight: 500;
146
+ }
147
+
148
+ .status-badge.running {
149
+ background: ${cssManager.bdTheme('#dcfce7', 'rgba(34, 197, 94, 0.2)')};
150
+ color: ${cssManager.bdTheme('#16a34a', '#22c55e')};
151
+ }
152
+
153
+ .status-badge.stopped {
154
+ background: ${cssManager.bdTheme('#f4f4f5', '#27272a')};
155
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
156
+ }
157
+
158
+ .status-badge.error {
159
+ background: ${cssManager.bdTheme('#fee2e2', 'rgba(239, 68, 68, 0.2)')};
160
+ color: ${cssManager.bdTheme('#dc2626', '#ef4444')};
161
+ }
162
+
163
+ .status-dot {
164
+ width: 8px;
165
+ height: 8px;
166
+ border-radius: 50%;
167
+ background: currentColor;
168
+ }
169
+
170
+ .header-actions {
171
+ display: flex;
172
+ gap: 8px;
173
+ }
174
+
175
+ .action-button {
176
+ display: inline-flex;
177
+ align-items: center;
178
+ gap: 6px;
179
+ padding: 8px 14px;
180
+ background: ${cssManager.bdTheme('#ffffff', '#09090b')};
181
+ border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
182
+ border-radius: 6px;
183
+ font-size: 13px;
184
+ font-weight: 500;
185
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
186
+ cursor: pointer;
187
+ transition: all 200ms ease;
188
+ }
189
+
190
+ .action-button:hover:not(:disabled) {
191
+ background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
192
+ }
193
+
194
+ .action-button:disabled {
195
+ opacity: 0.6;
196
+ cursor: not-allowed;
197
+ }
198
+
199
+ .action-button svg {
200
+ width: 14px;
201
+ height: 14px;
202
+ }
203
+
204
+ .action-button.danger {
205
+ color: ${cssManager.bdTheme('#dc2626', '#ef4444')};
206
+ border-color: ${cssManager.bdTheme('#fee2e2', 'rgba(239, 68, 68, 0.3)')};
207
+ }
208
+
209
+ .action-button.danger:hover:not(:disabled) {
210
+ background: ${cssManager.bdTheme('#fee2e2', 'rgba(239, 68, 68, 0.2)')};
211
+ }
212
+
213
+ .grid {
214
+ display: grid;
215
+ grid-template-columns: 1fr 1fr;
216
+ gap: 16px;
217
+ margin-bottom: 16px;
218
+ }
219
+
220
+ @media (max-width: 768px) {
221
+ .grid {
222
+ grid-template-columns: 1fr;
223
+ }
224
+ }
225
+
226
+ .section {
227
+ background: ${cssManager.bdTheme('#ffffff', '#09090b')};
228
+ border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
229
+ border-radius: 8px;
230
+ overflow: hidden;
231
+ }
232
+
233
+ .section.full-width {
234
+ grid-column: 1 / -1;
235
+ }
236
+
237
+ .section-header {
238
+ display: flex;
239
+ justify-content: space-between;
240
+ align-items: center;
241
+ padding: 14px 16px;
242
+ border-bottom: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
243
+ background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
244
+ }
245
+
246
+ .section-title {
247
+ font-size: 14px;
248
+ font-weight: 600;
249
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
250
+ display: flex;
251
+ align-items: center;
252
+ gap: 8px;
253
+ }
254
+
255
+ .section-title svg {
256
+ width: 16px;
257
+ height: 16px;
258
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
259
+ }
260
+
261
+ .section-content {
262
+ padding: 16px;
263
+ }
264
+
265
+ .info-row {
266
+ display: flex;
267
+ justify-content: space-between;
268
+ align-items: center;
269
+ padding: 10px 0;
270
+ border-bottom: 1px solid ${cssManager.bdTheme('#f4f4f5', '#27272a')};
271
+ }
272
+
273
+ .info-row:last-child {
274
+ border-bottom: none;
275
+ }
276
+
277
+ .info-label {
278
+ font-size: 13px;
279
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
280
+ }
281
+
282
+ .info-value {
283
+ font-size: 13px;
284
+ font-weight: 500;
285
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
286
+ font-family: monospace;
287
+ display: flex;
288
+ align-items: center;
289
+ gap: 8px;
290
+ }
291
+
292
+ .copy-button {
293
+ padding: 4px;
294
+ background: transparent;
295
+ border: none;
296
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
297
+ cursor: pointer;
298
+ border-radius: 4px;
299
+ transition: all 200ms ease;
300
+ }
301
+
302
+ .copy-button:hover {
303
+ background: ${cssManager.bdTheme('#f4f4f5', '#27272a')};
304
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
305
+ }
306
+
307
+ .metrics-grid {
308
+ display: grid;
309
+ grid-template-columns: repeat(4, 1fr);
310
+ gap: 12px;
311
+ }
312
+
313
+ @media (max-width: 600px) {
314
+ .metrics-grid {
315
+ grid-template-columns: repeat(2, 1fr);
316
+ }
317
+ }
318
+
319
+ .metric-card {
320
+ text-align: center;
321
+ padding: 12px;
322
+ background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
323
+ border-radius: 6px;
324
+ }
325
+
326
+ .metric-value {
327
+ font-size: 20px;
328
+ font-weight: 600;
329
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
330
+ }
331
+
332
+ .metric-label {
333
+ font-size: 12px;
334
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
335
+ margin-top: 2px;
336
+ }
337
+
338
+ .progress-bar {
339
+ height: 4px;
340
+ background: ${cssManager.bdTheme('#e4e4e7', '#27272a')};
341
+ border-radius: 2px;
342
+ margin-top: 8px;
343
+ overflow: hidden;
344
+ }
345
+
346
+ .progress-fill {
347
+ height: 100%;
348
+ border-radius: 2px;
349
+ transition: width 300ms ease;
350
+ }
351
+
352
+ .progress-fill.low {
353
+ background: ${cssManager.bdTheme('#22c55e', '#22c55e')};
354
+ }
355
+
356
+ .progress-fill.medium {
357
+ background: ${cssManager.bdTheme('#eab308', '#eab308')};
358
+ }
359
+
360
+ .progress-fill.high {
361
+ background: ${cssManager.bdTheme('#ef4444', '#ef4444')};
362
+ }
363
+
364
+ .log-container {
365
+ background: ${cssManager.bdTheme('#18181b', '#09090b')};
366
+ border-radius: 6px;
367
+ padding: 12px;
368
+ max-height: 300px;
369
+ overflow-y: auto;
370
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
371
+ font-size: 12px;
372
+ line-height: 1.6;
373
+ }
374
+
375
+ .log-entry {
376
+ display: flex;
377
+ gap: 12px;
378
+ padding: 4px 0;
379
+ }
380
+
381
+ .log-timestamp {
382
+ color: #71717a;
383
+ flex-shrink: 0;
384
+ }
385
+
386
+ .log-level {
387
+ flex-shrink: 0;
388
+ width: 50px;
389
+ text-transform: uppercase;
390
+ font-weight: 500;
391
+ }
392
+
393
+ .log-level.info {
394
+ color: #60a5fa;
395
+ }
396
+
397
+ .log-level.warn {
398
+ color: #fbbf24;
399
+ }
400
+
401
+ .log-level.error {
402
+ color: #f87171;
403
+ }
404
+
405
+ .log-level.debug {
406
+ color: #a1a1aa;
407
+ }
408
+
409
+ .log-message {
410
+ color: #fafafa;
411
+ word-break: break-word;
412
+ }
413
+
414
+ .config-item {
415
+ display: flex;
416
+ justify-content: space-between;
417
+ padding: 8px 0;
418
+ border-bottom: 1px solid ${cssManager.bdTheme('#f4f4f5', '#27272a')};
419
+ }
420
+
421
+ .config-item:last-child {
422
+ border-bottom: none;
423
+ }
424
+
425
+ .config-key {
426
+ font-size: 13px;
427
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
428
+ }
429
+
430
+ .config-value {
431
+ font-size: 13px;
432
+ font-weight: 500;
433
+ color: ${cssManager.bdTheme('#18181b', '#fafafa')};
434
+ }
435
+
436
+ .config-value.true {
437
+ color: ${cssManager.bdTheme('#16a34a', '#22c55e')};
438
+ }
439
+
440
+ .config-value.false {
441
+ color: ${cssManager.bdTheme('#dc2626', '#ef4444')};
442
+ }
443
+
444
+ .empty-state {
445
+ text-align: center;
446
+ padding: 40px 20px;
447
+ color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
448
+ }
449
+ `,
450
+ ];
451
+
452
+ public render(): TemplateResult {
453
+ if (!this.service) {
454
+ return html`<div class="empty-state">No service selected</div>`;
455
+ }
456
+
457
+ return html`
458
+ <div class="header">
459
+ <div class="header-info">
460
+ <div class="service-icon">
461
+ ${this.renderServiceIcon()}
462
+ </div>
463
+ <div class="service-details">
464
+ <div class="service-name">${this.service.name}</div>
465
+ <div class="service-meta">
466
+ <span class="status-badge ${this.service.status}">
467
+ <span class="status-dot"></span>
468
+ ${this.service.status.charAt(0).toUpperCase() + this.service.status.slice(1)}
469
+ </span>
470
+ <span>Version ${this.service.version}</span>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ <div class="header-actions">
475
+ ${this.service.status === 'running' ? html`
476
+ <button class="action-button" ?disabled=${this.actionLoading} @click=${() => this.handleRestart()}>
477
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
478
+ <polyline points="23 4 23 10 17 10"></polyline>
479
+ <polyline points="1 20 1 14 7 14"></polyline>
480
+ <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
481
+ </svg>
482
+ Restart
483
+ </button>
484
+ <button class="action-button danger" ?disabled=${this.actionLoading} @click=${() => this.handleStop()}>
485
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
486
+ <rect x="6" y="6" width="12" height="12" rx="1"></rect>
487
+ </svg>
488
+ Stop
489
+ </button>
490
+ ` : html`
491
+ <button class="action-button" ?disabled=${this.actionLoading} @click=${() => this.handleStart()}>
492
+ <svg viewBox="0 0 24 24" fill="currentColor">
493
+ <polygon points="5,3 19,12 5,21"></polygon>
494
+ </svg>
495
+ Start
496
+ </button>
497
+ `}
498
+ </div>
499
+ </div>
500
+
501
+ <div class="grid">
502
+ <!-- Connection Info -->
503
+ <div class="section">
504
+ <div class="section-header">
505
+ <div class="section-title">
506
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
507
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
508
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
509
+ </svg>
510
+ Connection
511
+ </div>
512
+ </div>
513
+ <div class="section-content">
514
+ <div class="info-row">
515
+ <span class="info-label">Host</span>
516
+ <span class="info-value">
517
+ ${this.service.host}
518
+ <button class="copy-button" @click=${() => this.copyToClipboard(this.service!.host)}>
519
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
520
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
521
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
522
+ </svg>
523
+ </button>
524
+ </span>
525
+ </div>
526
+ <div class="info-row">
527
+ <span class="info-label">Port</span>
528
+ <span class="info-value">${this.service.port}</span>
529
+ </div>
530
+ ${this.service.credentials?.username ? html`
531
+ <div class="info-row">
532
+ <span class="info-label">Username</span>
533
+ <span class="info-value">
534
+ ${this.service.credentials.username}
535
+ <button class="copy-button" @click=${() => this.copyToClipboard(this.service!.credentials!.username!)}>
536
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
537
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
538
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
539
+ </svg>
540
+ </button>
541
+ </span>
542
+ </div>
543
+ <div class="info-row">
544
+ <span class="info-label">Password</span>
545
+ <span class="info-value">••••••••</span>
546
+ </div>
547
+ ` : ''}
548
+ ${this.service.credentials?.accessKey ? html`
549
+ <div class="info-row">
550
+ <span class="info-label">Access Key</span>
551
+ <span class="info-value">
552
+ ${this.service.credentials.accessKey}
553
+ <button class="copy-button" @click=${() => this.copyToClipboard(this.service!.credentials!.accessKey!)}>
554
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
555
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
556
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
557
+ </svg>
558
+ </button>
559
+ </span>
560
+ </div>
561
+ <div class="info-row">
562
+ <span class="info-label">Secret Key</span>
563
+ <span class="info-value">••••••••</span>
564
+ </div>
565
+ ` : ''}
566
+ </div>
567
+ </div>
568
+
569
+ <!-- Configuration -->
570
+ <div class="section">
571
+ <div class="section-header">
572
+ <div class="section-title">
573
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
574
+ <circle cx="12" cy="12" r="3"></circle>
575
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
576
+ </svg>
577
+ Configuration
578
+ </div>
579
+ </div>
580
+ <div class="section-content">
581
+ ${Object.entries(this.service.config).map(([key, value]) => html`
582
+ <div class="config-item">
583
+ <span class="config-key">${this.formatConfigKey(key)}</span>
584
+ <span class="config-value ${typeof value === 'boolean' ? (value ? 'true' : 'false') : ''}">${this.formatConfigValue(value)}</span>
585
+ </div>
586
+ `)}
587
+ </div>
588
+ </div>
589
+
590
+ <!-- Metrics -->
591
+ ${this.service.metrics ? html`
592
+ <div class="section full-width">
593
+ <div class="section-header">
594
+ <div class="section-title">
595
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
596
+ <line x1="18" y1="20" x2="18" y2="10"></line>
597
+ <line x1="12" y1="20" x2="12" y2="4"></line>
598
+ <line x1="6" y1="20" x2="6" y2="14"></line>
599
+ </svg>
600
+ Resource Usage
601
+ </div>
602
+ </div>
603
+ <div class="section-content">
604
+ <div class="metrics-grid">
605
+ <div class="metric-card">
606
+ <div class="metric-value">${this.service.metrics.cpu}%</div>
607
+ <div class="metric-label">CPU</div>
608
+ <div class="progress-bar">
609
+ <div class="progress-fill ${this.getProgressClass(this.service.metrics.cpu)}" style="width: ${this.service.metrics.cpu}%"></div>
610
+ </div>
611
+ </div>
612
+ <div class="metric-card">
613
+ <div class="metric-value">${this.service.metrics.memory}%</div>
614
+ <div class="metric-label">Memory</div>
615
+ <div class="progress-bar">
616
+ <div class="progress-fill ${this.getProgressClass(this.service.metrics.memory)}" style="width: ${this.service.metrics.memory}%"></div>
617
+ </div>
618
+ </div>
619
+ <div class="metric-card">
620
+ <div class="metric-value">${this.service.metrics.storage}%</div>
621
+ <div class="metric-label">Storage</div>
622
+ <div class="progress-bar">
623
+ <div class="progress-fill ${this.getProgressClass(this.service.metrics.storage)}" style="width: ${this.service.metrics.storage}%"></div>
624
+ </div>
625
+ </div>
626
+ ${this.service.metrics.connections !== undefined ? html`
627
+ <div class="metric-card">
628
+ <div class="metric-value">${this.service.metrics.connections}</div>
629
+ <div class="metric-label">Connections</div>
630
+ </div>
631
+ ` : ''}
632
+ </div>
633
+ </div>
634
+ </div>
635
+ ` : ''}
636
+
637
+ <!-- Logs -->
638
+ <div class="section full-width">
639
+ <div class="section-header">
640
+ <div class="section-title">
641
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
642
+ <polyline points="4 17 10 11 4 5"></polyline>
643
+ <line x1="12" y1="19" x2="20" y2="19"></line>
644
+ </svg>
645
+ Logs
646
+ </div>
647
+ </div>
648
+ <div class="section-content">
649
+ <div class="log-container">
650
+ ${this.logs.length > 0 ? this.logs.map(log => html`
651
+ <div class="log-entry">
652
+ <span class="log-timestamp">${log.timestamp}</span>
653
+ <span class="log-level ${log.level}">${log.level}</span>
654
+ <span class="log-message">${log.message}</span>
655
+ </div>
656
+ `) : html`
657
+ <div style="color: #71717a; text-align: center; padding: 20px;">No logs available</div>
658
+ `}
659
+ </div>
660
+ </div>
661
+ </div>
662
+ </div>
663
+ `;
664
+ }
665
+
666
+ private renderServiceIcon(): TemplateResult {
667
+ const type = this.service?.type;
668
+
669
+ switch (type) {
670
+ case 'mongodb':
671
+ return html`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>`;
672
+ case 'minio':
673
+ return html`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M21 16.5c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18-.21 0-.41-.06-.57-.18l-7.9-4.44A.991.991 0 0 1 3 16.5v-9c0-.38.21-.71.53-.88l7.9-4.44c.16-.12.36-.18.57-.18.21 0 .41.06.57.18l7.9 4.44c.32.17.53.5.53.88v9z"/></svg>`;
674
+ case 'clickhouse':
675
+ return html`<svg viewBox="0 0 24 24" fill="currentColor"><rect x="2" y="2" width="6" height="20"/><rect x="9" y="7" width="6" height="15"/><rect x="16" y="12" width="6" height="10"/></svg>`;
676
+ case 'redis':
677
+ return html`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>`;
678
+ default:
679
+ return html`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect><rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line></svg>`;
680
+ }
681
+ }
682
+
683
+ private getProgressClass(value: number): string {
684
+ if (value < 50) return 'low';
685
+ if (value < 80) return 'medium';
686
+ return 'high';
687
+ }
688
+
689
+ private formatConfigKey(key: string): string {
690
+ return key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
691
+ }
692
+
693
+ private formatConfigValue(value: any): string {
694
+ if (typeof value === 'boolean') return value ? 'Enabled' : 'Disabled';
695
+ return String(value);
696
+ }
697
+
698
+ private copyToClipboard(text: string) {
699
+ navigator.clipboard.writeText(text);
700
+ this.dispatchEvent(new CustomEvent('copy', { detail: text, bubbles: true, composed: true }));
701
+ }
702
+
703
+ private handleStart() {
704
+ this.dispatchEvent(new CustomEvent('start', { detail: this.service, bubbles: true, composed: true }));
705
+ }
706
+
707
+ private handleStop() {
708
+ this.dispatchEvent(new CustomEvent('stop', { detail: this.service, bubbles: true, composed: true }));
709
+ }
710
+
711
+ private handleRestart() {
712
+ this.dispatchEvent(new CustomEvent('restart', { detail: this.service, bubbles: true, composed: true }));
713
+ }
714
+ }