<template> <div> <div v-if=" !store.state.serverConfiguration?.public && !store.state.serverConfiguration?.ldapEnabled " id="change-password" role="group" aria-labelledby="label-change-password" > <h2 id="label-change-password">Change password</h2> <div class="password-container"> <label for="current-password" class="sr-only"> Enter current password </label> <RevealPassword v-slot:default="slotProps"> <input id="current-password" autocomplete="current-password" :type="slotProps.isVisible ? 'text' : 'password'" name="old_password" class="input" placeholder="Enter current password" /> </RevealPassword> </div> <div class="password-container"> <label for="new-password" class="sr-only"> Enter desired new password </label> <RevealPassword v-slot:default="slotProps"> <input id="new-password" :type="slotProps.isVisible ? 'text' : 'password'" name="new_password" autocomplete="new-password" class="input" placeholder="Enter desired new password" /> </RevealPassword> </div> <div class="password-container"> <label for="new-password-verify" class="sr-only"> Repeat new password </label> <RevealPassword v-slot:default="slotProps"> <input id="new-password-verify" :type="slotProps.isVisible ? 'text' : 'password'" name="verify_password" autocomplete="new-password" class="input" placeholder="Repeat new password" /> </RevealPassword> </div> <div v-if="passwordChangeStatus && passwordChangeStatus.success" class="feedback success" > Successfully updated your password </div> <div v-else-if="passwordChangeStatus && passwordChangeStatus.error" class="feedback error" > {{ passwordErrors[passwordChangeStatus.error] }} </div> <div> <button type="submit" class="btn" @click.prevent="changePassword"> Change password </button> </div> </div> <div v-if="!store.state.serverConfiguration?.public" class="session-list" role="group"> <h2>Sessions</h2> <h3>Current session</h3> <Session v-if="currentSession" :session="currentSession" /> <template v-if="activeSessions.length > 0"> <h3>Active sessions</h3> <Session v-for="session in activeSessions" :key="session.token" :session="session" /> </template> <h3>Other sessions</h3> <p v-if="store.state.sessions.length === 0">Loading…</p> <p v-else-if="otherSessions.length === 0"> <em>You are not currently logged in to any other device.</em> </p> <Session v-for="session in otherSessions" v-else :key="session.token" :session="session" /> </div> </div> </template> <script lang="ts"> import socket from "../../js/socket"; import RevealPassword from "../RevealPassword.vue"; import Session from "../Session.vue"; import {computed, defineComponent, onMounted, PropType, ref} from "vue"; import {useStore} from "../../js/store"; export default defineComponent({ name: "UserSettings", components: { RevealPassword, Session, }, props: { settingsForm: { type: Object as PropType<HTMLFormElement>, required: true, }, }, setup(props) { const store = useStore(); const passwordErrors = { missing_fields: "Please enter a new password", password_mismatch: "Both new password fields must match", password_incorrect: "The current password field does not match your account password", update_failed: "Failed to update your password", }; const passwordChangeStatus = ref<{ success: boolean; error: keyof typeof passwordErrors; }>(); const currentSession = computed(() => { return store.state.sessions.find((item) => item.current); }); const activeSessions = computed(() => { return store.state.sessions.filter((item) => !item.current && item.active > 0); }); const otherSessions = computed(() => { return store.state.sessions.filter((item) => !item.current && !item.active); }); onMounted(() => { socket.emit("sessions:get"); }); const changePassword = () => { const allFields = new FormData(props.settingsForm); const data = { old_password: allFields.get("old_password"), new_password: allFields.get("new_password"), verify_password: allFields.get("verify_password"), }; if (!data.old_password || !data.new_password || !data.verify_password) { passwordChangeStatus.value = { success: false, error: "missing_fields", }; return; } if (data.new_password !== data.verify_password) { passwordChangeStatus.value = { success: false, error: "password_mismatch", }; return; } socket.once("change-password", (response) => { // TODO type passwordChangeStatus.value = response as any; }); socket.emit("change-password", data); }; return { store, passwordChangeStatus, passwordErrors, currentSession, activeSessions, otherSessions, changePassword, }; }, }); </script>