|
@@ -1,28 +1,15 @@
|
|
|
<script>
|
|
<script>
|
|
|
- import { Settings, Phone, Bell, HelpCircle, ShieldCheck } from 'lucide-svelte';
|
|
|
|
|
|
|
+ import { onMount } from 'svelte';
|
|
|
|
|
+ import { Settings, Phone, Bell, HelpCircle, ShieldCheck, LoaderCircle, RefreshCw, ExternalLink } from 'lucide-svelte';
|
|
|
import SlaConfigManager from '$lib/features/sla/SlaConfigManager.svelte';
|
|
import SlaConfigManager from '$lib/features/sla/SlaConfigManager.svelte';
|
|
|
|
|
+ import {
|
|
|
|
|
+ listWhatsappAccounts,
|
|
|
|
|
+ createWhatsappHostedLink,
|
|
|
|
|
+ reconnectWhatsappAccount
|
|
|
|
|
+ } from '$lib/features/integrations/unipile/api.js';
|
|
|
|
|
|
|
|
// Settings sections
|
|
// Settings sections
|
|
|
- const settingsSections = [
|
|
|
|
|
- {
|
|
|
|
|
- title: 'Integrações',
|
|
|
|
|
- icon: Phone,
|
|
|
|
|
- items: [
|
|
|
|
|
- {
|
|
|
|
|
- title: 'WhatsApp Business',
|
|
|
|
|
- description: 'Configure sua conta do WhatsApp Business para comunicação',
|
|
|
|
|
- status: 'connected',
|
|
|
|
|
- action: 'gerenciar'
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- title: 'WhatsApp dos Vendedores',
|
|
|
|
|
- description: 'Adicione os números de WhatsApp da equipe de vendas',
|
|
|
|
|
- status: 'partial',
|
|
|
|
|
- action: 'configurar',
|
|
|
|
|
- count: '3 de 5 configurados'
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ const staticSettingsSections = [
|
|
|
{
|
|
{
|
|
|
title: 'Notificações',
|
|
title: 'Notificações',
|
|
|
icon: Bell,
|
|
icon: Bell,
|
|
@@ -61,22 +48,279 @@
|
|
|
}
|
|
}
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
|
|
+ const MAX_WHATSAPP_POLL_ATTEMPTS = 12;
|
|
|
|
|
+
|
|
|
|
|
+ let whatsappAccounts = $state([]);
|
|
|
|
|
+ let whatsappLoading = $state(true);
|
|
|
|
|
+ let whatsappLoadError = $state('');
|
|
|
|
|
+ let whatsappActionError = $state('');
|
|
|
|
|
+ let whatsappOpening = $state(false);
|
|
|
|
|
+ let whatsappRefreshing = $state(false);
|
|
|
|
|
+ let whatsappPolling = $state(false);
|
|
|
|
|
+ let lastHostedAuthUrl = $state('');
|
|
|
|
|
+ let whatsappPollInterval = $state(null);
|
|
|
|
|
+ let whatsappPollTimeout = $state(null);
|
|
|
|
|
+ let whatsappPollAttempts = $state(0);
|
|
|
|
|
+
|
|
|
|
|
+ const primaryWhatsappAccount = $derived(
|
|
|
|
|
+ whatsappAccounts.find((account) => account?.isConnected) ?? whatsappAccounts[0] ?? null
|
|
|
|
|
+ );
|
|
|
|
|
+ const settingsSections = $derived([
|
|
|
|
|
+ {
|
|
|
|
|
+ title: 'Integrações',
|
|
|
|
|
+ icon: Phone,
|
|
|
|
|
+ items: [buildWhatsappItem(), {
|
|
|
|
|
+ key: 'whatsapp-sellers',
|
|
|
|
|
+ title: 'WhatsApp dos Vendedores',
|
|
|
|
|
+ description: 'Adicione os números de WhatsApp da equipe de vendas',
|
|
|
|
|
+ status: 'partial',
|
|
|
|
|
+ action: 'configurar',
|
|
|
|
|
+ count: '3 de 5 configurados'
|
|
|
|
|
+ }]
|
|
|
|
|
+ },
|
|
|
|
|
+ ...staticSettingsSections
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
function handleAction(item) {
|
|
function handleAction(item) {
|
|
|
// Handle different actions based on item
|
|
// Handle different actions based on item
|
|
|
- console.log(`Action: ${item.action} for ${item.title}`);
|
|
|
|
|
|
|
+ if (item.key === 'whatsapp-business') {
|
|
|
|
|
+ void handleWhatsappPrimaryAction();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// For WhatsApp integration, we could open a modal or navigate to a specific page
|
|
// For WhatsApp integration, we could open a modal or navigate to a specific page
|
|
|
- if (item.title === 'WhatsApp Business') {
|
|
|
|
|
|
|
+ if (item.key === 'whatsapp-business') {
|
|
|
// TODO: Open WhatsApp configuration modal
|
|
// TODO: Open WhatsApp configuration modal
|
|
|
- } else if (item.title === 'WhatsApp dos Vendedores') {
|
|
|
|
|
|
|
+ } else if (item.key === 'whatsapp-sellers') {
|
|
|
// TODO: Open sellers WhatsApp configuration
|
|
// TODO: Open sellers WhatsApp configuration
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function handleSecondaryAction(item) {
|
|
|
|
|
+ if (item.key === 'whatsapp-business') {
|
|
|
|
|
+ void refreshWhatsappAccounts();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function getWhatsappStatus() {
|
|
|
|
|
+ if (whatsappLoading) return 'loading';
|
|
|
|
|
+ if (whatsappOpening || whatsappPolling) return 'connecting';
|
|
|
|
|
+ if (whatsappLoadError || whatsappActionError) return 'error';
|
|
|
|
|
+ if (primaryWhatsappAccount?.isConnected) return 'connected';
|
|
|
|
|
+ if (primaryWhatsappAccount) return 'partial';
|
|
|
|
|
+ return 'disabled';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function buildWhatsappItem() {
|
|
|
|
|
+ const connectedAccounts = whatsappAccounts.filter((account) => account?.isConnected);
|
|
|
|
|
+ const status = getWhatsappStatus();
|
|
|
|
|
+ const hasAccount = primaryWhatsappAccount !== null;
|
|
|
|
|
+ const action = hasAccount ? 'reconectar' : 'conectar';
|
|
|
|
|
+ const description = whatsappLoading
|
|
|
|
|
+ ? 'Consultando o status da integração WhatsApp.'
|
|
|
|
|
+ : primaryWhatsappAccount?.isConnected
|
|
|
|
|
+ ? 'Conta conectada e pronta para receber e enviar mensagens pelo backend.'
|
|
|
|
|
+ : hasAccount
|
|
|
|
|
+ ? 'A conta existe, mas ainda não está conectada. Gere um novo link para concluir ou repetir o Hosted Auth.'
|
|
|
|
|
+ : 'Nenhuma conta WhatsApp conectada ainda. Gere um link, conclua o Hosted Auth e volte para atualizar o status.';
|
|
|
|
|
+ const count = whatsappLoading
|
|
|
|
|
+ ? ''
|
|
|
|
|
+ : connectedAccounts.length > 0
|
|
|
|
|
+ ? `${connectedAccounts.length} conta${connectedAccounts.length > 1 ? 's' : ''} conectada${connectedAccounts.length > 1 ? 's' : ''}`
|
|
|
|
|
+ : whatsappAccounts.length > 0
|
|
|
|
|
+ ? `${whatsappAccounts.length} conta${whatsappAccounts.length > 1 ? 's' : ''} cadastrada${whatsappAccounts.length > 1 ? 's' : ''}`
|
|
|
|
|
+ : '';
|
|
|
|
|
+ const meta = primaryWhatsappAccount
|
|
|
|
|
+ ? [
|
|
|
|
|
+ primaryWhatsappAccount.accountName ? `Conta: ${primaryWhatsappAccount.accountName}` : '',
|
|
|
|
|
+ primaryWhatsappAccount.accountId ? `Account ID: ${primaryWhatsappAccount.accountId}` : '',
|
|
|
|
|
+ primaryWhatsappAccount.status ? `Status: ${primaryWhatsappAccount.status}` : '',
|
|
|
|
|
+ primaryWhatsappAccount.lastSyncAt ? `Última sincronização: ${formatDateTime(primaryWhatsappAccount.lastSyncAt)}` : ''
|
|
|
|
|
+ ]
|
|
|
|
|
+ .filter(Boolean)
|
|
|
|
|
+ .join(' • ')
|
|
|
|
|
+ : '';
|
|
|
|
|
+ const helper = whatsappOpening || whatsappPolling
|
|
|
|
|
+ ? 'Complete a autenticação na janela do Unipile. Esta tela está verificando automaticamente se a conta entrou no backend.'
|
|
|
|
|
+ : lastHostedAuthUrl !== ''
|
|
|
|
|
+ ? 'Se você fechou a janela do Unipile, use “abrir link” para retomar o Hosted Auth ou “atualizar” para consultar o backend.'
|
|
|
|
|
+ : 'Depois de concluir o Hosted Auth, use “atualizar” para confirmar se o callback registrou a integração.';
|
|
|
|
|
+ return {
|
|
|
|
|
+ key: 'whatsapp-business',
|
|
|
|
|
+ title: 'WhatsApp Business',
|
|
|
|
|
+ description,
|
|
|
|
|
+ status,
|
|
|
|
|
+ action: whatsappOpening ? 'abrindo...' : action,
|
|
|
|
|
+ secondaryAction: whatsappRefreshing ? 'atualizando...' : 'atualizar',
|
|
|
|
|
+ tertiaryAction: lastHostedAuthUrl !== '' ? 'abrir link' : '',
|
|
|
|
|
+ count,
|
|
|
|
|
+ meta,
|
|
|
|
|
+ error: whatsappLoadError || whatsappActionError || primaryWhatsappAccount?.lastError || '',
|
|
|
|
|
+ helper,
|
|
|
|
|
+ disablePrimary: whatsappLoading || whatsappOpening,
|
|
|
|
|
+ disableSecondary: whatsappLoading || whatsappRefreshing,
|
|
|
|
|
+ disableTertiary: whatsappOpening
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function loadWhatsappAccounts({ silent = false } = {}) {
|
|
|
|
|
+ if (!silent) {
|
|
|
|
|
+ whatsappLoading = true;
|
|
|
|
|
+ whatsappLoadError = '';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const accounts = await listWhatsappAccounts();
|
|
|
|
|
+ whatsappAccounts = accounts;
|
|
|
|
|
+ if (accounts.some((account) => account?.isConnected)) {
|
|
|
|
|
+ stopWhatsappPolling();
|
|
|
|
|
+ }
|
|
|
|
|
+ return accounts;
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ const message = err?.message ?? 'Falha ao carregar as integrações do WhatsApp.';
|
|
|
|
|
+ if (silent) {
|
|
|
|
|
+ whatsappActionError = message;
|
|
|
|
|
+ return whatsappAccounts;
|
|
|
|
|
+ }
|
|
|
|
|
+ whatsappLoadError = message;
|
|
|
|
|
+ whatsappAccounts = [];
|
|
|
|
|
+ return [];
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (!silent) {
|
|
|
|
|
+ whatsappLoading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function refreshWhatsappAccounts() {
|
|
|
|
|
+ if (whatsappRefreshing) {
|
|
|
|
|
+ return whatsappAccounts;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ whatsappRefreshing = true;
|
|
|
|
|
+ whatsappActionError = '';
|
|
|
|
|
+ try {
|
|
|
|
|
+ return await loadWhatsappAccounts({ silent: true });
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ whatsappRefreshing = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function stopWhatsappPolling() {
|
|
|
|
|
+ if (whatsappPollTimeout !== null) {
|
|
|
|
|
+ window.clearTimeout(whatsappPollTimeout);
|
|
|
|
|
+ whatsappPollTimeout = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (whatsappPollInterval !== null) {
|
|
|
|
|
+ window.clearInterval(whatsappPollInterval);
|
|
|
|
|
+ whatsappPollInterval = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ whatsappPolling = false;
|
|
|
|
|
+ whatsappPollAttempts = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function startWhatsappPolling() {
|
|
|
|
|
+ stopWhatsappPolling();
|
|
|
|
|
+ whatsappPolling = true;
|
|
|
|
|
+ whatsappPollAttempts = 0;
|
|
|
|
|
+
|
|
|
|
|
+ const poll = async () => {
|
|
|
|
|
+ whatsappPollAttempts += 1;
|
|
|
|
|
+ const accounts = await refreshWhatsappAccounts();
|
|
|
|
|
+ if (accounts.some((account) => account?.isConnected) || whatsappPollAttempts >= MAX_WHATSAPP_POLL_ATTEMPTS) {
|
|
|
|
|
+ stopWhatsappPolling();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ whatsappPollTimeout = window.setTimeout(() => {
|
|
|
|
|
+ void poll();
|
|
|
|
|
+ }, 2000);
|
|
|
|
|
+ whatsappPollInterval = window.setInterval(() => {
|
|
|
|
|
+ void poll();
|
|
|
|
|
+ }, 5000);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function handleWhatsappPrimaryAction() {
|
|
|
|
|
+ if (whatsappOpening) return;
|
|
|
|
|
+
|
|
|
|
|
+ whatsappActionError = '';
|
|
|
|
|
+ whatsappOpening = true;
|
|
|
|
|
+ let popup = null;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ popup = window.open('', '_blank');
|
|
|
|
|
+ const url = primaryWhatsappAccount?.id
|
|
|
|
|
+ ? await reconnectWhatsappAccount(primaryWhatsappAccount.id)
|
|
|
|
|
+ : await createWhatsappHostedLink();
|
|
|
|
|
+
|
|
|
|
|
+ if (!url) {
|
|
|
|
|
+ throw new Error('A API não retornou a URL de conexão do WhatsApp.');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ lastHostedAuthUrl = url;
|
|
|
|
|
+
|
|
|
|
|
+ if (popup && !popup.closed) {
|
|
|
|
|
+ popup.location.replace(url);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const opened = window.open(url, '_blank');
|
|
|
|
|
+ if (!opened) {
|
|
|
|
|
+ throw new Error('Não foi possível abrir a janela do Unipile. Verifique o bloqueador de pop-up.');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ startWhatsappPolling();
|
|
|
|
|
+ await refreshWhatsappAccounts();
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ if (popup && !popup.closed) {
|
|
|
|
|
+ popup.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ whatsappActionError = err?.message ?? 'Falha ao iniciar a conexão do WhatsApp.';
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ whatsappOpening = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function openLastHostedAuthLink() {
|
|
|
|
|
+ if (!lastHostedAuthUrl) return;
|
|
|
|
|
+ window.open(lastHostedAuthUrl, '_blank');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function formatDateTime(value) {
|
|
|
|
|
+ if (!value) return '';
|
|
|
|
|
+ const parsed = new Date(value);
|
|
|
|
|
+ if (Number.isNaN(parsed.getTime())) {
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+ return new Intl.DateTimeFormat('pt-BR', {
|
|
|
|
|
+ dateStyle: 'short',
|
|
|
|
|
+ timeStyle: 'short'
|
|
|
|
|
+ }).format(parsed);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onMount(() => {
|
|
|
|
|
+ void loadWhatsappAccounts();
|
|
|
|
|
+
|
|
|
|
|
+ const onFocus = () => {
|
|
|
|
|
+ if (lastHostedAuthUrl !== '' || whatsappPolling) {
|
|
|
|
|
+ void refreshWhatsappAccounts();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('focus', onFocus);
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ window.removeEventListener('focus', onFocus);
|
|
|
|
|
+ stopWhatsappPolling();
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
function getStatusBadge(status) {
|
|
function getStatusBadge(status) {
|
|
|
const statusConfig = {
|
|
const statusConfig = {
|
|
|
|
|
+ loading: { text: 'Carregando', class: 'bg-slate-100 text-slate-800 dark:bg-slate-900/30 dark:text-slate-400' },
|
|
|
|
|
+ connecting: { text: 'Conectando', class: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400' },
|
|
|
connected: { text: 'Conectado', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
|
|
connected: { text: 'Conectado', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
|
|
|
partial: { text: 'Parcial', class: 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400' },
|
|
partial: { text: 'Parcial', class: 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400' },
|
|
|
|
|
+ error: { text: 'Erro', class: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400' },
|
|
|
enabled: { text: 'Ativo', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
|
|
enabled: { text: 'Ativo', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
|
|
|
disabled: { text: 'Inativo', class: 'bg-slate-100 text-slate-800 dark:bg-slate-900/30 dark:text-slate-400' },
|
|
disabled: { text: 'Inativo', class: 'bg-slate-100 text-slate-800 dark:bg-slate-900/30 dark:text-slate-400' },
|
|
|
configured: { text: 'Configurado', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
|
|
configured: { text: 'Configurado', class: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' },
|
|
@@ -121,7 +365,7 @@
|
|
|
<div class="divide-y divide-slate-200 dark:divide-slate-800">
|
|
<div class="divide-y divide-slate-200 dark:divide-slate-800">
|
|
|
{#each section.items as item}
|
|
{#each section.items as item}
|
|
|
<div class="p-5 hover:bg-slate-50 transition-colors duration-150 dark:hover:bg-slate-800/50">
|
|
<div class="p-5 hover:bg-slate-50 transition-colors duration-150 dark:hover:bg-slate-800/50">
|
|
|
- <div class="flex items-center justify-between">
|
|
|
|
|
|
|
+ <div class="flex items-start justify-between gap-4">
|
|
|
<div class="flex-1 min-w-0">
|
|
<div class="flex-1 min-w-0">
|
|
|
<div class="flex items-center gap-3">
|
|
<div class="flex items-center gap-3">
|
|
|
<h3 class="text-sm font-medium text-slate-900 dark:text-white">{item.title}</h3>
|
|
<h3 class="text-sm font-medium text-slate-900 dark:text-white">{item.title}</h3>
|
|
@@ -133,14 +377,51 @@
|
|
|
{#if item.count}
|
|
{#if item.count}
|
|
|
<p class="mt-1 text-xs text-amber-600 dark:text-amber-400">{item.count}</p>
|
|
<p class="mt-1 text-xs text-amber-600 dark:text-amber-400">{item.count}</p>
|
|
|
{/if}
|
|
{/if}
|
|
|
|
|
+ {#if item.meta}
|
|
|
|
|
+ <p class="mt-2 text-xs text-slate-500 dark:text-slate-400">{item.meta}</p>
|
|
|
|
|
+ {/if}
|
|
|
|
|
+ {#if item.error}
|
|
|
|
|
+ <p class="mt-2 text-xs text-red-600 dark:text-red-400">{item.error}</p>
|
|
|
|
|
+ {/if}
|
|
|
|
|
+ {#if item.helper}
|
|
|
|
|
+ <p class="mt-2 text-xs text-slate-500 dark:text-slate-400">{item.helper}</p>
|
|
|
|
|
+ {/if}
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="ml-4">
|
|
|
|
|
|
|
+ <div class="ml-4 flex shrink-0 flex-col items-end gap-2">
|
|
|
|
|
+ {#if item.secondaryAction}
|
|
|
|
|
+ <button
|
|
|
|
|
+ onclick={() => handleSecondaryAction(item)}
|
|
|
|
|
+ disabled={item.disableSecondary}
|
|
|
|
|
+ class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-slate-300 text-slate-700 hover:bg-slate-100 disabled:cursor-not-allowed disabled:opacity-60 transition-colors duration-150 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
|
|
|
|
+ >
|
|
|
|
|
+ {#if item.disableSecondary}
|
|
|
|
|
+ <LoaderCircle size={14} class="animate-spin" />
|
|
|
|
|
+ {:else}
|
|
|
|
|
+ <RefreshCw size={14} />
|
|
|
|
|
+ {/if}
|
|
|
|
|
+ {item.secondaryAction}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ {/if}
|
|
|
<button
|
|
<button
|
|
|
onclick={() => handleAction(item)}
|
|
onclick={() => handleAction(item)}
|
|
|
- class="inline-flex items-center px-3 py-1.5 text-xs font-medium rounded-md bg-indigo-600 text-white hover:bg-indigo-700 transition-colors duration-150"
|
|
|
|
|
|
|
+ disabled={item.disablePrimary}
|
|
|
|
|
+ class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-indigo-600 text-white hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-60 transition-colors duration-150"
|
|
|
>
|
|
>
|
|
|
|
|
+ {#if item.disablePrimary}
|
|
|
|
|
+ <LoaderCircle size={14} class="animate-spin" />
|
|
|
|
|
+ {/if}
|
|
|
{item.action}
|
|
{item.action}
|
|
|
</button>
|
|
</button>
|
|
|
|
|
+ {#if item.tertiaryAction}
|
|
|
|
|
+ <button
|
|
|
|
|
+ onclick={openLastHostedAuthLink}
|
|
|
|
|
+ disabled={item.disableTertiary}
|
|
|
|
|
+ class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md text-indigo-700 hover:bg-indigo-50 disabled:cursor-not-allowed disabled:opacity-60 transition-colors duration-150 dark:text-indigo-300 dark:hover:bg-indigo-500/10"
|
|
|
|
|
+ >
|
|
|
|
|
+ <ExternalLink size={14} />
|
|
|
|
|
+ {item.tertiaryAction}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ {/if}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|