Improve profile view and fix app install dialog overflow on mobile
This commit is contained in:
@@ -8,13 +8,16 @@ import { ref, onMounted, useTemplateRef } from 'vue';
|
||||
import { Button, Dropdown, Dialog, InputDialog, TextInput } from 'pankow';
|
||||
import { TOKEN_TYPES } from '../constants.js';
|
||||
import AppPasswords from '../components/AppPasswords.vue';
|
||||
import SettingsItem from '../components/SettingsItem.vue';
|
||||
import Section from '../components/Section.vue';
|
||||
import ApiTokens from '../components/ApiTokens.vue';
|
||||
|
||||
import DashboardModel from '../models/DashboardModel.js';
|
||||
import ProfileModel from '../models/ProfileModel.js';
|
||||
import CloudronModel from '../models/CloudronModel.js';
|
||||
import TokensModel from '../models/TokensModel.js';
|
||||
|
||||
const dashboardModel = DashboardModel.create();
|
||||
const profileModel = ProfileModel.create();
|
||||
const cloudronModel = CloudronModel.create();
|
||||
const tokensModel = TokensModel.create();
|
||||
@@ -129,6 +132,7 @@ async function onPasswordChange() {
|
||||
if (error) return console.error('Failed to change password', error);
|
||||
}
|
||||
|
||||
// TODO
|
||||
async function onPasswordReset() {
|
||||
const error = await profileModel.sendPasswordReset(user.value.email);
|
||||
if (error) return console.error('Failed to reset password:', error);
|
||||
@@ -221,6 +225,10 @@ onMounted(async () => {
|
||||
const usedLang = window.localStorage.NG_TRANSLATE_LANG_KEY || 'en';
|
||||
language.value = languages.value.find(l => l.id === usedLang).id;
|
||||
|
||||
[error, result] = await dashboardModel.config();
|
||||
if (error) return console.error(error);
|
||||
config.value = result;
|
||||
|
||||
[error, result] = await tokensModel.list();
|
||||
if (error) return console.error(error);
|
||||
|
||||
@@ -261,49 +269,68 @@ onMounted(async () => {
|
||||
<Button @click="profileModel.logout()" icon="fa fa-sign-out">{{ $t('main.logout') }}</Button>
|
||||
</template>
|
||||
|
||||
<div style="display: flex;">
|
||||
<div style="display: flex; flex-wrap: wrap">
|
||||
<div style="width: 150px;">
|
||||
<input type="file" ref="avatarFileInput" style="display: none" accept="image/*" @change="onAvatarChanged()"/>
|
||||
<div class="settings-avatar" :style="`background-image: url('${user.avatarUrl}');`" @click="avatarFileInput.click()">
|
||||
<i class="picture-edit-indicator fa fa-pencil-alt"></i>
|
||||
<div class="profile-avatar" :style="`background-image: url('${user.avatarUrl}');`" @click="avatarFileInput.click()">
|
||||
<i class="profile-avatar-edit-indicator fa fa-pencil-alt"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex-grow: 1;">
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-muted">{{ $t('main.username') }}</td>
|
||||
<td style="width: 100px; height: 34px;">{{ user.username }}</td>
|
||||
<td style="width: 32px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">{{ $t('main.displayName') }}</td>
|
||||
<td style="white-space: nowrap;">{{ user.displayName }}</td>
|
||||
<td><Button small tool outline @click="onChangeDisplayName(user.displayName)" v-show="!user.source && !config.profileLocked" icon="fa fa-edit text-small" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">{{ $t('profile.primaryEmail') }}</td>
|
||||
<td style="white-space: nowrap;">{{ user.email }}</td>
|
||||
<td><Button small tool outline @click="onChangeEmail(user.email)" v-show="!user.source && !config.profileLocked" icon="fa fa-edit text-small" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">{{ $t('profile.passwordRecoveryEmail') }}</td>
|
||||
<td style="white-space: nowrap;">{{ user.fallbackEmail }}</td>
|
||||
<td><Button small tool outline @click="onChangeFallbackEmail(user.fallbackEmail)" v-show="!user.source && !config.profileLocked" icon="fa fa-edit text-small" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">{{ $t('profile.language') }}</td>
|
||||
<td colspan="2" class="text-right"><Dropdown small tool outline v-model="language" :options="languages" option-label="display" option-key="id" @select="onSelectLanguage"/></td>
|
||||
</tr>
|
||||
<tr v-show="!user.source">
|
||||
<td colspan="3" class="text-right">
|
||||
<!-- <Button tool @click="onPasswordReset()">{{ $t('profile.passwordResetAction') }}</Button> -->
|
||||
<Button tool @click="onPasswordChange()">{{ $t('profile.changePasswordAction') }}</Button>
|
||||
<Button tool v-show="!user.source && !config.external2FA" @click="user.twoFactorAuthenticationEnabled ? onTwoFADisable() : onOpenTwoFASetupDialog()">{{ $t(user.twoFactorAuthenticationEnabled ? 'profile.disable2FAAction' : 'profile.enable2FAAction') }}</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<SettingsItem>
|
||||
<FormGroup>
|
||||
<label>{{ $t('main.username') }}</label>
|
||||
<div>{{ user.username }}</div>
|
||||
</FormGroup>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem>
|
||||
<FormGroup>
|
||||
<label>{{ $t('main.displayName') }}</label>
|
||||
<div>{{ user.displayName }}</div>
|
||||
</FormGroup>
|
||||
<div style="display: flex; align-items: center">
|
||||
<!-- TODO translate -->
|
||||
<Button tool plain @click="onChangeDisplayName(user.displayName)" v-show="!user.source && !config.profileLocked">Edit</Button>
|
||||
</div>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem>
|
||||
<FormGroup>
|
||||
<label>{{ $t('profile.primaryEmail') }}</label>
|
||||
<div>{{ user.email }}</div>
|
||||
</FormGroup>
|
||||
<div style="display: flex; align-items: center">
|
||||
<!-- TODO translate -->
|
||||
<Button tool plain @click="displayName(user.email)" v-show="!user.source && !config.profileLocked">Edit</Button>
|
||||
</div>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem>
|
||||
<FormGroup>
|
||||
<label>{{ $t('profile.passwordRecoveryEmail') }}</label>
|
||||
<div>{{ user.fallbackEmail || 'unset' }}</div>
|
||||
</FormGroup>
|
||||
<div style="display: flex; align-items: center">
|
||||
<!-- TODO translate -->
|
||||
<Button tool plain @click="onChangeFallbackEmail(user.fallbackEmail)" v-show="!user.source && !config.profileLocked">Edit</Button>
|
||||
</div>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem>
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="font-weight: bold">{{ $t('profile.language') }}</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center">
|
||||
<Dropdown small tool outline v-model="language" :options="languages" option-label="display" option-key="id" @select="onSelectLanguage"/>
|
||||
</div>
|
||||
</SettingsItem>
|
||||
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<!-- <Button tool @click="onPasswordReset()">{{ $t('profile.passwordResetAction') }}</Button> -->
|
||||
<Button tool @click="onPasswordChange()">{{ $t('profile.changePasswordAction') }}</Button>
|
||||
<Button tool v-show="!user.source && !config.external2FA" @click="user.twoFactorAuthenticationEnabled ? onTwoFADisable() : onOpenTwoFASetupDialog()">{{ $t(user.twoFactorAuthenticationEnabled ? 'profile.disable2FAAction' : 'profile.enable2FAAction') }}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
@@ -317,3 +344,37 @@ onMounted(async () => {
|
||||
</Section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.profile-avatar {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
background-position: center;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
border: 1px solid gray;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.profile-avatar-edit-indicator {
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
right: -4px;
|
||||
border-radius: 20px;
|
||||
padding: 5px;
|
||||
color: var(--pankow-text-color);
|
||||
background-color: var(--pankow-input-background-color);
|
||||
transition: all 250ms;
|
||||
}
|
||||
|
||||
.profile-avatar:hover .profile-avatar-edit-indicator {
|
||||
color: white;
|
||||
background: var(--pankow-color-primary);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user