chore(front): admin moderation pages
This commit is contained in:
parent
1409bc6761
commit
58f39630e2
|
@ -208,6 +208,7 @@ const launchAction = async () => {
|
||||||
:disabled="checked.length === 0 || undefined"
|
:disabled="checked.length === 0 || undefined"
|
||||||
:is-loading="isLoading"
|
:is-loading="isLoading"
|
||||||
:confirm-color="currentAction?.confirmColor ?? 'success'"
|
:confirm-color="currentAction?.confirmColor ?? 'success'"
|
||||||
|
style="margin-top: 7px;"
|
||||||
:aria-label="labels.performAction"
|
:aria-label="labels.performAction"
|
||||||
:title="t('components.common.ActionTable.modal.performAction.header', { action: currentActionName }, affectedObjectsCount)"
|
:title="t('components.common.ActionTable.modal.performAction.header', { action: currentActionName }, affectedObjectsCount)"
|
||||||
@confirm="launchAction"
|
@confirm="launchAction"
|
||||||
|
|
|
@ -11,7 +11,11 @@ import axios from 'axios'
|
||||||
|
|
||||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||||
import ActionTable from '~/components/common/ActionTable.vue'
|
import ActionTable from '~/components/common/ActionTable.vue'
|
||||||
import Pagination from '~/components/vui/Pagination.vue'
|
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
import Input from '~/components/ui/Input.vue'
|
||||||
|
import Pagination from '~/components/ui/Pagination.vue'
|
||||||
|
|
||||||
import useSmartSearch from '~/composables/navigation/useSmartSearch'
|
import useSmartSearch from '~/composables/navigation/useSmartSearch'
|
||||||
import useOrdering from '~/composables/navigation/useOrdering'
|
import useOrdering from '~/composables/navigation/useOrdering'
|
||||||
|
@ -98,22 +102,21 @@ const labels = computed(() => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<Spacer />
|
||||||
<div class="ui inline form">
|
<div class="ui inline form">
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="ui six wide field">
|
<form @submit.prevent="query = search.value">
|
||||||
<label for="accounts-search">{{ t('components.manage.moderation.AccountsTable.label.search') }}</label>
|
<Input
|
||||||
<form @submit.prevent="query = search.value">
|
id="accounts-search"
|
||||||
<input
|
v-model="query"
|
||||||
id="accounts-search"
|
search
|
||||||
ref="search"
|
:label="t('components.manage.moderation.AccountsTable.label.search')"
|
||||||
name="search"
|
:placeholder="labels.searchPlaceholder"
|
||||||
type="text"
|
/>
|
||||||
:value="query"
|
</form>
|
||||||
:placeholder="labels.searchPlaceholder"
|
<Spacer :size="16" />
|
||||||
>
|
<Layout flex>
|
||||||
</form>
|
<Spacer grow />
|
||||||
</div>
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="accounts-ordering">{{ t('components.manage.moderation.AccountsTable.ordering.label') }}</label>
|
<label for="accounts-ordering">{{ t('components.manage.moderation.AccountsTable.ordering.label') }}</label>
|
||||||
<select
|
<select
|
||||||
|
@ -145,103 +148,96 @@ const labels = computed(() => ({
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
</div>
|
|
||||||
<div class="dimmable">
|
|
||||||
<div
|
|
||||||
v-if="isLoading"
|
|
||||||
class="ui active inverted dimmer"
|
|
||||||
>
|
|
||||||
<div class="ui loader" />
|
|
||||||
</div>
|
|
||||||
<action-table
|
|
||||||
v-if="result"
|
|
||||||
:objects-data="result"
|
|
||||||
:actions="actions"
|
|
||||||
action-url="manage/accounts/action/"
|
|
||||||
:filters="actionFilters"
|
|
||||||
@action-launched="fetchData"
|
|
||||||
>
|
|
||||||
<template #header-cells>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.table.account.header.name') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.table.account.header.domain') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.table.account.header.uploads') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.table.account.header.firstSeen') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.table.account.header.lastSeen') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.table.account.header.moderationRule') }}
|
|
||||||
</th>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
#row-cells="scope"
|
|
||||||
>
|
|
||||||
<td>
|
|
||||||
<router-link :to="{name: 'manage.moderation.accounts.detail', params: {id: scope.obj.full_username }}">
|
|
||||||
{{ scope.obj.preferred_username }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<template v-if="!scope.obj.user">
|
|
||||||
<router-link :to="{name: 'manage.moderation.domains.detail', params: {id: scope.obj.domain }}">
|
|
||||||
<i class="wrench icon" />
|
|
||||||
</router-link>
|
|
||||||
<a
|
|
||||||
href=""
|
|
||||||
class="discrete link"
|
|
||||||
:title="scope.obj.domain"
|
|
||||||
@click.prevent="addSearchToken('domain', scope.obj.domain)"
|
|
||||||
>{{ scope.obj.domain }}</a>
|
|
||||||
</template>
|
|
||||||
<a
|
|
||||||
v-else
|
|
||||||
href=""
|
|
||||||
class="ui tiny accent icon link label"
|
|
||||||
@click.prevent="addSearchToken('domain', scope.obj.domain)"
|
|
||||||
>
|
|
||||||
<i class="home icon" />
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.link.local') }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ scope.obj.uploads_count }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<human-date :date="scope.obj.creation_date" />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<human-date
|
|
||||||
v-if="scope.obj.last_fetch_date"
|
|
||||||
:date="scope.obj.last_fetch_date"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="scope.obj.instance_policy"><i class="shield icon" />{{ t('components.manage.moderation.AccountsTable.table.account.moderationRule') }}</span>
|
|
||||||
</td>
|
|
||||||
</template>
|
|
||||||
</action-table>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<pagination
|
|
||||||
v-if="result && result.count > paginateBy"
|
|
||||||
v-model:current="page"
|
|
||||||
:compact="true"
|
|
||||||
:paginate-by="paginateBy"
|
|
||||||
:total="result.count"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span v-if="result && result.results.length > 0">
|
|
||||||
{{ t('components.manage.moderation.AccountsTable.pagination.results', {start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dimmable">
|
||||||
|
<Loader v-if="isLoading" />
|
||||||
|
<action-table
|
||||||
|
v-if="result"
|
||||||
|
:objects-data="result"
|
||||||
|
:actions="actions"
|
||||||
|
action-url="manage/accounts/action/"
|
||||||
|
:filters="actionFilters"
|
||||||
|
@action-launched="fetchData"
|
||||||
|
>
|
||||||
|
<template #header-cells>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.table.account.header.name') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.table.account.header.domain') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.table.account.header.uploads') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.table.account.header.firstSeen') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.table.account.header.lastSeen') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.table.account.header.moderationRule') }}
|
||||||
|
</th>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
#row-cells="scope"
|
||||||
|
>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{name: 'manage.moderation.accounts.detail', params: {id: scope.obj.full_username }}">
|
||||||
|
{{ scope.obj.preferred_username }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<template v-if="!scope.obj.user">
|
||||||
|
<router-link :to="{name: 'manage.moderation.domains.detail', params: {id: scope.obj.domain }}">
|
||||||
|
<i class="wrench icon" />
|
||||||
|
</router-link>
|
||||||
|
<a
|
||||||
|
href=""
|
||||||
|
class="discrete link"
|
||||||
|
:title="scope.obj.domain"
|
||||||
|
@click.prevent="addSearchToken('domain', scope.obj.domain)"
|
||||||
|
>{{ scope.obj.domain }}</a>
|
||||||
|
</template>
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
href=""
|
||||||
|
class="ui tiny accent icon link label"
|
||||||
|
@click.prevent="addSearchToken('domain', scope.obj.domain)"
|
||||||
|
>
|
||||||
|
<i class="home icon" />
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.link.local') }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ scope.obj.uploads_count }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<human-date :date="scope.obj.creation_date" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<human-date
|
||||||
|
v-if="scope.obj.last_fetch_date"
|
||||||
|
:date="scope.obj.last_fetch_date"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="scope.obj.instance_policy"><i class="shield icon" />{{ t('components.manage.moderation.AccountsTable.table.account.moderationRule') }}</span>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
</action-table>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Pagination
|
||||||
|
v-if="result && result.count > paginateBy"
|
||||||
|
v-model:current="page"
|
||||||
|
:paginate-by="paginateBy"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span v-if="result && result.results.length > 0">
|
||||||
|
{{ t('components.manage.moderation.AccountsTable.pagination.results', {start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -10,7 +10,12 @@ import { useI18n } from 'vue-i18n'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import ActionTable from '~/components/common/ActionTable.vue'
|
import ActionTable from '~/components/common/ActionTable.vue'
|
||||||
import Pagination from '~/components/vui/Pagination.vue'
|
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
import Loader from '~/components/ui/Loader.vue'
|
||||||
|
import Input from '~/components/ui/Input.vue'
|
||||||
|
import Pagination from '~/components/ui/Pagination.vue'
|
||||||
|
|
||||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||||
import useOrdering from '~/composables/navigation/useOrdering'
|
import useOrdering from '~/composables/navigation/useOrdering'
|
||||||
|
@ -120,19 +125,21 @@ const labels = computed(() => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="ui inline form">
|
||||||
<div class="ui inline form">
|
<div class="fields">
|
||||||
<div class="fields">
|
<div class="ui field">
|
||||||
<div class="ui field">
|
<Input
|
||||||
<label for="domains-search">{{ t('components.manage.moderation.DomainsTable.label.search') }}</label>
|
id="domains-search"
|
||||||
<input
|
v-model="query"
|
||||||
id="domains-search"
|
name="search"
|
||||||
v-model="query"
|
search
|
||||||
name="search"
|
:label="t('components.manage.moderation.DomainsTable.label.search')"
|
||||||
type="text"
|
:placeholder="labels.searchPlaceholder"
|
||||||
:placeholder="labels.searchPlaceholder"
|
/>
|
||||||
>
|
</div>
|
||||||
</div>
|
<Spacer :size="16" />
|
||||||
|
<Layout flex>
|
||||||
|
<Spacer grow />
|
||||||
<div
|
<div
|
||||||
v-if="allowListEnabled"
|
v-if="allowListEnabled"
|
||||||
class="field"
|
class="field"
|
||||||
|
@ -185,90 +192,85 @@ const labels = computed(() => ({
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
</div>
|
|
||||||
<div class="dimmable">
|
|
||||||
<div
|
|
||||||
v-if="isLoading"
|
|
||||||
class="ui active inverted dimmer"
|
|
||||||
>
|
|
||||||
<div class="ui loader" />
|
|
||||||
</div>
|
|
||||||
<action-table
|
|
||||||
v-if="result && result.results.length > 0"
|
|
||||||
:objects-data="result"
|
|
||||||
:actions="actions"
|
|
||||||
action-url="manage/federation/domains/action/"
|
|
||||||
id-field="name"
|
|
||||||
:filters="actionFilters"
|
|
||||||
@action-launched="fetchData"
|
|
||||||
>
|
|
||||||
<template #header-cells>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.DomainsTable.table.domain.header.name') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.DomainsTable.table.domain.header.users') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.DomainsTable.table.domain.header.receivedMessages') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.DomainsTable.table.domain.header.firstSeen') }}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{ t('components.manage.moderation.DomainsTable.table.domain.header.moderationRule') }}
|
|
||||||
</th>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
#row-cells="scope"
|
|
||||||
>
|
|
||||||
<td>
|
|
||||||
<router-link :to="{name: 'manage.moderation.domains.detail', params: {id: scope.obj.name }}">
|
|
||||||
{{ scope.obj.name }}
|
|
||||||
<i
|
|
||||||
v-if="allowListEnabled && scope.obj.allowed"
|
|
||||||
class="success check icon"
|
|
||||||
:title="labels.allowListTitle"
|
|
||||||
/>
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ scope.obj.actors_count }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ scope.obj.outbox_activities_count }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<human-date :date="scope.obj.creation_date" />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="scope.obj.instance_policy"><i class="shield icon" />{{ t('components.manage.moderation.DomainsTable.table.domain.moderationRule') }}</span>
|
|
||||||
</td>
|
|
||||||
</template>
|
|
||||||
</action-table>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="ui placeholder segment"
|
|
||||||
>
|
|
||||||
<div class="ui icon header">
|
|
||||||
<i class="server icon" />
|
|
||||||
{{ t('components.manage.moderation.DomainsTable.empty.noPods') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<pagination
|
|
||||||
v-if="result && result.count > paginateBy"
|
|
||||||
v-model:current="page"
|
|
||||||
:compact="true"
|
|
||||||
:paginate-by="paginateBy"
|
|
||||||
:total="result.count"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span v-if="result && result.results.length > 0">
|
|
||||||
{{ t('components.manage.moderation.DomainsTable.pagination.results', {start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dimmable">
|
||||||
|
<Loader v-if="isLoading" />
|
||||||
|
<action-table
|
||||||
|
v-if="result && result.results.length > 0"
|
||||||
|
:objects-data="result"
|
||||||
|
:actions="actions"
|
||||||
|
action-url="manage/federation/domains/action/"
|
||||||
|
id-field="name"
|
||||||
|
:filters="actionFilters"
|
||||||
|
@action-launched="fetchData"
|
||||||
|
>
|
||||||
|
<template #header-cells>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.DomainsTable.table.domain.header.name') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.DomainsTable.table.domain.header.users') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.DomainsTable.table.domain.header.receivedMessages') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.DomainsTable.table.domain.header.firstSeen') }}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{ t('components.manage.moderation.DomainsTable.table.domain.header.moderationRule') }}
|
||||||
|
</th>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
#row-cells="scope"
|
||||||
|
>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{name: 'manage.moderation.domains.detail', params: {id: scope.obj.name }}">
|
||||||
|
{{ scope.obj.name }}
|
||||||
|
<i
|
||||||
|
v-if="allowListEnabled && scope.obj.allowed"
|
||||||
|
class="success check icon"
|
||||||
|
:title="labels.allowListTitle"
|
||||||
|
/>
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ scope.obj.actors_count }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ scope.obj.outbox_activities_count }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<human-date :date="scope.obj.creation_date" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="scope.obj.instance_policy"><i class="shield icon" />{{ t('components.manage.moderation.DomainsTable.table.domain.moderationRule') }}</span>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
</action-table>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="ui placeholder segment"
|
||||||
|
>
|
||||||
|
<div class="ui icon header">
|
||||||
|
<i class="server icon" />
|
||||||
|
{{ t('components.manage.moderation.DomainsTable.empty.noPods') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<pagination
|
||||||
|
v-if="result && result.count > paginateBy"
|
||||||
|
v-model:current="page"
|
||||||
|
:compact="true"
|
||||||
|
:paginate-by="paginateBy"
|
||||||
|
:total="result.count"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span v-if="result && result.results.length > 0">
|
||||||
|
{{ t('components.manage.moderation.DomainsTable.pagination.results', {start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -11,6 +11,8 @@ import TracksTable from '~/components/manage/library/TracksTable.vue'
|
||||||
import UploadsTable from '~/components/manage/library/UploadsTable.vue'
|
import UploadsTable from '~/components/manage/library/UploadsTable.vue'
|
||||||
import UsersTable from '~/components/manage/users/UsersTable.vue'
|
import UsersTable from '~/components/manage/users/UsersTable.vue'
|
||||||
|
|
||||||
|
import Header from '~/components/ui/Header.vue'
|
||||||
|
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
@ -43,55 +45,48 @@ const title = computed(() => labels.value[props.type])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-title="title">
|
<Header :h1="title" />
|
||||||
<section class="ui vertical stripe segment">
|
<invitation-form v-if="type === 'invitations'" />
|
||||||
<h2 class="ui header">
|
<accounts-table
|
||||||
{{ title }}
|
v-if="type === 'accounts'"
|
||||||
</h2>
|
:update-url="true"
|
||||||
<invitation-form v-if="type === 'invitations'" />
|
:default-query="defaultQuery"
|
||||||
<div class="ui hidden divider" />
|
/>
|
||||||
<accounts-table
|
<albums-table
|
||||||
v-if="type === 'accounts'"
|
v-else-if="type === 'albums'"
|
||||||
:update-url="true"
|
:update-url="true"
|
||||||
:default-query="defaultQuery"
|
:default-query="defaultQuery"
|
||||||
/>
|
/>
|
||||||
<albums-table
|
<artists-table
|
||||||
v-else-if="type === 'albums'"
|
v-else-if="type === 'artists'"
|
||||||
:update-url="true"
|
:update-url="true"
|
||||||
:default-query="defaultQuery"
|
:default-query="defaultQuery"
|
||||||
/>
|
/>
|
||||||
<artists-table
|
<channels-table
|
||||||
v-else-if="type === 'artists'"
|
v-else-if="type === 'channels'"
|
||||||
:update-url="true"
|
:update-url="true"
|
||||||
:default-query="defaultQuery"
|
:default-query="defaultQuery"
|
||||||
/>
|
/>
|
||||||
<channels-table
|
<invitations-table v-else-if="type === 'invitations'" />
|
||||||
v-else-if="type === 'channels'"
|
<libraries-table
|
||||||
:update-url="true"
|
v-else-if="type === 'libraries'"
|
||||||
:default-query="defaultQuery"
|
:update-url="true"
|
||||||
/>
|
:default-query="defaultQuery"
|
||||||
<invitations-table v-else-if="type === 'invitations'" />
|
/>
|
||||||
<libraries-table
|
<tags-table
|
||||||
v-else-if="type === 'libraries'"
|
v-else-if="type === 'tags'"
|
||||||
:update-url="true"
|
:update-url="true"
|
||||||
:default-query="defaultQuery"
|
:default-query="defaultQuery"
|
||||||
/>
|
/>
|
||||||
<tags-table
|
<tracks-table
|
||||||
v-else-if="type === 'tags'"
|
v-else-if="type === 'tracks'"
|
||||||
:update-url="true"
|
:update-url="true"
|
||||||
:default-query="defaultQuery"
|
:default-query="defaultQuery"
|
||||||
/>
|
/>
|
||||||
<tracks-table
|
<uploads-table
|
||||||
v-else-if="type === 'tracks'"
|
v-else-if="type === 'uploads'"
|
||||||
:update-url="true"
|
:update-url="true"
|
||||||
:default-query="defaultQuery"
|
:default-query="defaultQuery"
|
||||||
/>
|
/>
|
||||||
<uploads-table
|
<users-table v-else-if="type === 'users'" />
|
||||||
v-else-if="type === 'uploads'"
|
|
||||||
:update-url="true"
|
|
||||||
:default-query="defaultQuery"
|
|
||||||
/>
|
|
||||||
<users-table v-else-if="type === 'users'" />
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -10,6 +10,12 @@ import { useStore } from '~/store'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
|
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Loader from '~/components/ui/Loader.vue'
|
||||||
|
import Button from '~/components/ui/Button.vue'
|
||||||
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
import Input from '~/components/ui/Input.vue'
|
||||||
|
|
||||||
import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue'
|
import InstancePolicyForm from '~/components/manage/moderation/InstancePolicyForm.vue'
|
||||||
import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue'
|
import InstancePolicyCard from '~/components/manage/moderation/InstancePolicyCard.vue'
|
||||||
|
|
||||||
|
@ -146,87 +152,70 @@ const updatePolicy = (newPolicy: InstancePolicy) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="page-admin-account-detail">
|
<Layout
|
||||||
<div
|
main
|
||||||
|
stack
|
||||||
|
class="page-admin-account-detail"
|
||||||
|
>
|
||||||
|
<Loader
|
||||||
v-if="isLoading"
|
v-if="isLoading"
|
||||||
class="ui vertical segment"
|
/>
|
||||||
>
|
|
||||||
<div :class="['ui', 'centered', 'active', 'inline', 'loader']" />
|
|
||||||
</div>
|
|
||||||
<template v-if="object">
|
<template v-if="object">
|
||||||
<section
|
<section
|
||||||
v-title="object.full_username"
|
v-title="object.full_username"
|
||||||
:class="['ui', 'head', 'vertical', 'stripe', 'segment']"
|
|
||||||
>
|
>
|
||||||
<div class="ui stackable two column grid">
|
<Layout flex>
|
||||||
<div class="ui column">
|
<h2 class="ui header">
|
||||||
<div class="segment-content">
|
<i class="bi-person-circle" />
|
||||||
<h2 class="ui header">
|
{{ object.full_username }}
|
||||||
<i class="circular inverted user icon" />
|
<div class="sub header">
|
||||||
<div class="content">
|
<template v-if="object.user">
|
||||||
{{ object.full_username }}
|
<span class="ui tiny accent label">
|
||||||
<div class="sub header">
|
<i class="bi-house-fill" />
|
||||||
<template v-if="object.user">
|
{{ t('views.admin.moderation.AccountsDetail.header.localAccount') }}
|
||||||
<span class="ui tiny accent label">
|
</span>
|
||||||
<i class="home icon" />
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.header.localAccount') }}
|
</template>
|
||||||
</span>
|
<a
|
||||||
|
:href="object.url || object.fid"
|
||||||
</template>
|
target="_blank"
|
||||||
<a
|
rel="noopener noreferrer"
|
||||||
:href="object.url || object.fid"
|
>
|
||||||
target="_blank"
|
{{ t('views.admin.moderation.AccountsDetail.link.openProfile') }}
|
||||||
rel="noopener noreferrer"
|
<i class="bi bi-box-arrow-up-right" />
|
||||||
>
|
</a>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.openProfile') }}
|
|
||||||
<i class="external icon" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</h2>
|
|
||||||
<div class="header-buttons">
|
|
||||||
<div class="ui icon buttons">
|
|
||||||
<a
|
|
||||||
v-if="object.user && store.state.auth.profile && store.state.auth.profile.is_superuser"
|
|
||||||
class="ui labeled icon button"
|
|
||||||
:href="store.getters['instance/absoluteUrl'](`/api/admin/users/user/${object.user.id}`)"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<i class="wrench icon" />
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.django') }}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
v-else-if="store.state.auth.profile && store.state.auth.profile.is_superuser"
|
|
||||||
class="ui labeled icon button"
|
|
||||||
:href="store.getters['instance/absoluteUrl'](`/api/admin/federation/actor/${object.id}`)"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<i class="wrench icon" />
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.django') }}
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
v-dropdown
|
|
||||||
class="ui floating dropdown icon button"
|
|
||||||
>
|
|
||||||
<i class="dropdown icon" />
|
|
||||||
<div class="menu">
|
|
||||||
<a
|
|
||||||
class="basic item"
|
|
||||||
:href="object.url || object.fid"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<i class="external icon" />
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.remoteProfile') }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</h2>
|
||||||
|
<Spacer grow />
|
||||||
|
<Layout stack>
|
||||||
|
<a
|
||||||
|
v-if="object.user && store.state.auth.profile && store.state.auth.profile.is_superuser"
|
||||||
|
class="ui labeled icon button"
|
||||||
|
:href="store.getters['instance/absoluteUrl'](`/api/admin/users/user/${object.user.id}`)"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<i class="bi bi-wrench" />
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.django') }}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
v-else-if="store.state.auth.profile && store.state.auth.profile.is_superuser"
|
||||||
|
:href="store.getters['instance/absoluteUrl'](`/api/admin/federation/actor/${object.id}`)"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<i class="bi bi-wrench" />
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.django') }}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
:href="object.url || object.fid"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<i class="bi bi-box-arrow-up-right" />
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.remoteProfile') }}
|
||||||
|
</a>
|
||||||
|
</Layout>
|
||||||
<div class="ui column">
|
<div class="ui column">
|
||||||
<div
|
<div
|
||||||
v-if="!object.user"
|
v-if="!object.user"
|
||||||
|
@ -244,19 +233,19 @@ const updatePolicy = (newPolicy: InstancePolicy) => {
|
||||||
<template v-else-if="!policy && !showPolicyForm">
|
<template v-else-if="!policy && !showPolicyForm">
|
||||||
<header class="ui header">
|
<header class="ui header">
|
||||||
<h3>
|
<h3>
|
||||||
<i class="shield icon" />
|
<i class="bi bi-shield-fill" />
|
||||||
{{ t('views.admin.moderation.AccountsDetail.header.noPolicy') }}
|
{{ t('views.admin.moderation.AccountsDetail.header.noPolicy') }}
|
||||||
</h3>
|
</h3>
|
||||||
</header>
|
</header>
|
||||||
<p>
|
<p>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.description.policy') }}
|
{{ t('views.admin.moderation.AccountsDetail.description.policy') }}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<Button
|
||||||
class="ui primary button"
|
primary
|
||||||
@click="showPolicyForm = true"
|
@click="showPolicyForm = true"
|
||||||
>
|
>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.button.addPolicy') }}
|
{{ t('views.admin.moderation.AccountsDetail.button.addPolicy') }}
|
||||||
</button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
<instance-policy-card
|
<instance-policy-card
|
||||||
v-else-if="policy && !showPolicyForm"
|
v-else-if="policy && !showPolicyForm"
|
||||||
|
@ -280,359 +269,349 @@ const updatePolicy = (newPolicy: InstancePolicy) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
</section>
|
</section>
|
||||||
<div class="ui vertical stripe segment">
|
<Layout flex>
|
||||||
<div class="ui stackable three column grid">
|
<div class="column">
|
||||||
<div class="column">
|
<section>
|
||||||
<section>
|
<h3 class="ui header">
|
||||||
<h3 class="ui header">
|
<i class="bi bi-info-circle-fill" />
|
||||||
<i class="info icon" />
|
{{ t('views.admin.moderation.AccountsDetail.header.accountData') }}
|
||||||
<div class="content">
|
</h3>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.header.accountData') }}
|
<table class="ui very basic table">
|
||||||
</div>
|
<tbody>
|
||||||
</h3>
|
<tr>
|
||||||
<table class="ui very basic table">
|
<td>
|
||||||
<tbody>
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.username') }}
|
||||||
<tr>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.username') }}
|
{{ object.preferred_username }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ object.preferred_username }}
|
<tr v-if="!object.user">
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
<router-link :to="{name: 'manage.moderation.domains.detail', params: {id: object.domain }}">
|
||||||
<tr v-if="!object.user">
|
{{ t('views.admin.moderation.AccountsDetail.link.domain') }}
|
||||||
<td>
|
</router-link>
|
||||||
<router-link :to="{name: 'manage.moderation.domains.detail', params: {id: object.domain }}">
|
</td>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.domain') }}
|
<td>
|
||||||
</router-link>
|
{{ object.domain }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ object.domain }}
|
<tr>
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.displayName') }}
|
||||||
<tr>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.displayName') }}
|
{{ object.name }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ object.name }}
|
<tr v-if="object.user">
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.email') }}
|
||||||
<tr v-if="object.user">
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.email') }}
|
{{ object.user.email }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ object.user.email }}
|
<tr v-if="object.user">
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.label') }}
|
||||||
<tr v-if="object.user">
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.label') }}
|
<div
|
||||||
</td>
|
v-if="object.user.username != store.state.auth.profile?.username"
|
||||||
<td>
|
class="ui toggle checkbox"
|
||||||
<div
|
>
|
||||||
v-if="object.user.username != store.state.auth.profile?.username"
|
<Input
|
||||||
class="ui toggle checkbox"
|
id="is-active"
|
||||||
>
|
v-model="object.user.is_active"
|
||||||
<input
|
type="checkbox"
|
||||||
id="is-active"
|
@change="updateUser('is_active')"
|
||||||
v-model="object.user.is_active"
|
|
||||||
type="checkbox"
|
|
||||||
@change="updateUser('is_active')"
|
|
||||||
>
|
|
||||||
<label for="is-active">
|
|
||||||
<span
|
|
||||||
v-if="object.user.is_active"
|
|
||||||
>{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.enabled') }}</span>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
>{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.disabled') }}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
v-else-if="object.user.is_active"
|
|
||||||
>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.enabled') }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.disabled') }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="object.user">
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.permissions') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<select
|
|
||||||
v-model="permissions"
|
|
||||||
multiple
|
|
||||||
class="ui search selection dropdown"
|
|
||||||
@change="updateUser('permissions')"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(p, key) in allPermissions"
|
|
||||||
:key="key"
|
|
||||||
:value="p.code"
|
|
||||||
>
|
|
||||||
{{ p.label }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<action-feedback :is-loading="updating.has('permissions')" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.userType') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ object.type }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="!object.user">
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.lastChecked') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<human-date
|
|
||||||
v-if="object.last_fetch_date"
|
|
||||||
:date="object.last_fetch_date"
|
|
||||||
/>
|
/>
|
||||||
<span
|
<label for="is-active">
|
||||||
v-else
|
<span
|
||||||
|
v-if="object.user.is_active"
|
||||||
|
>{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.enabled') }}</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
>{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.disabled') }}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
v-else-if="object.user.is_active"
|
||||||
|
>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.enabled') }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.loginStatus.disabled') }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="object.user">
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.permissions') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select
|
||||||
|
v-model="permissions"
|
||||||
|
multiple
|
||||||
|
class="ui search selection dropdown"
|
||||||
|
@change="updateUser('permissions')"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(p, key) in allPermissions"
|
||||||
|
:key="key"
|
||||||
|
:value="p.code"
|
||||||
>
|
>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.notApplicable') }}
|
{{ p.label }}
|
||||||
</span>
|
</option>
|
||||||
</td>
|
</select>
|
||||||
</tr>
|
<action-feedback :is-loading="updating.has('permissions')" />
|
||||||
<tr v-if="object.user">
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.signupDate') }}
|
<tr>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.userType') }}
|
||||||
<human-date :date="object.user.date_joined" />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
{{ object.type }}
|
||||||
<tr v-if="object.user">
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.accountData.lastActivity') }}
|
<tr v-if="!object.user">
|
||||||
</td>
|
<td>
|
||||||
<td>
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.lastChecked') }}
|
||||||
<human-date :date="object.user.last_activity" />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
<human-date
|
||||||
</tbody>
|
v-if="object.last_fetch_date"
|
||||||
</table>
|
:date="object.last_fetch_date"
|
||||||
</section>
|
/>
|
||||||
</div>
|
<span
|
||||||
<div class="column">
|
v-else
|
||||||
<section>
|
>
|
||||||
<h3 class="ui header">
|
{{ t('views.admin.moderation.AccountsDetail.notApplicable') }}
|
||||||
<i class="feed icon" />
|
</span>
|
||||||
<div class="content">
|
</td>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.header.activity') }}
|
</tr>
|
||||||
<span :data-tooltip="labels.statsWarning"><i class="question circle icon" /></span>
|
<tr v-if="object.user">
|
||||||
</div>
|
<td>
|
||||||
</h3>
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.signupDate') }}
|
||||||
<div
|
</td>
|
||||||
v-if="isLoadingStats"
|
<td>
|
||||||
class="ui placeholder"
|
<human-date :date="object.user.date_joined" />
|
||||||
>
|
</td>
|
||||||
<div class="full line" />
|
</tr>
|
||||||
<div class="short line" />
|
<tr v-if="object.user">
|
||||||
<div class="medium line" />
|
<td>
|
||||||
<div class="long line" />
|
{{ t('views.admin.moderation.AccountsDetail.table.accountData.lastActivity') }}
|
||||||
</div>
|
</td>
|
||||||
<table
|
<td>
|
||||||
v-else
|
<human-date :date="object.user.last_activity" />
|
||||||
class="ui very basic table"
|
</td>
|
||||||
>
|
</tr>
|
||||||
<tbody>
|
</tbody>
|
||||||
<tr v-if="!object.user">
|
</table>
|
||||||
<td>
|
</section>
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.activity.firstSeen') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<human-date :date="object.creation_date" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.activity.emittedMessages') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.outbox_activities }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.activity.receivedFollows') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.received_library_follows }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.activity.emittedFollows') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.emitted_library_follows }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<router-link :to="{name: 'manage.moderation.reports.list', query: {q: getQuery('target', `account:${object.full_username}`) }}">
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.linkedReports') }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.reports }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<router-link :to="{name: 'manage.moderation.requests.list', query: {q: getQuery('submitter', `${object.full_username}`) }}">
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.requests') }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.requests }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
<section>
|
|
||||||
<h3 class="ui header">
|
|
||||||
<i class="music icon" />
|
|
||||||
<div class="content">
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.header.audioContent') }}
|
|
||||||
<span :data-tooltip="labels.statsWarning"><i class="question circle icon" /></span>
|
|
||||||
</div>
|
|
||||||
</h3>
|
|
||||||
<div
|
|
||||||
v-if="isLoadingStats"
|
|
||||||
class="ui placeholder"
|
|
||||||
>
|
|
||||||
<div class="full line" />
|
|
||||||
<div class="short line" />
|
|
||||||
<div class="medium line" />
|
|
||||||
<div class="long line" />
|
|
||||||
</div>
|
|
||||||
<table
|
|
||||||
v-else
|
|
||||||
class="ui very basic table"
|
|
||||||
>
|
|
||||||
<tbody>
|
|
||||||
<tr v-if="!object.user">
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.cachedSize') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ humanSize(stats.media_downloaded_size) }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="object.user">
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.uploadQuota') }}
|
|
||||||
<span :data-tooltip="labels.uploadQuota"><i class="question circle icon" /></span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="ui right labeled input">
|
|
||||||
<input
|
|
||||||
v-model.number="object.user.upload_quota"
|
|
||||||
step="100"
|
|
||||||
name="quota"
|
|
||||||
type="number"
|
|
||||||
@change="updateUser('upload_quota', true)"
|
|
||||||
>
|
|
||||||
<div class="ui basic label">
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.megabyte') }}
|
|
||||||
</div>
|
|
||||||
<action-feedback
|
|
||||||
class="ui basic label"
|
|
||||||
size="tiny"
|
|
||||||
:is-loading="updating.has('upload_quota')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.totalSize') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ humanSize(stats.media_total_size) }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<router-link :to="{name: 'manage.channels', query: {q: getQuery('account', object.full_username) }}">
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.channels') }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.channels }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<router-link :to="{name: 'manage.library.libraries', query: {q: getQuery('account', object.full_username) }}">
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.libraries') }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.libraries }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<router-link :to="{name: 'manage.library.uploads', query: {q: getQuery('account', object.full_username) }}">
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.uploads') }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.uploads }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.artists') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.artists }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.albums') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.albums }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ t('views.admin.moderation.AccountsDetail.link.tracks') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ stats.tracks }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="column">
|
||||||
|
<section>
|
||||||
|
<h3 class="ui header">
|
||||||
|
<i class="bi bi-rss-fill" />
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.header.activity') }}
|
||||||
|
<span :data-tooltip="labels.statsWarning"><i class=" bi bi-question-circle-fill" /></span>
|
||||||
|
</h3>
|
||||||
|
<div
|
||||||
|
v-if="isLoadingStats"
|
||||||
|
class="ui placeholder"
|
||||||
|
>
|
||||||
|
<div class="full line" />
|
||||||
|
<div class="short line" />
|
||||||
|
<div class="medium line" />
|
||||||
|
<div class="long line" />
|
||||||
|
</div>
|
||||||
|
<table
|
||||||
|
v-else
|
||||||
|
class="ui very basic table"
|
||||||
|
>
|
||||||
|
<tbody>
|
||||||
|
<tr v-if="!object.user">
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.activity.firstSeen') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<human-date :date="object.creation_date" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.activity.emittedMessages') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.outbox_activities }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.activity.receivedFollows') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.received_library_follows }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.activity.emittedFollows') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.emitted_library_follows }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{name: 'manage.moderation.reports.list', query: {q: getQuery('target', `account:${object.full_username}`) }}">
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.linkedReports') }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.reports }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{name: 'manage.moderation.requests.list', query: {q: getQuery('submitter', `${object.full_username}`) }}">
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.requests') }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.requests }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<section>
|
||||||
|
<h3 class="ui header">
|
||||||
|
<i class="bi bi-music-note-beamed" />
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.header.audioContent') }}
|
||||||
|
<span :data-tooltip="labels.statsWarning"><i class="question circle icon" /></span>
|
||||||
|
</h3>
|
||||||
|
<div
|
||||||
|
v-if="isLoadingStats"
|
||||||
|
class="ui placeholder"
|
||||||
|
>
|
||||||
|
<div class="full line" />
|
||||||
|
<div class="short line" />
|
||||||
|
<div class="medium line" />
|
||||||
|
<div class="long line" />
|
||||||
|
</div>
|
||||||
|
<table
|
||||||
|
v-else
|
||||||
|
class="ui very basic table"
|
||||||
|
>
|
||||||
|
<tbody>
|
||||||
|
<tr v-if="!object.user">
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.cachedSize') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ humanSize(stats.media_downloaded_size) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="object.user">
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.uploadQuota') }}
|
||||||
|
<span :data-tooltip="labels.uploadQuota"><i class="question circle icon" /></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Input
|
||||||
|
v-model.number="object.user.upload_quota"
|
||||||
|
step="100"
|
||||||
|
name="quota"
|
||||||
|
type="number"
|
||||||
|
@change="updateUser('upload_quota', true)"
|
||||||
|
/>
|
||||||
|
<div class="ui basic label">
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.megabyte') }}
|
||||||
|
</div>
|
||||||
|
<action-feedback
|
||||||
|
class="ui basic label"
|
||||||
|
size="tiny"
|
||||||
|
:is-loading="updating.has('upload_quota')"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.table.audioContent.totalSize') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ humanSize(stats.media_total_size) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{name: 'manage.channels', query: {q: getQuery('account', object.full_username) }}">
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.channels') }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.channels }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{name: 'manage.library.libraries', query: {q: getQuery('account', object.full_username) }}">
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.libraries') }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.libraries }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{name: 'manage.library.uploads', query: {q: getQuery('account', object.full_username) }}">
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.uploads') }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.uploads }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.artists') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.artists }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.albums') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.albums }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ t('views.admin.moderation.AccountsDetail.link.tracks') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ stats.tracks }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
</main>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -7,6 +7,11 @@ import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Tabs from '~/components/ui/Tabs.vue'
|
||||||
|
import Tab from '~/components/ui/Tab.vue'
|
||||||
|
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
@ -26,55 +31,50 @@ fetchNodeInfo()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<!-- TODO: Replace with Tabs component -->
|
||||||
|
<Layout
|
||||||
v-title="labels.moderation"
|
v-title="labels.moderation"
|
||||||
class="main"
|
main
|
||||||
|
no-gap
|
||||||
>
|
>
|
||||||
<nav
|
<Tabs
|
||||||
class="ui secondary pointing menu"
|
|
||||||
role="navigation"
|
role="navigation"
|
||||||
:aria-label="labels.secondaryMenu"
|
:aria-label="labels.secondaryMenu"
|
||||||
>
|
>
|
||||||
<router-link
|
<Tab
|
||||||
class="ui item"
|
:title="t('views.admin.moderation.Base.link.reports')"
|
||||||
:to="{name: 'manage.moderation.reports.list', query: {q: 'resolved:no'}}"
|
:to="{name: 'manage.moderation.reports.list', query: {q: 'resolved:no'}}"
|
||||||
>
|
>
|
||||||
{{ t('views.admin.moderation.Base.link.reports') }}
|
|
||||||
<div
|
<div
|
||||||
v-if="store.state.ui.notifications.pendingReviewReports > 0"
|
v-if="store.state.ui.notifications.pendingReviewReports > 0"
|
||||||
:class="['ui', 'circular', 'mini', 'right floated', 'accent', 'label']"
|
:class="['ui', 'circular', 'mini', 'right floated', 'accent', 'label']"
|
||||||
>
|
>
|
||||||
{{ store.state.ui.notifications.pendingReviewReports }}
|
{{ store.state.ui.notifications.pendingReviewReports }}
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</Tab>
|
||||||
<router-link
|
<Tab
|
||||||
class="ui item"
|
:title="t('views.admin.moderation.Base.link.userRequests')"
|
||||||
:to="{name: 'manage.moderation.requests.list', query: {q: 'status:pending'}}"
|
:to="{name: 'manage.moderation.requests.list', query: {q: 'status:pending'}}"
|
||||||
>
|
>
|
||||||
{{ t('views.admin.moderation.Base.link.userRequests') }}
|
|
||||||
<div
|
<div
|
||||||
v-if="store.state.ui.notifications.pendingReviewRequests > 0"
|
v-if="store.state.ui.notifications.pendingReviewRequests > 0"
|
||||||
:class="['ui', 'circular', 'mini', 'right floated', 'accent', 'label']"
|
:class="['ui', 'circular', 'mini', 'right floated', 'accent', 'label']"
|
||||||
>
|
>
|
||||||
{{ store.state.ui.notifications.pendingReviewRequests }}
|
{{ store.state.ui.notifications.pendingReviewRequests }}
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</Tab>
|
||||||
<router-link
|
<Tab
|
||||||
class="ui item"
|
:title="t('views.admin.moderation.Base.link.domains')"
|
||||||
:to="{name: 'manage.moderation.domains.list'}"
|
:to="{name: 'manage.moderation.domains.list'}"
|
||||||
>
|
/>
|
||||||
{{ t('views.admin.moderation.Base.link.domains') }}
|
<Tab
|
||||||
</router-link>
|
:title="t('views.admin.moderation.Base.link.accounts')"
|
||||||
<router-link
|
|
||||||
class="ui item"
|
|
||||||
:to="{name: 'manage.moderation.accounts.list'}"
|
:to="{name: 'manage.moderation.accounts.list'}"
|
||||||
>
|
/>
|
||||||
{{ t('views.admin.moderation.Base.link.accounts') }}
|
</Tabs>
|
||||||
</router-link>
|
|
||||||
</nav>
|
|
||||||
<router-view
|
<router-view
|
||||||
:key="route.fullPath"
|
:key="route.fullPath"
|
||||||
:allow-list-enabled="allowListEnabled"
|
:allow-list-enabled="allowListEnabled"
|
||||||
/>
|
/>
|
||||||
</div>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -9,6 +9,14 @@ import axios from 'axios'
|
||||||
|
|
||||||
import DomainsTable from '~/components/manage/moderation/DomainsTable.vue'
|
import DomainsTable from '~/components/manage/moderation/DomainsTable.vue'
|
||||||
|
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
import Header from '~/components/ui/Header.vue'
|
||||||
|
import Toggle from '~/components/ui/Toggle.vue'
|
||||||
|
import Button from '~/components/ui/Button.vue'
|
||||||
|
import Input from '~/components/ui/Input.vue'
|
||||||
|
import Alert from '~/components/ui/Alert.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
allowListEnabled: boolean
|
allowListEnabled: boolean
|
||||||
}
|
}
|
||||||
|
@ -48,66 +56,59 @@ const createDomain = async () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-title="labels.domains">
|
<main v-title="labels.domains">
|
||||||
<section class="ui vertical stripe segment">
|
<Layout
|
||||||
<h2 class="ui left floated header">
|
form
|
||||||
{{ t('views.admin.moderation.DomainsList.header.domains') }}
|
@submit.prevent="createDomain"
|
||||||
</h2>
|
>
|
||||||
<form
|
<Header :h1="t('views.admin.moderation.DomainsList.header.domains')" />
|
||||||
class="ui right floated form"
|
<Alert
|
||||||
@submit.prevent="createDomain"
|
v-if="errors && errors.length > 0"
|
||||||
|
red
|
||||||
>
|
>
|
||||||
<div
|
<h4 class="header">
|
||||||
v-if="errors && errors.length > 0"
|
{{ t('views.admin.moderation.DomainsList.header.failure') }}
|
||||||
role="alert"
|
</h4>
|
||||||
class="ui negative message"
|
<ul class="list">
|
||||||
>
|
<li
|
||||||
<h4 class="header">
|
v-for="(error, key) in errors"
|
||||||
{{ t('views.admin.moderation.DomainsList.header.failure') }}
|
:key="key"
|
||||||
</h4>
|
|
||||||
<ul class="list">
|
|
||||||
<li
|
|
||||||
v-for="(error, key) in errors"
|
|
||||||
:key="key"
|
|
||||||
>
|
|
||||||
{{ error }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="inline fields">
|
|
||||||
<div class="field">
|
|
||||||
<label for="add-domain">{{ t('views.admin.moderation.DomainsList.label.addDomain') }}</label>
|
|
||||||
<input
|
|
||||||
id="add-domain"
|
|
||||||
v-model="domainName"
|
|
||||||
type="text"
|
|
||||||
name="domain"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="allowListEnabled"
|
|
||||||
class="field"
|
|
||||||
>
|
>
|
||||||
<input
|
{{ error }}
|
||||||
id="allowed"
|
</li>
|
||||||
v-model="domainAllowed"
|
</ul>
|
||||||
type="checkbox"
|
</Alert>
|
||||||
name="allowed"
|
<div class="field">
|
||||||
>
|
<Input
|
||||||
<label for="allowed">{{ t('views.admin.moderation.DomainsList.label.addToAllowList') }}</label>
|
id="add-domain"
|
||||||
</div>
|
v-model="domainName"
|
||||||
<div class="field">
|
type="text"
|
||||||
<button
|
name="domain"
|
||||||
:class="['ui', {'loading': isCreating}, 'success', 'button']"
|
:label="t('views.admin.moderation.DomainsList.label.addDomain')"
|
||||||
|
>
|
||||||
|
<template #input-right>
|
||||||
|
<Button
|
||||||
|
primary
|
||||||
|
:class="[{'loading': isCreating}, 'success']"
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="isCreating"
|
:disabled="isCreating"
|
||||||
>
|
>
|
||||||
{{ t('views.admin.moderation.DomainsList.button.add') }}
|
{{ t('views.admin.moderation.DomainsList.button.add') }}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</template>
|
||||||
|
</Input>
|
||||||
|
<div
|
||||||
|
v-if="allowListEnabled"
|
||||||
|
>
|
||||||
|
<Toggle
|
||||||
|
id="allowed"
|
||||||
|
v-model="domainAllowed"
|
||||||
|
name="allowed"
|
||||||
|
:label="t('views.admin.moderation.DomainsList.label.addToAllowList')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
<div class="ui clearing hidden divider" />
|
</Layout>
|
||||||
<domains-table :allow-list-enabled="allowListEnabled" />
|
<Spacer />
|
||||||
</section>
|
<domains-table :allow-list-enabled="allowListEnabled" />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -13,7 +13,12 @@ import axios from 'axios'
|
||||||
|
|
||||||
import ReportCategoryDropdown from '~/components/moderation/ReportCategoryDropdown.vue'
|
import ReportCategoryDropdown from '~/components/moderation/ReportCategoryDropdown.vue'
|
||||||
import ReportCard from '~/components/manage/moderation/ReportCard.vue'
|
import ReportCard from '~/components/manage/moderation/ReportCard.vue'
|
||||||
import Pagination from '~/components/vui/Pagination.vue'
|
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
import Header from '~/components/ui/Header.vue'
|
||||||
|
import Input from '~/components/ui/Input.vue'
|
||||||
|
import Pagination from '~/components/ui/Pagination.vue'
|
||||||
|
|
||||||
import useSmartSearch from '~/composables/navigation/useSmartSearch'
|
import useSmartSearch from '~/composables/navigation/useSmartSearch'
|
||||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||||
|
@ -99,27 +104,31 @@ const labels = computed(() => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-title="labels.reports">
|
<Layout
|
||||||
<section class="ui vertical stripe segment">
|
v-title="labels.reports"
|
||||||
<h2 class="ui header">
|
stack
|
||||||
{{ t('views.admin.moderation.ReportsList.header.reports') }}
|
>
|
||||||
</h2>
|
<Header :h1="t('views.admin.moderation.ReportsList.header.reports')" />
|
||||||
<div class="ui hidden divider" />
|
<div class="ui inline form">
|
||||||
<div class="ui inline form">
|
<div class="fields">
|
||||||
<div class="fields">
|
<div class="ui field">
|
||||||
<div class="ui field">
|
<label for="reports-search">{{ t('views.admin.moderation.ReportsList.label.search') }}</label>
|
||||||
<label for="reports-search">{{ t('views.admin.moderation.ReportsList.label.search') }}</label>
|
<form @submit.prevent="query = search.value">
|
||||||
<form @submit.prevent="query = search.value">
|
<Input
|
||||||
<input
|
id="reports-search"
|
||||||
id="reports-search"
|
ref="search"
|
||||||
ref="search"
|
name="search"
|
||||||
name="search"
|
search
|
||||||
type="text"
|
:value="query"
|
||||||
:value="query"
|
:placeholder="labels.searchPlaceholder"
|
||||||
:placeholder="labels.searchPlaceholder"
|
/>
|
||||||
>
|
</form>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
<Spacer :size="16" />
|
||||||
|
<Layout
|
||||||
|
flex
|
||||||
|
>
|
||||||
|
<Spacer grow />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="reports-status">{{ t('views.admin.moderation.ReportsList.label.status') }}</label>
|
<label for="reports-status">{{ t('views.admin.moderation.ReportsList.label.status') }}</label>
|
||||||
<select
|
<select
|
||||||
|
@ -177,36 +186,35 @@ const labels = computed(() => ({
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
</div>
|
</div>
|
||||||
<div
|
</div>
|
||||||
v-if="isLoading"
|
<div
|
||||||
class="ui active inverted dimmer"
|
v-if="isLoading"
|
||||||
>
|
class="ui active inverted dimmer"
|
||||||
<div class="ui loader" />
|
>
|
||||||
</div>
|
<div class="ui loader" />
|
||||||
<div v-else-if="!result || result.count === 0">
|
</div>
|
||||||
<empty-state
|
<div v-else-if="!result || result.count === 0">
|
||||||
:refresh="true"
|
<empty-state
|
||||||
@refresh="fetchData()"
|
:refresh="true"
|
||||||
/>
|
@refresh="fetchData()"
|
||||||
</div>
|
/>
|
||||||
<div v-else-if="mode === 'card'">
|
</div>
|
||||||
<report-card
|
<div v-else-if="mode === 'card'">
|
||||||
v-for="obj in result.results"
|
<report-card
|
||||||
:key="obj.uuid"
|
v-for="obj in result.results"
|
||||||
:init-obj="obj"
|
:key="obj.uuid"
|
||||||
@handled="fetchData"
|
:init-obj="obj"
|
||||||
/>
|
@handled="fetchData"
|
||||||
</div>
|
/>
|
||||||
<div class="ui center aligned basic segment">
|
</div>
|
||||||
<pagination
|
<div class="ui center aligned basic segment">
|
||||||
v-if="result && result.count > paginateBy"
|
<Pagination
|
||||||
v-model:current="page"
|
v-if="result && result.count > paginateBy"
|
||||||
:paginate-by="paginateBy"
|
v-model:current="page"
|
||||||
:total="result.count"
|
:paginate-by="paginateBy"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</Layout>
|
||||||
</main>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -12,7 +12,13 @@ import { useStore } from '~/store'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import UserRequestCard from '~/components/manage/moderation/UserRequestCard.vue'
|
import UserRequestCard from '~/components/manage/moderation/UserRequestCard.vue'
|
||||||
import Pagination from '~/components/vui/Pagination.vue'
|
|
||||||
|
import Layout from '~/components/ui/Layout.vue'
|
||||||
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
|
import Pagination from '~/components/ui/Pagination.vue'
|
||||||
|
import Input from '~/components/ui/Input.vue'
|
||||||
|
import Loader from '~/components/ui/Loader.vue'
|
||||||
|
import Header from '~/components/ui/Header.vue'
|
||||||
|
|
||||||
import useSmartSearch from '~/composables/navigation/useSmartSearch'
|
import useSmartSearch from '~/composables/navigation/useSmartSearch'
|
||||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||||
|
@ -92,110 +98,102 @@ const labels = computed(() => ({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-title="labels.reports">
|
<Header :h1="t('views.admin.moderation.RequestsList.header.userRequests')" />
|
||||||
<section class="ui vertical stripe segment">
|
<Spacer />
|
||||||
<h2 class="ui header">
|
<div class="ui inline form">
|
||||||
{{ t('views.admin.moderation.RequestsList.header.userRequests') }}
|
<div class="fields">
|
||||||
</h2>
|
<div class="ui field">
|
||||||
<div class="ui hidden divider" />
|
<form @submit.prevent="query = search.value">
|
||||||
<div class="ui inline form">
|
<Input
|
||||||
<div class="fields">
|
id="requests-search"
|
||||||
<div class="ui field">
|
ref="search"
|
||||||
<label for="requests-search">{{ t('views.admin.moderation.RequestsList.label.search') }}</label>
|
name="search"
|
||||||
<form @submit.prevent="query = search.value">
|
search
|
||||||
<input
|
:label="t('views.admin.moderation.RequestsList.label.search')"
|
||||||
id="requests-search"
|
:value="query"
|
||||||
ref="search"
|
:placeholder="labels.searchPlaceholder"
|
||||||
name="search"
|
|
||||||
type="text"
|
|
||||||
:value="query"
|
|
||||||
:placeholder="labels.searchPlaceholder"
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="requests-status">{{ t('views.admin.moderation.RequestsList.label.status') }}</label>
|
|
||||||
<select
|
|
||||||
id="requests-status"
|
|
||||||
class="ui dropdown"
|
|
||||||
:value="getTokenValue('status', '')"
|
|
||||||
@change="addSearchToken('status', ($event.target as HTMLSelectElement).value)"
|
|
||||||
>
|
|
||||||
<option value="">
|
|
||||||
{{ t('views.admin.moderation.RequestsList.option.status.all') }}
|
|
||||||
</option>
|
|
||||||
<option value="pending">
|
|
||||||
{{ t('views.admin.moderation.RequestsList.option.status.pending') }}
|
|
||||||
</option>
|
|
||||||
<option value="approved">
|
|
||||||
{{ t('views.admin.moderation.RequestsList.option.status.approved') }}
|
|
||||||
</option>
|
|
||||||
<option value="refused">
|
|
||||||
{{ t('views.admin.moderation.RequestsList.option.status.refused') }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="requests-ordering">{{ t('views.admin.moderation.RequestsList.ordering.label') }}</label>
|
|
||||||
<select
|
|
||||||
id="requests-ordering"
|
|
||||||
v-model="ordering"
|
|
||||||
class="ui dropdown"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(option, key) in orderingOptions"
|
|
||||||
:key="key"
|
|
||||||
:value="option[0]"
|
|
||||||
>
|
|
||||||
{{ sharedLabels.filters[option[1]] }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label for="requests-ordering-direction">{{ t('views.admin.moderation.RequestsList.ordering.direction.label') }}</label>
|
|
||||||
<select
|
|
||||||
id="requests-ordering-direction"
|
|
||||||
v-model="orderingDirection"
|
|
||||||
class="ui dropdown"
|
|
||||||
>
|
|
||||||
<option value="+">
|
|
||||||
{{ t('views.admin.moderation.RequestsList.ordering.direction.ascending') }}
|
|
||||||
</option>
|
|
||||||
<option value="-">
|
|
||||||
{{ t('views.admin.moderation.RequestsList.ordering.direction.descending') }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="isLoading"
|
|
||||||
class="ui active inverted dimmer"
|
|
||||||
>
|
|
||||||
<div class="ui loader" />
|
|
||||||
</div>
|
|
||||||
<div v-else-if="!result || result.count === 0">
|
|
||||||
<empty-state
|
|
||||||
:refresh="true"
|
|
||||||
@refresh="fetchData()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<template v-else>
|
|
||||||
<user-request-card
|
|
||||||
v-for="obj in result.results"
|
|
||||||
:key="obj.uuid"
|
|
||||||
:init-obj="obj"
|
|
||||||
@handled="fetchData"
|
|
||||||
/>
|
|
||||||
<div class="ui center aligned basic segment">
|
|
||||||
<pagination
|
|
||||||
v-if="result.count > paginateBy"
|
|
||||||
v-model:current="page"
|
|
||||||
:paginate-by="paginateBy"
|
|
||||||
:total="result.count"
|
|
||||||
/>
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<Spacer :size="16" />
|
||||||
|
<Layout flex>
|
||||||
|
<Spacer grow />
|
||||||
|
<div class="field">
|
||||||
|
<label for="requests-status">{{ t('views.admin.moderation.RequestsList.label.status') }}</label>
|
||||||
|
<select
|
||||||
|
id="requests-status"
|
||||||
|
class="ui dropdown"
|
||||||
|
:value="getTokenValue('status', '')"
|
||||||
|
@change="addSearchToken('status', ($event.target as HTMLSelectElement).value)"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ t('views.admin.moderation.RequestsList.option.status.all') }}
|
||||||
|
</option>
|
||||||
|
<option value="pending">
|
||||||
|
{{ t('views.admin.moderation.RequestsList.option.status.pending') }}
|
||||||
|
</option>
|
||||||
|
<option value="approved">
|
||||||
|
{{ t('views.admin.moderation.RequestsList.option.status.approved') }}
|
||||||
|
</option>
|
||||||
|
<option value="refused">
|
||||||
|
{{ t('views.admin.moderation.RequestsList.option.status.refused') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<div class="field">
|
||||||
</section>
|
<label for="requests-ordering">{{ t('views.admin.moderation.RequestsList.ordering.label') }}</label>
|
||||||
</main>
|
<select
|
||||||
|
id="requests-ordering"
|
||||||
|
v-model="ordering"
|
||||||
|
class="ui dropdown"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(option, key) in orderingOptions"
|
||||||
|
:key="key"
|
||||||
|
:value="option[0]"
|
||||||
|
>
|
||||||
|
{{ sharedLabels.filters[option[1]] }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="requests-ordering-direction">{{ t('views.admin.moderation.RequestsList.ordering.direction.label') }}</label>
|
||||||
|
<select
|
||||||
|
id="requests-ordering-direction"
|
||||||
|
v-model="orderingDirection"
|
||||||
|
class="ui dropdown"
|
||||||
|
>
|
||||||
|
<option value="+">
|
||||||
|
{{ t('views.admin.moderation.RequestsList.ordering.direction.ascending') }}
|
||||||
|
</option>
|
||||||
|
<option value="-">
|
||||||
|
{{ t('views.admin.moderation.RequestsList.ordering.direction.descending') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Loader v-if="isLoading" />
|
||||||
|
<div v-else-if="!result || result.count === 0">
|
||||||
|
<Spacer />
|
||||||
|
<empty-state
|
||||||
|
:refresh="true"
|
||||||
|
@refresh="fetchData()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<Spacer />
|
||||||
|
<user-request-card
|
||||||
|
v-for="obj in result.results"
|
||||||
|
:key="obj.uuid"
|
||||||
|
:init-obj="obj"
|
||||||
|
@handled="fetchData"
|
||||||
|
/>
|
||||||
|
<Pagination
|
||||||
|
v-if="result.count > paginateBy"
|
||||||
|
v-model:current="page"
|
||||||
|
:paginate-by="paginateBy"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Reference in New Issue