Notifications

La section Notifications des paramètres d’un produit d’IA. Une grille canal par événement avec bascules par ligne, par colonne et globale, des heures calmes avec une ligne « prochain calme » en direct, un résumé par e-mail, de vrais envois de test pour le bureau, l’e-mail, le push et Slack, la gestion des permissions du navigateur et un flux de connexion à Slack. S’intègre dans n’importe quelle section Settings.

Chaque produit d’IA finit par avoir la même page de notifications : une liste de ce qui peut arriver, une liste d’endroits où l’on peut vous joindre, et un moyen de tout couper la nuit. NotificationSettings est cette page en une seule section. Elle s’intègre dans une SettingsSection et utilise le même brouillon, la même barre d’enregistrement et la même protection contre les modifications non enregistrées que le reste de Settings.

Le cœur est une grille d’événements par canaux. La rangée du haut active ou désactive un canal pour tous les événements, la première colonne active ou désactive un événement partout, et la case du coin fait tout. Chacune affiche un tiret quand seules certaines sont activées. Au clavier, la grille forme un seul arrêt de tabulation : les flèches passent d’une case à l’autre, Home et End sautent le long d’une ligne, et Space bascule. Les lecteurs d’écran entendent chacune comme « Email for Agent finished ». Sur mobile, la grille devient une carte par événement avec de grandes chips de canal.

Chaque canal a un Send test qui envoie vraiment. Desktop et push affichent une bannière de notification sur la page, et desktop déclenche aussi une vraie notification système dès que le navigateur l’autorise. Email ouvre un aperçu du message dans une boîte de réception, la version résumé quand les e-mails sont regroupés. Slack montre le message tel que votre équipe le voit dans le canal.

Desktop lit la permission du navigateur. Si elle n’a pas été demandée, Allow la demande ; si elle est bloquée, il indique où la modifier ; si le navigateur ne peut pas afficher de notifications, il le dit. Slack démarre déconnecté : sa colonne est désactivée, et Connect ouvre une boîte de dialogue pour choisir un espace de travail et un canal. Si la connexion échoue, la raison s’affiche dans la boîte de dialogue et rien n’est perdu.

Les heures calmes retiennent les alertes desktop et push entre deux heures, les jours choisis, dans le fuseau horaire choisi, avec une ligne qui indique quand commence la prochaine période calme ou que c’est calme en ce moment. Les événements urgents comme les approbations peuvent toujours passer.

  1. Ajouter le registre Pro à components.json

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Ajouter votre token

    Créez un token sur votre page de compte et placez-le dans .env.local sous le nom HEXTAUI_PRO_TOKEN.

  3. Ajouter le block

    pnpm dlx shadcn@latest add @hextaui-pro/notifications

Branchez-le à votre API

Passez les préférences enregistrées et un onSave asynchrone. Les modifications restent en brouillon jusqu’à l’enregistrement depuis la barre ou avec ⌘S. Renvoyez des erreurs de champ pour les afficher sous le champ, ou levez une erreur pour afficher le message dans la barre d’enregistrement. onSendTest envoie un vrai test avant l’apparition de l’aperçu.

"use client"

import { IconBell } from "@tabler/icons-react"

import { SettingsSection, SettingsShell } from "../settings/settings"

import { NotificationSettings, type NotificationPreferences } from "@/components/blocks/notifications/notification-settings"

export function NotificationsPage({ preferences }: { preferences: NotificationPreferences }) {
  return (
    <SettingsShell
      sections={[{ id: "notifications", label: "Notifications", icon: <IconBell /> }]}
      className="h-svh"
    >
      <SettingsSection id="notifications">
        <NotificationSettings
          preferences={preferences}
          email="[email protected]"
          device="Mia’s iPhone"
          onSave={async (values) => {
            const response = await fetch("/api/notifications", {
              method: "PUT",
              body: JSON.stringify(values),
            })
            if (response.status === 422) return { quietDays: "Pick at least one day." }
            if (!response.ok) throw new Error("Check your connection and try again.")
          }}
          onSendTest={async ({ channel, event }) => {
            const response = await fetch("/api/notifications/test", {
              method: "POST",
              body: JSON.stringify({ channel, event: event.id }),
            })
            if (!response.ok) throw new Error("Couldn’t send the test.")
          }}
        />
      </SettingsSection>
    </SettingsShell>
  )
}

Vos propres événements et canaux

Passez events pour ajouter ou retirer des lignes et channels pour choisir des colonnes. Chaque événement peut porter un sample pour les aperçus de test et urgent pour passer malgré les heures calmes. onSlackConnect reçoit l’espace de travail et le canal choisis ; levez une erreur pour indiquer pourquoi cela a échoué, et la boîte de dialogue reste ouverte.

"use client"

import * as React from "react"

import {
  NotificationSettings,
  notificationEvents,
  type NotificationEvent,
  type NotificationPreferences,
  type SlackConnection,
  type SlackWorkspace,
} from "@/components/blocks/notifications/notification-settings"

const events: NotificationEvent[] = [
  ...notificationEvents.filter((event) => event.id !== "billing"),
  {
    id: "deploys",
    label: "Deploys",
    description: "An agent ships to production.",
    sample: {
      title: "Deployed to production",
      body: "“Fix flaky checkout test” is live. 2 checks passed.",
      subject: "Deployed: Fix flaky checkout test",
      action: "Open deploy",
    },
  },
]

