File size: 3,774 Bytes
aeea10c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
070c931
 
 
 
 
 
 
aeea10c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
070c931
 
 
 
aeea10c
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { useNavigate } from "react-router-dom";
import { useUpgrade } from "../hooks/useUpgrade";
import { CometIcon, RocketIcon, SparkleIcon } from "./icons/Icons";

const TONE_ICON = { limit: RocketIcon, upgrade: SparkleIcon, action: CometIcon };
const TONE_LABEL = { limit: "Fuel check", upgrade: "Unlock", action: "Next mission" };

/** One floating card, bottom right: the most useful next step, or a limit warning. */
export function NudgeDock() {
  const { nudge, dismissNudge, openUpgrade } = useUpgrade();
  const navigate = useNavigate();
  if (!nudge) return null;
  const Icon = TONE_ICON[nudge.tone];
  const go = () => {
    dismissNudge(nudge.id);
    if (nudge.to) navigate(nudge.to);
    else openUpgrade();
  };
  return (
    <aside key={nudge.id} className={`nudge nudge-${nudge.tone}`} role="status" aria-live="polite">
      <span className="nudge-icon">
        <Icon size={20} />
      </span>
      <div className="nudge-body">
        <p className="eyebrow">{TONE_LABEL[nudge.tone]}</p>
        <h3>{nudge.title}</h3>
        <p className="muted small">{nudge.body}</p>
        <div className="row">
          <button className={`btn btn-small ${nudge.tone === "action" ? "" : "btn-primary"}`} onClick={go}>
            {nudge.cta}
          </button>
          <button className="link-btn small" onClick={() => dismissNudge(nudge.id)}>
            Not now
          </button>
        </div>
      </div>
      <button className="icon-btn nudge-close" onClick={() => dismissNudge(nudge.id)} aria-label="Dismiss">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6">
          <path d="M6 6l12 12M18 6L6 18" />
        </svg>
      </button>
    </aside>
  );
}

/** Sidebar card: plan, monthly meters and the upgrade button. Collapses to a rocket button. */
/** What is left this period: "12 left", "45 min left" or "Unlimited". */
function fuelLeft(m: { used: number; limit: number; unit: string }): string {
  if (m.limit < 0) return "Unlimited";
  const left = Math.max(m.limit - m.used, 0);
  return `${Number.isInteger(left) ? left : left.toFixed(1)}${m.unit ? ` ${m.unit}` : ""} left`;
}

export function FuelGauge({ collapsed }: { collapsed: boolean }) {
  const { status, openUpgrade } = useUpgrade();
  if (!status) return null;
  const meters = status.meters.filter((m) => m.key !== "sources");
  const low = meters.some((m) => m.limit >= 0 && m.pct >= 0.8);
  if (collapsed) {
    return status.can_upgrade ? (
      <button className={`icon-btn fuel-mini${low ? " low" : ""}`} onClick={() => openUpgrade()} title="Upgrade" aria-label="Upgrade">
        <RocketIcon />
      </button>
    ) : null;
  }
  return (
    <div className={`fuel${low ? " low" : ""}`}>
      <div className="row between">
        <span className="mono small">{status.plan.name} plan</span>
        {low && status.can_upgrade && <span className="mono small fuel-warn">Low fuel</span>}
      </div>
      {meters.map((m) => (
        <div key={m.key} className="fuel-row" title={`${m.used} / ${m.limit < 0 ? "unlimited" : m.limit} ${m.unit}`}>
          <span className="fuel-label">
            <span className="mono">{m.label}</span>
            <span className={`mono fuel-left${m.limit >= 0 && m.pct >= 0.8 ? " hot" : ""}`}>{fuelLeft(m)}</span>
          </span>
          <div className="fuel-track">
            <div className={`fuel-fill${m.pct >= 0.8 ? " hot" : ""}`} style={{ width: `${m.limit < 0 ? 4 : Math.max(3, m.pct * 100)}%` }} />
          </div>
        </div>
      ))}
      {status.can_upgrade && (
        <button className="btn btn-primary btn-small fuel-cta" onClick={() => openUpgrade()}>
          <RocketIcon size={16} /> Upgrade now
        </button>
      )}
    </div>
  );
}