Skip to main content

Quick Wins Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.
Goal: Fix passkey SVG visibility in light mode, add a global bridge inbox toast for new X-ray arrivals, and replace the 153k hardcoded referral calculator default with live plan prices. Architecture: Three independent UI/backend fixes — no new files required, all changes extend existing components in-place. Tech Stack: React, TypeScript, Sonner (toasts), existing serverComm, lucide-react, Hono

File Map

FileChange
ui/public/passkey-rounded.svgfill="#ffffff"fill="currentColor"
ui/src/components/icons/PasskeyIcon.tsxForward className, wrap with theme-aware color
ui/src/contexts/ClinicEventsContext.tsxNo change — kept as pure state
ui/src/components/shared/XrayArrivalToast.tsxNew — tiny component, mounts once, fires toast on new_xray
ui/src/App.tsxMount <XrayArrivalToast /> inside ClinicEventsProvider
ui/src/components/referrals/ReferralsModule.tsxReplace '153000' default with fetched plan price
ui/src/lib/serverComm.tsAdd annualPrice to getSubscriptionPlans() return type

Task 1: Passkey SVG — Light Mode Fix

Files:
  • Modify: ui/public/passkey-rounded.svg
  • Modify: ui/src/components/icons/PasskeyIcon.tsx
  • Step 1: Fix SVG fill
Open ui/public/passkey-rounded.svg. Change fill="#ffffff" to fill="currentColor". The entire file should become:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor" style="opacity:1;"><path d="M5 20q-.825 0-1.412-.587T3 18v-.8q0-.85.438-1.562T4.6 14.55q1.55-.775 3.15-1.162T11 13q.35 0 .7.013t.7.062q.275.025.437.213t.163.462q.05 1.175.575 2.213t1.4 1.762q.175.125.275.313t.1.412V19q0 .425-.288.713T14.35 20zm6-8q-1.65 0-2.825-1.175T7 8t1.175-2.825T11 4t2.825 1.175T15 8t-1.175 2.825T11 12m7.5 2q.425 0 .713-.288T19.5 13t-.288-.712T18.5 12t-.712.288T17.5 13t.288.713t.712.287m.15 8.65l-1-1q-.05-.05-.15-.35v-4.45q-1.1-.325-1.8-1.237T15 13.5q0-1.45 1.025-2.475T18.5 10t2.475 1.025T22 13.5q0 1.125-.638 2t-1.612 1.25l.9.9q.15.15.15.35t-.15.35l-.8.8q-.15.15-.15.35t.15.35l.8.8q.15.15.15.35t-.15.35l-1.3 1.3q-.15.15-.35.15t-.35-.15"/></svg>
  • Step 2: Read PasskeyIcon.tsx
Read ui/src/components/icons/PasskeyIcon.tsx fully to understand its current props and usage.
  • Step 3: Add theme-aware color class
Replace the <img> tag (or however the SVG is rendered) so the icon inherits text-foreground for theme awareness. The component should accept and forward a className prop. If the icon currently uses <img src="/passkey-rounded.svg">, switch to an inline <img> with className that sets color context — but since SVG fill="currentColor" only works with inline SVG or CSS color, wrap the img in a span with text-foreground dark:text-white:
// If rendered as <img>, the fill="currentColor" trick doesn't work.
// Switch to inline SVG render using a CSS mask trick OR import as ReactComponent.
// Simplest safe fix: wrap in a styled container so the SVG uses CSS color filter.

export default function PasskeyIcon({ size = 24, className = '' }: { size?: number; className?: string }) {
  return (
    <span
      className={`inline-flex text-foreground ${className}`}
      style={{ width: size, height: size }}
    >
      <img
        src="/passkey-rounded.svg"
        width={size}
        height={size}
        alt="Passkey"
        style={{ filter: 'none' }}
        className="passkey-icon"
      />
    </span>
  );
}
Add to ui/src/index.css (or global styles):
/* Passkey icon: invert in light mode since SVG fill is currentColor white */
:root .passkey-icon {
  filter: invert(1);
}
.dark .passkey-icon {
  filter: none;
}
This inverts the white icon to black in light mode without touching the SVG path math.
  • Step 4: Verify visually
Run npm run dev in ui/. Open the PasskeySetupModal and SecuritySettingsPage in both light and dark mode. Confirm icon is visible in both.
  • Step 5: Commit
git add ui/public/passkey-rounded.svg ui/src/components/icons/PasskeyIcon.tsx ui/src/index.css
git commit -m "fix(ui): passkey icon visible in light mode via CSS invert"

Task 2: Bridge Inbox — Global Toast for New X-ray

Files:
  • Create: ui/src/components/shared/XrayArrivalToast.tsx
  • Modify: ui/src/App.tsx
Context: When a bridge device uploads an X-ray, the Cloudflare Durable Object broadcasts a new_xray event to all connected clinic users via SSE. This event lands in ClinicEventsContext as lastEvent. Currently no toast is shown. The bridge inbox view is at /dashboard?view=bridge-inbox.
  • Step 1: Create XrayArrivalToast.tsx
// ui/src/components/shared/XrayArrivalToast.tsx
import { useEffect, useRef } from 'react';
import { toast } from 'sonner';
import { useEventBus } from '@/contexts/ClinicEventsContext';

