Skip to content

Commit

Permalink
[MS] Client statistics page
Browse files Browse the repository at this point in the history
  • Loading branch information
Ironicbay committed Aug 2, 2024
1 parent 97723f5 commit a74f5f9
Show file tree
Hide file tree
Showing 8 changed files with 361 additions and 10 deletions.
22 changes: 22 additions & 0 deletions client/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,28 @@
},
"billingDetails": {
"retrieveFailed": "Failed to retrieve billing details."
},
"statistics": {
"titles": {
"stats": "Statistics",
"users": "Users",
"active": "active",
"revoked": "revoked",
"storage": "Used storage"
},
"admin": "Administrator",
"standard": "Standard",
"outsider": "Outsider",
"admins": "Administrators",
"standards": "Standards",
"outsiders": "Outsiders",
"consumptionDetail": "Consumption detail",
"nonPaying": "Non paying",
"paying": "charged",
"free": "free",
"ofWhich": "of which",
"data": "data",
"metadata": "metadata"
}
}
}
22 changes: 22 additions & 0 deletions client/src/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,28 @@
},
"billingDetails": {
"retrieveFailed": "Impossible de récupérer les informations de paiement."
},
"statistics": {
"titles": {
"stats": "Statistiques",
"users": "Utilisateurs",
"active": "actifs",
"revoked": "révoqués",
"storage": "Stockage utilisé"
},
"admin": "Administrateur",
"standard": "Standard",
"outsider": "Externe",
"admins": "Administrateurs",
"standards": "Standards",
"outsiders": "Externes",
"consumptionDetail": "Détail des consommations",
"nonPaying": "Non payant",
"paying": "payant",
"free": "gratuit",
"ofWhich": "dont",
"data": "de données",
"metadata": "métadonnées"
}
}
}
9 changes: 8 additions & 1 deletion client/src/services/bms/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,15 @@ async function getOrganizationStats(token: AuthenticationToken, query: Organizat
data: {
type: DataType.OrganizationStats,
dataSize: axiosResponse.data.data_size,
metadataSize: axiosResponse.data.metadata_size,
users: axiosResponse.data.users ?? 0,
activeUsers: axiosResponse.data.active_users ?? 0,
adminUsersDetail: axiosResponse.data.users_per_profile_detail.ADMIN,
standardUsersDetail: axiosResponse.data.users_per_profile_detail.STANDARD,
outsiderUsersDetail: axiosResponse.data.users_per_profile_detail.OUTSIDER,
freeSliceSize: axiosResponse.data.free_slice_size ?? 1024 * 1024 * 1024 * 200, // arbitrary value
payingSliceSize: axiosResponse.data.paying_slice_size ?? 1024 * 1024 * 1024 * 100, // arbitrary value
status: axiosResponse.data.status,
users: axiosResponse.data.users,
},
};
});
Expand Down
15 changes: 14 additions & 1 deletion client/src/services/bms/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,25 @@ interface ListOrganizationsResultData {
organizations: Array<BmsOrganization>;
}

interface UserPerProfileDetails {
active: number;
revoked: number;
}

interface OrganizationStatsResultData {
type: DataType.OrganizationStats;
realms?: number;
dataSize: number;
metadataSize: number;
users: number;
activeUsers: number;
adminUsersDetail: UserPerProfileDetails;
standardUsersDetail: UserPerProfileDetails;
outsiderUsersDetail: UserPerProfileDetails;
freeSliceSize: number;
payingSliceSize: number;
// Add more status
status: 'ok';
users: number;
}

