290 lines
9.6 KiB
Vue
290 lines
9.6 KiB
Vue
<template>
|
|
<div class="general my-account flex flex-col items-center w-full h-full p-8">
|
|
<main class="flex main-part flex-col px-8 mt-6 w-[720px]">
|
|
<h1
|
|
id="general_acct_info_user_name"
|
|
class="text-[32px] leading-[64px] font-medium mb-2"
|
|
>
|
|
{{ name }}
|
|
</h1>
|
|
<div class="general-status-container">
|
|
<Badge displayText="Admin" :isActivated="is_admin" />
|
|
<Badge displayText="Suspended" :isActivated="!is_active" />
|
|
</div>
|
|
<div
|
|
id="general_account_visit_info"
|
|
class="w-full border-b border-b-[#CBD5E1] border-b-[1px] mt-3 pb-4 w-full"
|
|
>
|
|
Total visits
|
|
<span class="text-[#0099FF]">
|
|
{{ visitTime }}
|
|
</span>
|
|
times.
|
|
</div>
|
|
</main>
|
|
<main class="flex flex-col pt-6 px-8 w-[720px]">
|
|
<h1 class="text-[20px] font-medium mb-4">
|
|
{{ i18next.t("AcctMgmt.UserInfo") }}
|
|
</h1>
|
|
<div class="row w-full flex py-2 h-[40px] mb-4 items-center">
|
|
<div class="field-label text-sm flex items-center font-medium mr-4">
|
|
<span class="align-right-span flex w-[80px]">
|
|
{{ i18next.t("AcctMgmt.Account") }}
|
|
</span>
|
|
</div>
|
|
<div class="account flex flex-col">
|
|
<span class="flex text-[#0F172A] text-[16px]">{{ username }}</span>
|
|
<div v-show="false" class="error-wrapper my-2">
|
|
<div class="error-msg-section flex justify-start">
|
|
<img
|
|
src="@/assets/icon-alert.svg"
|
|
alt="!"
|
|
class="exclamation-img flex mr-2"
|
|
/>
|
|
<span class="error-msg-text flex text-[#FF3366] h-[24px]">
|
|
{{ i18next.t("AcctMgmt.AccountNotUnique") }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row w-full flex py-2 h-[40px] mb-4 items-center text-[14px]">
|
|
<div class="field-label text-sm flex font-medium mr-4 box-border">
|
|
<span class="flex w-[80px]">
|
|
{{ i18next.t("AcctMgmt.FullName") }}
|
|
</span>
|
|
</div>
|
|
<input
|
|
v-if="isNameEditable"
|
|
id="input_name_field"
|
|
class="w-[280px] h-[40px] rounded p-1 border border-[1px] border-[#64748B] flex items-center outline-none"
|
|
v-model="inputName"
|
|
autocomplete="off"
|
|
/>
|
|
<div v-else class="not-editable displayable name flex w-[280px]">
|
|
{{ name }}
|
|
</div>
|
|
<div
|
|
v-if="isNameEditable"
|
|
class="save-btn-container flex ml-[112px]"
|
|
@click="onSaveNameClick"
|
|
>
|
|
<ButtonFilled :buttonText="i18next.t('Global.Save')" />
|
|
</div>
|
|
<div
|
|
v-if="isNameEditable"
|
|
class="cancel-btn btn-container flex ml-[8px]"
|
|
@click="onCancelNameClick"
|
|
>
|
|
<Button :buttonText="i18next.t('Global.Cancel')" />
|
|
</div>
|
|
<div
|
|
v-else="isPwdEditable"
|
|
class="edit-btn btn-container flex ml-[204px]"
|
|
@click="onEditNameClick"
|
|
>
|
|
<Button :buttonText="i18next.t('Global.Edit')" />
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="row w-full flex py-2 mb-4 border-b border-b-[#CBD5E1] border-b-[1px]"
|
|
>
|
|
<div class="field-label flex flex-col text-sm font-medium mr-4">
|
|
<span class="flex h-[40px] w-[80px] items-center">
|
|
{{ i18next.t("AcctMgmt.Password") }}
|
|
</span>
|
|
<div class="flex dummy-cell"></div>
|
|
</div>
|
|
<div class="flex input-and-error-msg flex-col w-[280px]">
|
|
<div
|
|
v-if="isPwdEditable"
|
|
class="input-and-eye flex items-center h-[40px] relative border-[1px] rounded"
|
|
:class="{
|
|
'border-[#FF3366]': !isPwdLengthValid,
|
|
'border-[#64748B]': isPwdLengthValid,
|
|
}"
|
|
>
|
|
<input
|
|
class="outline-none p-1 w-[280px]"
|
|
:type="isPwdEyeOn ? 'text' : 'password'"
|
|
v-model="inputPwd"
|
|
autocomplete="off"
|
|
:class="{
|
|
'color-[#FF3366]': !isPwdLengthValid,
|
|
'color-[#64748B]': isPwdLengthValid,
|
|
}"
|
|
/>
|
|
<img
|
|
id="eye_button"
|
|
v-if="isPwdEyeOn"
|
|
src="@/assets/icon-eye-open.svg"
|
|
class="absolute right-[8px] cursor-pointer"
|
|
@mousedown="togglePwdEyeBtn(true)"
|
|
@mouseup="togglePwdEyeBtn(false)"
|
|
alt=""
|
|
/>
|
|
<img
|
|
v-else
|
|
src="@/assets/icon-eye-hide.svg"
|
|
class="absolute right-[8px] cursor-pointer"
|
|
@mousedown="togglePwdEyeBtn(true)"
|
|
@mouseup="togglePwdEyeBtn(false)"
|
|
alt="eye"
|
|
/>
|
|
</div>
|
|
<div class="error-msg-section flex justify-start mt-4">
|
|
<img
|
|
v-show="!isPwdLengthValid"
|
|
src="@/assets/icon-alert.svg"
|
|
alt="!"
|
|
class="exclamation-img flex mr-2"
|
|
/>
|
|
<span
|
|
class="error-msg-text flex text-[#FF3366] h-[24px] text-[14px]"
|
|
>
|
|
{{
|
|
isPwdLengthValid ? "" : i18next.t("AcctMgmt.PwdLengthNotEnough")
|
|
}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="button-and-dummy-column flex flex-col text-[14px]">
|
|
<div class="button-group flex w-min-[80px] h-[40px]">
|
|
<div
|
|
v-if="isPwdEditable"
|
|
class="save-btn-container flex ml-[112px]"
|
|
@click="onSavePwdClick"
|
|
>
|
|
<ButtonFilled :buttonText="i18next.t('Global.Save')" />
|
|
</div>
|
|
<div
|
|
v-if="isPwdEditable"
|
|
class="cancel-btn btn-container flex ml-[8px]"
|
|
@click="onCancelPwdClick"
|
|
>
|
|
<Button :buttonText="i18next.t('Global.Cancel')" />
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="reset-btn btn-container flex ml-[460px]"
|
|
@click="onResetPwdClick"
|
|
>
|
|
<Button :buttonText="i18next.t('Global.Reset')" />
|
|
</div>
|
|
</div>
|
|
<div class="flex dummy-cell h-[40px]"></div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<main class="session flex flex-col pt-6 px-8 w-[720px]">
|
|
<h1 class="text-[20px] font-medium mb-4">
|
|
{{ i18next.t("AcctMgmt.Session") }}
|
|
</h1>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
// The Lucia project.
|
|
// Copyright 2024-2026 DSP, inc. All rights reserved.
|
|
// Authors:
|
|
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
|
// imacat.yang@dsp.im (imacat), 2023/9/23
|
|
/**
|
|
* @module views/AccountManagement/MyAccount My Account page for
|
|
* viewing and editing the current user's name and password.
|
|
*/
|
|
|
|
import { onMounted, computed, ref } from "vue";
|
|
import i18next from "@/i18n/i18n.js";
|
|
import { useLoginStore } from "@/stores/login";
|
|
import { useAcctMgmtStore } from "@/stores/acctMgmt";
|
|
import Badge from "../../components/Badge.vue";
|
|
import { useLoadingStore } from "@/stores/loading";
|
|
import Button from "@/components/Button.vue";
|
|
import ButtonFilled from "@/components/ButtonFilled.vue";
|
|
import { useToast } from "vue-toast-notification";
|
|
import { PWD_VALID_LENGTH } from "@/constants/constants.js";
|
|
|
|
const loadingStore = useLoadingStore();
|
|
const loginStore = useLoginStore();
|
|
const acctMgmtStore = useAcctMgmtStore();
|
|
const toast = useToast();
|
|
|
|
const visitTime = ref(0);
|
|
const currentViewingUser = computed(() => acctMgmtStore.currentViewingUser);
|
|
const name = computed(() => currentViewingUser.value.name);
|
|
const { username, is_admin, is_active } = currentViewingUser.value;
|
|
|
|
const inputName = ref(name.value);
|
|
const inputPwd = ref("");
|
|
const isNameEditable = ref(false);
|
|
const isPwdEditable = ref(false);
|
|
const isPwdEyeOn = ref(false);
|
|
const isPwdLengthValid = ref(true);
|
|
|
|
/** Enables the name editing input. */
|
|
const onEditNameClick = () => {
|
|
isNameEditable.value = true;
|
|
};
|
|
|
|
/** Enables the password editing input. */
|
|
const onResetPwdClick = () => {
|
|
isPwdEditable.value = true;
|
|
};
|
|
|
|
/** Validates that the password meets the minimum length requirement. */
|
|
const validatePwdLength = () => {
|
|
isPwdLengthValid.value = inputPwd.value.length >= PWD_VALID_LENGTH;
|
|
};
|
|
|
|
/** Saves the edited name to the server and refreshes user data. */
|
|
const onSaveNameClick = async () => {
|
|
if (inputName.value.length > 0) {
|
|
await acctMgmtStore.editAccountName(username, inputName.value);
|
|
await toast.success(i18next.t("AcctMgmt.MsgAccountEdited"));
|
|
await acctMgmtStore.getUserDetail(username);
|
|
isNameEditable.value = false;
|
|
inputName.value = name.value;
|
|
}
|
|
};
|
|
|
|
/** Validates and saves the new password. */
|
|
const onSavePwdClick = async () => {
|
|
validatePwdLength();
|
|
if (isPwdLengthValid.value) {
|
|
isPwdEditable.value = false;
|
|
await acctMgmtStore.editAccountPwd(username, inputPwd.value);
|
|
await toast.success(i18next.t("AcctMgmt.MsgAccountEdited"));
|
|
inputPwd.value = "";
|
|
await acctMgmtStore.getUserDetail(loginStore.userData.username);
|
|
}
|
|
};
|
|
|
|
/** Cancels name editing and restores the original value. */
|
|
const onCancelNameClick = () => {
|
|
isNameEditable.value = false;
|
|
inputName.value = name.value;
|
|
};
|
|
|
|
/** Cancels password editing and clears the input. */
|
|
const onCancelPwdClick = () => {
|
|
isPwdEditable.value = false;
|
|
inputPwd.value = "";
|
|
isPwdLengthValid.value = true;
|
|
};
|
|
|
|
/**
|
|
* Toggles the password visibility eye button.
|
|
* @param {boolean} toBeOpen - Whether to show the password.
|
|
*/
|
|
const togglePwdEyeBtn = (toBeOpen) => {
|
|
isPwdEyeOn.value = toBeOpen;
|
|
};
|
|
|
|
onMounted(async () => {
|
|
loadingStore.setIsLoading(false);
|
|
await acctMgmtStore.getUserDetail(loginStore.userData.username);
|
|
});
|
|
</script>
|