137 lines
5.5 KiB
Vue
137 lines
5.5 KiB
Vue
<script setup lang="ts">
|
||
import type { AdminTenant, TenantStatus } from '../../../composables/mockTenants';
|
||
|
||
defineProps<{
|
||
tenants: AdminTenant[];
|
||
}>();
|
||
|
||
const emit = defineEmits<{
|
||
rowClick: [tenant: AdminTenant];
|
||
impersonate: [tenant: AdminTenant];
|
||
editBalance: [tenant: AdminTenant];
|
||
}>();
|
||
|
||
function formatRub(v: number): string {
|
||
return new Intl.NumberFormat('ru-RU').format(v) + ' ₽';
|
||
}
|
||
|
||
function formatBalance(v: number): string {
|
||
if (v === 0) return '0';
|
||
if (v < 0) return '−' + new Intl.NumberFormat('ru-RU').format(Math.abs(v));
|
||
return new Intl.NumberFormat('ru-RU').format(v);
|
||
}
|
||
|
||
function statusColor(s: TenantStatus): string {
|
||
if (s === 'active') return 'success';
|
||
if (s === 'trial') return 'info';
|
||
if (s === 'overdue') return 'warning';
|
||
return 'error';
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<v-card variant="outlined" class="mt-4 panel">
|
||
<v-data-table
|
||
:items="tenants"
|
||
:headers="[
|
||
{ title: 'Тенант', key: 'name', sortable: false },
|
||
{ title: 'Статус', key: 'status', sortable: false },
|
||
{ title: 'Тариф', key: 'tariff', sortable: false },
|
||
{ title: 'Баланс ₽', key: 'balanceRub', align: 'end', sortable: false },
|
||
{ title: 'Желаем×факт сегодня', key: 'today', align: 'end', sortable: false },
|
||
{ title: 'MRR', key: 'mrrRub', align: 'end', sortable: false },
|
||
{ title: 'Активность', key: 'activitySince', sortable: false },
|
||
{ title: 'Действия', key: 'actions', align: 'end', sortable: false, width: 96 },
|
||
]"
|
||
items-per-page="-1"
|
||
hide-default-footer
|
||
hover
|
||
density="comfortable"
|
||
@click:row="(_e: Event, { item }: { item: AdminTenant }) => emit('rowClick', item)"
|
||
>
|
||
<template #[`item.name`]="{ item }: { item: AdminTenant }">
|
||
<div class="cell-tenant">
|
||
<div class="t-name">{{ item.name }}</div>
|
||
<div class="t-inn text-caption text-medium-emphasis">ИНН {{ item.inn }}</div>
|
||
</div>
|
||
</template>
|
||
<template #[`item.status`]="{ item }: { item: AdminTenant }">
|
||
<v-chip size="small" variant="tonal" :color="statusColor(item.status)">
|
||
{{ item.statusText }}
|
||
</v-chip>
|
||
</template>
|
||
<template #[`item.balanceRub`]="{ item }: { item: AdminTenant }">
|
||
<span
|
||
class="num"
|
||
:class="{ 'text-error': item.balanceRub < 0, 'text-medium-emphasis': item.balanceRub === 0 }"
|
||
>
|
||
{{ formatBalance(item.balanceRub) }}
|
||
</span>
|
||
</template>
|
||
<template #[`item.today`]="{ item }: { item: AdminTenant }">
|
||
<span class="num">{{ item.todayDesired }} × {{ item.todayActual }}</span>
|
||
</template>
|
||
<template #[`item.mrrRub`]="{ item }: { item: AdminTenant }">
|
||
<span v-if="item.mrrRub !== null" class="num">{{ formatRub(item.mrrRub) }}</span>
|
||
<span v-else class="text-medium-emphasis">—</span>
|
||
</template>
|
||
<template #[`item.activitySince`]="{ item }: { item: AdminTenant }">
|
||
<span class="num text-medium-emphasis">{{ item.activitySince }}</span>
|
||
</template>
|
||
<template #[`item.actions`]="{ item }: { item: AdminTenant }">
|
||
<v-tooltip text="Изменить баланс" location="top" aria-label="Изменить баланс">
|
||
<template #activator="{ props: tipProps }">
|
||
<v-btn
|
||
v-bind="tipProps"
|
||
icon="mdi-cash-edit"
|
||
variant="text"
|
||
size="small"
|
||
density="comfortable"
|
||
:aria-label="`Изменить баланс для ${item.name}`"
|
||
:data-testid="`edit-balance-btn-${item.id}`"
|
||
@click.stop="emit('editBalance', item)"
|
||
/>
|
||
</template>
|
||
</v-tooltip>
|
||
<v-tooltip
|
||
text="Войти как клиент (impersonation)"
|
||
location="top"
|
||
aria-label="Войти как клиент (impersonation)"
|
||
>
|
||
<template #activator="{ props: tipProps }">
|
||
<v-btn
|
||
v-bind="tipProps"
|
||
icon="mdi-account-switch"
|
||
variant="text"
|
||
size="small"
|
||
density="comfortable"
|
||
:aria-label="`Войти как клиент (impersonation) для ${item.name}`"
|
||
:disabled="item.status === 'suspended'"
|
||
:data-testid="`impersonate-btn-${item.id}`"
|
||
@click.stop="emit('impersonate', item)"
|
||
/>
|
||
</template>
|
||
</v-tooltip>
|
||
</template>
|
||
</v-data-table>
|
||
</v-card>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.panel {
|
||
background: #fff;
|
||
}
|
||
.num {
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||
font-feature-settings: 'tnum';
|
||
font-weight: 500;
|
||
}
|
||
.cell-tenant {
|
||
padding: 4px 0;
|
||
}
|
||
.t-name {
|
||
font-weight: 500;
|
||
color: #081319;
|
||
}
|
||
</style>
|