Skip to content

Commit 6f690c8

Browse files
committed
slightly better multi provider styling
1 parent b2d04f2 commit 6f690c8

1 file changed

Lines changed: 213 additions & 53 deletions

File tree

src/components/HeaderNav.vue

Lines changed: 213 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ function getProviderIcon(name: string): string {
157157
return icons[name] || '🔑'
158158
}
159159
160+
// Copy text to clipboard
161+
async function copyToClipboard(text: string) {
162+
try {
163+
await navigator.clipboard.writeText(text)
164+
console.log('Error message copied to clipboard')
165+
} catch (err) {
166+
console.error('Failed to copy text to clipboard:', err)
167+
}
168+
}
169+
160170
const clearActiveTab = () => {
161171
const activeLinks = document.querySelectorAll<HTMLElement>('.router-link')
162172
for (const active of activeLinks) {
@@ -313,28 +323,84 @@ const getCurrentPath = () => {
313323
<!-- Provider Selection Dialog -->
314324
<el-dialog
315325
v-model="showProviderSelector"
316-
title="Select Identity Provider"
317-
width="450px"
326+
title="Login"
327+
width="500px"
318328
:close-on-click-modal="true"
319329
>
320-
<div class="provider-list">
321-
<div
322-
v-for="provider in availableProviders.filter(p => p.available)"
323-
:key="provider.name"
324-
class="provider-item"
325-
@click="loginWithProvider(provider.name); showProviderSelector = false"
326-
>
327-
<div class="provider-icon">{{ getProviderIcon(provider.name) }}</div>
328-
<div class="provider-info">
329-
<h4>{{ formatProviderName(provider.name) }}</h4>
330-
<span class="provider-status">Available</span>
330+
<!-- No providers available -->
331+
<div v-if="availableProviders.filter(p => p.available).length === 0" class="no-providers-error">
332+
<p class="error-message">No authentication providers available.</p>
333+
<p class="error-hint">Please contact your administrator.</p>
334+
335+
<!-- Show unavailable providers even when no available providers -->
336+
<div v-if="availableProviders.filter(p => !p.available).length > 0" class="unavailable-section">
337+
<p class="unavailable-header">Currently unavailable:</p>
338+
<div
339+
v-for="provider in availableProviders.filter(p => !p.available)"
340+
:key="provider.name"
341+
class="provider-unavailable"
342+
>
343+
<div class="provider-unavailable-header">
344+
<span class="provider-status-indicator offline">●</span>
345+
<span class="provider-name">{{ formatProviderName(provider.name) }}</span>
346+
<span class="unavailable-label">Unavailable</span>
347+
</div>
348+
<div v-if="provider.error" class="provider-error">
349+
<div class="provider-error-text">{{ provider.error }}</div>
350+
<button
351+
@click.stop="copyToClipboard(provider.error)"
352+
class="copy-button"
353+
title="Copy error message"
354+
>
355+
📋
356+
</button>
357+
</div>
331358
</div>
332-
<div class="provider-arrow">→</div>
333359
</div>
360+
</div>
334361

335-
<div v-if="availableProviders.filter(p => p.available).length === 0" class="no-providers">
336-
<p>No identity providers available</p>
337-
<p class="error-hint">Please contact your administrator</p>
362+
<!-- Available providers -->
363+
<div v-else class="provider-selection">
364+
<p class="selection-hint">Choose your authentication provider:</p>
365+
366+
<div class="available-providers">
367+
<button
368+
v-for="provider in availableProviders.filter(p => p.available)"
369+
:key="provider.name"
370+
class="provider-button"
371+
@click="loginWithProvider(provider.name); showProviderSelector = false"
372+
>
373+
<span class="provider-button-content">
374+
<span class="provider-status-indicator online">●</span>
375+
<span class="provider-button-text">{{ formatProviderName(provider.name) }}</span>
376+
</span>
377+
</button>
378+
</div>
379+
380+
<!-- Unavailable providers section -->
381+
<div v-if="availableProviders.filter(p => !p.available).length > 0" class="unavailable-section">
382+
<p class="unavailable-header">Currently unavailable:</p>
383+
<div
384+
v-for="provider in availableProviders.filter(p => !p.available)"
385+
:key="provider.name"
386+
class="provider-unavailable"
387+
>
388+
<div class="provider-unavailable-header">
389+
<span class="provider-status-indicator offline">●</span>
390+
<span class="provider-name">{{ formatProviderName(provider.name) }}</span>
391+
<span class="unavailable-label">Unavailable</span>
392+
</div>
393+
<div v-if="provider.error" class="provider-error">
394+
<div class="provider-error-text">{{ provider.error }}</div>
395+
<button
396+
@click.stop="copyToClipboard(provider.error)"
397+
class="copy-button"
398+
title="Copy error message"
399+
>
400+
📋
401+
</button>
402+
</div>
403+
</div>
338404
</div>
339405
</div>
340406
</el-dialog>
@@ -435,71 +501,165 @@ button.login-button-disabled {
435501
}
436502
437503
/* Provider Selection Dialog */
438-
.provider-list {
504+
.provider-selection {
439505
display: flex;
440506
flex-direction: column;
441-
gap: 12px;
507+
gap: 16px;
508+
}
509+
510+
.selection-hint {
511+
text-align: center;
512+
font-size: 14px;
513+
color: #666;
514+
margin: 0 0 8px 0;
442515
}
443516
444-
.provider-item {
517+
.available-providers {
445518
display: flex;
446-
align-items: center;
447-
padding: 16px;
448-
border: 2px solid #e0e0e0;
519+
flex-direction: column;
520+
gap: 12px;
521+
}
522+
523+
.provider-button {
524+
width: 100%;
525+
padding: 14px 20px;
526+
background-color: #32b9ce;
527+
color: white;
528+
border: none;
449529
border-radius: 8px;
530+
font-size: 15px;
531+
font-family: 'Roboto', sans-serif;
450532
cursor: pointer;
451-
transition: all 0.2s;
452-
background-color: #ffffff;
533+
transition: all 0.2s ease;
534+
font-weight: 500;
453535
}
454536
455-
.provider-item:hover {
456-
border-color: #32b9ce;
457-
background-color: #f0f9fa;
458-
transform: translateX(4px);
537+
.provider-button:hover {
538+
background-color: #2a9fb0;
539+
transform: translateY(-1px);
540+
box-shadow: 0 4px 8px rgba(50, 185, 206, 0.3);
459541
}
460542
461-
.provider-icon {
462-
font-size: 32px;
463-
margin-right: 16px;
464-
min-width: 40px;
465-
text-align: center;
543+
.provider-button-content {
544+
display: flex;
545+
align-items: center;
546+
justify-content: space-between;
547+
width: 100%;
466548
}
467549
468-
.provider-info {
550+
.provider-button-text {
469551
flex: 1;
552+
text-align: left;
553+
margin-left: 8px;
470554
}
471555
472-
.provider-info h4 {
473-
margin: 0 0 4px 0;
474-
font-size: 16px;
475-
color: #39455f;
476-
font-weight: 500;
556+
.provider-status-indicator {
557+
font-size: 14px;
558+
margin-right: 8px;
477559
}
478560
479-
.provider-status {
480-
font-size: 12px;
561+
.provider-status-indicator.online {
481562
color: #10b981;
563+
}
564+
565+
.provider-status-indicator.offline {
566+
color: #ef4444;
567+
}
568+
569+
/* Unavailable providers section */
570+
.unavailable-section {
571+
margin-top: 24px;
572+
padding-top: 20px;
573+
border-top: 1px solid #e5e7eb;
574+
}
575+
576+
.unavailable-header {
577+
text-align: center;
578+
font-size: 13px;
579+
color: #9ca3af;
580+
margin: 0 0 12px 0;
581+
}
582+
583+
.provider-unavailable {
584+
width: 100%;
585+
padding: 12px 16px;
586+
border-radius: 8px;
587+
border: 1px solid #d1d5db;
588+
background-color: #f9fafb;
589+
opacity: 0.7;
590+
margin-bottom: 8px;
591+
}
592+
593+
.provider-unavailable-header {
594+
display: flex;
595+
align-items: center;
596+
justify-content: space-between;
597+
gap: 8px;
598+
}
599+
600+
.provider-name {
601+
flex: 1;
602+
color: #4b5563;
603+
font-size: 14px;
604+
}
605+
606+
.unavailable-label {
607+
font-size: 11px;
608+
color: #ef4444;
482609
font-weight: 500;
610+
text-transform: uppercase;
611+
}
612+
613+
.provider-error {
614+
display: flex;
615+
align-items: start;
616+
gap: 8px;
617+
margin-top: 8px;
618+
margin-left: 22px;
619+
}
620+
621+
.provider-error-text {
622+
flex: 1;
623+
font-size: 11px;
624+
color: #6b7280;
625+
max-height: 80px;
626+
overflow-y: auto;
627+
word-break: break-word;
628+
white-space: pre-wrap;
629+
line-height: 1.4;
630+
}
631+
632+
.copy-button {
633+
background: none;
634+
border: none;
635+
cursor: pointer;
636+
font-size: 14px;
637+
padding: 0;
638+
opacity: 0.6;
639+
transition: opacity 0.2s;
640+
flex-shrink: 0;
483641
}
484642
485-
.provider-arrow {
486-
font-size: 20px;
487-
color: #32b9ce;
488-
margin-left: 12px;
643+
.copy-button:hover {
644+
opacity: 1;
489645
}
490646
491-
.no-providers {
647+
/* No providers error state */
648+
.no-providers-error {
492649
text-align: center;
493-
padding: 32px;
494-
color: #999;
650+
padding: 24px;
495651
}
496652
497-
.no-providers p {
498-
margin: 8px 0;
653+
.error-message {
654+
color: #ef4444;
655+
font-size: 15px;
656+
margin: 0 0 8px 0;
657+
font-weight: 500;
499658
}
500659
501660
.error-hint {
502-
font-size: 12px;
503-
color: #999;
661+
font-size: 13px;
662+
color: #6b7280;
663+
margin: 0;
504664
}
505665
</style>

0 commit comments

Comments
 (0)