Show user and actors media usage in admin
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
51e760485b
commit
6c0a4c0d7d
@ -10,6 +10,7 @@ export const FETCH_PERSON = gql`
|
||||
summary
|
||||
preferredUsername
|
||||
suspended
|
||||
mediaSize
|
||||
avatar {
|
||||
id
|
||||
name
|
||||
@ -51,6 +52,7 @@ export const GET_PERSON = gql`
|
||||
summary
|
||||
preferredUsername
|
||||
suspended
|
||||
mediaSize
|
||||
avatar {
|
||||
id
|
||||
name
|
||||
|
@ -84,6 +84,7 @@ export const GROUP_FIELDS_FRAGMENTS = gql`
|
||||
id
|
||||
url
|
||||
}
|
||||
mediaSize
|
||||
organizedEvents(
|
||||
afterDatetime: $afterDateTime
|
||||
beforeDatetime: $beforeDateTime
|
||||
|
@ -200,6 +200,7 @@ export const GET_USER = gql`
|
||||
currentSignInAt
|
||||
locale
|
||||
disabled
|
||||
mediaSize
|
||||
defaultActor {
|
||||
id
|
||||
}
|
||||
|
@ -799,5 +799,6 @@
|
||||
"Mobilizon uses a system of profiles to compartiment your activities. You will be able to create as many profiles as you want.": "Mobilizon uses a system of profiles to compartiment your activities. You will be able to create as many profiles as you want.",
|
||||
"Mobilizon is a federated software, meaning you can interact - depending on your admin's federation settings - with content from other instances, such as joining groups or events that were created elsewhere.": "Mobilizon is a federated software, meaning you can interact - depending on your admin federation settings - with content from other instances, such as joining groups or events that were created elsewhere.",
|
||||
"This instance, <b>{instanceName} ({domain})</b>, hosts your profile, so remember its name.": "This instance, <b>{instanceName} ({domain})</b>, hosts your profile, so remember its name.",
|
||||
"If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:": "If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:"
|
||||
"If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:": "If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:",
|
||||
"Uploaded media size": "Uploaded media size"
|
||||
}
|
||||
|
@ -887,5 +887,6 @@
|
||||
"Mobilizon uses a system of profiles to compartiment your activities. You will be able to create as many profiles as you want.": "Mobilizon utilise un système de profils pour compartimenter vos activités. Vous pourrez créer autant de profils que vous voulez.",
|
||||
"Mobilizon is a federated software, meaning you can interact - depending on your admin's federation settings - with content from other instances, such as joining groups or events that were created elsewhere.": "Mobilizon est un logiciel fédéré, ce qui signifie que vous pouvez interagir - en fonction des paramètres de fédération de votre administrateur·ice - avec du contenu d'autres instances, comme par exemple rejoindre des groupes ou des événements ayant été créés ailleurs.",
|
||||
"This instance, <b>{instanceName} ({domain})</b>, hosts your profile, so remember its name.": "Cette instance, <b>{instanceName} ({domain})</b>, héberge votre profil, donc notez bien son nom.",
|
||||
"If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:": "Si l'on vous demande votre identité fédérée, elle est composée de votre nom d'utilisateur·ice et de votre instance. Par exemple, l'identité fédérée de votre premier profil est :"
|
||||
"If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:": "Si l'on vous demande votre identité fédérée, elle est composée de votre nom d'utilisateur·ice et de votre instance. Par exemple, l'identité fédérée de votre premier profil est :",
|
||||
"Uploaded media size": "Taille des médias téléversés"
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ export interface IActor {
|
||||
url: string;
|
||||
name: string;
|
||||
domain: string | null;
|
||||
mediaSize: number;
|
||||
summary: string;
|
||||
preferredUsername: string;
|
||||
suspended: boolean;
|
||||
@ -30,6 +31,8 @@ export class Actor implements IActor {
|
||||
|
||||
domain: string | null = null;
|
||||
|
||||
mediaSize = 0;
|
||||
|
||||
name = "";
|
||||
|
||||
preferredUsername = "";
|
||||
|
@ -39,6 +39,7 @@ export interface IUser extends ICurrentUser {
|
||||
actors: IPerson[];
|
||||
disabled: boolean;
|
||||
participations: Paginate<IParticipant>;
|
||||
mediaSize: number;
|
||||
drafts: IEvent[];
|
||||
settings: IUserSettings;
|
||||
locale: string;
|
||||
|
@ -18,4 +18,17 @@ function localeShortWeekDayNames(): string[] {
|
||||
return weekDayNames;
|
||||
}
|
||||
|
||||
export { localeMonthNames, localeShortWeekDayNames };
|
||||
// https://stackoverflow.com/a/18650828/10204399
|
||||
function formatBytes(bytes: number, decimals = 2): string {
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
export { localeMonthNames, localeShortWeekDayNames, formatBytes };
|
||||
|
@ -198,6 +198,7 @@
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from "vue-property-decorator";
|
||||
import { GET_GROUP, REFRESH_PROFILE } from "@/graphql/group";
|
||||
import { formatBytes } from "@/utils/datetime";
|
||||
import { SUSPEND_PROFILE, UNSUSPEND_PROFILE } from "../../graphql/actor";
|
||||
import { IGroup, MemberRole } from "../../types/actor";
|
||||
import { usernameWithDomain, IActor } from "../../types/actor/actor.model";
|
||||
@ -258,6 +259,10 @@ export default class AdminGroupProfile extends Vue {
|
||||
key: this.$t("Domain") as string,
|
||||
value: (this.group.domain ? this.group.domain : this.$t("Local")) as string,
|
||||
},
|
||||
{
|
||||
key: this.$i18n.t("Uploaded media size") as string,
|
||||
value: formatBytes(this.group.mediaSize),
|
||||
},
|
||||
];
|
||||
return res;
|
||||
}
|
||||
|
@ -126,11 +126,11 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from "vue-property-decorator";
|
||||
import { formatBytes } from "@/utils/datetime";
|
||||
import { GET_PERSON, SUSPEND_PROFILE, UNSUSPEND_PROFILE } from "../../graphql/actor";
|
||||
import { IPerson } from "../../types/actor";
|
||||
import { usernameWithDomain } from "../../types/actor/actor.model";
|
||||
import RouteName from "../../router/name";
|
||||
import { IEvent } from "../../types/event.model";
|
||||
import ActorCard from "../../components/Account/ActorCard.vue";
|
||||
|
||||
const EVENTS_PER_PAGE = 10;
|
||||
@ -171,9 +171,9 @@ export default class AdminProfile extends Vue {
|
||||
|
||||
participationsPage = 1;
|
||||
|
||||
get metadata(): Array<object> {
|
||||
get metadata(): Array<Record<string, unknown>> {
|
||||
if (!this.person) return [];
|
||||
const res: object[] = [
|
||||
const res: Record<string, unknown>[] = [
|
||||
{
|
||||
key: this.$t("Status") as string,
|
||||
value: this.person.suspended ? this.$t("Suspended") : this.$t("Active"),
|
||||
@ -182,6 +182,10 @@ export default class AdminProfile extends Vue {
|
||||
key: this.$t("Domain") as string,
|
||||
value: this.person.domain ? this.person.domain : this.$t("Local"),
|
||||
},
|
||||
{
|
||||
key: this.$i18n.t("Uploaded media size"),
|
||||
value: formatBytes(this.person.mediaSize),
|
||||
},
|
||||
];
|
||||
if (!this.person.domain && this.person.user) {
|
||||
res.push({
|
||||
@ -193,7 +197,7 @@ export default class AdminProfile extends Vue {
|
||||
return res;
|
||||
}
|
||||
|
||||
async suspendProfile() {
|
||||
async suspendProfile(): Promise<void> {
|
||||
this.$apollo.mutate<{ suspendProfile: { id: string } }>({
|
||||
mutation: SUSPEND_PROFILE,
|
||||
variables: {
|
||||
@ -229,7 +233,7 @@ export default class AdminProfile extends Vue {
|
||||
});
|
||||
}
|
||||
|
||||
async unsuspendProfile() {
|
||||
async unsuspendProfile(): Promise<void> {
|
||||
const profileID = this.id;
|
||||
this.$apollo.mutate<{ unsuspendProfile: { id: string } }>({
|
||||
mutation: UNSUSPEND_PROFILE,
|
||||
@ -249,7 +253,7 @@ export default class AdminProfile extends Vue {
|
||||
});
|
||||
}
|
||||
|
||||
async onOrganizedEventsPageChange(page: number) {
|
||||
async onOrganizedEventsPageChange(page: number): Promise<void> {
|
||||
this.organizedEventsPage = page;
|
||||
await this.$apollo.queries.person.fetchMore({
|
||||
variables: {
|
||||
@ -274,7 +278,7 @@ export default class AdminProfile extends Vue {
|
||||
});
|
||||
}
|
||||
|
||||
async onParticipationsPageChange(page: number) {
|
||||
async onParticipationsPageChange(page: number): Promise<void> {
|
||||
this.participationsPage = page;
|
||||
await this.$apollo.queries.person.fetchMore({
|
||||
variables: {
|
||||
|
@ -26,7 +26,7 @@
|
||||
</nav>
|
||||
<table v-if="metadata.length > 0" class="table is-fullwidth">
|
||||
<tbody>
|
||||
<tr v-for="{ key, value, link, elements } in metadata" :key="key">
|
||||
<tr v-for="{ key, value, link, elements, type } in metadata" :key="key">
|
||||
<td>{{ key }}</td>
|
||||
<td v-if="elements && elements.length > 0">
|
||||
<ul v-for="{ value, link: elementLink, active } in elements" :key="value">
|
||||
@ -46,6 +46,9 @@
|
||||
{{ value }}
|
||||
</router-link>
|
||||
</td>
|
||||
<td v-else-if="type == 'code'">
|
||||
<code>{{ value }}</code>
|
||||
</td>
|
||||
<td v-else>{{ value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -60,6 +63,7 @@
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from "vue-property-decorator";
|
||||
import { Route } from "vue-router";
|
||||
import { formatBytes } from "@/utils/datetime";
|
||||
import { GET_USER, SUSPEND_USER } from "../../graphql/user";
|
||||
import { usernameWithDomain } from "../../types/actor/actor.model";
|
||||
import RouteName from "../../router/name";
|
||||
@ -139,11 +143,16 @@ export default class AdminUserProfile extends Vue {
|
||||
{
|
||||
key: this.$i18n.t("Last IP adress"),
|
||||
value: this.user.currentSignInIp || this.$t("Unknown"),
|
||||
type: "code",
|
||||
},
|
||||
{
|
||||
key: this.$i18n.t("Participations"),
|
||||
value: this.user.participations.total,
|
||||
},
|
||||
{
|
||||
key: this.$i18n.t("Uploaded media size"),
|
||||
value: formatBytes(this.user.mediaSize),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user