|
17 | 17 | packages?: string[]; |
18 | 18 | custom_script?: string; |
19 | 19 | dotfiles_repo?: string; |
| 20 | + updated_at?: string; |
20 | 21 | } |
21 | 22 |
|
| 23 | + let copiedId = $state(''); |
| 24 | +
|
22 | 25 | let configs = $state<Config[]>([]); |
23 | 26 | let loading = $state(true); |
24 | 27 | let showModal = $state(false); |
|
254 | 257 | } |
255 | 258 | } |
256 | 259 |
|
257 | | - function copyToClipboard(text: string) { |
| 260 | + function copyToClipboard(text: string, configId: string) { |
258 | 261 | navigator.clipboard.writeText(text); |
259 | | - toast = 'Copied!'; |
260 | | - setTimeout(() => toast = '', 2000); |
| 262 | + copiedId = configId; |
| 263 | + setTimeout(() => copiedId = '', 2000); |
| 264 | + } |
| 265 | +
|
| 266 | + function formatDate(dateStr?: string): string { |
| 267 | + if (!dateStr) return ''; |
| 268 | + const date = new Date(dateStr + 'Z'); |
| 269 | + const now = new Date(); |
| 270 | + const diff = now.getTime() - date.getTime(); |
| 271 | + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); |
| 272 | + if (days === 0) return 'Today'; |
| 273 | + if (days === 1) return 'Yesterday'; |
| 274 | + if (days < 7) return `${days} days ago`; |
| 275 | + return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); |
261 | 276 | } |
262 | 277 |
|
263 | 278 | function getInstallUrl(config: Config): string { |
|
301 | 316 | {:else} |
302 | 317 | <div class="configs-grid"> |
303 | 318 | {#each configs as config} |
304 | | - <div class="config-card"> |
| 319 | + <div class="config-card" onclick={() => editConfig(config.slug)} role="button" tabindex="0" onkeydown={(e) => e.key === 'Enter' && editConfig(config.slug)}> |
305 | 320 | <div class="config-header"> |
306 | 321 | <div> |
307 | 322 | <div class="config-name">{config.name}</div> |
|
322 | 337 | {/if} |
323 | 338 | <div class="config-meta"> |
324 | 339 | <span class="config-meta-item">Preset: <strong>{config.base_preset}</strong></span> |
| 340 | + {#if config.updated_at} |
| 341 | + <span class="config-meta-item">Modified: <strong>{formatDate(config.updated_at)}</strong></span> |
| 342 | + {/if} |
325 | 343 | </div> |
326 | | - <div class="config-url"> |
| 344 | + <div class="config-url" onclick={(e) => e.stopPropagation()}> |
327 | 345 | <code>curl -fsSL {getInstallUrl(config)} | bash</code> |
328 | | - <button class="copy-btn" onclick={() => copyToClipboard(`curl -fsSL https://${getInstallUrl(config)} | bash`)}>Copy</button> |
| 346 | + <button class="copy-btn" onclick={() => copyToClipboard(`curl -fsSL https://${getInstallUrl(config)} | bash`, config.id)}>{copiedId === config.id ? 'Copied!' : 'Copy'}</button> |
329 | 347 | </div> |
330 | | - <div class="config-actions"> |
| 348 | + <div class="config-actions" onclick={(e) => e.stopPropagation()}> |
331 | 349 | <Button variant="secondary" onclick={() => editConfig(config.slug)}>Edit</Button> |
332 | 350 | <Button variant="danger" onclick={() => deleteConfig(config.slug)}>Delete</Button> |
333 | 351 | </div> |
|
563 | 581 | border-radius: 12px; |
564 | 582 | padding: 20px; |
565 | 583 | transition: all 0.2s; |
| 584 | + cursor: pointer; |
566 | 585 | } |
567 | 586 |
|
568 | 587 | .config-card:hover { |
569 | 588 | border-color: var(--border-hover); |
| 589 | + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
570 | 590 | } |
571 | 591 |
|
572 | 592 | .config-header { |
|
0 commit comments