core/app/pages/admin/users/index.vue

177 lines
6.7 KiB
Vue

<script setup lang="ts">
import AppSidebar from '@/components/AppSidebar.vue'
import SiteHeader from '@/components/SiteHeader.vue'
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar'
import { ChevronDown, Plus, X } from '@lucide/vue'
import { computed, ref } from 'vue'
import { toast } from 'vue-sonner'
import DataTable from '@/components/data-table/DataTable.vue'
import DeleteDialog from '@/components/DeleteDialog.vue'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { type User, getColumns } from './columns'
const router = useRouter()
const config = useRuntimeConfig()
const statusFilter = ref('')
const { data: users, status, refresh } = useFetch<User[]>(
() => `${config.public.apiBase}/users${statusFilter.value ? `?status=${statusFilter.value}` : ''}`,
{ watch: [statusFilter] },
)
const tableRef = ref<InstanceType<typeof DataTable>>()
const deleteOpen = ref(false)
const deleteLoading = ref(false)
const selectedUser = ref<User | null>(null)
function handleEdit(user: User) {
router.push(`/admin/users/${user.id}/edit`)
}
function handleDelete(user: User) {
selectedUser.value = user
deleteOpen.value = true
}
const columns = computed(() => getColumns({ onEdit: handleEdit, onDelete: handleDelete }))
async function handleConfirmDelete() {
if (!selectedUser.value) return
deleteLoading.value = true
try {
await $fetch(`${config.public.apiBase}/users/${selectedUser.value.id}`, {
method: 'DELETE',
})
toast.success('Pengguna berhasil dihapus')
deleteOpen.value = false
selectedUser.value = null
await refresh()
} catch (e: any) {
toast.error(e?.data?.message ?? 'Gagal menghapus pengguna')
} finally {
deleteLoading.value = false
}
}
function hasActiveFilter() {
return !!statusFilter.value
}
function resetFilters() {
statusFilter.value = ''
}
</script>
<template>
<SidebarProvider :style="{
'--sidebar-width': 'calc(var(--spacing) * 72)',
'--header-height': 'calc(var(--spacing) * 12)',
}">
<AppSidebar variant="inset" />
<SidebarInset>
<SiteHeader />
<div class="flex flex-1 flex-col">
<div class="@container/main flex flex-1 flex-col gap-2">
<div class="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
<div class="px-4 lg:px-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold">Pengguna</h1>
<Button size="sm" @click="router.push('/admin/users/create')">
<Plus class="h-4 w-4 mr-1" />
Tambah
</Button>
</div>
</div>
<div class="px-4 lg:px-6">
<DataTable ref="tableRef" :columns="columns" :data="users ?? []" :loading="status === 'pending'">
<template #filters="{ table }">
<div class="flex items-center gap-2 py-4">
<Input class="max-w-sm" placeholder="Cari email atau nama..."
:model-value="(table.getColumn('search')?.getFilterValue() as string) ?? ''"
@update:model-value="table.getColumn('search')?.setFilterValue($event)" />
<Select :model-value="statusFilter || 'all'"
@update:model-value="statusFilter = $event === 'all' ? '' : $event">
<SelectTrigger class="hidden h-8 w-auto md:flex">
<SelectValue placeholder="Semua Status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Semua Status</SelectItem>
<SelectItem value="ACTIVE">Aktif</SelectItem>
<SelectItem value="ONBOARDING">Onboarding</SelectItem>
<SelectItem value="INACTIVE">Nonaktif</SelectItem>
<SelectItem value="SUSPENDED">Ditangguhkan</SelectItem>
<SelectItem value="DISABLED">Dinonaktifkan</SelectItem>
</SelectContent>
</Select>
<Button v-if="hasActiveFilter()" variant="ghost" size="sm" class="h-8"
@click="resetFilters()">
<X class="h-4 w-4 mr-1" />
Reset
</Button>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="outline" class="ml-auto md:hidden">
Filter
<ChevronDown class="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Status</DropdownMenuLabel>
<div class="px-2 pb-2">
<Select :model-value="statusFilter || 'all'"
@update:model-value="statusFilter = $event === 'all' ? '' : $event">
<SelectTrigger class="h-8 w-37.5">
<SelectValue placeholder="Semua Status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Semua Status</SelectItem>
<SelectItem value="ACTIVE">Aktif</SelectItem>
<SelectItem value="ONBOARDING">Onboarding</SelectItem>
<SelectItem value="INACTIVE">Nonaktif</SelectItem>
<SelectItem value="SUSPENDED">Ditangguhkan</SelectItem>
<SelectItem value="DISABLED">Dinonaktifkan</SelectItem>
</SelectContent>
</Select>
</div>
<DropdownMenuSeparator />
<DropdownMenuItem :disabled="!hasActiveFilter()" @click="resetFilters()">
Reset Filter
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</template>
</DataTable>
</div>
</div>
</div>
</div>
</SidebarInset>
<DeleteDialog v-model:open="deleteOpen" title="Hapus Pengguna"
:item-name="selectedUser?.email ?? selectedUser?.username" :loading="deleteLoading"
@confirm="handleConfirmDelete" />
</SidebarProvider>
</template>