Profil

Der Bereich „Profil“ in den Einstellungen eines KI-Produkts. Ein Foto kreisförmig zuschneiden, einen Benutzernamen wählen, der beim Tippen geprüft wird, eine neue E-Mail mit einem 6-stelligen Code bestätigen, Links hinzufügen, die die Website erkennen, und eine Live-Karte sehen, wie andere dich sehen.

ProfileSettings ist die Profilseite eines Produkts wie Claude oder Cursor, gebaut, um in jeden SettingsShell-Bereich zu passen. Gib ihm das gespeicherte Profil und ein onSave, und es erledigt den Rest: einen Entwurf, der nur speichert, wenn du es sagst, Feldfehler aus deinen Prüfungen oder deinem Server und die schwebende Speicherleiste aus Settings mit ⌘S.

Oben zeigt eine Card, wie andere dein Profil sehen, und ändert sich beim Tippen: Foto, Name, Handle, Bio und Links. Schaltest du Show my profile aus, wird die Card abgedunkelt und sagt, dass nur du sie siehst. Copy link legt die Profil-URL in die Zwischenablage.

Wähle ein Foto oder ziehe eines auf die Zeile, und es öffnet sich in einem Zuschneide-Dialog. Ziehe zum Verschieben, zoome mit dem Slider, dem Mausrad oder einer Pinch-Geste auf dem Trackpad, und das Foto wird im Browser quadratisch zugeschnitten, bevor es als Data-URL bei onSave ankommt. Dateien des falschen Typs, zu große oder unter 128 Pixeln werden in der Zeile mit Begründung abgelehnt.

Der Benutzername wird beim Tippen geprüft, ohne das Tippen zu bremsen. Prüfungen warten 350 ms nach der letzten Taste, veraltete Prüfungen werden über ein AbortSignal abgebrochen, und bereits geprüfte Namen antworten sofort. Ein vergebener Name zeigt deine Vorschläge als Ein-Tipp-Korrekturen. Speicherst du, bevor eine Prüfung fertig ist, wartet das Speichern darauf.

E-Mail gehört nicht zum Entwurf. Change öffnet einen zweistufigen Dialog: die neue Adresse eingeben, dann den 6-stelligen Code, der dorthin gesendet wurde und sich selbst prüft, sobald die letzte Ziffer eingegeben ist. Ein falscher Code wackelt und wird geleert, Resend wird nach 30 Sekunden freigeschaltet, und die Zeile aktualisiert sich, sobald der Code bestätigt ist. Eine unbestätigte Adresse zeigt Resend link.

Links akzeptieren, was Leute einfügen: github.com/you, eine vollständige URL oder eine www-Adresse. Bekannte Websites erhalten ihr Icon, alles andere seinen ersten Buchstaben. Beim Speichern werden sie zu https-URLs normalisiert und leere verworfen. Adressen, die keine Links sind oder eine andere wiederholen, werden einzeln markiert.

  1. Die Pro-Registry zu components.json hinzufügen

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Token hinzufügen

    Erstelle auf deiner Kontoseite einen Token und trage ihn in .env.local als HEXTAUI_PRO_TOKEN ein.

  3. Den Block hinzufügen

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

An deine API anbinden

Übergib das gespeicherte Profil und ein onSave, das Feldfehler zurückgibt oder einen Fehler wirft. checkUsername erhält ein AbortSignal, sodass veraltete Prüfungen beim Tippen abgebrochen werden.

"use client"

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

import { ProfileSettings, type ProfileValues } from "@/components/blocks/profile/profile"

export function ProfilePage({ profile, email }: { profile: ProfileValues; email: string }) {
  return (
    <SettingsShell sections={[{ id: "profile", label: "Profile" }]} className="h-svh">
      <SettingsSection id="profile">
        <ProfileSettings
          profile={profile}
          email={email}
          checkUsername={async (username, signal) => {
            const response = await fetch(`/api/usernames/${username}`, { signal })
            return response.json()
          }}
          onSave={async (values) => {
            const response = await fetch("/api/profile", {
              method: "PATCH",
              body: JSON.stringify(values),
            })
            if (response.status === 409) return { username: "That username is taken." }
            if (!response.ok) throw new Error("Check your connection and try again.")
          }}
        />
      </SettingsSection>
    </SettingsShell>
  )
}

