#179-suspensions #188
@@ -1,152 +1,157 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, watch } from "vue";
|
import { ref, computed, onMounted, watch } from "vue";
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import {
|
import {
|
||||||
Ellipsis, Search, Trash2, UserX,
|
Ellipsis, Search, Trash2, UserX,
|
||||||
X,
|
X,
|
||||||
} from "lucide-vue-next";
|
} from "lucide-vue-next";
|
||||||
import {
|
import {
|
||||||
Pagination,
|
Pagination,
|
||||||
PaginationContent,
|
PaginationContent,
|
||||||
PaginationEllipsis,
|
PaginationEllipsis,
|
||||||
PaginationItem,
|
PaginationItem,
|
||||||
PaginationNext,
|
PaginationNext,
|
||||||
PaginationPrevious,
|
PaginationPrevious,
|
||||||
} from '@/components/ui/pagination'
|
} from '@/components/ui/pagination'
|
||||||
|
|
||||||
// API & Types
|
// API & Types
|
||||||
import { getMembersFiltered } from "@/api/member";
|
import { getMembersFiltered } from "@/api/member";
|
||||||
import { getUnits } from "@/api/units";
|
import { getUnits } from "@/api/units";
|
||||||
import type { Member } from "@shared/types/member";
|
import type { Member } from "@shared/types/member";
|
||||||
import { MemberState } from "@shared/types/member";
|
import { MemberState } from "@shared/types/member";
|
||||||
import type { Unit } from "@shared/types/units";
|
import type { Unit } from "@shared/types/units";
|
||||||
import type { pagination as PaginationType } from "@shared/types/pagination";
|
import type { pagination as PaginationType } from "@shared/types/pagination";
|
||||||
|
|
||||||
// UI Components
|
// UI Components
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import Badge from "@/components/ui/badge/Badge.vue";
|
import Badge from "@/components/ui/badge/Badge.vue";
|
||||||
import Input from "@/components/ui/input/Input.vue";
|
import Input from "@/components/ui/input/Input.vue";
|
||||||
import Spinner from "@/components/ui/spinner/Spinner.vue";
|
import Spinner from "@/components/ui/spinner/Spinner.vue";
|
||||||
import DischargeMember from "@/components/members/DischargeMember.vue";
|
import DischargeMember from "@/components/members/DischargeMember.vue";
|
||||||
import MemberCard from "@/components/members/MemberCard.vue";
|
import MemberCard from "@/components/members/MemberCard.vue";
|
||||||
|
|
||||||
// --- State ---
|
// --- State ---
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const members = ref<Member[]>([]);
|
const members = ref<Member[]>([]);
|
||||||
const units = ref<Unit[]>([]);
|
const units = ref<Unit[]>([]);
|
||||||
const isLoaded = ref(false);
|
const isLoaded = ref(false);
|
||||||
const pagination = ref<PaginationType>({
|
const pagination = ref<PaginationType>({
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 15,
|
pageSize: 15,
|
||||||
total: 0,
|
total: 0,
|
||||||
totalPages: 0,
|
totalPages: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const filters = ref<{ search: string; status: "all" | MemberState; unitId: string }>({
|
const filters = ref<{ search: string; status: "all" | MemberState; unitId: string }>({
|
||||||
search: "",
|
|
||||||
status: MemberState.Member,
|
|
||||||
unitId: "all"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Pagination State
|
|
||||||
const pageNum = ref(1);
|
|
||||||
const pageSize = ref(15);
|
|
||||||
const pageSizeOptions = [10, 15, 30];
|
|
||||||
|
|
||||||
const MEMBER_STATUSES = Object.values(MemberState);
|
|
||||||
|
|
||||||
// --- Methods ---
|
|
||||||
const fetchMembers = async () => {
|
|
||||||
isLoaded.value = false;
|
|
||||||
try {
|
|
||||||
const result = await getMembersFiltered({
|
|
||||||
page: pageNum.value,
|
|
||||||
pageSize: pageSize.value,
|
|
||||||
search: filters.value.search || undefined,
|
|
||||||
status: filters.value.status,
|
|
||||||
unitId: filters.value.unitId,
|
|
||||||
});
|
|
||||||
members.value = result.data;
|
|
||||||
pagination.value = result.pagination;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch members:', error);
|
|
||||||
members.value = [];
|
|
||||||
} finally {
|
|
||||||
isLoaded.value = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchUnits = async () => {
|
|
||||||
try {
|
|
||||||
units.value = await getUnits();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch units:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navigateToMember = (id: string | number) => router.push(`/member/${id}`);
|
|
||||||
|
|
||||||
const setPage = (num: number) => {
|
|
||||||
pageNum.value = num;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setPageSize = (size: number) => {
|
|
||||||
pageSize.value = size;
|
|
||||||
pageNum.value = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Computed ---
|
|
||||||
const paginatedMembers = computed(() => members.value);
|
|
||||||
const totalItems = computed(() => pagination.value.total);
|
|
||||||
|
|
||||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
// Watch pagination (Immediate)
|
|
||||||
watch([pageNum, pageSize], () => {
|
|
||||||
if (debounceTimer) clearTimeout(debounceTimer);
|
|
||||||
fetchMembers();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch filters (Debounced)
|
|
||||||
watch(filters, () => {
|
|
||||||
if (debounceTimer) clearTimeout(debounceTimer);
|
|
||||||
debounceTimer = setTimeout(() => {
|
|
||||||
fetchMembers();
|
|
||||||
}, 300);
|
|
||||||
}, { deep: true });
|
|
||||||
|
|
||||||
function clearFilters() {
|
|
||||||
filters.value = {
|
|
||||||
search: "",
|
search: "",
|
||||||
status: "all",
|
status: MemberState.Member,
|
||||||
unitId: "all"
|
unitId: "all"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pagination State
|
||||||
|
const pageNum = ref(1);
|
||||||
|
const pageSize = ref(15);
|
||||||
|
const pageSizeOptions = [10, 15, 30];
|
||||||
|
|
||||||
|
const MEMBER_STATUSES = Object.entries(MemberState)
|
||||||
|
.filter(([key, value]) => isNaN(Number(key)))
|
||||||
|
.map(([label, id]) => ({
|
||||||
|
label,
|
||||||
|
id: id as number // Casting back to number for your SQL logic
|
||||||
|
}));
|
||||||
|
|
||||||
|
// --- Methods ---
|
||||||
|
const fetchMembers = async () => {
|
||||||
|
isLoaded.value = false;
|
||||||
|
try {
|
||||||
|
const result = await getMembersFiltered({
|
||||||
|
page: pageNum.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
search: filters.value.search || undefined,
|
||||||
|
status: filters.value.status,
|
||||||
|
unitId: filters.value.unitId,
|
||||||
|
});
|
||||||
|
members.value = result.data;
|
||||||
|
pagination.value = result.pagination;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch members:', error);
|
||||||
|
members.value = [];
|
||||||
|
} finally {
|
||||||
|
isLoaded.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchUnits = async () => {
|
||||||
|
try {
|
||||||
|
units.value = await getUnits();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch units:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigateToMember = (id: string | number) => router.push(`/member/${id}`);
|
||||||
|
|
||||||
|
const setPage = (num: number) => {
|
||||||
|
pageNum.value = num;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPageSize = (size: number) => {
|
||||||
|
pageSize.value = size;
|
||||||
|
pageNum.value = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Computed ---
|
||||||
|
const paginatedMembers = computed(() => members.value);
|
||||||
|
const totalItems = computed(() => pagination.value.total);
|
||||||
|
|
||||||
|
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
// Watch pagination (Immediate)
|
||||||
|
watch([pageNum, pageSize], () => {
|
||||||
|
if (debounceTimer) clearTimeout(debounceTimer);
|
||||||
|
fetchMembers();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watch filters (Debounced)
|
||||||
|
watch(filters, () => {
|
||||||
|
if (debounceTimer) clearTimeout(debounceTimer);
|
||||||
|
debounceTimer = setTimeout(() => {
|
||||||
|
fetchMembers();
|
||||||
|
}, 300);
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
function clearFilters() {
|
||||||
|
filters.value = {
|
||||||
|
search: "",
|
||||||
|
status: "all",
|
||||||
|
unitId: "all"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchUnits();
|
fetchUnits();
|
||||||
fetchMembers();
|
fetchMembers();
|
||||||
});
|
});
|
||||||
|
|
||||||
//discharge form logic
|
//discharge form logic
|
||||||
const isDischargeOpen = ref(false)
|
const isDischargeOpen = ref(false)
|
||||||
const targetMember = ref(null)
|
const targetMember = ref(null)
|
||||||
|
|
||||||
function openDischargeModal(member: Member) {
|
function openDischargeModal(member: Member) {
|
||||||
targetMember.value = member
|
targetMember.value = member
|
||||||
isDischargeOpen.value = true
|
isDischargeOpen.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function suspendMember(member: Member) {
|
function suspendMember(member: Member) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDischargeSuccess(data) {
|
function handleDischargeSuccess(data) {
|
||||||
fetchMembers();
|
fetchMembers();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -171,8 +176,8 @@ function handleDischargeSuccess(data) {
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="all">All Statuses</SelectItem>
|
<SelectItem value="all">All Statuses</SelectItem>
|
||||||
<SelectItem v-for="s in MEMBER_STATUSES" :key="s" :value="s">
|
<SelectItem v-for="s in MEMBER_STATUSES" :key="s.id" :value="s.id">
|
||||||
<span class="capitalize">{{ s }}</span>
|
<span class="capitalize">{{ s.label }}</span>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
Reference in New Issue
Block a user