04/18 Enhance app, make sidebar collapsible
This commit is contained in:
+1086
-253
File diff suppressed because it is too large
Load Diff
+1531
-739
File diff suppressed because it is too large
Load Diff
+559
-131
@@ -1,63 +1,140 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}My Vault — PassKeeper{% endblock %}
|
||||
{% block body_class %}vault-page{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% 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">
|
||||
<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">
|
||||
<span class="sidebar-icon">🏠</span> <span class="sidebar-label">All Items</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">🔑</span> <span class="sidebar-label">Passwords</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">📝</span> <span class="sidebar-label">Secure Notes</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">💳</span> <span class="sidebar-label">Payment Cards</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">🏦</span> <span class="sidebar-label">Bank Accounts</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">🏠</span> <span class="sidebar-label">Addresses</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">🪪</span> <span class="sidebar-label">Identities</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">🛡️</span> <span class="sidebar-label">Security</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">🔗</span> <span class="sidebar-label">Sharing</span>
|
||||
<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">
|
||||
<span class="sidebar-icon">🚨</span> <span class="sidebar-label">Emergency Access</span>
|
||||
<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-generator">
|
||||
<span class="sidebar-icon">⚡</span> <span class="sidebar-label">Password Generator</span>
|
||||
<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">
|
||||
Folders
|
||||
<button class="btn-new-folder" id="btn-new-folder" title="New folder">+</button>
|
||||
<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">
|
||||
<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>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-icon"
|
||||
id="btn-cancel-folder"
|
||||
title="Cancel"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<ul id="sidebar-folders"></ul>
|
||||
@@ -65,18 +142,27 @@
|
||||
</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
|
||||
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">
|
||||
<span class="sidebar-icon">🚪</span> <span class="sidebar-label">Sign out</span>
|
||||
<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">
|
||||
@@ -84,7 +170,12 @@
|
||||
<div class="vault-toolbar">
|
||||
<div class="search-wrapper">
|
||||
<span class="search-icon">🔍</span>
|
||||
<input type="search" id="search-input" placeholder="Search vault…" autocomplete="off">
|
||||
<input
|
||||
type="search"
|
||||
id="search-input"
|
||||
placeholder="Search vault…"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="sort-wrapper">
|
||||
<select id="sort-select" title="Sort by">
|
||||
@@ -98,13 +189,17 @@
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
@@ -116,16 +211,22 @@
|
||||
<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>
|
||||
<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>
|
||||
<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 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">
|
||||
@@ -142,13 +243,19 @@
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
@@ -158,28 +265,44 @@
|
||||
<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>
|
||||
<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
|
||||
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>
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
@@ -190,11 +313,23 @@
|
||||
</div>
|
||||
|
||||
<!-- ── Add / Edit Item Modal ─────────────────────────────────────────── -->
|
||||
<div id="item-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
||||
<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>
|
||||
<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">
|
||||
@@ -210,82 +345,260 @@
|
||||
</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">
|
||||
<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-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>
|
||||
<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 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-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>
|
||||
<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 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 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 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 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-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 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-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 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-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 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="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>
|
||||
<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>
|
||||
<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 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="button" class="btn-secondary" id="btn-cancel-modal">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn-primary">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -297,7 +610,13 @@
|
||||
<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>
|
||||
<button
|
||||
class="btn-icon btn-close"
|
||||
id="btn-close-settings"
|
||||
aria-label="Close"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- MFA Section -->
|
||||
@@ -306,23 +625,48 @@
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
<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>
|
||||
@@ -330,7 +674,9 @@
|
||||
|
||||
<!-- Sharing Keys Section -->
|
||||
<div class="settings-section">
|
||||
<h4 class="settings-section-title">🔗 Sharing & Emergency Access Keys</h4>
|
||||
<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>
|
||||
@@ -338,37 +684,70 @@
|
||||
<!-- 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>
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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>
|
||||
<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 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>
|
||||
<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>
|
||||
<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>
|
||||
@@ -376,23 +755,36 @@
|
||||
<!-- Danger Zone -->
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
<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>
|
||||
<button class="btn-danger" id="btn-delete-account">
|
||||
Delete My Account
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -401,7 +793,13 @@
|
||||
<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>
|
||||
<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>
|
||||
@@ -411,10 +809,16 @@
|
||||
</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">
|
||||
<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="button" class="btn-secondary" id="btn-cancel-share">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn-primary">Share</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -426,17 +830,27 @@
|
||||
<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>
|
||||
<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.
|
||||
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">
|
||||
<input
|
||||
type="email"
|
||||
id="emergency-email"
|
||||
placeholder="contact@example.com"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="emergency-wait-days">Wait period (days)</label>
|
||||
@@ -449,7 +863,9 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn-secondary" id="btn-cancel-emergency">Cancel</button>
|
||||
<button type="button" class="btn-secondary" id="btn-cancel-emergency">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn-primary">Send Invitation</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -457,22 +873,34 @@
|
||||
</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
|
||||
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>
|
||||
<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>
|
||||
<button type="button" class="btn-secondary" id="btn-detail-done">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% 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>
|
||||
|
||||
Reference in New Issue
Block a user