E-Mail ändern und bestätigen

Füge onRequestEmailCode und onVerifyEmailCode hinzu, um den Change-Button zu aktivieren. Gib aus onRequestEmailCode eine Meldung zurück, um eine Adresse abzulehnen, und aus onVerifyEmailCode false für einen falschen Code.

"use client"

import * as React from "react"

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

import { ProfileSettings, type ProfileValues } from "@/components/blocks/profile/profile"

export function ProfileWithEmail({ profile, account }: {
  profile: ProfileValues
  account: { email: string; verified: boolean }
}) {
  const [email, setEmail] = React.useState(account.email)

  return (
    <SettingsShell sections={[{ id: "profile", label: "Profile" }]} className="h-svh">
      <SettingsSection id="profile">
        <ProfileSettings
          profile={profile}
          email={email}
          emailVerified={account.verified}
          onSave={(values) => fetch("/api/profile", { method: "PATCH", body: JSON.stringify(values) }).then(() => {})}
          onResendVerification={() => fetch("/api/email/verify", { method: "POST" })}
          onRequestEmailCode={async (next) => {
            const response = await fetch("/api/email/change", {
              method: "POST",
              body: JSON.stringify({ email: next }),
            })
            if (response.status === 409) return "That address belongs to another account."
            if (!response.ok) throw new Error("Couldn’t send the code. Try again.")
          }}
          onVerifyEmailCode={async (next, code) => {
            const response = await fetch("/api/email/change/verify", {
              method: "POST",
              body: JSON.stringify({ email: next, code }),
            })
            return response.ok
          }}
          onEmailChange={setEmail}
        />
      </SettingsSection>
    </SettingsShell>
  )
}

Aufbau

Die Teile, die du zusammensetzt, von außen nach innen.

PartBeschreibung
ProfileSettingsDer Bereich: Vorschau-Card, Foto, Name, wie der Assistent dich nennt, Benutzername, Bio, öffentliches Profil, Links und E-Mail.
ProfileCardDie Live-Vorschau des öffentlichen Profils, mit Copy link. Einzeln verwendbar.
AvatarCropDialogSchneidet ein Bild per Ziehen, Zoom und Tastatur quadratisch zu und gibt eine Data-URL zurück.
ChangeEmailDialogDie zweistufige E-Mail-Änderung: neue Adresse, dann ein 6-stelliger Code.
useUsernameCheckEntprellte, abbrechbare, gecachte Verfügbarkeitsprüfungen für einen Benutzernamen.

ProfileSettings

Verwende es innerhalb einer SettingsSection.

