1055 lines
35 KiB
HTML
1055 lines
35 KiB
HTML
{% extends "base.html" %} {% block title %}My Vault — PassKeeper{% endblock %}
|
||
{% block body_class %}vault-page{% endblock %} {% block body %}
|
||
<div class="app-layout">
|
||
<!-- ── Mobile top bar (phones only) ────────────────────────────── -->
|
||
<div class="mobile-topbar" id="mobile-topbar">
|
||
<button class="btn-mobile-menu" id="btn-mobile-menu" aria-label="Open menu">
|
||
☰
|
||
</button>
|
||
<span class="mobile-logo">🔒 PassKeeper</span>
|
||
<button
|
||
class="btn-fab btn-fab-mobile"
|
||
id="btn-add-item-mobile"
|
||
title="Add item"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
|
||
<!-- ── Sidebar backdrop (mobile) ───────────────────────────────── -->
|
||
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
||
|
||
<!-- ── Sidebar ─────────────────────────────────────────────────── -->
|
||
<aside class="sidebar" id="sidebar">
|
||
<div class="sidebar-header">
|
||
<span class="logo-icon">🔒</span>
|
||
<span class="logo-text">PassKeeper</span>
|
||
<button
|
||
class="btn-sidebar-toggle"
|
||
id="btn-sidebar-toggle"
|
||
aria-label="Collapse sidebar"
|
||
title="Collapse sidebar"
|
||
>
|
||
❮
|
||
</button>
|
||
</div>
|
||
|
||
<nav class="sidebar-nav">
|
||
<ul>
|
||
<li
|
||
class="sidebar-item active"
|
||
id="sidebar-all"
|
||
data-view="vault"
|
||
data-tooltip="All Items"
|
||
>
|
||
<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"
|
||
data-tooltip="Passwords"
|
||
>
|
||
<span class="sidebar-icon">🔑</span>
|
||
<span class="sidebar-label">Passwords</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
data-type-filter="note"
|
||
data-tooltip="Secure Notes"
|
||
>
|
||
<span class="sidebar-icon">📝</span>
|
||
<span class="sidebar-label">Secure Notes</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
data-type-filter="card"
|
||
data-tooltip="Payment Cards"
|
||
>
|
||
<span class="sidebar-icon">💳</span>
|
||
<span class="sidebar-label">Payment Cards</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
data-type-filter="bank"
|
||
data-tooltip="Bank Accounts"
|
||
>
|
||
<span class="sidebar-icon">🏦</span>
|
||
<span class="sidebar-label">Bank Accounts</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
data-type-filter="address"
|
||
data-tooltip="Addresses"
|
||
>
|
||
<span class="sidebar-icon">🏠</span>
|
||
<span class="sidebar-label">Addresses</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
data-type-filter="ssn"
|
||
data-tooltip="Identities"
|
||
>
|
||
<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"
|
||
data-tooltip="Security"
|
||
>
|
||
<span class="sidebar-icon">🛡️</span>
|
||
<span class="sidebar-label">Security</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
id="sidebar-sharing"
|
||
data-view="sharing"
|
||
data-tooltip="Sharing"
|
||
>
|
||
<span class="sidebar-icon">🔗</span>
|
||
<span class="sidebar-label">Sharing</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
id="sidebar-emergency"
|
||
data-view="emergency"
|
||
data-tooltip="Emergency Access"
|
||
>
|
||
<span class="sidebar-icon">🚨</span>
|
||
<span class="sidebar-label">Emergency Access</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
id="sidebar-import-export"
|
||
data-view="import-export"
|
||
data-tooltip="Import / Export"
|
||
>
|
||
<span class="sidebar-icon">📤</span>
|
||
<span class="sidebar-label">Import / Export</span>
|
||
</li>
|
||
<li
|
||
class="sidebar-item"
|
||
id="sidebar-generator"
|
||
data-tooltip="Password Generator"
|
||
>
|
||
<span class="sidebar-icon">⚡</span>
|
||
<span class="sidebar-label">Password Generator</span>
|
||
</li>
|
||
<li class="sidebar-section-header">Tags</li>
|
||
<ul id="sidebar-tags" class="sidebar-tag-list"></ul>
|
||
<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"
|
||
data-tooltip="Settings"
|
||
>
|
||
<span class="sidebar-icon">⚙️</span>
|
||
<span class="sidebar-label">Settings</span>
|
||
</button>
|
||
<button
|
||
class="sidebar-item btn-text"
|
||
id="btn-logout"
|
||
data-tooltip="Sign out"
|
||
>
|
||
<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">
|
||
<div class="vault-header-inner">
|
||
<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>
|
||
</div>
|
||
</header>
|
||
<div id="vault-spinner" class="spinner-overlay hidden">
|
||
<div class="spinner"></div>
|
||
</div>
|
||
<div class="vault-list">
|
||
<div class="vault-list-inner">
|
||
<ul id="vault-list"></ul>
|
||
</div>
|
||
</div>
|
||
</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>
|
||
|
||
<!-- Import / Export view -->
|
||
<div id="view-import-export" class="hidden">
|
||
<header class="vault-header">
|
||
<h2 class="vault-title">Import / Export</h2>
|
||
</header>
|
||
<div class="panel-body">
|
||
<!-- Export -->
|
||
<section class="import-export-section">
|
||
<h3 class="import-export-heading">Export Vault</h3>
|
||
<p class="import-export-desc">
|
||
Download an encrypted backup of your entire vault. The file contains
|
||
AES-256-GCM ciphertext — your master password is required to read
|
||
it. Keep it safe.
|
||
</p>
|
||
<div class="import-export-row">
|
||
<button class="btn-primary" id="btn-export-json">
|
||
Download encrypted JSON
|
||
</button>
|
||
<button class="btn-secondary" id="btn-export-csv">
|
||
Download CSV (plaintext — handle with care)
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Import -->
|
||
<section class="import-export-section">
|
||
<h3 class="import-export-heading">Import</h3>
|
||
<p class="import-export-desc">
|
||
Import from a PassKeeper encrypted JSON export, or from a CSV file
|
||
exported by Chrome, Bitwarden, or 1Password. Duplicates are not
|
||
checked — review your vault after importing.
|
||
</p>
|
||
<div class="import-export-row">
|
||
<label
|
||
class="btn-secondary import-file-label"
|
||
for="import-file-input"
|
||
>
|
||
Choose file…
|
||
</label>
|
||
<input
|
||
type="file"
|
||
id="import-file-input"
|
||
accept=".json,.csv"
|
||
class="hidden"
|
||
/>
|
||
<span id="import-file-name" class="import-file-name"
|
||
>No file chosen</span
|
||
>
|
||
</div>
|
||
<div id="import-preview" class="import-preview hidden"></div>
|
||
<div class="import-export-row">
|
||
<button class="btn-primary" id="btn-import-confirm" disabled>
|
||
Import items
|
||
</button>
|
||
</div>
|
||
<div id="import-result" class="import-result hidden"></div>
|
||
</section>
|
||
</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 class="form-group">
|
||
<label for="field-totp-uri"
|
||
>Authenticator Key (TOTP)
|
||
<span class="field-label-optional">optional</span></label
|
||
>
|
||
<input
|
||
type="text"
|
||
id="field-totp-uri"
|
||
autocomplete="off"
|
||
placeholder="otpauth://totp/… or plain base32 secret"
|
||
/>
|
||
<p class="field-hint">
|
||
Paste the otpauth URI from a QR code scanner, or the plain base32
|
||
secret key. PassKeeper will generate your 2FA codes.
|
||
</p>
|
||
</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 note card bank address ssn"
|
||
>
|
||
<div class="form-group">
|
||
<label for="field-tags">Tags</label>
|
||
<input
|
||
type="text"
|
||
id="field-tags"
|
||
placeholder="e.g. work, finance, personal (comma-separated)"
|
||
autocomplete="off"
|
||
/>
|
||
<div id="field-tags-preview" class="tag-preview"></div>
|
||
</div>
|
||
</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>
|
||
|
||
<!-- Change Password Section -->
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title">🔑 Change Master Password</h4>
|
||
<p class="settings-desc">
|
||
Your vault will be automatically re-encrypted with the new password.
|
||
</p>
|
||
<div id="change-password-area">
|
||
<div class="form-group">
|
||
<label for="cp-current">Current master password</label>
|
||
<input
|
||
type="password"
|
||
id="cp-current"
|
||
autocomplete="current-password"
|
||
placeholder="Current password"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="cp-new">New master password</label>
|
||
<input
|
||
type="password"
|
||
id="cp-new"
|
||
autocomplete="new-password"
|
||
placeholder="Minimum 12 characters"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="cp-confirm">Confirm new password</label>
|
||
<input
|
||
type="password"
|
||
id="cp-confirm"
|
||
autocomplete="new-password"
|
||
placeholder="Repeat new password"
|
||
/>
|
||
</div>
|
||
<p id="cp-error" class="form-error hidden"></p>
|
||
<button class="btn-primary" id="btn-change-password">
|
||
Change Password
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Recovery Code Section -->
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title">🆘 Account Recovery</h4>
|
||
<p class="settings-desc">
|
||
A recovery code lets you regain access if you forget your master
|
||
password. Store it somewhere safe — it is shown only once and never
|
||
stored on our servers.
|
||
</p>
|
||
<p id="recovery-status-text" class="settings-desc"></p>
|
||
<div id="recovery-code-display" class="hidden">
|
||
<div class="recovery-code-box">
|
||
<code id="recovery-code-value"></code>
|
||
<button
|
||
class="btn-icon"
|
||
id="btn-copy-recovery-code"
|
||
title="Copy to clipboard"
|
||
>
|
||
📋
|
||
</button>
|
||
</div>
|
||
<p class="settings-desc warning-text">
|
||
⚠️ Copy this code now. It will not be shown again.
|
||
</p>
|
||
<button class="btn-secondary" id="btn-recovery-done">
|
||
I've saved my recovery code
|
||
</button>
|
||
</div>
|
||
<div id="recovery-actions"></div>
|
||
</div>
|
||
|
||
<!-- Danger Zone -->
|
||
<div class="settings-section">
|
||
<h4 class="settings-section-title">⏱️ Auto-Lock</h4>
|
||
<p class="settings-desc">
|
||
Automatically lock the vault after a period of inactivity.
|
||
</p>
|
||
<div class="form-group">
|
||
<label for="web-idle-select">Lock after</label>
|
||
<select id="web-idle-select" class="form-control">
|
||
<option value="0">Never</option>
|
||
<option value="5">5 minutes</option>
|
||
<option value="10">10 minutes</option>
|
||
<option value="15" selected>15 minutes</option>
|
||
<option value="30">30 minutes</option>
|
||
<option value="60">1 hour</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-section settings-section-danger">
|
||
<h4 class="settings-section-title">⚠️ Danger Zone</h4>
|
||
<p class="settings-desc">
|
||
Permanently delete your account and all vault data. This cannot be
|
||
undone.
|
||
</p>
|
||
<div id="delete-account-area">
|
||
<div id="delete-confirm-area" class="hidden">
|
||
<div class="form-group">
|
||
<label for="delete-password"
|
||
>Enter your master password to confirm</label
|
||
>
|
||
<input
|
||
type="password"
|
||
id="delete-password"
|
||
autocomplete="current-password"
|
||
placeholder="Master password"
|
||
/>
|
||
</div>
|
||
<p id="delete-error" class="form-error hidden"></p>
|
||
<div class="modal-actions">
|
||
<button class="btn-secondary" id="btn-delete-cancel">Cancel</button>
|
||
<button class="btn-danger" id="btn-delete-confirm">
|
||
Permanently Delete Account
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<button class="btn-danger" id="btn-delete-account">
|
||
Delete My Account
|
||
</button>
|
||
</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 %}
|