summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/users.ts1
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue65
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue92
3 files changed, 88 insertions, 70 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 {
+.userItemSub {
+ padding: 6px 12px;
+ font-size: 85%;
+ color: var(--fgTransparentWeak);
+}
+
+.userItemMainBody {
flex: 1;
min-width: 0;
+ margin-right: 8px;
+
+ &:hover {
+ text-decoration: none;
+ }
}
-.expiresAt,
+.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;
-
- > .avatar {
- width: 50px;
- height: 50px;
- }
-
- > .body {
- flex: 1;
- padding: 8px;
+<style lang="scss" module>
+.userItem {
+ display: flex;
+}
- > .name {
- display: block;
- font-weight: bold;
- }
+.userItemBody {
+ flex: 1;
+ min-width: 0;
+ margin-right: 8px;
- > .acct {
- opacity: 0.5;
- }
- }
- }
- }
- }
+ &:hover {
+ text-decoration: none;
}
}
+
+.remove {
+ width: 32px;
+ height: 32px;
+ align-self: center;
+}
</style>