From d0bbeeee526543b48e3143231fabaa0c0e10e0c4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 1 Mar 2023 14:22:53 +0900 Subject: [PATCH] :art: --- .../server/api/endpoints/admin/roles/users.ts | 1 + .../frontend/src/pages/admin/roles.role.vue | 69 +++++++++----- packages/frontend/src/pages/my-lists/list.vue | 92 +++++++++---------- 3 files changed, 90 insertions(+), 72 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index 930d3fee40..35edca5460 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -69,6 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { return await Promise.all(assigns.map(async assign => ({ id: assign.id, + createdAt: assign.createdAt, user: await this.userEntityService.pack(assign.user!, me, { detail: true }), expiresAt: assign.expiresAt, }))); diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index 7951086bf1..c5792f649c 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -30,12 +30,19 @@ <template #default="{ items }"> <div class="_gaps_s"> - <div v-for="item in items" :key="item.user.id" :class="$style.userItem"> - <MkA :class="$style.user" :to="`/user-info/${item.user.id}`"> - <MkUserCardMini :user="item.user"/> - </MkA> - <button v-if="item.expiresAt != null" class="_button" :class="$style.expiresAt" @click="showExpireInfo(item, $event)"><i class="ti ti-clock-hour-3"></i></button> - <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> + <div v-for="item in items" :key="item.user.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="`/user-info/${item.user.id}`"> + <MkUserCardMini :user="item.user"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub"> + <div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ item.expiresAt.toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </div> </div> </div> </template> @@ -77,6 +84,8 @@ const usersPagination = { })), }; +let expandedItems = $ref([]); + const role = reactive(await os.api('admin/roles/show', { roleId: props.id, })); @@ -129,7 +138,7 @@ async function assign() { : null; await os.apiWithDialog('admin/roles/assign', { roleId: role.id, userId: user.id, expiresAt }); - role.users.push(user); + //role.users.push(user); } async function unassign(user, ev) { @@ -139,16 +148,17 @@ async function unassign(user, ev) { danger: true, action: async () => { await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.id }); - role.users = role.users.filter(u => u.id !== user.id); + //role.users = role.users.filter(u => u.id !== user.id); }, }], ev.currentTarget ?? ev.target); } -async function showExpireInfo(assignment) { - os.alert({ - type: 'info', - text: assignment.expiresAt.toLocaleString(), - }); +async function toggleItem(item) { + if (expandedItems.includes(item.id)) { + expandedItems = expandedItems.filter(x => x !== item.id); + } else { + expandedItems.push(item.id); + } } const headerActions = $computed(() => []); @@ -162,24 +172,41 @@ definePageMetadata(computed(() => ({ </script> <style lang="scss" module> -.userItem { +.userItemMain { display: flex; } -.user { - flex: 1; - min-width: 0; +.userItemSub { + padding: 6px 12px; + font-size: 85%; + color: var(--fgTransparentWeak); } -.expiresAt, +.userItemMainBody { + flex: 1; + min-width: 0; + margin-right: 8px; + + &:hover { + text-decoration: none; + } +} + +.userToggle, .unassign { width: 32px; height: 32px; - margin-left: 8px; align-self: center; } -.expiresAt + .unassign { - margin-left: 0; +.chevron { + display: block; + transition: transform 0.1s ease-out; +} + +.userItem.userItemOpend { + .chevron { + transform: rotateX(180deg); + } } </style> diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index f47b4bf90f..a6a3974d0c 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -2,13 +2,13 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="700"> - <div class="mk-list-page"> + <div> <Transition :name="$store.state.animation ? '_transition_zoom' : ''" mode="out-in"> <div v-if="list" class=""> - <div class=""> + <div class="_buttons"> <MkButton inline @click="addUser()">{{ i18n.ts.addUser }}</MkButton> <MkButton inline @click="renameList()">{{ i18n.ts.rename }}</MkButton> - <MkButton inline @click="deleteList()">{{ i18n.ts.delete }}</MkButton> + <MkButton inline danger @click="deleteList()">{{ i18n.ts.delete }}</MkButton> </div> </div> </Transition> @@ -16,18 +16,12 @@ <Transition :name="$store.state.animation ? '_transition_zoom' : ''" mode="out-in"> <div v-if="list" class="members _margin"> <div class="">{{ i18n.ts.members }}</div> - <div class=""> - <div class="users"> - <div v-for="user in users" :key="user.id" class="user _panel"> - <MkAvatar :user="user" class="avatar" indicator link preview/> - <div class="body"> - <MkUserName :user="user" class="name"/> - <MkAcct :user="user" class="acct"/> - </div> - <div class="action"> - <button class="_button" @click="removeUser(user)"><i class="ti ti-x"></i></button> - </div> - </div> + <div class="_gaps_s"> + <div v-for="user in users" :key="user.id" :class="$style.userItem"> + <MkA :class="$style.userItemBody" :to="`${userPage(user)}`"> + <MkUserCardMini :user="user"/> + </MkA> + <button class="_button" :class="$style.remove" @click="removeUser(user, $event)"><i class="ti ti-x"></i></button> </div> </div> </div> @@ -44,6 +38,8 @@ import * as os from '@/os'; import { mainRouter } from '@/router'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; +import { userPage } from '@/filters/user'; +import MkUserCardMini from '@/components/MkUserCardMini.vue'; const props = defineProps<{ listId: string; @@ -76,13 +72,20 @@ function addUser() { }); } -function removeUser(user) { - os.api('users/lists/pull', { - listId: list.id, - userId: user.id, - }).then(() => { - users = users.filter(x => x.id !== user.id); - }); +async function removeUser(user, ev) { + os.popupMenu([{ + text: i18n.ts.remove, + icon: 'ti ti-x', + danger: true, + action: async () => { + os.api('users/lists/pull', { + listId: list.id, + userId: user.id, + }).then(() => { + users = users.filter(x => x.id !== user.id); + }); + }, + }], ev.currentTarget ?? ev.target); } async function renameList() { @@ -126,37 +129,24 @@ definePageMetadata(computed(() => list ? { } : null)); </script> -<style lang="scss" scoped> -.mk-list-page { - > .members { - > ._content { - > .users { - > .user { - display: flex; - align-items: center; - padding: 16px; +<style lang="scss" module> +.userItem { + display: flex; +} - > .avatar { - width: 50px; - height: 50px; - } +.userItemBody { + flex: 1; + min-width: 0; + margin-right: 8px; - > .body { - flex: 1; - padding: 8px; - - > .name { - display: block; - font-weight: bold; - } - - > .acct { - opacity: 0.5; - } - } - } - } - } + &:hover { + text-decoration: none; } } + +.remove { + width: 32px; + height: 32px; + align-self: center; +} </style>