PropTypStandard
profileDas jetzt Gespeicherte: { avatar, name, nickname, username, bio, links, public }. Ändert es sich und gibt es keine Änderungen, folgt der Entwurf ihm.
ProfileValues–
onSaveWird mit getrimmten Werten und normalisierten Links aufgerufen. Gib { field: message } für Feldfehler zurück oder wirf einen Fehler, um die Meldung in der Speicherleiste zu zeigen.
(values) => void | errors | Promise<void | errors>–
emailDie Anmeldeadresse.
string–
emailVerifiedZeigt Verified oder Not verified mit Resend link.
booleantrue
checkUsernamePrüft, ob ein Benutzername frei ist. Erhält den kleingeschriebenen Namen und ein AbortSignal. Ohne ihn wird nur das Format geprüft.
(username, signal) => Promise<{ available, message?, suggestions? }>–
onRequestEmailCodeSendet einen Code an eine neue Adresse. Gib eine Meldung zurück, um die Adresse abzulehnen. Aktiviert zusammen mit onVerifyEmailCode den Change-Button.
(email) => Promise<void | string>–
onVerifyEmailCodePrüft den Code. Gib false für einen falschen Code zurück oder wirf einen Fehler, um deine Meldung zu zeigen.
(email, code) => Promise<boolean>–
onEmailChangeWird aufgerufen, sobald die neue Adresse bestätigt ist.
(email) => void–
onResendVerificationFügt Resend link hinzu, solange die Adresse unbestätigt ist.
() => Promise<unknown>–
profileUrlDie öffentliche Profil-URL, unter dem Benutzernamen angezeigt und von Copy link kopiert.
(username) => stringhttps://hexta.app/@username
assistantNameWird in „Wie soll Hexta dich nennen?“ verwendet.
string"Hexta"
maxAvatarBytesGrößtes akzeptiertes Foto.
number5 MB
maxLinksWie viele Links ein Profil haben kann.
number5
maxBioLänge der Bio, mit Zähler.
number160
PropTypStandard
profileWas angezeigt werden soll.
{ avatar, name, username, bio, links, public }–
urlDie Profil-URL zum Anzeigen und Kopieren.
string–
onCopiedWird aufgerufen, nachdem der Link kopiert wurde.
() => void–
PropTypStandard
sourceDas Bild zum Zuschneiden. Geöffnet, solange gesetzt.
{ url, width, height } | null–
onOpenChangeLeert source beim Schließen.
(open) => void–
onApplyLiefert das zugeschnittene Quadrat als WebP, oder als PNG, wo WebP nicht unterstützt wird.
(dataUrl) => void–
outputSizeGrößte Seite der Ausgabe in Pixeln. Skaliert nie über den Zuschnitt hinaus.
number512
maxZoomWie weit hineingezoomt werden kann.
number4
PropTypStandard
openOb es geöffnet ist.
boolean–
currentEmailWird im Intro angezeigt und als neue Adresse abgelehnt.
string–
onRequestCodeSendet den Code. Gib eine Meldung zurück, um abzulehnen.
(email) => Promise<void | string>–
onVerifyCodePrüft den Code.
(email, code) => Promise<boolean>–
onChangedWird aufgerufen, nachdem der Code bestätigt wurde, bevor der Dialog schließt.
(email) => void–
resendAfterSekunden, bis Resend code freigeschaltet wird.
number30

useUsernameCheck(username, options)

Gibt { status, result, problem, retry, resolve } zurück. status ist idle, invalid, checking, available, taken oder error.

PropTypStandard
currentDer gespeicherte Benutzername, der nie geprüft wird.
string–
checkDeine Verfügbarkeitsprüfung.
(username, signal) => Promise<UsernameCheck>–
delayMillisekunden, die nach dem Ende des Tippens gewartet wird.
number350
TasteAktion
EnterFügt im letzten Link einen weiteren Link hinzu und springt dorthin.
←→↑↓Verschiebt im Zuschneide-Dialog das Foto. Shift verschiebt weiter.
+−Zoomt im Zuschneide-Dialog hinein und heraus. 0 setzt zurück.
⌘SSpeichert, solange etwas ungespeichert ist. Strg+S unter Windows und Linux.
EscSchließt einen Dialog und gibt den Fokus an den Button zurück, der ihn geöffnet hat.
  • Jedes Feld trägt den Namen seiner Zeile, und Buttons, die ein Wort wiederholen, sagen, worauf sie wirken: Upload photo, Change email, Remove link 2, github.com/you.
  • Ergebnisse der Benutzernamenprüfung, kopierte Links, Fotoänderungen, entfernte Links und eine geänderte E-Mail werden höflich angesagt. Das Prüfen selbst wird nicht angesagt, sodass das Tippen ruhig bleibt.
  • Feldfehler markieren nur das fehlerhafte Feld, und der Fokus springt zum ersten, wenn ein Speichern abgelehnt wird. Der E-Mail-Code sagt Prüfen, Bestätigt und Falsch an.
  • Das Entfernen eines Links setzt den Fokus auf den nächsten Link oder auf Add link, wenn keiner übrig ist. Das Entfernen des Fotos setzt den Fokus auf Upload.
  • Der Zuschneidebereich ist fokussierbar und funktioniert mit Pfeiltasten sowie Plus und Minus, und der Zoom-Slider nennt seinen Wert als Prozentzahl.
  • Auf Touchscreens nutzen Inputs 16-px-Text, damit iOS nicht zoomt, und Icon-Buttons haben 44-px-Ziele.

Gebaut mit

Die kostenlosen HextaUI-Komponenten, aus denen Profile besteht. Jede lässt sich einzeln installieren.

Code

6 Dateien, hinzugefügt zu components/blocks/profile.