Files
milsim-site-v4/ui/src/components/roles/addMember.vue

103 lines
3.4 KiB
Vue

<script setup lang="ts">
import { addMemberToRole, removeMemberFromRole } from '@/api/roles';
import { MemberLight } from '@shared/types/member';
import { Role } from '@shared/types/roles';
import { computed, ref } from 'vue';
import Dialog from '../ui/dialog/Dialog.vue'
import DialogContent from '../ui/dialog/DialogContent.vue'
import DialogHeader from '../ui/dialog/DialogHeader.vue'
import DialogTitle from '../ui/dialog/DialogTitle.vue'
import DialogDescription from '../ui/dialog/DialogDescription.vue'
import DialogFooter from '../ui/dialog/DialogFooter.vue'
import Button from '../ui/button/Button.vue';
import InputGroup from '../ui/input-group/InputGroup.vue';
import InputGroupAddon from '../ui/input-group/InputGroupAddon.vue';
import { SearchIcon } from 'lucide-vue-next';
const props = defineProps<{
allMembers: MemberLight[],
role: Role
}>()
const emit = defineEmits(['submit'])
const showAddMemberDialog = ref(false)
const memberToAdd = ref<MemberLight | null>(null)
const searchQuery = ref('')
const filteredMembers = computed(() => {
const q = searchQuery.value.trim().toLowerCase()
if (!q) return props.allMembers
return props.allMembers.filter(m =>
m.displayName?.toLowerCase().includes(q) ||
m.username?.toLowerCase().includes(q)
)
})
defineExpose({
openDialog,
})
function openDialog() {
showAddMemberDialog.value = true;
}
async function handleAddMember() {
//guard
if (memberToAdd.value == null)
return;
await addMemberToRole(memberToAdd.value.id, props.role.id);
emit('submit');
showAddMemberDialog.value = false;
}
</script>
<template>
<Dialog v-model:open="showAddMemberDialog">
<DialogContent class="sm:max-w-md">
<DialogHeader>
<DialogTitle>Add member to {{ props.role?.name }}</DialogTitle>
<DialogDescription>
Search for a member to add to this group.
</DialogDescription>
</DialogHeader>
<!-- Search -->
<InputGroup>
<InputGroupAddon>
<SearchIcon class="h-4 w-4 text-muted-foreground" />
</InputGroupAddon>
<input v-model="searchQuery" type="text" placeholder="Search members…"
class="flex-1 bg-transparent outline-none text-sm" />
</InputGroup>
<!-- Results -->
<div class="mt-3 h-64 overflow-y-auto scrollbar-themed rounded-md border">
<ul class="divide-y">
<li v-for="member in filteredMembers" :key="member.id" @click="memberToAdd = member"
class="flex items-center justify-between px-3 py-2 cursor-pointer hover:bg-muted"
:class="memberToAdd?.id === member.id && 'bg-muted'">
<span>{{ member.displayName || member.username }}</span>
<span v-if="memberToAdd?.id === member.id" class="text-xs text-muted-foreground">
Selected
</span>
</li>
</ul>
</div>
<DialogFooter>
<Button variant="secondary" @click="showAddMemberDialog = false">
Cancel
</Button>
<Button :disabled="!memberToAdd" @click="handleAddMember">
Add
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>