Skip to content

Commit

Permalink
add: approval section in control panel
Browse files Browse the repository at this point in the history
  • Loading branch information
Mar0xy authored and kakkokari-gtyih committed Dec 29, 2023
1 parent d222c79 commit 37a1418
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 1 deletion.
1 change: 1 addition & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface Locale {
"uploading": string;
"save": string;
"users": string;
"approvals": string;
"addUser": string;
"favorite": string;
"favorites": string;
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ signup: "新規登録"
uploading: "アップロード中"
save: "保存"
users: "ユーザー"
approvals: "承認"
addUser: "ユーザーを追加"
favorite: "お気に入り"
favorites: "お気に入り"
Expand Down
114 changes: 114 additions & 0 deletions packages/frontend/src/components/SkApprovalUser.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<template>
<MkFolder :expanded="false">
<template #icon><i class="ph-user ph-bold ph-lg"></i></template>
<template #label>{{ i18n.ts.user }}: {{ user.username }}</template>

<div class="_gaps_s" :class="$style.root">
<div :class="$style.items">
<div>
<div :class="$style.label">{{ i18n.ts.createdAt }}</div>
<div><MkTime :time="user.createdAt" mode="absolute"/></div>
</div>
<div v-if="email">
<div :class="$style.label">{{ i18n.ts.emailAddress }}</div>
<div>{{ email }}</div>
</div>
<div>
<div :class="$style.label">Reason</div>
<div>{{ reason }}</div>
</div>
</div>
<div :class="$style.buttons">
<MkButton inline success @click="approveAccount(user)">{{ i18n.ts.approveAccount }}</MkButton>
<MkButton inline danger @click="deleteAccount(user)">{{ i18n.ts.denyAccount }}</MkButton>
</div>
</div>
</MkFolder>
</template>

<script lang="ts" setup>
import * as Misskey from 'misskey-js';
import MkFolder from '@/components/MkFolder.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
const props = defineProps<{
user: Misskey.entities.User;
}>();
let reason = $ref('');
let email = $ref('');
function getReason() {
return os.api('admin/show-user', {
userId: props.user.id,
}).then(info => {
reason = info?.signupReason;
email = info?.email;
});
}
getReason();
const emits = defineEmits<{
(event: 'deleted', value: string): void;
}>();
async function deleteAccount(user) {
const confirm = await os.confirm({
type: 'warning',
text: i18n.ts.deleteAccountConfirm,
});
if (confirm.canceled) return;
const typed = await os.inputText({
text: i18n.t('typeToConfirm', { x: user?.username }),
});
if (typed.canceled) return;
if (typed.result === user?.username) {
await os.apiWithDialog('admin/delete-account', {
userId: user.id,
});
emits('deleted', user.id);
} else {
os.alert({
type: 'error',
text: 'input not match',
});
}
}
async function approveAccount(user) {
const confirm = await os.confirm({
type: 'warning',
text: i18n.ts.approveConfirm,
});
if (confirm.canceled) return;
await os.api('admin/approve-user', { userId: user.id });
emits('deleted', user.id);
}
</script>

<style lang="scss" module>
.root {
text-align: left;
}
.items {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
grid-gap: 12px;
}
.label {
font-size: 0.85em;
padding: 0 0 8px 0;
user-select: none;
opacity: 0.7;
}
.buttons {
display: flex;
gap: 8px;
}
</style>
72 changes: 72 additions & 0 deletions packages/frontend/src/pages/admin/approvals.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<div>
<MkStickyContainer>
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="900">
<div class="_gaps_m">
<MkPagination ref="paginationComponent" :pagination="pagination">
<template #default="{ items }">
<div class="_gaps_s">
<SkApprovalUser v-for="item in items" :key="item.id" :user="(item as any)" :onDeleted="deleted"/>
</div>
</template>
</MkPagination>
</div>
</MkSpacer>
</MkStickyContainer>
</div>
</template>

<script lang="ts" setup>
import { computed, shallowRef } from 'vue';
import XHeader from './_header_.vue';
import MkPagination from '@/components/MkPagination.vue';
import SkApprovalUser from '@/components/SkApprovalUser.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';

let paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();

const pagination = {
endpoint: 'admin/show-users' as const,
limit: 10,
params: computed(() => ({
sort: '+createdAt',
state: 'approved',
origin: 'local',
})),
offsetMode: true,
};

function deleted(id: string) {
if (paginationComponent.value) {
paginationComponent.value.items.delete(id);
}
}

const headerActions = $computed(() => []);

const headerTabs = $computed(() => []);

definePageMetadata(computed(() => ({
title: i18n.ts.approvals,
icon: 'ph-chalkboard-teacher ph-bold pg-lg',
})));
</script>

<style lang="scss" module>
.inputs {
display: flex;
gap: 8px;
flex-wrap: wrap;
}

.input {
flex: 1;
}
</style>
7 changes: 6 additions & 1 deletion packages/frontend/src/pages/admin/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
<MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
<MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
<MkInfo v-if="pendingUserApprovals" warn class="info">{{ i18n.ts.pendingUserApprovals }} <MkA to="/admin/users" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>
<MkInfo v-if="pendingUserApprovals" warn class="info">{{ i18n.ts.pendingUserApprovals }} <MkA to="/admin/approvals" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>

<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
</div>
Expand Down Expand Up @@ -114,6 +114,11 @@ const menuDef = computed(() => [{
text: i18n.ts.invite,
to: '/admin/invites',
active: currentPage.value?.route.name === 'invites',
}, {
icon: 'ti ti-check',
text: i18n.ts.approvals,
to: '/admin/approvals',
active: currentPage.value?.route.name === 'approvals',
}, {
icon: 'ti ti-badges',
text: i18n.ts.roles,
Expand Down
4 changes: 4 additions & 0 deletions packages/frontend/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ export const routes = [{
path: '/invites',
name: 'invites',
component: page(() => import('./pages/admin/invites.vue')),
}, {
path: '/approvals',
name: 'approvals',
component: page(() => import('./pages/admin/approvals.vue')),
}, {
path: '/',
component: page(() => import('./pages/_empty_.vue')),
Expand Down

0 comments on commit 37a1418

Please sign in to comment.