export function XrayArrivalToast() {
  const { lastEvent } = useEventBus();
  const seenRef = useRef<Set<string>>(new Set());

  useEffect(() => {
    if (!lastEvent) return;
    if (lastEvent.type !== 'new_xray' && lastEvent.type !== 'new_dicom') return;

    // Deduplicate by ts to avoid double-firing on re-renders
    const key = `${lastEvent.type}-${lastEvent.ts}`;
    if (seenRef.current.has(key)) return;
    seenRef.current.add(key);

    const patientLabel = lastEvent.patientName
      ? `for ${lastEvent.patientName}`
      : 'from unmatched patient';

    toast.info(`New X-ray arrived ${patientLabel}`, {
      description: lastEvent.toothNumber
        ? `Tooth ${lastEvent.toothNumber}`
        : 'Open bridge inbox to review and assign',
      duration: 10000,
      action: {
        label: 'Open Inbox',
        onClick: () => {
          window.history.pushState({}, '', '/dashboard?view=bridge-inbox');
          window.dispatchEvent(new PopStateEvent('popstate'));
        },
      },
    });
  }, [lastEvent]);

  return null;
}
  • Step 2: Mount in App.tsx
Read ui/src/App.tsx to find where ClinicEventsProvider wraps the app. Add <XrayArrivalToast /> inside that provider (it needs access to useEventBus). Find the <ClinicEventsProvider> tag and add the component as a child:
import { XrayArrivalToast } from '@/components/shared/XrayArrivalToast';

// Inside the JSX, immediately after <ClinicEventsProvider> opens:
<ClinicEventsProvider>
  <XrayArrivalToast />
  {/* ...rest of children */}
</ClinicEventsProvider>
  • Step 3: Verify
With the dev server running, trigger a test: open browser console and manually dispatch a fake event to verify the toast appears with the “Open Inbox” button. Or test via the actual bridge device if available.
// In browser console to simulate:
window.__testXrayToast = () => {
  // Find the pushEvent from ClinicEventsContext — this is for manual testing only
};
The toast should read: “New X-ray arrived for [Patient Name]” with action button “Open Inbox” navigating to /dashboard?view=bridge-inbox.
  • Step 4: Commit
git add ui/src/components/shared/XrayArrivalToast.tsx ui/src/App.tsx
git commit -m "feat(ui): global toast for new X-ray arrivals with bridge inbox deep link"

Task 3: Referral Calculator — Remove 153k Hardcoding

Files:
  • Modify: ui/src/lib/serverComm.ts — add annualPrice to return type
  • Modify: ui/src/components/referrals/ReferralsModule.tsx — fetch plans, default to Pro annual price
Context: The referral calculator has exampleNetAmount initialized to '153000' (the Pro annual price). The actual Pro annual price in DB is 152990.00. Rather than hardcode it, fetch plans from the API and use the Pro plan’s annualPrice. The calculator input field stays editable — we just initialize it correctly and add a plan selector.
  • Step 1: Add annualPrice to serverComm type
In ui/src/lib/serverComm.ts, find the getSubscriptionPlans() function (around line 2097) and update its return type:
export async function getSubscriptionPlans(): Promise<Array<{
  id: string;
  planName: string;
  planTier: string;
  monthlyPrice: string | null;
  annualPrice: string | null;  // ADD THIS
  maxUsers: number | null;
  features: any;
}>> {
  const response = await fetchWithAuth('/api/v1/protected/subscription-plans');
  return response.json();
}
  • Step 2: Update ReferralsModule.tsx
Read ui/src/components/referrals/ReferralsModule.tsx fully first. Replace the hardcoded useState('153000') with a plan-driven default. Add plan fetching on mount. The calculator gets a plan selector dropdown above the amount input:
// Add near top of component, after existing useState declarations:
const [plans, setPlans] = useState<Array<{ id: string; planName: string; annualPrice: string | null; planTier: string }>>([]);
const [selectedPlanId, setSelectedPlanId] = useState<string>('');

// Replace: const [exampleNetAmount, setExampleNetAmount] = useState('153000');
// With:
const [exampleNetAmount, setExampleNetAmount] = useState('');

// Add useEffect to load plans:
useEffect(() => {
  getSubscriptionPlans()
    .then(allPlans => {
      const payable = allPlans.filter(p => p.planTier !== 'trial' && p.planTier !== 'enterprise' && p.annualPrice);
      setPlans(payable);
      if (payable.length > 0) {
        const pro = payable.find(p => p.planTier === 'pro') || payable[0];
        setSelectedPlanId(pro.id);
        setExampleNetAmount(pro.annualPrice ? String(Math.round(Number(pro.annualPrice))) : '');
      }
    })
    .catch(() => {
      setExampleNetAmount('152990'); // Fallback if API fails
    });
}, []);
Add a plan selector just above the amount input field in the JSX. Find where exampleNetAmount input is rendered (around line 223) and add above it:
{plans.length > 1 && (
  <div className="flex gap-2 mb-2">
    {plans.map(p => (
      <button
        key={p.id}
        type="button"
        onClick={() => {
          setSelectedPlanId(p.id);
          if (p.annualPrice) setExampleNetAmount(String(Math.round(Number(p.annualPrice))));
        }}
        className={`px-3 py-1 rounded-full text-xs border transition-colors ${
          selectedPlanId === p.id
            ? 'bg-primary text-primary-foreground border-primary'
            : 'border-border text-muted-foreground hover:border-primary'
        }`}
      >
        {p.planName} Annual
      </button>
    ))}
  </div>
)}
Also update the input placeholder from "153000" to "Enter annual plan amount".
  • Step 3: Verify
Open Settings > Referrals. The calculator should default to the Pro plan’s annual price (≈152,990) and show plan toggle buttons. Changing plan updates the calculator. The input is still editable manually.
  • Step 4: Commit
git add ui/src/lib/serverComm.ts ui/src/components/referrals/ReferralsModule.tsx
git commit -m "feat(ui): referral calculator uses live plan prices instead of hardcoded 153k"