@@ -0,0 +1,419 @@
{% extends "base.html" %}
{% block title %}My Vault — PassKeeper{% endblock %}
{% block body_class %}vault-page{% endblock %}
{% block body %}
< div class = "app-layout" >
<!-- ── Sidebar ─────────────────────────────────────────────────── -->
< aside class = "sidebar" >
< div class = "sidebar-header" >
< span class = "logo-icon" > 🔒< / span >
< span class = "logo-text" > PassKeeper< / span >
< / div >
< nav class = "sidebar-nav" >
< ul >
< li class = "sidebar-item active" id = "sidebar-all" data-view = "vault" >
< span class = "sidebar-icon" > 🏠< / span > < span class = "sidebar-label" > All Items< / span >
< / li >
< li class = "sidebar-section-header" > Item Types< / li >
< li class = "sidebar-item" data-type-filter = "password" >
< span class = "sidebar-icon" > 🔑< / span > < span class = "sidebar-label" > Passwords< / span >
< / li >
< li class = "sidebar-item" data-type-filter = "note" >
< span class = "sidebar-icon" > 📝< / span > < span class = "sidebar-label" > Secure Notes< / span >
< / li >
< li class = "sidebar-item" data-type-filter = "card" >
< span class = "sidebar-icon" > 💳< / span > < span class = "sidebar-label" > Payment Cards< / span >
< / li >
< li class = "sidebar-item" data-type-filter = "bank" >
< span class = "sidebar-icon" > 🏦< / span > < span class = "sidebar-label" > Bank Accounts< / span >
< / li >
< li class = "sidebar-item" data-type-filter = "address" >
< span class = "sidebar-icon" > 🏠< / span > < span class = "sidebar-label" > Addresses< / span >
< / li >
< li class = "sidebar-item" data-type-filter = "ssn" >
< span class = "sidebar-icon" > 🪪< / span > < span class = "sidebar-label" > Identities< / span >
< / li >
< li class = "sidebar-section-header" > Tools< / li >
< li class = "sidebar-item" id = "sidebar-security" data-view = "security" >
< span class = "sidebar-icon" > 🛡️< / span > < span class = "sidebar-label" > Security< / span >
< / li >
< li class = "sidebar-item" id = "sidebar-sharing" data-view = "sharing" >
< span class = "sidebar-icon" > 🔗< / span > < span class = "sidebar-label" > Sharing< / span >
< / li >
< li class = "sidebar-item" id = "sidebar-emergency" data-view = "emergency" >
< span class = "sidebar-icon" > 🚨< / span > < span class = "sidebar-label" > Emergency Access< / span >
< / li >
< li class = "sidebar-section-header" >
Folders
< button class = "btn-new-folder" id = "btn-new-folder" title = "New folder" > +< / button >
< / li >
< li id = "new-folder-row" class = "new-folder-row hidden" >
< form id = "new-folder-form" >
< input type = "text" id = "new-folder-input" placeholder = "Folder name" maxlength = "128" >
< button type = "submit" class = "btn-icon" title = "Save" > ✓< / button >
< button type = "button" class = "btn-icon" id = "btn-cancel-folder" title = "Cancel" > ✕< / button >
< / form >
< / li >
< ul id = "sidebar-folders" > < / ul >
< / ul >
< / nav >
< div class = "sidebar-footer" >
< button class = "sidebar-item btn-text" id = "btn-settings" >
< span class = "sidebar-icon" > ⚙️< / span > < span class = "sidebar-label" > Settings< / span >
< / button >
< button class = "sidebar-item btn-text" id = "btn-logout" >
< span class = "sidebar-icon" > 🚪< / span > < span class = "sidebar-label" > Sign out< / span >
< / button >
< / div >
< / aside >
<!-- ── Main ───────────────────────────────────────────────────── -->
< main class = "vault-main" >
<!-- Vault view -->
< div id = "view-vault" >
< header class = "vault-header" >
< h2 class = "vault-title" id = "vault-title" > All Items< / h2 >
< div class = "vault-toolbar" >
< div class = "search-wrapper" >
< span class = "search-icon" > 🔍< / span >
< input type = "search" id = "search-input" placeholder = "Search vault…" autocomplete = "off" >
< / div >
< div class = "sort-wrapper" >
< select id = "sort-select" title = "Sort by" >
< option value = "name-asc" > Name A– Z< / option >
< option value = "name-desc" > Name Z– A< / option >
< option value = "date-desc" > Newest first< / option >
< option value = "date-asc" > Oldest first< / option >
< option value = "folder" > By folder< / option >
< / select >
< / div >
< button class = "btn-fab" id = "btn-add-item" title = "Add item" > +< / button >
< / div >
< / header >
< div id = "vault-spinner" class = "spinner-overlay hidden" > < div class = "spinner" > < / div > < / div >
< ul id = "vault-list" class = "vault-list" > < / ul >
< / div >
<!-- Security dashboard view -->
< div id = "view-security" class = "hidden" >
< header class = "vault-header" > < h2 class = "vault-title" > Security Dashboard< / h2 > < / header >
< div class = "security-dashboard" >
< div class = "security-summary" id = "security-summary" > < / div >
< div class = "security-sections" id = "security-sections" > < / div >
< / div >
< / div >
<!-- Sharing view -->
< div id = "view-sharing" class = "hidden" >
< header class = "vault-header" >
< h2 class = "vault-title" > Sharing< / h2 >
< div class = "vault-toolbar" >
< button class = "btn-primary btn-sm" id = "btn-share-item" > + Share an item< / button >
< / div >
< / header >
< div class = "panel-body" >
< div id = "sharing-keys-notice" class = "notice-info hidden" >
You need to set up sharing keys before you can share items.
< button class = "btn-primary btn-sm" id = "btn-setup-keys-from-sharing" > Set up keys< / button >
< / div >
< div class = "panel-tabs" >
< button class = "tab-btn active" data-tab = "shared-out" > Shared by me< / button >
< button class = "tab-btn" data-tab = "shared-in" > Shared with me< / button >
< / div >
< div id = "tab-shared-out" class = "tab-pane" >
< ul id = "shared-out-list" class = "share-list" > < / ul >
< / div >
< div id = "tab-shared-in" class = "tab-pane hidden" >
< ul id = "shared-in-list" class = "share-list" > < / ul >
< / div >
< / div >
< / div >
<!-- Emergency Access view -->
< div id = "view-emergency" class = "hidden" >
< header class = "vault-header" >
< h2 class = "vault-title" > Emergency Access< / h2 >
< div class = "vault-toolbar" >
< button class = "btn-primary btn-sm" id = "btn-add-emergency" > + Add trusted contact< / button >
< / div >
< / header >
< div class = "panel-body" >
< div class = "panel-tabs" >
< button class = "tab-btn active" data-tab = "em-grants" > My trusted contacts< / button >
< button class = "tab-btn" data-tab = "em-access" > I'm a trusted contact< / button >
< / div >
< div id = "tab-em-grants" class = "tab-pane" >
< ul id = "em-grants-list" class = "share-list" > < / ul >
< / div >
< div id = "tab-em-access" class = "tab-pane hidden" >
< ul id = "em-access-list" class = "share-list" > < / ul >
< div id = "em-vault-panel" class = "em-vault-panel hidden" >
< div class = "em-vault-header" >
< h4 > Emergency Vault — Decrypted Items< / h4 >
< button class = "btn-secondary btn-sm" id = "btn-em-vault-back" > ← Back< / button >
< / div >
< ul id = "em-vault-list" class = "em-vault-list" > < / ul >
< / div >
< / div >
< / div >
< / div >
< / main >
< / div >
<!-- ── Unlock overlay ──────────────────────────────────────────────── -->
< div id = "unlock-overlay" class = "modal-overlay" role = "dialog" aria-modal = "true" aria-labelledby = "unlock-title" >
< div class = "modal modal-unlock" >
< div class = "auth-logo unlock-logo" > < span class = "logo-icon" > 🔒< / span > < span class = "logo-text" > PassKeeper< / span > < / div >
< h3 id = "unlock-title" class = "unlock-title" > Unlock your vault< / h3 >
< p class = "unlock-subtitle" > Re-enter your master password to continue.< / p >
< p id = "unlock-error" class = "form-error hidden" > Incorrect master password.< / p >
< form id = "unlock-form" novalidate >
< div class = "form-group" >
< label for = "unlock-password" > Master password< / label >
< input type = "password" id = "unlock-password" autocomplete = "current-password" placeholder = "Enter master password" >
< / div >
< div class = "modal-actions" >
< a href = "/login" class = "btn-secondary btn-link" > Sign out< / a >
< button type = "submit" class = "btn-primary" > Unlock< / button >
< / div >
< / form >
< / div >
< / div >
<!-- ── Add / Edit Item Modal ─────────────────────────────────────────── -->
< div id = "item-modal" class = "modal-overlay" role = "dialog" aria-modal = "true" aria-labelledby = "modal-title" >
< div class = "modal modal-item" >
< div class = "modal-header" >
< h3 id = "modal-title" > Add Item< / h3 >
< button class = "btn-icon btn-close" id = "btn-close-modal" aria-label = "Close" > ✕< / button >
< / div >
< form id = "item-form" novalidate >
< div class = "form-group" id = "type-selector-group" >
< label for = "field-type" > Item Type< / label >
< select id = "field-type" name = "item_type" >
< option value = "password" > 🔑 Password< / option >
< option value = "note" > 📝 Secure Note< / option >
< option value = "card" > 💳 Payment Card< / option >
< option value = "bank" > 🏦 Bank Account< / option >
< option value = "address" > 🏠 Address< / option >
< option value = "ssn" > 🪪 Identity (SSN)< / option >
< / select >
< / div >
< div class = "form-group" >
< label for = "field-name" > Name < span class = "required" > *< / span > < / label >
< input type = "text" id = "field-name" name = "name" required placeholder = "e.g. Gmail" >
< / div >
< div class = "type-fields" data-for-types = "password" >
< div class = "form-group" > < label for = "field-url" > Website URL< / label > < input type = "url" id = "field-url" placeholder = "https://example.com" > < / div >
< div class = "form-group" > < label for = "field-username" > Username / Email< / label > < input type = "text" id = "field-username" autocomplete = "off" placeholder = "username or email" > < / div >
< div class = "form-group" >
< label for = "field-password" > Password< / label >
< div class = "input-with-toggle" >
< input type = "password" id = "field-password" autocomplete = "new-password" placeholder = "Enter password" >
< button type = "button" class = "btn-show-pass" id = "btn-toggle-pass" aria-label = "Toggle" > 👁< / button >
< / div >
< / div >
< / div >
< div class = "type-fields hidden" data-for-types = "note" >
< div class = "form-group" > < label for = "field-note-body" > Note< / label > < textarea id = "field-note-body" rows = "6" placeholder = "Enter your secure note…" > < / textarea > < / div >
< / div >
< div class = "type-fields hidden" data-for-types = "card" >
< div class = "form-group" > < label for = "field-cardholder" > Cardholder Name< / label > < input type = "text" id = "field-cardholder" placeholder = "Name on card" > < / div >
< div class = "form-group" > < label for = "field-card-number" > Card Number< / label > < input type = "text" id = "field-card-number" autocomplete = "off" placeholder = "•••• •••• •••• ••••" maxlength = "19" > < / div >
< div class = "form-row" >
< div class = "form-group" > < label for = "field-expiry-month" > Expiry Month< / label >
< select id = "field-expiry-month" > < option value = "" > MM< / option >
< option value = "01" > 01< / option > < option value = "02" > 02< / option > < option value = "03" > 03< / option > < option value = "04" > 04< / option >
< option value = "05" > 05< / option > < option value = "06" > 06< / option > < option value = "07" > 07< / option > < option value = "08" > 08< / option >
< option value = "09" > 09< / option > < option value = "10" > 10< / option > < option value = "11" > 11< / option > < option value = "12" > 12< / option >
< / select >
< / div >
< div class = "form-group" > < label for = "field-expiry-year" > Year< / label > < input type = "text" id = "field-expiry-year" placeholder = "YYYY" maxlength = "4" > < / div >
< div class = "form-group" > < label for = "field-cvv" > CVV< / label > < input type = "text" id = "field-cvv" autocomplete = "off" placeholder = "•••" maxlength = "4" > < / div >
< / div >
< / div >
< div class = "type-fields hidden" data-for-types = "bank" >
< div class = "form-group" > < label for = "field-bank-name" > Bank Name< / label > < input type = "text" id = "field-bank-name" placeholder = "e.g. Chase" > < / div >
< div class = "form-group" > < label for = "field-account-type" > Account Type< / label >
< select id = "field-account-type" > < option value = "" > — Select —< / option > < option value = "checking" > Checking< / option > < option value = "savings" > Savings< / option > < option value = "money_market" > Money Market< / option > < option value = "other" > Other< / option > < / select >
< / div >
< div class = "form-group" > < label for = "field-routing" > Routing Number< / label > < input type = "text" id = "field-routing" autocomplete = "off" placeholder = "9-digit routing number" maxlength = "9" > < / div >
< div class = "form-group" > < label for = "field-account-number" > Account Number< / label > < input type = "text" id = "field-account-number" autocomplete = "off" placeholder = "Account number" > < / div >
< / div >
< div class = "type-fields hidden" data-for-types = "address" >
< div class = "form-row" >
< div class = "form-group" > < label for = "field-first-name" > First Name< / label > < input type = "text" id = "field-first-name" placeholder = "First name" > < / div >
< div class = "form-group" > < label for = "field-last-name" > Last Name< / label > < input type = "text" id = "field-last-name" placeholder = "Last name" > < / div >
< / div >
< div class = "form-group" > < label for = "field-company" > Company< / label > < input type = "text" id = "field-company" placeholder = "Company (optional)" > < / div >
< div class = "form-group" > < label for = "field-address-line" > Street Address< / label > < input type = "text" id = "field-address-line" placeholder = "123 Main St" > < / div >
< div class = "form-row" >
< div class = "form-group" > < label for = "field-city" > City< / label > < input type = "text" id = "field-city" placeholder = "City" > < / div >
< div class = "form-group" > < label for = "field-state" > State< / label > < input type = "text" id = "field-state" placeholder = "State" > < / div >
< / div >
< div class = "form-row" >
< div class = "form-group" > < label for = "field-zip" > ZIP< / label > < input type = "text" id = "field-zip" placeholder = "ZIP" > < / div >
< div class = "form-group" > < label for = "field-country" > Country< / label > < input type = "text" id = "field-country" placeholder = "Country" > < / div >
< / div >
< div class = "form-row" >
< div class = "form-group" > < label for = "field-phone" > Phone< / label > < input type = "tel" id = "field-phone" placeholder = "+1 555-000-0000" > < / div >
< div class = "form-group" > < label for = "field-email" > Email< / label > < input type = "email" id = "field-email" placeholder = "email@example.com" > < / div >
< / div >
< / div >
< div class = "type-fields hidden" data-for-types = "ssn" >
< div class = "form-group" > < label for = "field-ssn-number" > Social Security Number< / label >
< div class = "input-with-toggle" >
< input type = "password" id = "field-ssn-number" autocomplete = "off" placeholder = "•••-••-••••" maxlength = "11" >
< button type = "button" class = "btn-show-pass" id = "btn-toggle-ssn" aria-label = "Toggle" > 👁< / button >
< / div >
< / div >
< / div >
< div class = "form-group" >
< label for = "field-folder" > Folder< / label >
< select id = "field-folder" > < option value = "" > — No folder —< / option > < / select >
< / div >
< div class = "type-fields" data-for-types = "password card bank address ssn" >
< div class = "form-group" > < label for = "field-notes" > Notes< / label > < textarea id = "field-notes" rows = "3" placeholder = "Optional notes…" > < / textarea > < / div >
< / div >
< div class = "modal-actions" >
< button type = "button" class = "btn-secondary" id = "btn-cancel-modal" > Cancel< / button >
< button type = "submit" class = "btn-primary" > Save< / button >
< / div >
< / form >
< / div >
< / div >
<!-- ── Account Settings Modal ────────────────────────────────────────── -->
< div id = "settings-modal" class = "modal-overlay" role = "dialog" aria-modal = "true" >
< div class = "modal modal-settings" >
< div class = "modal-header" >
< h3 > Account Settings< / h3 >
< button class = "btn-icon btn-close" id = "btn-close-settings" aria-label = "Close" > ✕< / button >
< / div >
<!-- MFA Section -->
< div class = "settings-section" >
< h4 class = "settings-section-title" > 🔐 Two-Factor Authentication< / h4 >
< div id = "mfa-status-area" >
< p id = "mfa-status-text" class = "settings-desc" > < / p >
< div id = "mfa-setup-area" class = "hidden" >
< img id = "mfa-qr-img" src = "" alt = "QR Code" class = "mfa-qr" >
< p class = "settings-desc" > Scan with Google Authenticator or Authy, then enter the 6-digit code below.< / p >
< p class = "settings-desc mfa-secret-text" > Manual key: < code id = "mfa-secret-display" > < / code > < / p >
< div class = "form-group" >
< label for = "mfa-verify-code" > Verification code< / label >
< input type = "text" id = "mfa-verify-code" inputmode = "numeric" maxlength = "6" placeholder = "000000" >
< / div >
< button class = "btn-primary" id = "btn-mfa-confirm" > Enable MFA< / button >
< button class = "btn-secondary" id = "btn-mfa-cancel-setup" > Cancel< / button >
< / div >
< div id = "mfa-disable-area" class = "hidden" >
< div class = "form-group" >
< label for = "mfa-disable-code" > Enter your current code to disable MFA< / label >
< input type = "text" id = "mfa-disable-code" inputmode = "numeric" maxlength = "6" placeholder = "000000" >
< / div >
< button class = "btn-primary" id = "btn-mfa-disable-confirm" > Disable MFA< / button >
< button class = "btn-secondary" id = "btn-mfa-disable-cancel" > Cancel< / button >
< / div >
< div id = "mfa-actions" > < / div >
< / div >
< / div >
<!-- Sharing Keys Section -->
< div class = "settings-section" >
< h4 class = "settings-section-title" > 🔗 Sharing & Emergency Access Keys< / h4 >
< p id = "sharing-keys-status" class = "settings-desc" > < / p >
< div id = "sharing-keys-actions" > < / div >
< / div >
< / div >
< / div >
<!-- ── Share Item Modal ───────────────────────────────────────────────── -->
< div id = "share-modal" class = "modal-overlay" role = "dialog" aria-modal = "true" >
< div class = "modal" >
< div class = "modal-header" >
< h3 > Share an Item< / h3 >
< button class = "btn-icon btn-close" id = "btn-close-share-modal" aria-label = "Close" > ✕< / button >
< / div >
< form id = "share-form" novalidate >
< p id = "share-error" class = "form-error hidden" > < / p >
< div class = "form-group" >
< label for = "share-item-select" > Item to share< / label >
< select id = "share-item-select" > < / select >
< / div >
< div class = "form-group" >
< label for = "share-recipient-email" > Recipient's email< / label >
< input type = "email" id = "share-recipient-email" placeholder = "recipient@example.com" >
< / div >
< div class = "modal-actions" >
< button type = "button" class = "btn-secondary" id = "btn-cancel-share" > Cancel< / button >
< button type = "submit" class = "btn-primary" > Share< / button >
< / div >
< / form >
< / div >
< / div >
<!-- ── Emergency Access Setup Modal ──────────────────────────────────── -->
< div id = "emergency-modal" class = "modal-overlay" role = "dialog" aria-modal = "true" >
< div class = "modal" >
< div class = "modal-header" >
< h3 > Add Emergency Contact< / h3 >
< button class = "btn-icon btn-close" id = "btn-close-emergency-modal" aria-label = "Close" > ✕< / button >
< / div >
< form id = "emergency-form" novalidate >
< p id = "emergency-error" class = "form-error hidden" > < / p >
< p class = "settings-desc" >
Your trusted contact will be able to request access to your vault after a waiting period
if you are unable to access your account.
< / p >
< div class = "form-group" >
< label for = "emergency-email" > Trusted contact's email< / label >
< input type = "email" id = "emergency-email" placeholder = "contact@example.com" >
< / div >
< div class = "form-group" >
< label for = "emergency-wait-days" > Wait period (days)< / label >
< select id = "emergency-wait-days" >
< option value = "1" > 1 day< / option >
< option value = "3" > 3 days< / option >
< option value = "7" selected > 7 days< / option >
< option value = "14" > 14 days< / option >
< option value = "30" > 30 days< / option >
< / select >
< / div >
< div class = "modal-actions" >
< button type = "button" class = "btn-secondary" id = "btn-cancel-emergency" > Cancel< / button >
< button type = "submit" class = "btn-primary" > Send Invitation< / button >
< / div >
< / form >
< / div >
< / div >
<!-- ── Item Detail Modal (read - only: shared items) ────────────────────── -->
< div id = "detail-modal" class = "modal-overlay" role = "dialog" aria-modal = "true" aria-labelledby = "detail-modal-title" >
< div class = "modal modal-detail" >
< div class = "modal-header" >
< h3 id = "detail-modal-title" > Item Details< / h3 >
< button class = "btn-icon btn-close" id = "btn-close-detail-modal" aria-label = "Close" > ✕< / button >
< / div >
< div id = "detail-modal-body" class = "detail-body" > < / div >
< div class = "modal-actions" >
< button type = "button" class = "btn-secondary" id = "btn-detail-done" > Close< / button >
< / div >
< / div >
< / div >
{% endblock %}
{% block scripts %}
< script src = "{{ url_for('static', filename='js/crypto.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/auth.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/sharing.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/vault.js') }}" > < / script >
{% endblock %}