04/18 Enhance app, make sidebar collapsible

This commit is contained in:
2026-04-18 14:46:49 -04:00
parent f7fdb9db8a
commit 4aca3b2fb9
3 changed files with 3228 additions and 1175 deletions
+1086 -253
View File
File diff suppressed because it is too large Load Diff
+1583 -791
View File
File diff suppressed because it is too large Load Diff
+559 -131
View File
@@ -1,63 +1,140 @@
{% extends "base.html" %} {% extends "base.html" %} {% block title %}My Vault — PassKeeper{% endblock %}
{% block title %}My Vault — PassKeeper{% endblock %} {% block body_class %}vault-page{% endblock %} {% block body %}
{% block body_class %}vault-page{% endblock %}
{% block body %}
<div class="app-layout"> <div class="app-layout">
<!-- ── Sidebar ─────────────────────────────────────────────────── --> <!-- ── Sidebar ─────────────────────────────────────────────────── -->
<aside class="sidebar"> <aside class="sidebar" id="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
<span class="logo-icon">🔒</span> <span class="logo-icon">🔒</span>
<span class="logo-text">PassKeeper</span> <span class="logo-text">PassKeeper</span>
<button
class="btn-sidebar-toggle"
id="btn-sidebar-toggle"
aria-label="Collapse sidebar"
title="Collapse sidebar"
>
&#x276E;
</button>
</div> </div>
<nav class="sidebar-nav"> <nav class="sidebar-nav">
<ul> <ul>
<li class="sidebar-item active" id="sidebar-all" data-view="vault"> <li
<span class="sidebar-icon">🏠</span> <span class="sidebar-label">All Items</span> 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>
<li class="sidebar-section-header">Item Types</li> <li class="sidebar-section-header">Item Types</li>
<li class="sidebar-item" data-type-filter="password"> <li
<span class="sidebar-icon">🔑</span> <span class="sidebar-label">Passwords</span> class="sidebar-item"
data-type-filter="password"
data-tooltip="Passwords"
>
<span class="sidebar-icon">🔑</span>
<span class="sidebar-label">Passwords</span>
</li> </li>
<li class="sidebar-item" data-type-filter="note"> <li
<span class="sidebar-icon">📝</span> <span class="sidebar-label">Secure Notes</span> 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>
<li class="sidebar-item" data-type-filter="card"> <li
<span class="sidebar-icon">💳</span> <span class="sidebar-label">Payment Cards</span> 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>
<li class="sidebar-item" data-type-filter="bank"> <li
<span class="sidebar-icon">🏦</span> <span class="sidebar-label">Bank Accounts</span> 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>
<li class="sidebar-item" data-type-filter="address"> <li
<span class="sidebar-icon">🏠</span> <span class="sidebar-label">Addresses</span> class="sidebar-item"
data-type-filter="address"
data-tooltip="Addresses"
>
<span class="sidebar-icon">🏠</span>
<span class="sidebar-label">Addresses</span>
</li> </li>
<li class="sidebar-item" data-type-filter="ssn"> <li
<span class="sidebar-icon">🪪</span> <span class="sidebar-label">Identities</span> class="sidebar-item"
data-type-filter="ssn"
data-tooltip="Identities"
>
<span class="sidebar-icon">🪪</span>
<span class="sidebar-label">Identities</span>
</li> </li>
<li class="sidebar-section-header">Tools</li> <li class="sidebar-section-header">Tools</li>
<li class="sidebar-item" id="sidebar-security" data-view="security"> <li
<span class="sidebar-icon">🛡️</span> <span class="sidebar-label">Security</span> 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>
<li class="sidebar-item" id="sidebar-sharing" data-view="sharing"> <li
<span class="sidebar-icon">🔗</span> <span class="sidebar-label">Sharing</span> 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>
<li class="sidebar-item" id="sidebar-emergency" data-view="emergency"> <li
<span class="sidebar-icon">🚨</span> <span class="sidebar-label">Emergency Access</span> 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>
<li class="sidebar-item" id="sidebar-generator"> <li
<span class="sidebar-icon"></span> <span class="sidebar-label">Password Generator</span> class="sidebar-item"
id="sidebar-generator"
data-tooltip="Password Generator"
>
<span class="sidebar-icon"></span>
<span class="sidebar-label">Password Generator</span>
</li> </li>
<li class="sidebar-section-header"> <li class="sidebar-section-header">
Folders 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>
<li id="new-folder-row" class="new-folder-row hidden"> <li id="new-folder-row" class="new-folder-row hidden">
<form id="new-folder-form"> <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="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> </form>
</li> </li>
<ul id="sidebar-folders"></ul> <ul id="sidebar-folders"></ul>
@@ -65,18 +142,27 @@
</nav> </nav>
<div class="sidebar-footer"> <div class="sidebar-footer">
<button class="sidebar-item btn-text" id="btn-settings"> <button
<span class="sidebar-icon">⚙️</span> <span class="sidebar-label">Settings</span> class="sidebar-item btn-text"
id="btn-settings"
data-tooltip="Settings"
>
<span class="sidebar-icon">⚙️</span>
<span class="sidebar-label">Settings</span>
</button> </button>
<button class="sidebar-item btn-text" id="btn-logout"> <button
<span class="sidebar-icon">🚪</span> <span class="sidebar-label">Sign out</span> 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> </button>
</div> </div>
</aside> </aside>
<!-- ── Main ───────────────────────────────────────────────────── --> <!-- ── Main ───────────────────────────────────────────────────── -->
<main class="vault-main"> <main class="vault-main">
<!-- Vault view --> <!-- Vault view -->
<div id="view-vault"> <div id="view-vault">
<header class="vault-header"> <header class="vault-header">
@@ -84,7 +170,12 @@
<div class="vault-toolbar"> <div class="vault-toolbar">
<div class="search-wrapper"> <div class="search-wrapper">
<span class="search-icon">🔍</span> <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>
<div class="sort-wrapper"> <div class="sort-wrapper">
<select id="sort-select" title="Sort by"> <select id="sort-select" title="Sort by">
@@ -98,13 +189,17 @@
<button class="btn-fab" id="btn-add-item" title="Add item">+</button> <button class="btn-fab" id="btn-add-item" title="Add item">+</button>
</div> </div>
</header> </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> <ul id="vault-list" class="vault-list"></ul>
</div> </div>
<!-- Security dashboard view --> <!-- Security dashboard view -->
<div id="view-security" class="hidden"> <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-dashboard">
<div class="security-summary" id="security-summary"></div> <div class="security-summary" id="security-summary"></div>
<div class="security-sections" id="security-sections"></div> <div class="security-sections" id="security-sections"></div>
@@ -116,16 +211,22 @@
<header class="vault-header"> <header class="vault-header">
<h2 class="vault-title">Sharing</h2> <h2 class="vault-title">Sharing</h2>
<div class="vault-toolbar"> <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> </div>
</header> </header>
<div class="panel-body"> <div class="panel-body">
<div id="sharing-keys-notice" class="notice-info hidden"> <div id="sharing-keys-notice" class="notice-info hidden">
You need to set up sharing keys before you can share items. 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>
<div class="panel-tabs"> <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> <button class="tab-btn" data-tab="shared-in">Shared with me</button>
</div> </div>
<div id="tab-shared-out" class="tab-pane"> <div id="tab-shared-out" class="tab-pane">
@@ -142,13 +243,19 @@
<header class="vault-header"> <header class="vault-header">
<h2 class="vault-title">Emergency Access</h2> <h2 class="vault-title">Emergency Access</h2>
<div class="vault-toolbar"> <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> </div>
</header> </header>
<div class="panel-body"> <div class="panel-body">
<div class="panel-tabs"> <div class="panel-tabs">
<button class="tab-btn active" data-tab="em-grants">My trusted contacts</button> <button class="tab-btn active" data-tab="em-grants">
<button class="tab-btn" data-tab="em-access">I'm a trusted contact</button> My trusted contacts
</button>
<button class="tab-btn" data-tab="em-access">
I'm a trusted contact
</button>
</div> </div>
<div id="tab-em-grants" class="tab-pane"> <div id="tab-em-grants" class="tab-pane">
<ul id="em-grants-list" class="share-list"></ul> <ul id="em-grants-list" class="share-list"></ul>
@@ -158,28 +265,44 @@
<div id="em-vault-panel" class="em-vault-panel hidden"> <div id="em-vault-panel" class="em-vault-panel hidden">
<div class="em-vault-header"> <div class="em-vault-header">
<h4>Emergency Vault — Decrypted Items</h4> <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> </div>
<ul id="em-vault-list" class="em-vault-list"></ul> <ul id="em-vault-list" class="em-vault-list"></ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
</div> </div>
<!-- ── Unlock overlay ──────────────────────────────────────────────── --> <!-- ── 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="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> <h3 id="unlock-title" class="unlock-title">Unlock your vault</h3>
<p class="unlock-subtitle">Re-enter your master password to continue.</p> <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> <form id="unlock-form" novalidate>
<div class="form-group"> <div class="form-group">
<label for="unlock-password">Master password</label> <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>
<div class="modal-actions"> <div class="modal-actions">
<a href="/login" class="btn-secondary btn-link">Sign out</a> <a href="/login" class="btn-secondary btn-link">Sign out</a>
@@ -190,11 +313,23 @@
</div> </div>
<!-- ── Add / Edit Item Modal ─────────────────────────────────────────── --> <!-- ── 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 modal-item">
<div class="modal-header"> <div class="modal-header">
<h3 id="modal-title">Add Item</h3> <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> </div>
<form id="item-form" novalidate> <form id="item-form" novalidate>
<div class="form-group" id="type-selector-group"> <div class="form-group" id="type-selector-group">
@@ -210,82 +345,260 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="field-name">Name <span class="required">*</span></label> <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>
<div class="type-fields" data-for-types="password"> <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">
<div class="form-group"><label for="field-username">Username / Email</label><input type="text" id="field-username" autocomplete="off" placeholder="username or email"></div> <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"> <div class="form-group">
<label for="field-password">Password</label> <label for="field-password">Password</label>
<div class="input-with-toggle"> <div class="input-with-toggle">
<input type="password" id="field-password" autocomplete="new-password" placeholder="Enter password"> <input
<button type="button" class="btn-show-pass" id="btn-toggle-pass" aria-label="Toggle">👁</button> 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>
</div> </div>
<div class="type-fields hidden" data-for-types="note"> <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>
<div class="type-fields hidden" data-for-types="card"> <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">
<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> <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-row">
<div class="form-group"><label for="field-expiry-month">Expiry Month</label> <div class="form-group">
<select id="field-expiry-month"><option value="">MM</option> <label for="field-expiry-month">Expiry Month</label>
<option value="01">01</option><option value="02">02</option><option value="03">03</option><option value="04">04</option> <select id="field-expiry-month">
<option value="05">05</option><option value="06">06</option><option value="07">07</option><option value="08">08</option> <option value="">MM</option>
<option value="09">09</option><option value="10">10</option><option value="11">11</option><option value="12">12</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> </select>
</div> </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">
<div class="form-group"><label for="field-cvv">CVV</label><input type="text" id="field-cvv" autocomplete="off" placeholder="•••" maxlength="4"></div> <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> </div>
<div class="type-fields hidden" data-for-types="bank"> <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">
<div class="form-group"><label for="field-account-type">Account Type</label> <label for="field-bank-name">Bank Name</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> ><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="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>
<div class="type-fields hidden" data-for-types="address"> <div class="type-fields hidden" data-for-types="address">
<div class="form-row"> <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">
<div class="form-group"><label for="field-last-name">Last Name</label><input type="text" id="field-last-name" placeholder="Last name"></div> <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>
<div class="form-group"><label for="field-company">Company</label><input type="text" id="field-company" placeholder="Company (optional)"></div> <div class="form-group">
<div class="form-group"><label for="field-address-line">Street Address</label><input type="text" id="field-address-line" placeholder="123 Main St"></div> <label for="field-company">Company</label
<div class="form-row"> ><input
<div class="form-group"><label for="field-city">City</label><input type="text" id="field-city" placeholder="City"></div> type="text"
<div class="form-group"><label for="field-state">State</label><input type="text" id="field-state" placeholder="State"></div> 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>
<div class="form-row"> <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">
<div class="form-group"><label for="field-country">Country</label><input type="text" id="field-country" placeholder="Country"></div> <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>
<div class="form-row"> <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">
<div class="form-group"><label for="field-email">Email</label><input type="email" id="field-email" placeholder="email@example.com"></div> <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> </div>
<div class="type-fields hidden" data-for-types="ssn"> <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"> <div class="input-with-toggle">
<input type="password" id="field-ssn-number" autocomplete="off" placeholder="•••-••-••••" maxlength="11"> <input
<button type="button" class="btn-show-pass" id="btn-toggle-ssn" aria-label="Toggle">👁</button> 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>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="field-folder">Folder</label> <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>
<div class="type-fields" data-for-types="password card bank address ssn"> <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>
<div class="modal-actions"> <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> <button type="submit" class="btn-primary">Save</button>
</div> </div>
</form> </form>
@@ -297,7 +610,13 @@
<div class="modal modal-settings"> <div class="modal modal-settings">
<div class="modal-header"> <div class="modal-header">
<h3>Account Settings</h3> <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> </div>
<!-- MFA Section --> <!-- MFA Section -->
@@ -306,23 +625,48 @@
<div id="mfa-status-area"> <div id="mfa-status-area">
<p id="mfa-status-text" class="settings-desc"></p> <p id="mfa-status-text" class="settings-desc"></p>
<div id="mfa-setup-area" class="hidden"> <div id="mfa-setup-area" class="hidden">
<img id="mfa-qr-img" src="" alt="QR Code" class="mfa-qr"> <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">
<p class="settings-desc mfa-secret-text">Manual key: <code id="mfa-secret-display"></code></p> 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"> <div class="form-group">
<label for="mfa-verify-code">Verification code</label> <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> </div>
<button class="btn-primary" id="btn-mfa-confirm">Enable MFA</button> <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>
<div id="mfa-disable-area" class="hidden"> <div id="mfa-disable-area" class="hidden">
<div class="form-group"> <div class="form-group">
<label for="mfa-disable-code">Enter your current code to disable MFA</label> <label for="mfa-disable-code"
<input type="text" id="mfa-disable-code" inputmode="numeric" maxlength="6" placeholder="000000"> >Enter your current code to disable MFA</label
>
<input
type="text"
id="mfa-disable-code"
inputmode="numeric"
maxlength="6"
placeholder="000000"
/>
</div> </div>
<button class="btn-primary" id="btn-mfa-disable-confirm">Disable MFA</button> <button class="btn-primary" id="btn-mfa-disable-confirm">
<button class="btn-secondary" id="btn-mfa-disable-cancel">Cancel</button> Disable MFA
</button>
<button class="btn-secondary" id="btn-mfa-disable-cancel">
Cancel
</button>
</div> </div>
<div id="mfa-actions"></div> <div id="mfa-actions"></div>
</div> </div>
@@ -330,7 +674,9 @@
<!-- Sharing Keys Section --> <!-- Sharing Keys Section -->
<div class="settings-section"> <div class="settings-section">
<h4 class="settings-section-title">🔗 Sharing &amp; Emergency Access Keys</h4> <h4 class="settings-section-title">
🔗 Sharing &amp; Emergency Access Keys
</h4>
<p id="sharing-keys-status" class="settings-desc"></p> <p id="sharing-keys-status" class="settings-desc"></p>
<div id="sharing-keys-actions"></div> <div id="sharing-keys-actions"></div>
</div> </div>
@@ -338,37 +684,70 @@
<!-- Change Password Section --> <!-- Change Password Section -->
<div class="settings-section"> <div class="settings-section">
<h4 class="settings-section-title">🔑 Change Master Password</h4> <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 id="change-password-area">
<div class="form-group"> <div class="form-group">
<label for="cp-current">Current master password</label> <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>
<div class="form-group"> <div class="form-group">
<label for="cp-new">New master password</label> <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>
<div class="form-group"> <div class="form-group">
<label for="cp-confirm">Confirm new password</label> <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> </div>
<p id="cp-error" class="form-error hidden"></p> <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>
</div> </div>
<!-- Recovery Code Section --> <!-- Recovery Code Section -->
<div class="settings-section"> <div class="settings-section">
<h4 class="settings-section-title">🆘 Account Recovery</h4> <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> <p id="recovery-status-text" class="settings-desc"></p>
<div id="recovery-code-display" class="hidden"> <div id="recovery-code-display" class="hidden">
<div class="recovery-code-box"> <div class="recovery-code-box">
<code id="recovery-code-value"></code> <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> </div>
<p class="settings-desc warning-text">⚠️ Copy this code now. It will not be shown again.</p> <p class="settings-desc warning-text">
<button class="btn-secondary" id="btn-recovery-done">I've saved my recovery code</button> ⚠️ 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>
<div id="recovery-actions"></div> <div id="recovery-actions"></div>
</div> </div>
@@ -376,23 +755,36 @@
<!-- Danger Zone --> <!-- Danger Zone -->
<div class="settings-section settings-section-danger"> <div class="settings-section settings-section-danger">
<h4 class="settings-section-title">⚠️ Danger Zone</h4> <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-account-area">
<div id="delete-confirm-area" class="hidden"> <div id="delete-confirm-area" class="hidden">
<div class="form-group"> <div class="form-group">
<label for="delete-password">Enter your master password to confirm</label> <label for="delete-password"
<input type="password" id="delete-password" autocomplete="current-password" placeholder="Master password"> >Enter your master password to confirm</label
>
<input
type="password"
id="delete-password"
autocomplete="current-password"
placeholder="Master password"
/>
</div> </div>
<p id="delete-error" class="form-error hidden"></p> <p id="delete-error" class="form-error hidden"></p>
<div class="modal-actions"> <div class="modal-actions">
<button class="btn-secondary" id="btn-delete-cancel">Cancel</button> <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>
</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>
</div> </div>
</div> </div>
@@ -401,7 +793,13 @@
<div class="modal"> <div class="modal">
<div class="modal-header"> <div class="modal-header">
<h3>Share an Item</h3> <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> </div>
<form id="share-form" novalidate> <form id="share-form" novalidate>
<p id="share-error" class="form-error hidden"></p> <p id="share-error" class="form-error hidden"></p>
@@ -411,10 +809,16 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="share-recipient-email">Recipient's email</label> <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>
<div class="modal-actions"> <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> <button type="submit" class="btn-primary">Share</button>
</div> </div>
</form> </form>
@@ -426,17 +830,27 @@
<div class="modal"> <div class="modal">
<div class="modal-header"> <div class="modal-header">
<h3>Add Emergency Contact</h3> <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> </div>
<form id="emergency-form" novalidate> <form id="emergency-form" novalidate>
<p id="emergency-error" class="form-error hidden"></p> <p id="emergency-error" class="form-error hidden"></p>
<p class="settings-desc"> <p class="settings-desc">
Your trusted contact will be able to request access to your vault after a waiting period Your trusted contact will be able to request access to your vault after
if you are unable to access your account. a waiting period if you are unable to access your account.
</p> </p>
<div class="form-group"> <div class="form-group">
<label for="emergency-email">Trusted contact's email</label> <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>
<div class="form-group"> <div class="form-group">
<label for="emergency-wait-days">Wait period (days)</label> <label for="emergency-wait-days">Wait period (days)</label>
@@ -449,7 +863,9 @@
</select> </select>
</div> </div>
<div class="modal-actions"> <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> <button type="submit" class="btn-primary">Send Invitation</button>
</div> </div>
</form> </form>
@@ -457,22 +873,34 @@
</div> </div>
<!-- ── Item Detail Modal (read-only: shared items) ────────────────────── --> <!-- ── 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 modal-detail">
<div class="modal-header"> <div class="modal-header">
<h3 id="detail-modal-title">Item Details</h3> <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>
<div id="detail-modal-body" class="detail-body"></div> <div id="detail-modal-body" class="detail-body"></div>
<div class="modal-actions"> <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> </div>
</div> </div>
{% endblock %} {% endblock %} {% block scripts %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/crypto.js') }}"></script> <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/auth.js') }}"></script>
<script src="{{ url_for('static', filename='js/sharing.js') }}"></script> <script src="{{ url_for('static', filename='js/sharing.js') }}"></script>