Initial work on the profile view
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<InputDialog ref="inputDialog" />
|
||||
|
||||
<h1>{{ $t('profile.title') }}</h1>
|
||||
<Card>
|
||||
<div style="display: flex;">
|
||||
<div style="width: 150px;">
|
||||
<div class="settings-avatar" :style="`background-image: url('${user.avatarUrl}');`" ng-click="avatarChange.showChangeAvatar()">
|
||||
<i class="picture-edit-indicator fa fa-pencil-alt"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex-grow: 1;">
|
||||
<table style="width: 100%; max-width: 1024px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ $t('main.username') }}</td>
|
||||
<td class="text-right" style="vertical-align: top;">
|
||||
{{ user.username }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ $t('main.displayName') }}</td>
|
||||
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
|
||||
{{ user.displayName }} <Button small tool outline @click="onChangeDisplayName(user.displayName)" v-show="!user.source && !config.profileLocked" icon="fa fa-edit text-small"></Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ $t('profile.primaryEmail') }}</td>
|
||||
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
|
||||
{{ user.email }} <a href="" ng-click="emailChange.show()" ng-hide="user.source || config.profileLocked"><i class="fa fa-edit text-small"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: top;">{{ $t('profile.passwordRecoveryEmail') }}</td>
|
||||
<td class="text-right" style="vertical-align: top; white-space: nowrap;">
|
||||
{{ user.fallbackEmail }} <a href="" ng-click="fallbackEmailChange.show()" ng-hide="user.source || config.profileLocked"><i class="fa fa-edit text-small"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-hide="user.source">
|
||||
<td colspan="2" class="text-right">
|
||||
<a href="" ng-click="sendPasswordReset()">{{ $t('profile.passwordResetAction') }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td colspan="2"> </td></tr>
|
||||
<tr>
|
||||
<td class="text-muted" style="vertical-align: middle;">{{ $t('profile.language') }}</td>
|
||||
<td class="text-right" style="vertical-align: middle;">
|
||||
<Dropdown small v-model="language" :options="languages" option-label="display" option-key="id" @select="onSelectLanguage"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref, onMounted, useTemplateRef } from 'vue';
|
||||
import { Button, Dropdown, InputDialog } from 'pankow';
|
||||
import Card from './Card.vue';
|
||||
import ProfileModel from '../models/ProfileModel.js';
|
||||
import CloudronModel from '../models/CloudronModel.js';
|
||||
|
||||
const i18n = useI18n();
|
||||
const t = i18n.t;
|
||||
|
||||
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? import.meta.env.VITE_API_ORIGIN : window.location.origin;
|
||||
|
||||
const profileModel = ProfileModel.create(API_ORIGIN, localStorage.token);
|
||||
const cloudronModel = CloudronModel.create(API_ORIGIN, localStorage.token);
|
||||
|
||||
// TODO what is this?
|
||||
const config = ref({});
|
||||
|
||||
const user = ref({});
|
||||
const languages = ref([]);
|
||||
const language = ref('');
|
||||
const inputDialog = useTemplateRef('inputDialog');
|
||||
|
||||
onMounted(async () => {
|
||||
user.value = await profileModel.get();
|
||||
|
||||
const langs = await cloudronModel.languages();
|
||||
languages.value = langs.map(l => {
|
||||
return {
|
||||
id: l,
|
||||
display: t(`lang.${l}`)
|
||||
};
|
||||
}).sort((a, b) => {
|
||||
return a.display.localeCompare(b.display);
|
||||
});
|
||||
|
||||
const usedLang = window.localStorage.NG_TRANSLATE_LANG_KEY || 'en';
|
||||
language.value = languages.value.find(l => l.id === usedLang).id;
|
||||
});
|
||||
|
||||
async function onSelectLanguage(lang) {
|
||||
window.localStorage.NG_TRANSLATE_LANG_KEY = lang.id;
|
||||
|
||||
const error = await profileModel.setLanguage(lang.id);
|
||||
if (error) console.error('Failed to set language', error);
|
||||
else window.location.reload();
|
||||
|
||||
// TODO dynamically change lange instead of reloading
|
||||
}
|
||||
|
||||
async function onChangeDisplayName(currentDisplayName) {
|
||||
const displayName = await inputDialog.value.prompt({
|
||||
message: 'Display Name',
|
||||
modal: false,
|
||||
value: currentDisplayName,
|
||||
confirmStyle: 'success',
|
||||
confirmLabel: 'Save',
|
||||
rejectLabel: 'Close'
|
||||
});
|
||||
|
||||
if (!displayName) return;
|
||||
|
||||
const error = await profileModel.setDisplayName(displayName);
|
||||
if (error) return console.error('Failed to set displayName', error);
|
||||
|
||||
user.value = await profileModel.get();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.content {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user