Files
OSIT-AE-App-Svelte/src/lib/app_components/e_app_sign_in_out.svelte
Scott Idem e5b4805916 feat(sign-in): inline status feedback on username/password sign-in button
Replaces all alert() calls in the user/pass auth flow with reactive state.
Button shows: Verifying… (disabled) → Failed — retry? (red) →
Enter credentials first (amber) → Username/User ID Sign In (default).
Error messages (wrong password, no person record, no server response)
appear as small text below the button on failure.
Clicking the button resets to default so retry is clean.
Also removes dead commented-out alert and cleans up the promise chains.

No type="button" issues found — all non-submit buttons were already typed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 17:09:09 -04:00

826 lines
39 KiB
Svelte

<script lang="ts">
import { untrack } from 'svelte';
// *** Import Svelte specific
import { browser } from '$app/environment';
import { goto, invalidateAll } from '$app/navigation';
import { Modal } from 'flowbite-svelte';
// *** Import other supporting libraries
import {
CircleX,
Eye,
EyeOff,
Key,
LogIn,
LogOut,
LockKeyhole,
Mail,
MailCheck,
UserCheck
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
import type { key_val } from '$lib/stores/ae_stores';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { core_func } from '$lib/ae_core/ae_core_functions';
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
data?: any;
hidden: null | boolean;
}
let { log_lvl = $bindable(0), data = null, hidden = $bindable(true) }: Props = $props();
let url_user_id = $state(untrack(() => data?.url?.searchParams?.get('user_id')));
let url_user_key = $state(untrack(() => data?.url?.searchParams?.get('user_key'))); // Reminder that "key" is the site's auth key.
let url_user_username = $state(untrack(() => data?.url?.searchParams?.get('username')));
let url_user_email = $state(untrack(() => data?.url?.searchParams?.get('user_email')));
$effect(() => {
// NOTE: Sync URL params to state.
// We use untrack to prevent infinite loops if navigation triggers within this effect.
// WARNING: Ensure this doesn't clobber user-entered data during background URL updates.
untrack(() => {
url_user_id = data?.url?.searchParams?.get('user_id');
url_user_key = data?.url?.searchParams?.get('user_key');
url_user_username = data?.url?.searchParams?.get('username');
url_user_email = data?.url?.searchParams?.get('user_email');
});
});
let ae_promises: key_val = {};
let trigger: null | boolean = $state(null); // Use $state to ensure reactivity
let user_id: string | null = $state(null); // Use $state to ensure reactivity
let user_obj: key_val = $state({}); // Use $state to ensure reactivity
let person_id: string | null = $state(null); // Use $state to ensure reactivity
let person_obj: key_val = $state({}); // Use $state to ensure reactivity
$effect(() => {
if (user_id && person_id && trigger) {
// alert(`Ready to sign in person! \n\nuser_id: ${user_id}\nperson_id: ${person_id}\n\nThis is a test alert to confirm the values are set.`);
trigger = false;
sign_in();
}
});
let user_email = $state('scott.idem@gmail.com'); // Used for quick lookup of user by email address
let new_password = $state('test12345');
let confirm_password = $state('test12345');
let is_changing_password = $state(false);
let show_password_text = $state('password'); // password or text
// Email sign-in status: null | 'sending' | 'sent' | 'not_found' | 'no_email' | 'error'
let email_send_status: null | string = $state(null);
// Username/password sign-in status: null | 'signing_in' | 'error' | 'no_credentials'
let user_signin_status: null | string = $state(null);
let user_signin_error: null | string = $state(null);
function sign_in() {
$ae_loc.jwt = user_obj.jwt; // Store the JSON Web Token
$ae_loc.person_id = person_id; // Set the person_id in the ae_loc store
$ae_loc.person = person_obj; // Store the full person object for reference
$ae_loc.user_id = user_id; // Set the user_id in the ae_loc store
$ae_loc.user = user_obj; // Store the full user object for reference
if (user_obj.super) {
$ae_loc.access_type = 'super';
} else if (user_obj.manager) {
$ae_loc.access_type = 'manager';
} else if (user_obj.administrator) {
$ae_loc.access_type = 'administrator';
} else if (user_obj.verified) {
// They are "trusted" because the user has been verified. External staff usually start with this level.
$ae_loc.access_type = 'trusted';
} else if (user_obj.public) {
$ae_loc.access_type = 'public';
} else {
$ae_loc.access_type = 'authenticated';
}
$ae_loc.user_access_type = $ae_loc.access_type; // Used to revert back to the user's access type after quick access (temporarily escalate permissions) is turned off.
let access_checks_results = ae_util.process_permission_checks($ae_loc.access_type);
// WARNING: I think this is causing a loop in Svelte or something.
// Last ten effects were:
// Uncaught Svelte error: effect_update_depth_exceeded
// Maximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops
// https://svelte.dev/e/effect_update_depth_exceeded
$ae_loc = {
...$ae_loc,
...access_checks_results
};
// $ae_loc = {...access_checks_results};
console.log('Remove the sign in fields from the URL.');
data.url.searchParams.delete('user_id');
data.url.searchParams.delete('user_key');
data.url.searchParams.delete('username');
data.url.searchParams.delete('user_email');
data.url.searchParams.delete('valid_email'); // Part of sign in email for possible future use
let new_url = data.url.toString();
// We need to set browser history and force all load functions to rerun.
goto(new_url, { replaceState: true, invalidateAll: true });
}
function sign_out() {
// Clear the session information
$ae_loc.jwt = null;
$ae_loc.person_id = null;
$ae_loc.person = {};
$ae_loc.user_id = null;
$ae_loc.user = {};
user_id = null; // Clear the user_id
user_obj = {}; // Clear the user object
person_id = null; // Clear the person_id
person_obj = {}; // Clear the person object
$ae_loc.access_type = ''; // Clear the access type
let access_checks_results = ae_util.process_permission_checks('');
$ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_sess.auth__entered_user_id = null;
$ae_sess.auth__entered_user_key = null;
// $ae_sess.auth__entered_username = null; // Keeping the username
$ae_sess.auth__entered_password = null;
indexedDB.deleteDatabase('ae_archives_db'); // Archives module
indexedDB.deleteDatabase('ae_core_db');
indexedDB.deleteDatabase('ae_events_db'); // Events module
indexedDB.deleteDatabase('ae_journals_db'); // Journals module
indexedDB.deleteDatabase('ae_posts_db'); // Posts module
indexedDB.deleteDatabase('ae_sponsorships_db'); // Sponsorships module
// $ae_loc.allow_access = false;
$ae_loc.authenticated_access = false;
$ae_loc.edit_mode = false;
localStorage.clear();
sessionStorage.clear();
console.log('Remove the sign out fields from the URL.');
data.url.searchParams.delete('user_id');
data.url.searchParams.delete('user_key');
data.url.searchParams.delete('username');
data.url.searchParams.delete('user_email');
data.url.searchParams.delete('valid_email'); // Part of sign in email for possible future use
let new_url = data.url.toString();
// We need to set browser history and force all load functions to rerun.
// goto(new_url, {replaceState: true, invalidateAll: true});
// invalidateAll();
window.location.reload();
console.log('Signed out successfully.');
}
$effect(() => {
if (browser) {
untrack(() => {
if (url_user_id) {
// Pre-fill the auth__entered_user_id if passed in via the URL.
$ae_sess.auth__entered_user_id = url_user_id;
}
if (url_user_key) {
// Pre-fill the auth__entered_key if passed in via the URL.
$ae_sess.auth__entered_user_key = url_user_key;
}
if (url_user_username) {
// Pre-fill the auth__entered_username if passed in via the URL.
$ae_sess.auth__entered_username = url_user_username;
}
if (url_user_email) {
// Pre-fill the auth__entered_email if passed in via the URL.
$ae_sess.auth__entered_email = url_user_email;
}
});
}
});
// Use core_func.send_email_auth_ae_obj__user_id
function handle_send_auth_email({ user_id }: { user_id: string }) {
console.log('handle_send_auth_email()');
console.log($ae_loc.base_url); // URL origin
console.log($ae_loc.hostname); // URL hostname
// This function creates a new auth_key and then sends an email to the user with the new auth key.
ae_promises.send_email_auth_ae_obj__user_id = core_func.send_email_auth_ae_obj__user_id({
api_cfg: $ae_api,
account_id: $slct.account_id,
user_id: user_id,
base_url: $ae_loc.base_url,
log_lvl: 0
});
}
// Use core_func.qry_ae_obj_li__user_email
function handle_lookup_user_email({ email }: { email: string }) {
console.log('handle_lookup_user_email()');
// If a user is returned then send the new auth key email
ae_promises.load__user_obj_li = core_func
.qry_ae_obj_li__user_email({
api_cfg: $ae_api,
account_id: $slct.account_id,
null_account_id: false,
email: email,
log_lvl: 0
})
.then((user_response) => {
if (user_response?.user_id_random) {
console.log(`User found for email:`, user_response);
handle_send_auth_email({ user_id: user_response.user_id_random });
email_send_status = 'sent';
} else if (user_response && user_response.length > 0) {
console.log(`Multiple users found for email:`, user_response);
handle_send_auth_email({ user_id: user_response[0].user_id_random });
email_send_status = 'sent';
} else {
console.warn('No user found for email:', email);
email_send_status = 'not_found';
}
})
.catch(() => {
email_send_status = 'error';
});
}
async function handle_change_password() {
if (!new_password) {
alert('Password cannot be empty.');
return;
}
if (new_password !== confirm_password) {
alert('Passwords do not match.');
return;
}
let use_user_id = $ae_loc.user_id;
let use_password = new_password;
let wait_for_lookup = true;
// Look up the user by email address to get their user ID
if (user_email) {
wait_for_lookup = true;
// ae_promises.load__user_obj_li = await core_func.qry_ae_obj_li__user_email({
// api_cfg: $ae_api,
// account_id: $slct.account_id,
// null_account_id: false,
// email: user_email,
// log_lvl: 1
// }).then((user_response) => {
// if (!user_response) {
// // This means a 404 was returned
// alert('No user found with that email address.');
// return;
// } else if (user_response?.user_id_random) {
// console.log(`User found for email:`, user_response);
// use_user_id = user_response.user_id_random;
// } else if (user_response.length > 0) {
// console.log(`Multiple users found for email:`, user_response);
// use_user_id = user_response[0].user_id_random;
// }
// });
ae_promises.load__user_obj_li = await core_func.qry_ae_obj_li__user_email({
api_cfg: $ae_api,
account_id: $slct.account_id,
null_account_id: false,
email: user_email,
log_lvl: 0
});
if (!ae_promises.load__user_obj_li) {
// This means a 404 was returned
alert('No user found with that email address.');
return;
} else if (ae_promises.load__user_obj_li?.user_id_random) {
console.log(`User found for email:`, ae_promises.load__user_obj_li);
use_user_id = ae_promises.load__user_obj_li.user_id_random;
} else if (ae_promises.load__user_obj_li.length > 0) {
console.log(`Multiple users found for email:`, ae_promises.load__user_obj_li);
use_user_id = ae_promises.load__user_obj_li[0].user_id_random;
}
} else {
wait_for_lookup = false;
}
is_changing_password = true;
if (user_email && wait_for_lookup) {
console.log('Waiting for user lookup to complete...');
// await ae_promises.load__user_obj_li;
}
try {
ae_promises.change_password = await core_func.auth_ae_obj__user_id_change_password({
api_cfg: $ae_api,
account_id: $ae_loc.account_id,
user_id: use_user_id,
password: use_password,
log_lvl: log_lvl
});
// await core_func.auth_ae_obj__user_id_change_password({
// api_cfg: $ae_api,
// account_id: $ae_loc.account_id,
// user_id: use_user_id,
// password: use_password,
// log_lvl: log_lvl
// });
// alert('Password changed successfully.');
// $ae_sess.show__modal_change_password = false;
} catch (error) {
console.error('Error changing password:', error);
alert('Unexpected Response. Failed to change password.');
} finally {
is_changing_password = false;
}
if (ae_promises.change_password) {
console.log('Password changed successfully.');
alert('Password changed successfully.');
$ae_sess.show__modal_change_password = false;
} else {
console.error('Failed to change password.');
alert('Failed to change password. Check password length.');
}
}
</script>
<!-- It is important to keep in mind that a Person can only fully sign in if they have a linked User record. -->
<!-- *:hover:inline -->
<section
id="AE-Sign-In-Out"
class="
ae_sign_in_out
hidden-print
bg-blue-100 text-gray-900
dark:bg-blue-800 dark:text-gray-200
flex flex-col flex-wrap gap-1
items-end justify-center
w-72 max-w-72
p-1
border-2 border-gray-200
duration-300 delay-150 hover:delay-1000 hover:ease-out
transition-all
"
class:hidden
>
<span class="flex flex-col gap-2 w-full">
{#if !$ae_loc?.person_id && !$ae_loc?.user_id}
<!-- Form for user look up based on email address -->
<form
class="ae_sign_in_form flex flex-col gap-2 w-full"
onsubmit={async (e) => {
e.preventDefault();
if ($ae_sess.auth__entered_email) {
email_send_status = 'sending';
handle_lookup_user_email({
email: $ae_sess.auth__entered_email
});
} else {
email_send_status = 'no_email';
}
}}
>
<div class="text-xs text-gray-400 dark:text-gray-500 font-medium">Email magic link</div>
<input
type="text"
class="input w-full text-sm"
placeholder="Email Address"
value={$ae_sess.auth__entered_email ?? ''}
oninput={(e) => ($ae_sess.auth__entered_email = (e.target as HTMLInputElement).value)}
/>
<button
type="submit"
class="btn btn-sm w-full justify-center transition-all
{email_send_status === 'sent' ? 'preset-filled-success-500'
: email_send_status === 'sending' ? 'preset-tonal-surface opacity-70 cursor-wait'
: email_send_status === 'not_found' || email_send_status === 'error' ? 'preset-tonal-error border border-error-500'
: 'preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500'}"
disabled={email_send_status === 'sending'}
title="Look up user by email and send sign in email"
onclick={() => { email_send_status = null; }}
>
<MailCheck size="1em" class="shrink-0" />
<span class="ml-1">
{#if email_send_status === 'sending'}
Sending…
{:else if email_send_status === 'sent'}
Email sent ✓
{:else if email_send_status === 'not_found'}
No account found
{:else if email_send_status === 'error'}
Error — retry?
{:else if email_send_status === 'no_email'}
Enter an email first
{:else}
Send Sign In Email
{/if}
</span>
</button>
</form>
<!-- Divider between the two sign-in methods -->
<div class="flex items-center gap-2 text-xs text-gray-400 dark:text-gray-600">
<div class="flex-1 border-t border-gray-200/60 dark:border-gray-700/60"></div>
<span>or</span>
<div class="flex-1 border-t border-gray-200/60 dark:border-gray-700/60"></div>
</div>
<!-- We need to get the person's linked User ID and auth_key or User username and password to sign in. -->
<form
class="ae_sign_in_form flex flex-col gap-2 w-full"
onsubmit={async (e) => {
e.preventDefault();
// WARNING: Logging in as a global user does not work yet. The API needs to be updated. Currently it returns multiple user records from the v_user view if there is more than one person record linked to the user ID.
// Helper to set error state without repeating the pattern
const set_error = (msg: string) => {
user_signin_status = 'error';
user_signin_error = msg;
};
if ($ae_sess.auth__entered_user_id && $ae_sess.auth__entered_user_key) {
user_signin_status = 'signing_in';
user_signin_error = null;
ae_promises['user'] = await core_func
.auth_ae_obj__user_id_user_auth_key({
api_cfg: $ae_api,
account_id: $ae_loc.account_id,
// null_account_id: false, // Set to true to allow to authenticate as global user (Super or Manager)
user_id: $ae_sess.auth__entered_user_id,
user_auth_key: $ae_sess.auth__entered_user_key,
log_lvl: 0
})
.then((user_response) => {
if (user_response?.user_id) {
console.log(`Successfully authenticated with User ID and Auth Key: ${user_response.username}`, user_response);
user_obj = user_response;
user_id = user_obj.user_id;
} else if (!user_response) {
set_error('No server response. Check your connection and retry.');
} else {
// API returns 'detail' for validation errors (FastAPI standard), 'error' for app-level errors
const reason = user_response?.detail || user_response?.error || 'Invalid User ID or Auth Key.';
set_error(reason);
}
})
.then(() => {
if (!user_id) {
// Auth failed above — status already set
console.error('Auth (user_id+key): user_id not set after authentication attempt.');
return;
}
// Look up the person record linked to this user.
// We use enabled:'all' and hidden:'all' to find the record even if
// technical fields like 'hide' are NULL or the record is temporarily disabled.
ae_promises['person'] = core_func
.load_ae_obj_li__person({
api_cfg: $ae_api,
for_obj_type: 'account',
for_obj_id: $ae_loc.account_id,
qry_user_id: user_id,
enabled: 'all',
hidden: 'all',
log_lvl: 0
})
.then((person_response) => {
// V3 CRUD returns 'id' as the random identifier; 'person_id_random' is a legacy field name.
const person_rec = person_response?.[0];
if (person_rec && (person_rec.id || person_rec.person_id_random)) {
console.log(`Successfully loaded person (${user_id}):`, person_rec);
person_obj = person_rec;
person_id = person_rec.id ?? person_rec.person_id_random;
trigger = true; // triggers sign_in() via $effect → page redirect
} else {
set_error('No person record linked to this account. Contact your administrator.');
}
})
.then(() => {
if (!user_id || !person_id) {
console.error('Auth (user_id+key): finished but missing user_id or person_id — sign_in() will not be called.');
}
});
});
} else if ($ae_sess.auth__entered_username && $ae_sess.auth__entered_password) {
user_signin_status = 'signing_in';
user_signin_error = null;
ae_promises['user'] = await core_func
.auth_ae_obj__username_password({
api_cfg: $ae_api,
account_id: $ae_loc.account_id,
// null_account_id: false, // Set to true to allow to authenticate as global user (Super or Manager)
username: $ae_sess.auth__entered_username,
password: $ae_sess.auth__entered_password,
log_lvl: 0
})
.then((user_response) => {
if (user_response?.user_id) {
console.log(`Successfully authenticated with Username (${user_response.username}) and Password:`, user_response);
user_obj = user_response;
user_id = user_obj.user_id;
} else if (!user_response) {
set_error('No server response. Check your connection and retry.');
} else {
// API returns 'detail' for validation errors (FastAPI standard), 'error' for app-level errors
const reason = user_response?.detail || user_response?.error || 'Invalid username or password.';
set_error(reason);
}
})
.then(() => {
if (!user_id) {
// Auth failed above — status already set
console.error('Auth (username+password): user_id not set after authentication attempt.');
return;
}
// Look up the person record linked to this user.
// We use enabled:'all' and hidden:'all' to find the record even if
// technical fields like 'hide' are NULL or the record is temporarily disabled.
ae_promises['person'] = core_func
.load_ae_obj_li__person({
api_cfg: $ae_api,
for_obj_type: 'account',
for_obj_id: $ae_loc.account_id,
qry_user_id: user_id,
enabled: 'all',
hidden: 'all',
log_lvl: 0
})
.then((person_response) => {
// V3 CRUD returns 'id' as the random identifier; 'person_id_random' is a legacy field name.
const person_rec = person_response?.[0];
if (person_rec && (person_rec.id || person_rec.person_id_random)) {
console.log(`Successfully loaded person (${user_id}):`, person_rec);
person_obj = person_rec;
person_id = person_rec.id ?? person_rec.person_id_random;
trigger = true; // triggers sign_in() via $effect → page redirect
} else {
set_error('No person record linked to this account. Contact your administrator.');
}
})
.then(() => {
if (!user_id || !person_id) {
console.error('Auth (username+password): finished but missing user_id or person_id — sign_in() will not be called.');
}
});
});
} else {
user_signin_status = 'no_credentials';
return false;
}
}}
>
<div class="text-xs text-gray-400 dark:text-gray-500 font-medium">
{#if $ae_sess.auth__entered_user_id}
User ID &amp; Auth Key
{:else}
Username &amp; password
{/if}
</div>
{#if $ae_sess.auth__entered_user_id}
<input
type="text"
class="input w-full text-sm"
placeholder="User ID"
value={$ae_sess.auth__entered_user_id ?? ''}
oninput={(e) => ($ae_sess.auth__entered_user_id = (e.target as HTMLInputElement).value)}
/>
<input
type="text"
class="input w-full text-sm"
placeholder="Auth Key"
value={$ae_sess.auth__entered_user_key ?? ''}
oninput={(e) => ($ae_sess.auth__entered_user_key = (e.target as HTMLInputElement).value)}
/>
{:else}
<input
type="text"
class="input w-full text-sm"
placeholder="Username"
value={$ae_sess.auth__entered_username ?? ''}
oninput={(e) => ($ae_sess.auth__entered_username = (e.target as HTMLInputElement).value)}
/>
<input
type="password"
class="input w-full text-sm"
placeholder="Password"
value={$ae_sess.auth__entered_password ?? ''}
oninput={(e) => ($ae_sess.auth__entered_password = (e.target as HTMLInputElement).value)}
/>
{/if}
<button
type="submit"
class="btn btn-sm w-full justify-center transition-all
{user_signin_status === 'error' ? 'preset-tonal-error border border-error-500'
: user_signin_status === 'signing_in' ? 'preset-tonal-surface opacity-70 cursor-wait'
: user_signin_status === 'no_credentials' ? 'preset-tonal-warning border border-warning-500'
: 'preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500'}"
disabled={user_signin_status === 'signing_in'}
title="Sign in with username and password"
onclick={() => { if (user_signin_status !== 'signing_in') { user_signin_status = null; user_signin_error = null; } }}
>
<UserCheck size="1em" class="shrink-0" />
<span class="ml-1">
{#if user_signin_status === 'signing_in'}
Verifying…
{:else if user_signin_status === 'error'}
Failed — retry?
{:else if user_signin_status === 'no_credentials'}
Enter credentials first
{:else}
{$ae_sess.auth__entered_user_id ? 'User ID' : 'Username'} Sign In
{/if}
</span>
</button>
{#if user_signin_error}
<p class="text-xs text-error-600 dark:text-error-400 text-center leading-snug">
{user_signin_error}
</p>
{/if}
</form>
{:else}
<!-- Signed in: show username, optional change password, sign out -->
<div class="flex flex-col gap-2 items-stretch w-full">
<div class="text-sm text-center text-gray-500 dark:text-gray-400 py-1 font-medium">
{$ae_loc?.user?.username ?? '-- not set --'}
</div>
<!-- Change password — edit mode only -->
{#if $ae_loc.edit_mode}
<button
type="button"
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500 w-full justify-center"
title="Change Password"
onclick={() => {
$ae_sess.show__modal_change_password = true;
}}
>
<LockKeyhole size="1em" class="shrink-0" />
<span class="ml-1">Change Password</span>
</button>
{/if}
<!-- Sign out -->
<button
type="button"
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500 w-full justify-center"
title="Sign Out"
onclick={async () => {
if (confirm('Are you sure you want to sign out?')) {
sign_out();
}
}}
>
<LogOut size="1em" class="shrink-0" />
<span class="ml-1">Sign Out</span>
</button>
</div>
{/if}
</span>
</section>
<!-- Change Password Modal -->
<!-- They should be able to see their password (text) -->
<!-- autocomplete="off" -->
{#if $ae_sess.show__modal_change_password}
<Modal
title="Change Password"
bind:open={$ae_sess.show__modal_change_password}
autoclose={false}
placement="top-center"
size="md"
class="top-center bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md relative mx-auto w-full divide-y"
>
<div class="modal-box flex flex-col gap-2 items-center justify-center">
<!-- If the user is a global Manager or Super then they can change the password for any user. Otherwise, they can only change their own password. Show email address field for a quick lookup to get the user.id. -->
<div class="flex flex-col flex-wrap gap-2">
<span class="text-sm text-gray-500">
Your Username: {$ae_loc?.user?.username ?? '-- not set --'}
</span>
<span class="text-sm text-gray-500">
Your Email: {$ae_loc?.user?.email ?? '-- not set --'}
</span>
<span class="text-sm text-gray-500">
Your User ID: {$ae_loc?.user_id ?? '-- not set --'}
</span>
</div>
{#if $ae_loc?.manager_access}
<div class="flex flex-row flex-wrap gap-2">
<input
type="text"
name="user_email"
autocomplete="off"
placeholder="User's email address"
bind:value={user_email}
class="input input-bordered w-48 max-w-full"
/>
</div>
{/if}
<!-- Allow for new password and confirm password fields. -->
<div class="flex flex-row flex-wrap gap-2 items-center justify-center">
<span class="flex flex-row flex-wrap gap-1">
<input
required
type={show_password_text}
autocomplete="new-password"
name="new_password"
placeholder="New Password"
bind:value={new_password}
class="input input-bordered w-48 max-w-full required:border-red-500"
/>
<input
required
type={show_password_text}
autocomplete="off"
name="confirm_password"
placeholder="Confirm Password"
bind:value={confirm_password}
class="input input-bordered w-48 max-w-full required:border-red-500"
/>
</span>
<!-- Show/Hide Password Text -->
<button
type="button"
class="btn btn-icon preset-tonal-secondary"
onclick={() => {
// const inputs = document.querySelectorAll('.modal-box input[type="password"]');
// inputs.forEach((input) => {
// input.type = input.type === 'password' ? 'text' : 'password';
// });
if (show_password_text === 'password') {
show_password_text = 'text';
} else {
show_password_text = 'password';
}
}}
title="Show/Hide Password Text"
>
{#if show_password_text === 'password'}
<EyeOff class="mx-1" />
{:else}
<Eye class="mx-1" />
{/if}
<!-- <Eye class="mx-1"/> -->
<!-- <span class="hidden sm:inline">
Show/Hide
</span> -->
</button>
<div>
<span class="text-sm text-warning-800 dark:text-gray-200">
Password must be at <strong>least 10 characters</strong> long. It is recommend
that you use a pass phrase (multiple words as your password) or a complex password
(uppercase letter, lowercase letters, numbers, and special characters).
</span>
</div>
</div>
<div class="flex flex-row flex-wrap gap-2">
<button
type="button"
class="btn preset-filled-warning-500"
onclick={handle_change_password}
disabled={is_changing_password}
>
<Key class="mx-1" />
Change Password
</button>
<button
type="button"
class="btn preset-tonal-secondary"
onclick={() => ($ae_sess.show__modal_change_password = false)}
>
<CircleX class="mx-1" />
Cancel
</button>
</div>
</div>
</Modal>
{/if}
<style lang="scss">
</style>