<seva-my-profile>
The <seva-my-profile> component renders a trigger button and a modal overlay for profile management. Users can view their login info, edit member directory details, and add or change their phone number with SMS OTP verification.
Overview
Section titled “Overview”When opened, the modal fetches the user’s profile from the API and displays login info (email, phone with verified/unverified badge) and member directory info (if the user is a member). Members can edit their directory contact info and add or verify a phone number. If the user is not authenticated, the modal shows a sign-in prompt with an embedded <seva-auth> form.
This is a modal component — it self-manages authentication from localStorage and does not require a glue script to propagate auth tokens. See Inline vs. modal components for details.
Quick Example
Section titled “Quick Example”<script type="module" src="YOUR_COMPONENTS_URL/seva-my-profile.js"></script>
<seva-my-profile tenant-slug="my-club" api-url="https://api.seva.tools"></seva-my-profile>
<!-- The element above includes a built-in trigger button. You can also open the modal from your own button: --><button onclick="document.dispatchEvent(new CustomEvent('seva:open-my-profile'))"> My Profile</button>The <seva-my-profile> element must be in the DOM — it listens for the seva:open-my-profile event. The element renders its own “My Profile” trigger button, but you can also open the modal from a custom button by dispatching the event on document.
Attributes
Section titled “Attributes”| Attribute | Type | Default | Description |
|---|---|---|---|
tenant-slug | string | '' | Required. Your organization’s tenant slug. |
api-url | string | '' | Base URL of the Seva API. |
theme | 'light' | 'dark' | 'light' | Color theme. Set via JavaScript property. |
auth-token | string | '' | Session token. Modal components typically self-manage this from localStorage, so you rarely need to set it manually. |
Events emitted
Section titled “Events emitted”| Event | Payload | When it fires |
|---|---|---|
seva:profile-updated | {} | Member directory info or phone number was saved successfully. |
Events listened for
Section titled “Events listened for”The component listens on document for:
| Event | Effect |
|---|---|
seva:authenticated | Stores the token, refreshes profile data if the modal is open. |
seva:signed-out | Clears auth state and profile data. |
seva:open-my-profile | Opens the modal. |
Views / flow
Section titled “Views / flow”loading ──→ display ──→ edit-member ──→ display (on save) └──→ phone-otp ──→ display (on verify success) │ ├── enter-phone (OTP not yet sent) └── enter-code (OTP sent, awaiting code)- loading — Spinner while fetching profile from API.
- display — Shows login info section (email, phone with Verified/Unverified badge) and member directory info section (email, phone, with Edit button for active members). Inactive members see an “inactive membership” notice.
- edit-member — Form to edit directory contact info (phone, email). Validates with Zod on submit.
- phone-otp — Two-step phone verification: enter phone number → receive SMS code → enter code → phone verified.
Opening the modal
Section titled “Opening the modal”The modal can be opened in three ways:
- Built-in trigger button — The component renders a “My Profile” button automatically.
- Custom event — Dispatch
seva:open-my-profileondocument:document.dispatchEvent(new CustomEvent('seva:open-my-profile')); - Direct method call — Call
openModal()on the element:document.querySelector('seva-my-profile').openModal();
Profile sections
Section titled “Profile sections”Login info
Section titled “Login info”Displays the user’s email and phone number. If a phone number is set, a Verified or Unverified badge is shown. A Change (or Add) button opens the phone OTP flow.
Member directory info
Section titled “Member directory info”Shown only if the user has a linked member record. Displays directory email and phone, with an Edit button for active members. If the membership is inactive, an “inactive membership” notice is shown instead of the edit button.
Key types
Section titled “Key types”type MyProfileView = 'loading' | 'display' | 'edit-member' | 'phone-otp';
interface MyProfileUser { id: string; firstName: string; lastName: string; email: string; phoneNumber: string | null; phoneNumberVerified: boolean;}
interface MyProfileMember { id: string; firstName: string | null; lastName: string | null; email: string | null; phone: string | null; membershipNumber: string | null; membershipTypeName: string | null; duesStatus: string | null; active: boolean; joinDate: string | null;}
interface MyProfileResponse { user: MyProfileUser; member: MyProfileMember | null;}Full Example
Section titled “Full Example”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Profile</title> <script type="module" src="YOUR_COMPONENTS_URL/seva-my-profile.js"></script> <style> body { font-family: system-ui, sans-serif; max-width: 400px; margin: 2rem auto; } </style></head><body> <h1>My Account</h1>
<seva-my-profile tenant-slug="my-club" api-url="https://api.seva.tools"> </seva-my-profile>
<!-- Or use a custom button --> <button onclick="document.dispatchEvent(new CustomEvent('seva:open-my-profile'))"> Open Profile </button>
<script> document.addEventListener('seva:profile-updated', () => { console.log('Profile was updated'); }); </script></body></html>