Make username optional. Sometimes we just want to store passwords
This commit is contained in:
parent
84f861a06a
commit
7a930228fc
67
dist/index.html
vendored
67
dist/index.html
vendored
@ -4840,7 +4840,7 @@ function generateId() {
|
|||||||
* @typedef {Object} CredentialEntry
|
* @typedef {Object} CredentialEntry
|
||||||
* @property {string} id - Unique identifier
|
* @property {string} id - Unique identifier
|
||||||
* @property {string} title - Display name (e.g. "GitHub", "Gmail")
|
* @property {string} title - Display name (e.g. "GitHub", "Gmail")
|
||||||
* @property {string} username - Login username or email
|
* @property {string} [username] - Login username or email (optional)
|
||||||
* @property {string} encryptedPassword - AES-GCM encrypted password blob (JSON string)
|
* @property {string} encryptedPassword - AES-GCM encrypted password blob (JSON string)
|
||||||
* @property {string} [url] - Website URL
|
* @property {string} [url] - Website URL
|
||||||
* @property {string} [notes] - Free-form notes
|
* @property {string} [notes] - Free-form notes
|
||||||
@ -4854,7 +4854,7 @@ function generateId() {
|
|||||||
*
|
*
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* @param {string} data.title
|
* @param {string} data.title
|
||||||
* @param {string} data.username
|
* @param {string} [data.username]
|
||||||
* @param {string} data.encryptedPassword - Must already be encrypted
|
* @param {string} data.encryptedPassword - Must already be encrypted
|
||||||
* @param {string} [data.url]
|
* @param {string} [data.url]
|
||||||
* @param {string} [data.notes]
|
* @param {string} [data.notes]
|
||||||
@ -4867,7 +4867,7 @@ function createEntry(data) {
|
|||||||
return {
|
return {
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
title: data.title.trim(),
|
title: data.title.trim(),
|
||||||
username: data.username.trim(),
|
username: data.username?.trim() || "",
|
||||||
encryptedPassword: data.encryptedPassword,
|
encryptedPassword: data.encryptedPassword,
|
||||||
url: data.url?.trim() || "",
|
url: data.url?.trim() || "",
|
||||||
notes: data.notes?.trim() || "",
|
notes: data.notes?.trim() || "",
|
||||||
@ -4888,7 +4888,7 @@ function updateEntry$1(existing, data) {
|
|||||||
return {
|
return {
|
||||||
...existing,
|
...existing,
|
||||||
title: data.title !== void 0 ? data.title.trim() : existing.title,
|
title: data.title !== void 0 ? data.title.trim() : existing.title,
|
||||||
username: data.username !== void 0 ? data.username.trim() : existing.username,
|
username: data.username !== void 0 ? data.username?.trim() || "" : existing.username,
|
||||||
encryptedPassword: data.encryptedPassword !== void 0 ? data.encryptedPassword : existing.encryptedPassword,
|
encryptedPassword: data.encryptedPassword !== void 0 ? data.encryptedPassword : existing.encryptedPassword,
|
||||||
url: data.url !== void 0 ? data.url.trim() : existing.url,
|
url: data.url !== void 0 ? data.url.trim() : existing.url,
|
||||||
notes: data.notes !== void 0 ? data.notes.trim() : existing.notes,
|
notes: data.notes !== void 0 ? data.notes.trim() : existing.notes,
|
||||||
@ -4940,7 +4940,6 @@ function createGroup(name, color) {
|
|||||||
function validateEntry(data) {
|
function validateEntry(data) {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
if (!data.title || !data.title.trim()) errors.push("Title is required");
|
if (!data.title || !data.title.trim()) errors.push("Title is required");
|
||||||
if (!data.username || !data.username.trim()) errors.push("Username is required");
|
|
||||||
if (!data.encryptedPassword) errors.push("Password is required");
|
if (!data.encryptedPassword) errors.push("Password is required");
|
||||||
return {
|
return {
|
||||||
valid: errors.length === 0,
|
valid: errors.length === 0,
|
||||||
@ -6110,7 +6109,7 @@ function EntryList($$anchor, $$props) {
|
|||||||
reset(tr);
|
reset(tr);
|
||||||
template_effect(() => {
|
template_effect(() => {
|
||||||
set_text(text_6, get(entry).title);
|
set_text(text_6, get(entry).title);
|
||||||
set_text(text_7, get(entry).username);
|
set_text(text_7, get(entry).username || "—");
|
||||||
set_text(text_8, get(entry).url || "—");
|
set_text(text_8, get(entry).url || "—");
|
||||||
});
|
});
|
||||||
delegated("click", tr, () => $$props.onSelect(get(entry).id));
|
delegated("click", tr, () => $$props.onSelect(get(entry).id));
|
||||||
@ -6138,10 +6137,11 @@ var root_1$5 = /* @__PURE__ */ from_html(`<div class="toast svelte-dssgjx"> </di
|
|||||||
var root_2$3 = /* @__PURE__ */ from_html(`<div class="loading svelte-dssgjx">Loading...</div>`);
|
var root_2$3 = /* @__PURE__ */ from_html(`<div class="loading svelte-dssgjx">Loading...</div>`);
|
||||||
var root_3$3 = /* @__PURE__ */ from_html(`<div class="error-banner svelte-dssgjx"> </div>`);
|
var root_3$3 = /* @__PURE__ */ from_html(`<div class="error-banner svelte-dssgjx"> </div>`);
|
||||||
var root_4$3 = /* @__PURE__ */ from_html(`<div class="empty-state svelte-dssgjx">Entry not found</div>`);
|
var root_4$3 = /* @__PURE__ */ from_html(`<div class="empty-state svelte-dssgjx">Entry not found</div>`);
|
||||||
var root_6$3 = /* @__PURE__ */ from_html(`<div class="detail-field"><span class="field-label svelte-dssgjx">URL</span> <div class="field-value svelte-dssgjx"><a target="_blank" rel="noopener noreferrer" class="svelte-dssgjx"> </a> <button class="btn btn-ghost btn-sm copy-btn svelte-dssgjx" title="Copy URL">📋</button></div></div>`);
|
var root_6$3 = /* @__PURE__ */ from_html(`<div class="detail-field"><span class="field-label svelte-dssgjx">Username</span> <div class="field-value svelte-dssgjx"><span> </span> <button class="btn btn-ghost btn-sm copy-btn svelte-dssgjx" title="Copy username">📋</button></div></div>`);
|
||||||
var root_7$1 = /* @__PURE__ */ from_html(`<div class="detail-field"><span class="field-label svelte-dssgjx">Notes</span> <div class="field-value notes svelte-dssgjx"> </div></div>`);
|
var root_7$1 = /* @__PURE__ */ from_html(`<div class="detail-field"><span class="field-label svelte-dssgjx">URL</span> <div class="field-value svelte-dssgjx"><a target="_blank" rel="noopener noreferrer" class="svelte-dssgjx"> </a> <button class="btn btn-ghost btn-sm copy-btn svelte-dssgjx" title="Copy URL">📋</button></div></div>`);
|
||||||
var root_8 = /* @__PURE__ */ from_html(`<div class="modal-overlay svelte-dssgjx" role="presentation"><div class="modal svelte-dssgjx" role="dialog" aria-modal="true" aria-label="Delete confirmation" tabindex="-1"><h3 class="svelte-dssgjx">Delete Entry</h3> <p class="svelte-dssgjx">Are you sure you want to delete "<strong> </strong>"? This cannot be undone.</p> <div class="modal-actions svelte-dssgjx"><button class="btn btn-danger"> </button> <button class="btn btn-ghost">Cancel</button></div></div></div>`);
|
var root_8 = /* @__PURE__ */ from_html(`<div class="detail-field"><span class="field-label svelte-dssgjx">Notes</span> <div class="field-value notes svelte-dssgjx"> </div></div>`);
|
||||||
var root_5$2 = /* @__PURE__ */ from_html(`<div class="detail-card svelte-dssgjx"><div class="detail-header svelte-dssgjx"><h2 class="svelte-dssgjx"> </h2> <div class="header-actions svelte-dssgjx"><button class="btn btn-ghost btn-sm">✏️ Edit</button> <button class="btn btn-danger btn-sm">🗑 Delete</button></div></div> <div class="detail-fields svelte-dssgjx"><div class="detail-field"><span class="field-label svelte-dssgjx">Username</span> <div class="field-value svelte-dssgjx"><span> </span> <button class="btn btn-ghost btn-sm copy-btn svelte-dssgjx" title="Copy username">📋</button></div></div> <div class="detail-field"><span class="field-label svelte-dssgjx">Password</span> <div class="field-value svelte-dssgjx"><span> </span> <button class="btn btn-ghost btn-sm" title="Toggle visibility"> </button> <button class="btn btn-ghost btn-sm copy-btn svelte-dssgjx" title="Copy password">📋</button></div></div> <!> <!></div> <div class="detail-meta svelte-dssgjx"><span class="text-xs text-muted"> </span> <span class="text-xs text-muted"> </span></div></div> <!>`, 1);
|
var root_9 = /* @__PURE__ */ from_html(`<div class="modal-overlay svelte-dssgjx" role="presentation"><div class="modal svelte-dssgjx" role="dialog" aria-modal="true" aria-label="Delete confirmation" tabindex="-1"><h3 class="svelte-dssgjx">Delete Entry</h3> <p class="svelte-dssgjx">Are you sure you want to delete "<strong> </strong>"? This cannot be undone.</p> <div class="modal-actions svelte-dssgjx"><button class="btn btn-danger"> </button> <button class="btn btn-ghost">Cancel</button></div></div></div>`);
|
||||||
|
var root_5$2 = /* @__PURE__ */ from_html(`<div class="detail-card svelte-dssgjx"><div class="detail-header svelte-dssgjx"><h2 class="svelte-dssgjx"> </h2> <div class="header-actions svelte-dssgjx"><button class="btn btn-ghost btn-sm">✏️ Edit</button> <button class="btn btn-danger btn-sm">🗑 Delete</button></div></div> <div class="detail-fields svelte-dssgjx"><!> <div class="detail-field"><span class="field-label svelte-dssgjx">Password</span> <div class="field-value svelte-dssgjx"><span> </span> <button class="btn btn-ghost btn-sm" title="Toggle visibility"> </button> <button class="btn btn-ghost btn-sm copy-btn svelte-dssgjx" title="Copy password">📋</button></div></div> <!> <!></div> <div class="detail-meta svelte-dssgjx"><span class="text-xs text-muted"> </span> <span class="text-xs text-muted"> </span></div></div> <!>`, 1);
|
||||||
var root$4 = /* @__PURE__ */ from_html(`<div class="entry-detail"><!> <!></div>`);
|
var root$4 = /* @__PURE__ */ from_html(`<div class="entry-detail"><!> <!></div>`);
|
||||||
function EntryDetail($$anchor, $$props) {
|
function EntryDetail($$anchor, $$props) {
|
||||||
push($$props, true);
|
push($$props, true);
|
||||||
@ -6244,7 +6244,9 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
reset(div_7);
|
reset(div_7);
|
||||||
reset(div_6);
|
reset(div_6);
|
||||||
var div_8 = sibling(div_6, 2);
|
var div_8 = sibling(div_6, 2);
|
||||||
var div_9 = child(div_8);
|
var node_2 = child(div_8);
|
||||||
|
var consequent_4 = ($$anchor) => {
|
||||||
|
var div_9 = root_6$3();
|
||||||
var div_10 = sibling(child(div_9), 2);
|
var div_10 = sibling(child(div_9), 2);
|
||||||
var span = child(div_10);
|
var span = child(div_10);
|
||||||
var text_4 = child(span, true);
|
var text_4 = child(span, true);
|
||||||
@ -6252,7 +6254,14 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
var button_2 = sibling(span, 2);
|
var button_2 = sibling(span, 2);
|
||||||
reset(div_10);
|
reset(div_10);
|
||||||
reset(div_9);
|
reset(div_9);
|
||||||
var div_11 = sibling(div_9, 2);
|
template_effect(() => set_text(text_4, get(entry).username));
|
||||||
|
delegated("click", button_2, () => copyToClipboard(get(entry).username, "Username"));
|
||||||
|
append($$anchor, div_9);
|
||||||
|
};
|
||||||
|
if_block(node_2, ($$render) => {
|
||||||
|
if (get(entry).username) $$render(consequent_4);
|
||||||
|
});
|
||||||
|
var div_11 = sibling(node_2, 2);
|
||||||
var div_12 = sibling(child(div_11), 2);
|
var div_12 = sibling(child(div_11), 2);
|
||||||
var span_1 = child(div_12);
|
var span_1 = child(div_12);
|
||||||
var text_5 = child(span_1, true);
|
var text_5 = child(span_1, true);
|
||||||
@ -6263,9 +6272,9 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
var button_4 = sibling(button_3, 2);
|
var button_4 = sibling(button_3, 2);
|
||||||
reset(div_12);
|
reset(div_12);
|
||||||
reset(div_11);
|
reset(div_11);
|
||||||
var node_2 = sibling(div_11, 2);
|
var node_3 = sibling(div_11, 2);
|
||||||
var consequent_4 = ($$anchor) => {
|
var consequent_5 = ($$anchor) => {
|
||||||
var div_13 = root_6$3();
|
var div_13 = root_7$1();
|
||||||
var div_14 = sibling(child(div_13), 2);
|
var div_14 = sibling(child(div_13), 2);
|
||||||
var a = child(div_14);
|
var a = child(div_14);
|
||||||
var text_7 = child(a, true);
|
var text_7 = child(a, true);
|
||||||
@ -6280,12 +6289,12 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
delegated("click", button_5, () => copyToClipboard(get(entry).url, "URL"));
|
delegated("click", button_5, () => copyToClipboard(get(entry).url, "URL"));
|
||||||
append($$anchor, div_13);
|
append($$anchor, div_13);
|
||||||
};
|
};
|
||||||
if_block(node_2, ($$render) => {
|
if_block(node_3, ($$render) => {
|
||||||
if (get(entry).url) $$render(consequent_4);
|
if (get(entry).url) $$render(consequent_5);
|
||||||
});
|
});
|
||||||
var node_3 = sibling(node_2, 2);
|
var node_4 = sibling(node_3, 2);
|
||||||
var consequent_5 = ($$anchor) => {
|
var consequent_6 = ($$anchor) => {
|
||||||
var div_15 = root_7$1();
|
var div_15 = root_8();
|
||||||
var div_16 = sibling(child(div_15), 2);
|
var div_16 = sibling(child(div_15), 2);
|
||||||
var text_8 = child(div_16, true);
|
var text_8 = child(div_16, true);
|
||||||
reset(div_16);
|
reset(div_16);
|
||||||
@ -6293,8 +6302,8 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
template_effect(() => set_text(text_8, get(entry).notes));
|
template_effect(() => set_text(text_8, get(entry).notes));
|
||||||
append($$anchor, div_15);
|
append($$anchor, div_15);
|
||||||
};
|
};
|
||||||
if_block(node_3, ($$render) => {
|
if_block(node_4, ($$render) => {
|
||||||
if (get(entry).notes) $$render(consequent_5);
|
if (get(entry).notes) $$render(consequent_6);
|
||||||
});
|
});
|
||||||
reset(div_8);
|
reset(div_8);
|
||||||
var div_17 = sibling(div_8, 2);
|
var div_17 = sibling(div_8, 2);
|
||||||
@ -6306,9 +6315,9 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
reset(span_3);
|
reset(span_3);
|
||||||
reset(div_17);
|
reset(div_17);
|
||||||
reset(div_5);
|
reset(div_5);
|
||||||
var node_4 = sibling(div_5, 2);
|
var node_5 = sibling(div_5, 2);
|
||||||
var consequent_6 = ($$anchor) => {
|
var consequent_7 = ($$anchor) => {
|
||||||
var div_18 = root_8();
|
var div_18 = root_9();
|
||||||
var div_19 = child(div_18);
|
var div_19 = child(div_18);
|
||||||
var p = sibling(child(div_19), 2);
|
var p = sibling(child(div_19), 2);
|
||||||
var strong = sibling(child(p));
|
var strong = sibling(child(p));
|
||||||
@ -6335,12 +6344,11 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
delegated("click", button_7, () => set(showDeleteConfirm, false));
|
delegated("click", button_7, () => set(showDeleteConfirm, false));
|
||||||
append($$anchor, div_18);
|
append($$anchor, div_18);
|
||||||
};
|
};
|
||||||
if_block(node_4, ($$render) => {
|
if_block(node_5, ($$render) => {
|
||||||
if (get(showDeleteConfirm)) $$render(consequent_6);
|
if (get(showDeleteConfirm)) $$render(consequent_7);
|
||||||
});
|
});
|
||||||
template_effect(($0, $1) => {
|
template_effect(($0, $1) => {
|
||||||
set_text(text_3, get(entry).title);
|
set_text(text_3, get(entry).title);
|
||||||
set_text(text_4, get(entry).username);
|
|
||||||
set_text(text_5, get(passwordVisible) ? get(decryptedPassword) : "••••••••••••");
|
set_text(text_5, get(passwordVisible) ? get(decryptedPassword) : "••••••••••••");
|
||||||
set_text(text_6, get(passwordVisible) ? "🙈" : "👁");
|
set_text(text_6, get(passwordVisible) ? "🙈" : "👁");
|
||||||
set_text(text_9, `Created: ${$0 ?? ""}`);
|
set_text(text_9, `Created: ${$0 ?? ""}`);
|
||||||
@ -6348,7 +6356,6 @@ function EntryDetail($$anchor, $$props) {
|
|||||||
}, [() => new Date(get(entry).createdAt).toLocaleString(), () => new Date(get(entry).updatedAt).toLocaleString()]);
|
}, [() => new Date(get(entry).createdAt).toLocaleString(), () => new Date(get(entry).updatedAt).toLocaleString()]);
|
||||||
delegated("click", button, () => $$props.onEdit(get(entry).id));
|
delegated("click", button, () => $$props.onEdit(get(entry).id));
|
||||||
delegated("click", button_1, () => set(showDeleteConfirm, true));
|
delegated("click", button_1, () => set(showDeleteConfirm, true));
|
||||||
delegated("click", button_2, () => copyToClipboard(get(entry).username, "Username"));
|
|
||||||
delegated("click", button_3, () => set(passwordVisible, !get(passwordVisible)));
|
delegated("click", button_3, () => set(passwordVisible, !get(passwordVisible)));
|
||||||
delegated("click", button_4, () => copyToClipboard(get(decryptedPassword), "Password"));
|
delegated("click", button_4, () => copyToClipboard(get(decryptedPassword), "Password"));
|
||||||
append($$anchor, fragment);
|
append($$anchor, fragment);
|
||||||
@ -6372,7 +6379,7 @@ var root_3$2 = /* @__PURE__ */ from_html(`<div class="error-banner svelte-pafazm
|
|||||||
var root_5$1 = /* @__PURE__ */ from_html(`<div class="validation-error svelte-pafazm"> </div>`);
|
var root_5$1 = /* @__PURE__ */ from_html(`<div class="validation-error svelte-pafazm"> </div>`);
|
||||||
var root_4$2 = /* @__PURE__ */ from_html(`<div class="validation-errors svelte-pafazm"></div>`);
|
var root_4$2 = /* @__PURE__ */ from_html(`<div class="validation-errors svelte-pafazm"></div>`);
|
||||||
var root_6$2 = /* @__PURE__ */ from_html(`<option> </option>`);
|
var root_6$2 = /* @__PURE__ */ from_html(`<option> </option>`);
|
||||||
var root_2$2 = /* @__PURE__ */ from_html(`<!> <form class="form-card svelte-pafazm"><!> <div class="form-group"><label for="title">Title *</label> <input id="title" type="text" placeholder="e.g. GitHub, Gmail"/></div> <div class="form-group"><label for="username">Username / Email *</label> <input id="username" type="text" placeholder="username or email"/></div> <div class="form-group"><label for="password">Password *</label> <div class="password-input-group svelte-pafazm"><input id="password" placeholder="Password" class="svelte-pafazm"/> <button type="button" class="btn btn-ghost btn-sm" title="Toggle visibility"> </button> <button type="button" class="btn btn-ghost btn-sm" title="Generate password">🎲</button></div></div> <div class="form-group"><label for="url">URL</label> <input id="url" type="url" placeholder="https://example.com"/></div> <div class="form-group"><label for="group">Group</label> <select id="group"><option>No group</option><!></select></div> <div class="form-group"><label for="notes">Notes</label> <textarea id="notes" placeholder="Any additional notes..."></textarea></div> <div class="form-actions svelte-pafazm"><button type="submit" class="btn btn-primary"> </button> <button type="button" class="btn btn-ghost">Cancel</button></div></form>`, 1);
|
var root_2$2 = /* @__PURE__ */ from_html(`<!> <form class="form-card svelte-pafazm"><!> <div class="form-group"><label for="title">Title *</label> <input id="title" type="text" placeholder="e.g. GitHub, Gmail"/></div> <div class="form-group"><label for="username">Username / Email</label> <input id="username" type="text" placeholder="username or email"/></div> <div class="form-group"><label for="password">Password *</label> <div class="password-input-group svelte-pafazm"><input id="password" placeholder="Password" class="svelte-pafazm"/> <button type="button" class="btn btn-ghost btn-sm" title="Toggle visibility"> </button> <button type="button" class="btn btn-ghost btn-sm" title="Generate password">🎲</button></div></div> <div class="form-group"><label for="url">URL</label> <input id="url" type="url" placeholder="https://example.com"/></div> <div class="form-group"><label for="group">Group</label> <select id="group"><option>No group</option><!></select></div> <div class="form-group"><label for="notes">Notes</label> <textarea id="notes" placeholder="Any additional notes..."></textarea></div> <div class="form-actions svelte-pafazm"><button type="submit" class="btn btn-primary"> </button> <button type="button" class="btn btn-ghost">Cancel</button></div></form>`, 1);
|
||||||
var root$2 = /* @__PURE__ */ from_html(`<div class="entry-form"><!></div>`);
|
var root$2 = /* @__PURE__ */ from_html(`<div class="entry-form"><!></div>`);
|
||||||
function EntryForm($$anchor, $$props) {
|
function EntryForm($$anchor, $$props) {
|
||||||
push($$props, true);
|
push($$props, true);
|
||||||
|
|||||||
@ -100,6 +100,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-fields">
|
<div class="detail-fields">
|
||||||
|
{#if entry.username}
|
||||||
<div class="detail-field">
|
<div class="detail-field">
|
||||||
<span class="field-label">Username</span>
|
<span class="field-label">Username</span>
|
||||||
<div class="field-value">
|
<div class="field-value">
|
||||||
@ -107,6 +108,7 @@
|
|||||||
<button class="btn btn-ghost btn-sm copy-btn" onclick={() => copyToClipboard(entry.username, 'Username')} title="Copy username">📋</button>
|
<button class="btn btn-ghost btn-sm copy-btn" onclick={() => copyToClipboard(entry.username, 'Username')} title="Copy username">📋</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="detail-field">
|
<div class="detail-field">
|
||||||
<span class="field-label">Password</span>
|
<span class="field-label">Password</span>
|
||||||
|
|||||||
@ -118,7 +118,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="username">Username / Email *</label>
|
<label for="username">Username / Email</label>
|
||||||
<input id="username" type="text" bind:value={username} placeholder="username or email" />
|
<input id="username" type="text" bind:value={username} placeholder="username or email" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -87,7 +87,7 @@
|
|||||||
<span class="entry-title">{entry.title}</span>
|
<span class="entry-title">{entry.title}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="entry-username">{entry.username}</span>
|
<span class="entry-username">{entry.username || '—'}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="entry-url truncate">{entry.url || '—'}</span>
|
<span class="entry-url truncate">{entry.url || '—'}</span>
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export function generateId() {
|
|||||||
* @typedef {Object} CredentialEntry
|
* @typedef {Object} CredentialEntry
|
||||||
* @property {string} id - Unique identifier
|
* @property {string} id - Unique identifier
|
||||||
* @property {string} title - Display name (e.g. "GitHub", "Gmail")
|
* @property {string} title - Display name (e.g. "GitHub", "Gmail")
|
||||||
* @property {string} username - Login username or email
|
* @property {string} [username] - Login username or email (optional)
|
||||||
* @property {string} encryptedPassword - AES-GCM encrypted password blob (JSON string)
|
* @property {string} encryptedPassword - AES-GCM encrypted password blob (JSON string)
|
||||||
* @property {string} [url] - Website URL
|
* @property {string} [url] - Website URL
|
||||||
* @property {string} [notes] - Free-form notes
|
* @property {string} [notes] - Free-form notes
|
||||||
@ -36,7 +36,7 @@ export function generateId() {
|
|||||||
*
|
*
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* @param {string} data.title
|
* @param {string} data.title
|
||||||
* @param {string} data.username
|
* @param {string} [data.username]
|
||||||
* @param {string} data.encryptedPassword - Must already be encrypted
|
* @param {string} data.encryptedPassword - Must already be encrypted
|
||||||
* @param {string} [data.url]
|
* @param {string} [data.url]
|
||||||
* @param {string} [data.notes]
|
* @param {string} [data.notes]
|
||||||
@ -49,7 +49,7 @@ export function createEntry(data) {
|
|||||||
return {
|
return {
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
title: data.title.trim(),
|
title: data.title.trim(),
|
||||||
username: data.username.trim(),
|
username: data.username?.trim() || '',
|
||||||
encryptedPassword: data.encryptedPassword,
|
encryptedPassword: data.encryptedPassword,
|
||||||
url: data.url?.trim() || '',
|
url: data.url?.trim() || '',
|
||||||
notes: data.notes?.trim() || '',
|
notes: data.notes?.trim() || '',
|
||||||
@ -71,7 +71,7 @@ export function updateEntry(existing, data) {
|
|||||||
return {
|
return {
|
||||||
...existing,
|
...existing,
|
||||||
title: data.title !== undefined ? data.title.trim() : existing.title,
|
title: data.title !== undefined ? data.title.trim() : existing.title,
|
||||||
username: data.username !== undefined ? data.username.trim() : existing.username,
|
username: data.username !== undefined ? (data.username?.trim() || '') : existing.username,
|
||||||
encryptedPassword: data.encryptedPassword !== undefined ? data.encryptedPassword : existing.encryptedPassword,
|
encryptedPassword: data.encryptedPassword !== undefined ? data.encryptedPassword : existing.encryptedPassword,
|
||||||
url: data.url !== undefined ? data.url.trim() : existing.url,
|
url: data.url !== undefined ? data.url.trim() : existing.url,
|
||||||
notes: data.notes !== undefined ? data.notes.trim() : existing.notes,
|
notes: data.notes !== undefined ? data.notes.trim() : existing.notes,
|
||||||
@ -119,7 +119,7 @@ export function createGroup(name, color) {
|
|||||||
export function validateEntry(data) {
|
export function validateEntry(data) {
|
||||||
const errors = []
|
const errors = []
|
||||||
if (!data.title || !data.title.trim()) errors.push('Title is required')
|
if (!data.title || !data.title.trim()) errors.push('Title is required')
|
||||||
if (!data.username || !data.username.trim()) errors.push('Username is required')
|
|
||||||
if (!data.encryptedPassword) errors.push('Password is required')
|
if (!data.encryptedPassword) errors.push('Password is required')
|
||||||
return { valid: errors.length === 0, errors }
|
return { valid: errors.length === 0, errors }
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user