AkurAI Build
Menu

Public repository / 0216cf0f6008aae88a648a82e9b3a462a6688302

AkurAI-Mail / src/lib/webmail/SignalBadge.svelte

Latest change ecdd82c68dc11022c41dbf1ff22cac68e002a3e1 - OLI-247: wave 2 — signal-badge view model and signals-based sync state on framework runtime by Ólafur Búi Ólafsson

<script lang="ts">
  import { Bell, Clock, Flag, MessageCircle } from "lucide-svelte";
  import { signalLabel, signalColorClass, unansweredAgeLabel } from "../../framework/signal-badge.js";

  let { signalType, signal, onCorrect, onDismiss, convId, compact = false }: {
    signalType: string;
    signal: any;
    onCorrect?: (convId: string, type: string, action: string, value?: any) => void;
    onDismiss?: (convId: string, type: string, action: string) => void;
    convId: string;
    compact?: boolean;
  } = $props();

  const dismissed = $derived(signal?.dismissed ?? false);

  function iconFor(type: string) {
    if (type === "replyNeeded") return MessageCircle;
    if (type === "followUpDue") return Clock;
    if (type === "priority") return Flag;
    return Bell;
  }
</script>

{#if signal && !dismissed}
  <span class="signal-badge {signalColorClass(signalType, signal?.value)}" class:compact title={signal?.evidence?.join("; ") ?? ""}>
    {#if compact}
      {@const Icon = iconFor(signalType)}
      <Icon size={13} strokeWidth={2} />
    {:else}
      {@const Icon = iconFor(signalType)}
      <Icon size={14} strokeWidth={2} />
      <span>{signalLabel(signalType, signal)}</span>
      {#if signalType === "replyNeeded" && signal?.unansweredAge}
        <em>{unansweredAgeLabel(signal.unansweredAge)}</em>
      {/if}
      {#if onDismiss}
        <button class="signal-dismiss" aria-label="Dismiss" onclick={() => onDismiss(convId, signalType, "dismiss")}>&times;</button>
      {/if}
    {/if}
  </span>
{/if}

<style>
  .signal-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 600;
    padding: 3px 8px;
    border-radius: 5px;
    line-height: 1;
    white-space: nowrap;
  }
  .signal-badge.compact {
    padding: 2px 4px;
    gap: 0;
    border-radius: 4px;
    min-width: 0;
  }
  .signal-amber {
    background: #fff7e0;
    color: #9a6b00;
    border: 1px solid #f0d060;
  }
  .signal-blue {
    background: #e8f0fe;
    color: #1a56b4;
    border: 1px solid #b8d4f8;
  }
  .signal-red {
    background: #fdecea;
    color: #b3261e;
    border: 1px solid #f5c6c2;
  }
  .signal-muted {
    background: #f3f4f6;
    color: #61716c;
    border: 1px solid #dce4e1;
  }
  .signal-badge em {
    font-style: normal;
    opacity: 0.7;
    margin-left: 1px;
  }
  .signal-dismiss {
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
    padding: 0 2px;
    margin-left: 2px;
    font-size: 15px;
    line-height: 1;
    opacity: 0.5;
  }
  .signal-dismiss:hover {
    opacity: 1;
  }
</style>