interface OrganizationStatusResultData {
Expand Down
195 changes: 191 additions & 4 deletions client/src/views/client-area/StatisticsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,217 @@

<template>
<div>
<h1>STATISTICS</h1>
<h1>{{ $msTranslate('clientArea.statistics.titles.stats') }}</h1>
<div v-if="stats">
{{ stats }}
<div>
<div class="subtitle">
<h2>{{ $msTranslate('clientArea.statistics.titles.users') }}</h2>
<ion-text>{{ $msTranslate('clientArea.statistics.titles.active') }}</ion-text>
</div>
<div class="card-row">
<ion-card class="card card-active">
{{ stats.adminUsersDetail.active }}
{{ $msTranslate(stats.adminUsersDetail.active > 1 ? 'clientArea.statistics.admins' : 'clientArea.statistics.admin') }}
</ion-card>
<ion-card class="card card-active">
{{ stats.standardUsersDetail.active }}
{{ $msTranslate(stats.standardUsersDetail.active > 1 ? 'clientArea.statistics.standards' : 'clientArea.statistics.standard') }}
</ion-card>
<ion-card class="card card-active">
{{ stats.outsiderUsersDetail.active }}
{{ $msTranslate(stats.outsiderUsersDetail.active > 1 ? 'clientArea.statistics.outsiders' : 'clientArea.statistics.outsider') }}
</ion-card>
</div>
</div>
<div>
<div class="subtitle">
<h2>{{ $msTranslate('clientArea.statistics.titles.users') }}</h2>
<ion-text>{{ $msTranslate('clientArea.statistics.titles.revoked') }}</ion-text>
</div>
<div class="card-row">
<ion-card class="card card-revoked">
{{ stats.adminUsersDetail.revoked }}
{{ $msTranslate(stats.adminUsersDetail.revoked > 1 ? 'clientArea.statistics.admins' : 'clientArea.statistics.admin') }}
</ion-card>
<ion-card class="card card-revoked">
{{ stats.standardUsersDetail.revoked }}
{{ $msTranslate(stats.standardUsersDetail.revoked > 1 ? 'clientArea.statistics.standards' : 'clientArea.statistics.standard') }}
</ion-card>
<ion-card class="card card-revoked">
{{ stats.outsiderUsersDetail.revoked }}
{{ $msTranslate(stats.outsiderUsersDetail.revoked > 1 ? 'clientArea.statistics.outsiders' : 'clientArea.statistics.outsider') }}
</ion-card>
</div>
</div>
<div class="bottom-part">
<h2>{{ $msTranslate('clientArea.statistics.titles.storage') }}</h2>
<div class="bottom-part-data">
<div>
<h2>{{ $msTranslate(formatFileSize(totalData)) }}</h2>
{{ $msTranslate('clientArea.statistics.ofWhich') }}
{{ $msTranslate(formatFileSize(stats.dataSize)) }} {{ $msTranslate('clientArea.statistics.data') }}
{{ $msTranslate(formatFileSize(stats.metadataSize)) }} {{ $msTranslate('clientArea.statistics.metadata') }}
</div>
<div>
{{ $msTranslate('clientArea.statistics.consumptionDetail') }}
<div v-if="firstBarData">
<div id="firstBar">
<ion-progress-bar :value="firstBarData.progress" />
{{ $msTranslate(formatFileSize(firstBarData.amount)) }}
{{ firstBarData.percentage.toFixed(0) }}%
</div>
{{ $msTranslate('clientArea.statistics.free') }}
</div>
<div v-if="secondBarData">
<div id="secondBar">
{{ $msTranslate(formatFileSize(secondBarData.amount)) }}
{{ secondBarData.multiplier > 0 ? `x${secondBarData.multiplier}` : '' }}
{{ secondBarData.percentage.toFixed(0) }}%
<ion-progress-bar :value="secondBarData.progress" />
</div>
<div
v-if="thirdBarData"
id="thirdBar"
>
{{ $msTranslate(formatFileSize(thirdBarData.amount)) }}
{{ thirdBarData.percentage.toFixed(0) }}%
<ion-progress-bar :value="thirdBarData.progress" />
</div>
{{ $msTranslate('clientArea.statistics.paying') }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { BmsAccessInstance, DataType, BmsOrganization, OrganizationStatsResultData } from '@/services/bms';
import { onMounted, ref } from 'vue';
import { IonCard, IonProgressBar, IonText } from '@ionic/vue';
import { formatFileSize } from '@/common/file';
interface ProgressBarData {
amount: number;
percentage: number;
progress: number;
multiplier: number;
}
const props = defineProps<{
organization: BmsOrganization;
}>();
const stats = ref<OrganizationStatsResultData | undefined>(undefined);
const stats = ref<OrganizationStatsResultData>();
const totalData = ref<number>(0);
const freeSliceSize = ref<number>(0);
const payingSliceSize = ref<number>(0);
const firstBarData = ref<ProgressBarData>();
const secondBarData = ref<ProgressBarData>();
const thirdBarData = ref<ProgressBarData>();
function getFirstBarData(): ProgressBarData {
return {
amount: totalData.value > freeSliceSize.value ? freeSliceSize.value : totalData.value,
percentage: totalData.value > freeSliceSize.value ? 100 : (totalData.value * 100) / freeSliceSize.value,
progress: totalData.value > freeSliceSize.value ? 1 : totalData.value / freeSliceSize.value,
multiplier: 0,
};
}
function getSecondBarData(): ProgressBarData | undefined {
if (totalData.value < freeSliceSize.value) {
return undefined;
}
const truncatedAmount: number = (totalData.value - freeSliceSize.value) % payingSliceSize.value;
const multiplier = Math.floor((totalData.value - freeSliceSize.value) / payingSliceSize.value);
if (multiplier > 0) {
// Second bar full, third bar will exist
return {
amount: payingSliceSize.value,
percentage: 100,
progress: 1,
multiplier: multiplier,
};
}
// Second bar not full, third bar will not be defined
return {
amount: truncatedAmount,
percentage: (truncatedAmount * 100) / payingSliceSize.value,
progress: truncatedAmount / payingSliceSize.value,
multiplier: 0,
};
}
function getThirdBarData(): ProgressBarData | undefined {
if (totalData.value < freeSliceSize.value + payingSliceSize.value) {
// First two bars are enough
return undefined;
}
const truncatedAmount: number = (totalData.value - freeSliceSize.value) % payingSliceSize.value;
return {
amount: truncatedAmount,
percentage: (truncatedAmount * 100) / payingSliceSize.value,
progress: truncatedAmount / payingSliceSize.value,
multiplier: 0,
};
}
onMounted(async () => {
const response = await BmsAccessInstance.get().getOrganizationStats(props.organization.bmsId);
if (!response.isError && response.data && response.data.type === DataType.OrganizationStats) {
stats.value = response.data;
totalData.value = stats.value.dataSize + stats.value.metadataSize;
freeSliceSize.value = stats.value.freeSliceSize;
payingSliceSize.value = stats.value.payingSliceSize;
firstBarData.value = getFirstBarData();
secondBarData.value = getSecondBarData();
thirdBarData.value = getThirdBarData();
}
});
</script>

<style scoped lang="scss"></style>
<style scoped lang="scss">
.subtitle {
color: var(--parsec-color-light-primary-700);
}
.bottom-part {
background-color: var(--parsec-color-light-secondary-premiere);
display: flex;
flex-direction: column;
&-data {
display: flex;
flex-direction: row;
}
}
.card {
width: 186px;
height: 76.1px;
&-active {
background-color: var(--parsec-color-light-primary-30);
}
&-revoked {
background-color: var(--parsec-color-light-secondary-background);
}
&-row {
display: flex;
flex-direction: row;
}
}
</style>
Loading

0 comments on commit a74f5f9

Please sign in to comment.