Hangar/frontend/pages/_user.vue

170 lines
6.6 KiB
Vue
Raw Normal View History

2021-03-15 01:16:16 +08:00
<template>
<div>
<v-row>
2021-03-22 11:43:46 +08:00
<v-col class="flex-grow-0">
2021-03-15 01:16:16 +08:00
<UserAvatar :username="user.name" :avatar-url="$util.avatarUrl(user.name)" :clazz="avatarClazz"></UserAvatar>
</v-col>
2021-03-22 11:43:46 +08:00
<v-col>
<h1>
{{ user.name }}
<v-tooltip bottom>
<template #activator="{ on }">
<v-btn icon small color="info" class="ml-n2" :href="$util.forumUrl(user.name)" v-on="on">
<v-icon small>mdi-open-in-new</v-icon>
</v-btn>
</template>
<span>{{ $t('author.viewOnForums') }}</span>
</v-tooltip>
</h1>
<div class="text--secondary">
<span v-if="user.tagline">{{ user.tagline }}</span>
<span v-else-if="canEditCurrent">{{ $t('author.addTagline') }}</span>
<HangarModal
v-if="canEditCurrent"
ref="taglineModal"
:title="$t('author.editTagline')"
:submit-label="$t('general.change')"
:submit-disabled="taglineForm === user.tagline"
:submit="changeTagline"
@open="taglineForm = user.tagline"
>
<template #activator="{ on, attrs }">
<v-btn icon small color="warning" v-bind="attrs" v-on="on">
<v-icon small>mdi-pencil</v-icon>
</v-btn>
</template>
<v-text-field
v-model.trim="taglineForm"
:counter="validations.userTagline.max"
:label="$t('author.taglineLabel')"
:rules="[$util.$vc.require($t('author.taglineLabel')), $util.$vc.maxLength(validations.userTagline.max)]"
/>
<template #other-btns>
<v-btn color="info" text :loading="loading.resetTagline" :disabled="!user.tagline" @click.stop="resetTagline">
{{ $t('general.reset') }}
</v-btn>
</template>
</HangarModal>
2021-03-15 01:16:16 +08:00
</div>
2021-03-22 11:43:46 +08:00
<v-tooltip v-for="btn in buttons" :key="btn.name" bottom>
<template #activator="{ on }">
<v-btn icon small :href="btn.external ? btn.url : undefined" :to="btn.external ? undefined : btn.url" :nuxt="!btn.external" v-on="on">
<v-icon small>{{ btn.icon }}</v-icon>
</v-btn>
</template>
<span>{{ $t(`author.tooltips.${btn.name}`) }}</span>
</v-tooltip>
2021-03-15 01:16:16 +08:00
</v-col>
<v-spacer />
2021-03-22 11:43:46 +08:00
<v-col cols="auto">
<v-sheet rounded color="lighten-1" class="text--secondary px-3 py-2 text-right">
<span>{{ $tc('author.numProjects', user.projectCount, [user.projectCount]) }}</span>
<br />
<span>{{ $t('author.memberSince', [$util.prettyDate(user.joinDate)]) }}</span>
<br />
<span v-for="role in user.roles" :key="role.roleId" :style="{ backgroundColor: role.color }" class="user-role-badge">{{ role.title }}</span>
</v-sheet>
2021-03-15 01:16:16 +08:00
</v-col>
</v-row>
2021-03-22 11:43:46 +08:00
<v-divider class="my-3" />
2021-03-18 04:41:18 +08:00
<NuxtChild :user="user" />
2021-03-15 01:16:16 +08:00
</div>
</template>
<script lang="ts">
2021-03-21 10:06:09 +08:00
import { Component } from 'nuxt-property-decorator';
2021-03-18 04:41:18 +08:00
import { Context } from '@nuxt/types';
2021-03-21 10:06:09 +08:00
import { User } from 'hangar-api';
2021-03-20 15:24:17 +08:00
import UserAvatar from '../components/users/UserAvatar.vue';
2021-03-18 07:55:48 +08:00
import HangarModal from '~/components/modals/HangarModal.vue';
2021-03-21 10:06:09 +08:00
import { HangarComponent } from '~/components/mixins';
2021-03-15 01:16:16 +08:00
interface Button {
2021-03-18 06:23:37 +08:00
icon: string;
2021-03-15 01:16:16 +08:00
action?: Function;
2021-03-18 06:23:37 +08:00
external?: boolean;
url: string;
name: string;
2021-03-15 01:16:16 +08:00
}
@Component({
2021-03-18 07:55:48 +08:00
components: { HangarModal, UserAvatar },
2021-03-15 01:16:16 +08:00
})
2021-03-21 10:06:09 +08:00
export default class UserParentPage extends HangarComponent {
user!: User;
2021-03-18 07:55:48 +08:00
taglineForm: string | null = null;
loading = {
resetTagline: false,
};
2021-03-15 01:16:16 +08:00
get buttons(): Button[] {
const buttons = [] as Button[];
2021-03-18 06:23:37 +08:00
buttons.push({ icon: 'mdi-cog', url: `${process.env.authHost}/accounts/settings`, external: true, name: 'settings' });
buttons.push({ icon: 'mdi-lock-open-outline', url: '', name: 'lock' });
buttons.push({ icon: 'mdi-lock-outline', url: '', name: 'unlock' });
buttons.push({ icon: 'mdi-key', url: '/' + this.user.name + '/settings/api-keys', name: 'apiKeys' });
buttons.push({ icon: 'mdi-calendar', url: '', name: 'activity' });
buttons.push({ icon: 'mdi-wrench', url: '', name: 'admin' });
2021-03-15 01:16:16 +08:00
return buttons;
}
2021-03-18 04:41:18 +08:00
2021-03-18 07:55:48 +08:00
get canEditCurrent() {
2021-03-21 10:06:09 +08:00
return this.$perms.canDoManualValueChanges || this.user.name === this.currentUser.name /* || org perms */;
2021-03-18 07:55:48 +08:00
}
2021-03-18 04:41:18 +08:00
get avatarClazz(): String {
return 'user-avatar-md';
// todo check org an add 'organization-avatar'
}
2021-03-18 07:55:48 +08:00
changeTagline() {
return this.$api
2021-03-21 10:06:09 +08:00
.requestInternal(`users/${this.user.name}/settings/tagline`, true, 'post', {
2021-03-18 07:55:48 +08:00
content: this.taglineForm,
})
.then(() => {
this.$nuxt.refresh();
})
.catch(this.$util.handleRequestError);
}
$refs!: {
taglineModal: HangarModal;
};
resetTagline() {
this.loading.resetTagline = true;
this.$api
2021-03-21 10:06:09 +08:00
.requestInternal(`users/${this.user.name}/settings/resetTagline`, true, 'post')
2021-03-18 07:55:48 +08:00
.then(() => {
this.$refs.taglineModal.close();
this.$nuxt.refresh();
})
.catch(this.$util.handleRequestError)
.finally(() => {
this.loading.resetTagline = false;
});
}
2021-03-18 04:41:18 +08:00
async asyncData({ $api, $util, params }: Context) {
2021-03-21 10:06:09 +08:00
const user = await $api.request<User>(`users/${params.user}`, false).catch<any>($util.handlePageRequestError);
2021-03-18 04:41:18 +08:00
if (typeof user === 'undefined') return;
return { user };
}
2021-03-15 01:16:16 +08:00
}
</script>
2021-03-22 11:43:46 +08:00
<style lang="scss" scoped>
@import '~vuetify/src/styles/styles';
.user-role-badge {
padding: 2px 4px;
border-radius: 2px;
color: map-deep-get($material-dark, 'text', 'primary');
&:not(:last-child) {
margin-right: 5px;
}
}
</style>