| … | … | @@ -29,6 +29,25 @@ async function initDashboard (view, params) { |
| 29 | 29 | return |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | // Auto-claim unclaimed provider on first dashboard visit |
| 33 | if (view === 'home' || view === 'clients' || view === 'packages' || view === 'availability' || view === 'analytics' || view === 'settings') { |
| 34 | try { |
| 35 | const provData = await api('GET', '/api/dashboard/provider') |
| 36 | if (!provData.provider) { |
| 37 | content.innerHTML = ` |
| 38 | <div class="card" style="text-align:center;padding:3rem;"> |
| 39 | <h2>No Provider Profile</h2> |
| 40 | <p style="color:var(--text-secondary);margin:1rem 0;">You need a provider profile to use the dashboard.</p> |
| 41 | <p style="color:var(--text-secondary);">If you just signed in for the first time, refresh the page — your account may have been auto-linked.</p> |
| 42 | <button class="btn btn-accent" onclick="location.reload()" style="margin-top:1rem;">Refresh</button> |
| 43 | </div>` |
| 44 | return |
| 45 | } |
| 46 | } catch (err) { |
| 47 | // Non-blocking — continue anyway |
| 48 | } |
| 49 | } |
| 50 | |
| 32 | 51 | switch (view) { |
| 33 | 52 | case 'home': return renderCalendar() |
| 34 | 53 | case 'clients': return renderClients() |
| … | … | @@ -648,24 +667,64 @@ async function renderSettings () { |
| 648 | 667 | |
| 649 | 668 | async function renderProfile () { |
| 650 | 669 | const hasPk = HyperBookingIdentity.hasKeypair() |
| 670 | |
| 671 | if (!hasPk) { |
| 672 | content.innerHTML = ` |
| 673 | <div class="card" style="text-align:center;padding:2rem;"> |
| 674 | <p>You are not signed in.</p> |
| 675 | <button class="btn btn-accent" onclick="signIn()" style="margin-top:1rem;">Create Identity</button> |
| 676 | </div>` |
| 677 | return |
| 678 | } |
| 679 | |
| 680 | let profile = {} |
| 681 | try { |
| 682 | profile = await api('GET', '/api/profile') |
| 683 | } catch {} |
| 684 | |
| 651 | 685 | content.innerHTML = ` |
| 652 | 686 | <div class="card"> |
| 687 | <h3 style="margin-bottom:1rem;">Your Profile</h3> |
| 688 | <form id="profile-form"> |
| 689 | <label>Display Name</label> |
| 690 | <input type="text" id="prof-name" value="${esc(profile.displayName || '')}" placeholder="Your name"> |
| 691 | <label>Email (for notifications)</label> |
| 692 | <input type="email" id="prof-email" value="${esc(profile.email || '')}" placeholder="you@email.com"> |
| 693 | <label>Phone (for SMS reminders, optional)</label> |
| 694 | <input type="tel" id="prof-phone" value="${esc(profile.phone || '')}" placeholder="+1 555 123 4567"> |
| 695 | <button type="submit" class="btn btn-accent">Save Profile</button> |
| 696 | </form> |
| 697 | </div> |
| 698 | |
| 699 | <div class="card" style="margin-top:1rem;"> |
| 653 | 700 | <h3 style="margin-bottom:1rem;">Identity</h3> |
| 654 | | ${hasPk ? ` |
| 655 | | <p style="font-size:0.85rem;color:var(--text-secondary);word-break:break-all;"> |
| 656 | | Public Key: ${HyperBookingIdentity.getPublicKey()} |
| 657 | | </p> |
| 658 | | <div style="margin-top:1rem;display:flex;gap:0.5rem;"> |
| 659 | | <button class="btn btn-secondary" onclick="exportKeypair()">Export Keypair</button> |
| 660 | | <button class="btn btn-secondary" onclick="importKeypairPrompt()">Import Keypair</button> |
| 661 | | <button class="btn btn-danger" onclick="signOut()">Sign Out</button> |
| 662 | | </div> |
| 663 | | ` : ` |
| 664 | | <p>You are not signed in.</p> |
| 665 | | <button class="btn btn-accent" onclick="signIn()">Create Identity</button> |
| 666 | | `} |
| 701 | <p style="font-size:0.85rem;color:var(--text-secondary);word-break:break-all;"> |
| 702 | Public Key: ${HyperBookingIdentity.getPublicKey()} |
| 703 | </p> |
| 704 | <div style="margin-top:1rem;display:flex;gap:0.5rem;flex-wrap:wrap;"> |
| 705 | <button class="btn btn-secondary" onclick="exportKeypair()">Export Keypair</button> |
| 706 | <button class="btn btn-secondary" onclick="importKeypairPrompt()">Import Keypair</button> |
| 707 | <button class="btn btn-danger" onclick="signOut()">Sign Out</button> |
| 708 | </div> |
| 667 | 709 | </div> |
| 668 | 710 | ` |
| 711 | |
| 712 | document.getElementById('profile-form').addEventListener('submit', async (e) => { |
| 713 | e.preventDefault() |
| 714 | try { |
| 715 | await api('PUT', '/api/profile', { |
| 716 | displayName: document.getElementById('prof-name').value, |
| 717 | email: document.getElementById('prof-email').value, |
| 718 | phone: document.getElementById('prof-phone').value |
| 719 | }) |
| 720 | showToast('Profile saved', 'success') |
| 721 | // Update nav display name |
| 722 | const nameEl = document.getElementById('user-name') |
| 723 | if (nameEl) nameEl.textContent = document.getElementById('prof-name').value |
| 724 | } catch (err) { |
| 725 | showToast(err.message, 'error') |
| 726 | } |
| 727 | }) |
| 669 | 728 | } |
| 670 | 729 | |
| 671 | 730 | // ---- Portal ---- |