export function TeamNotifications({
  preferences,
  workspaces,
  connection,
}: {
  preferences: NotificationPreferences
  workspaces: SlackWorkspace[]
  connection: SlackConnection | null
}) {
  const [slack, setSlack] = React.useState(connection)

  return (
    <NotificationSettings
      preferences={preferences}
      events={events}
      channels={["email", "slack"]}
      email="[email protected]"
      slack={slack}
      slackWorkspaces={workspaces}
      onSlackConnect={async ({ workspace, channel }) => {
        const response = await fetch("/api/slack/connect", {
          method: "POST",
          body: JSON.stringify({ workspace: workspace.id, channel: channel.id }),
        })
        if (response.status === 403) {
          throw new Error(`Hexta isn’t in #${channel.name}. Run /invite @Hexta there first.`)
        }
        if (!response.ok) throw new Error("Slack didn’t answer. Try again.")
        setSlack({ workspace: workspace.name, channel: channel.name })
      }}
      onSlackDisconnect={async () => {
        await fetch("/api/slack/connect", { method: "DELETE" })
        setSlack(null)
      }}
      onSave={(values) =>
        fetch("/api/notifications", { method: "PUT", body: JSON.stringify(values) }).then(() => {})
      }
    />
  )
}

Anatomie

Les parties à composer, de l’extérieur vers l’intérieur.

PartieDescription
NotificationSettingsToute la section : la grille d’événements, les canaux avec tests, les heures calmes et le résumé par e-mail.
notificationEventsLes événements par défaut : Agent finished, Needs approval, Run failed, Mentions, Weekly summary et Billing.
notificationChannelsLes canaux par défaut : desktop, email, push et slack.

NotificationSettings

Doit se trouver dans une SettingsSection.

PropTypePar défaut
preferencesCe qui est enregistré actuellement : { matrix, quietHours, quietFrom, quietTo, quietDays, timeZone, quietUrgent, digest }. matrix associe chaque id d’événement aux canaux activés. Quand elle change et qu’il n’y a pas de modifications, le brouillon la suit.
NotificationPreferences–
onSaveEnregistre le brouillon. Renvoyez { quietDays: "…" } et similaires pour afficher des erreurs de champ, ou levez une erreur pour afficher le message dans la barre d’enregistrement.
(preferences) => void | errors | Promise<void | errors>–
eventsLignes de la grille : { id, label, description?, urgent?, sample? }. sample est { title, body, subject, action } pour les aperçus de test. Les événements urgent peuvent passer malgré les heures calmes.
NotificationEvent[]notificationEvents
channelsColonnes de la grille et lignes sous Channels, dans l’ordre. N’importe lesquelles parmi "desktop", "email", "push" et "slack".
NotificationChannel[]notificationChannels
emailOù va l’e-mail. Affiché sous Email et dans l’e-mail de test.
string"[email protected]"
deviceLe téléphone qui reçoit les notifications push.
string"your phone"
slackLe canal Slack connecté. Tant qu’il vaut null, la colonne Slack est désactivée.
{ workspace, channel } | nullnull
slackWorkspacesEspaces de travail à choisir lors de la connexion : { id, name, channels: { id, name, private? }[] }.
SlackWorkspace[][]
onSlackConnectConnecte le canal choisi. Levez une erreur pour afficher le message dans la boîte de dialogue.
({ workspace, channel }) => void | Promise<void>–
onSlackDisconnectDéconnecte Slack après confirmation de la personne.
() => void | Promise<void>–
onSendTestEnvoie un vrai test. L’aperçu s’affiche une fois la promesse résolue, et le bouton propose de réessayer en cas d’erreur. event est le premier activé pour ce canal.
({ channel, event }) => void | Promise<void>–
ToucheAction
TabEntre une seule fois dans la grille, sur la dernière case utilisée, puis passe aux canaux.
↑↓←→Dans la grille, passe d’une case à l’autre. Gauche et droite suivent le sens de lecture.
HomeEndDans la grille, saute au début ou à la fin de la ligne. Avec Ctrl ou ⌘, à la première ou dernière case.
SpaceActive ou désactive la case ayant le focus. Sur une case de ligne, de colonne ou de coin, les active toutes, ou les désactive si toutes sont activées.
EscFerme une bannière de notification de test tant qu’elle a le focus.
  • La grille est une grille ARIA avec en-têtes de colonne et de ligne, et chaque case est nommée par son canal et son événement, par exemple « Mobile push for Run failed ».
  • Les cases de ligne, de colonne et de coin sont mixtes quand seules certaines sont activées, donc les lecteurs d’écran entendent « partially checked ».
  • Les envois de test, les résultats de permission et les changements Slack sont annoncés poliment. La prochaine période calme est une ligne de statut qui se met à jour pendant la modification.
  • Les bannières de test se mettent en pause au survol ou au focus, ont un bouton de fermeture et s’estompent au lieu de glisser avec réduction des animations.
  • Sur mobile, chaque chip de canal est une cible de 44px.

Construit avec

Les composants HextaUI gratuits dont Notifications est constitué. Chacun s’installe séparément.

Code

5 fichiers, ajoutés à components/blocks/notifications.