ComponentsFeedback

Connection status

Whether Ascern can still read a member's mail, calendar or meetings, when it last did, and the one thing to do about it. A row in Settings, a dot in the account menu, a banner across the app while it needs action.

Playground

Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.

GmailConnected

Last synced 5 min ago

import { ConnectionStatus } from '@/ui/connection-status';<ConnectionStatus service="Gmail" lastSynced="5 min ago" />

Row

Settings › Connections and the top of Inbox and Meetings: the service, its state in words, the account, the last sync and one action. Connected offers Sync now, not connected offers Connect.

GmailConnected

maya@northbridge.co

Last synced 5 min ago

Google CalendarSyncing

maya@northbridge.co

Syncing now · last synced 2 h ago

EchoNot connected

import { EnvelopeSimpleIcon, CalendarBlankIcon, WaveformIcon } from '@phosphor-icons/react';
import { ConnectionStatus } from '@/ui/connection-status';

// Settings › Connections: one row per service, each with its state, its last sync and its one action.
export default function ConnectionStatusRow() {
  return (
    <div className="flex w-full flex-col divide-y divide-border rounded-large px-200 inset-ring inset-ring-border">
      <ConnectionStatus service="Gmail" account="maya@northbridge.co" status="connected" lastSynced="5 min ago" icon={<EnvelopeSimpleIcon />} onSyncNow={() => {}} />
      <ConnectionStatus service="Google Calendar" account="maya@northbridge.co" status="syncing" lastSynced="2 h ago" icon={<CalendarBlankIcon />} onSyncNow={() => {}} />
      <ConnectionStatus service="Echo" status="disconnected" icon={<WaveformIcon />} onConnect={() => {}} />
    </div>
  );
}

Needs reconnect and failed

The provider stopped the connection (an expired token), or the last sync didn't finish. The line says why and how long nothing new has come in; Reconnect is the one way out.

GmailNeeds reconnect

maya@northbridge.co

Google stopped the connection. Nothing new since 3 days ago.

Google CalendarSync failed

maya@northbridge.co

The last sync didn’t finish. Nothing new since yesterday.

import { CalendarBlankIcon, EnvelopeSimpleIcon } from '@phosphor-icons/react';
import { ConnectionStatus } from '@/ui/connection-status';

// The provider stopped the connection (an expired token), or the last sync failed. Reconnect is the one way out.
export default function ConnectionStatusAttention() {
  return (
    <div className="flex w-full flex-col divide-y divide-border rounded-large px-200 inset-ring inset-ring-border">
      <ConnectionStatus service="Gmail" account="maya@northbridge.co" status="needs-reconnect" reason="Google stopped the connection." lastSynced="3 days ago" icon={<EnvelopeSimpleIcon />} onReconnect={() => {}} />
      <ConnectionStatus service="Google Calendar" account="maya@northbridge.co" status="failed" lastSynced="yesterday" icon={<CalendarBlankIcon />} onReconnect={() => {}} />
    </div>
  );
}

Sync now

Press Sync now: the button keeps its width and spins, the state reads Syncing, then the last sync updates.

GmailConnected

maya@northbridge.co

Last synced 2 h ago

import { EnvelopeSimpleIcon } from '@phosphor-icons/react';
import { useState } from 'react';
import { ConnectionStatus } from '@/ui/connection-status';

// Press Sync now: the button holds its width and spins, the state reads Syncing, then the last sync updates.
export default function ConnectionStatusSync() {
  const [syncing, setSyncing] = useState(false);
  const [last, setLast] = useState('2 h ago');
  const sync = () => {
    setSyncing(true);
    setTimeout(() => {
      setSyncing(false);
      setLast('just now');
    }, 1500);
  };
  return (
    <div className="w-full rounded-large px-200 inset-ring inset-ring-border">
      <ConnectionStatus service="Gmail" account="maya@northbridge.co" status={syncing ? 'syncing' : 'connected'} lastSynced={last} icon={<EnvelopeSimpleIcon />} onSyncNow={sync} />
    </div>
  );
}

Badge

variant="badge" for the account menu: a dot and the state in words. A consultant who never opens Inbox still sees that mail stopped.

Gmail · Connected
Google Calendar · Syncing
Echo · Needs reconnect
Outlook · Sync failed
Zoom · Not connected
import { ConnectionStatus } from '@/ui/connection-status';

// In the account menu: a dot and the state in words, so a consultant who never opens Inbox still sees it.
export default function ConnectionStatusBadge() {
  return (
    <div className="flex flex-col gap-100">
      <ConnectionStatus variant="badge" service="Gmail" status="connected" />
      <ConnectionStatus variant="badge" service="Google Calendar" status="syncing" />
      <ConnectionStatus variant="badge" service="Echo" status="needs-reconnect" />
      <ConnectionStatus variant="badge" service="Outlook" status="failed" />
      <ConnectionStatus variant="badge" service="Zoom" status="disconnected" />
    </div>
  );
}

variant="banner" across the app while a connection needs reconnecting or failed. It shows nothing in any other state, so the app can always render it. Press Reconnect, then Replay.

import { useState } from 'react';
import { Button } from '@/ui/button';
import { ConnectionStatus } from '@/ui/connection-status';

// Across the app while a connection needs action. Press Reconnect: once it is fixed the banner goes.
export default function ConnectionStatusBanner() {
  const [status, setStatus] = useState<'needs-reconnect' | 'connected'>('needs-reconnect');
  return (
    <div className="flex w-full flex-col items-start gap-200">
      <ConnectionStatus variant="banner" service="Gmail" status={status} onReconnect={() => setStatus('connected')} />
      {status === 'connected' ? (
        <Button size="sm" onClick={() => setStatus('needs-reconnect')}>
          Replay
        </Button>
      ) : null}
    </div>
  );
}

On this page