420 lines
22 KiB
HTML
420 lines
22 KiB
HTML
{% 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 %}
|