summaryrefslogtreecommitdiff
path: root/packages/frontend/src/pages/admin/users.vue
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-12-27 14:36:33 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-12-27 14:36:33 +0900
commit9384f5399da39e53855beb8e7f8ded1aa56bf72e (patch)
treece5959571a981b9c4047da3c7b3fd080aa44222c /packages/frontend/src/pages/admin/users.vue
parentwip: retention for dashboard (diff)
downloadmisskey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.tar.gz
misskey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.tar.bz2
misskey-9384f5399da39e53855beb8e7f8ded1aa56bf72e.zip
rename: client -> frontend
Diffstat (limited to 'packages/frontend/src/pages/admin/users.vue')
-rw-r--r--packages/frontend/src/pages/admin/users.vue170
1 files changed, 170 insertions, 0 deletions
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
new file mode 100644
index 0000000000..d466e21907
--- /dev/null
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -0,0 +1,170 @@
+<template>
+<div>
+ <MkStickyContainer>
+ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
+ <MkSpacer :content-max="900">
+ <div class="lknzcolw">
+ <div class="users">
+ <div class="inputs">
+ <MkSelect v-model="sort" style="flex: 1;">
+ <template #label>{{ i18n.ts.sort }}</template>
+ <option value="-createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.ascendingOrder }})</option>
+ <option value="+createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.descendingOrder }})</option>
+ <option value="-updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.ascendingOrder }})</option>
+ <option value="+updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.descendingOrder }})</option>
+ </MkSelect>
+ <MkSelect v-model="state" style="flex: 1;">
+ <template #label>{{ i18n.ts.state }}</template>
+ <option value="all">{{ i18n.ts.all }}</option>
+ <option value="available">{{ i18n.ts.normal }}</option>
+ <option value="admin">{{ i18n.ts.administrator }}</option>
+ <option value="moderator">{{ i18n.ts.moderator }}</option>
+ <option value="silenced">{{ i18n.ts.silence }}</option>
+ <option value="suspended">{{ i18n.ts.suspend }}</option>
+ </MkSelect>
+ <MkSelect v-model="origin" style="flex: 1;">
+ <template #label>{{ i18n.ts.instance }}</template>
+ <option value="combined">{{ i18n.ts.all }}</option>
+ <option value="local">{{ i18n.ts.local }}</option>
+ <option value="remote">{{ i18n.ts.remote }}</option>
+ </MkSelect>
+ </div>
+ <div class="inputs">
+ <MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false" @update:model-value="$refs.users.reload()">
+ <template #prefix>@</template>
+ <template #label>{{ i18n.ts.username }}</template>
+ </MkInput>
+ <MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'" @update:model-value="$refs.users.reload()">
+ <template #prefix>@</template>
+ <template #label>{{ i18n.ts.host }}</template>
+ </MkInput>
+ </div>
+
+ <MkPagination v-slot="{items}" ref="paginationComponent" :pagination="pagination" class="users">
+ <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${new Date(user.updatedAt).toLocaleString()}`" class="user" :to="`/user-info/${user.id}`">
+ <MkUserCardMini :user="user"/>
+ </MkA>
+ </MkPagination>
+ </div>
+ </div>
+ </MkSpacer>
+ </MkStickyContainer>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue';
+import XHeader from './_header_.vue';
+import MkInput from '@/components/form/input.vue';
+import MkSelect from '@/components/form/select.vue';
+import MkPagination from '@/components/MkPagination.vue';
+import * as os from '@/os';
+import { lookupUser } from '@/scripts/lookup-user';
+import { i18n } from '@/i18n';
+import { definePageMetadata } from '@/scripts/page-metadata';
+import MkUserCardMini from '@/components/MkUserCardMini.vue';
+
+let paginationComponent = $ref<InstanceType<typeof MkPagination>>();
+
+let sort = $ref('+createdAt');
+let state = $ref('all');
+let origin = $ref('local');
+let searchUsername = $ref('');
+let searchHost = $ref('');
+const pagination = {
+ endpoint: 'admin/show-users' as const,
+ limit: 10,
+ params: computed(() => ({
+ sort: sort,
+ state: state,
+ origin: origin,
+ username: searchUsername,
+ hostname: searchHost,
+ })),
+ offsetMode: true,
+};
+
+function searchUser() {
+ os.selectUser().then(user => {
+ show(user);
+ });
+}
+
+async function addUser() {
+ const { canceled: canceled1, result: username } = await os.inputText({
+ title: i18n.ts.username,
+ });
+ if (canceled1) return;
+
+ const { canceled: canceled2, result: password } = await os.inputText({
+ title: i18n.ts.password,
+ type: 'password',
+ });
+ if (canceled2) return;
+
+ os.apiWithDialog('admin/accounts/create', {
+ username: username,
+ password: password,
+ }).then(res => {
+ paginationComponent.reload();
+ });
+}
+
+function show(user) {
+ os.pageWindow(`/user-info/${user.id}`);
+}
+
+const headerActions = $computed(() => [{
+ icon: 'ti ti-search',
+ text: i18n.ts.search,
+ handler: searchUser,
+}, {
+ asFullButton: true,
+ icon: 'ti ti-plus',
+ text: i18n.ts.addUser,
+ handler: addUser,
+}, {
+ asFullButton: true,
+ icon: 'ti ti-search',
+ text: i18n.ts.lookup,
+ handler: lookupUser,
+}]);
+
+const headerTabs = $computed(() => []);
+
+definePageMetadata(computed(() => ({
+ title: i18n.ts.users,
+ icon: 'ti ti-users',
+})));
+</script>
+
+<style lang="scss" scoped>
+.lknzcolw {
+ > .users {
+
+ > .inputs {
+ display: flex;
+ margin-bottom: 16px;
+
+ > * {
+ margin-right: 16px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ }
+
+ > .users {
+ margin-top: var(--margin);
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
+ grid-gap: 12px;
+
+ > .user:hover {
+ text-decoration: none;
+ }
+ }
+ }
+}
+</style>