update filter for customer features
This commit is contained in:
@@ -120,6 +120,9 @@ export async function PUT(
|
||||
lastNameJp: body.customerInfo.lastNameJp,
|
||||
firstNameJpKana: body.customerInfo.firstNameJpKana,
|
||||
lastNameJpKana: body.customerInfo.lastNameJpKana,
|
||||
gender: body.customerInfo.gender,
|
||||
martial: body.customerInfo.martial,
|
||||
status: body.customerInfo.status,
|
||||
email: body.customerContact.email,
|
||||
mobile: body.customerContact.mobile,
|
||||
originAdd1: body.customerContact.originAdd1,
|
||||
|
||||
@@ -23,6 +23,9 @@ export async function POST(request: NextRequest) {
|
||||
lastNameJp: validatedData.customerInfo.lastNameJp,
|
||||
firstNameJpKana: validatedData.customerInfo.firstNameJpKana,
|
||||
lastNameJpKana: validatedData.customerInfo.lastNameJpKana,
|
||||
gender: validatedData.customerInfo.gender,
|
||||
martial: validatedData.customerInfo.martial,
|
||||
status: validatedData.customerInfo.status,
|
||||
email: validatedData.customerContact.email,
|
||||
mobile: validatedData.customerContact.mobile,
|
||||
originAdd1: validatedData.customerContact.originAdd1,
|
||||
@@ -110,6 +113,9 @@ export async function GET(request: NextRequest) {
|
||||
const page = parseInt(searchParams.get("page") || "1");
|
||||
const pageSize = parseInt(searchParams.get("pageSize") || "10");
|
||||
const search = searchParams.get("search") || "";
|
||||
const gender = searchParams.get("gender") || "";
|
||||
const martial = searchParams.get("martial") || "";
|
||||
const status = searchParams.get("status") || "";
|
||||
const sortBy = searchParams.get("sortBy") || "id";
|
||||
const sortOrder = searchParams.get("sortOrder") || "asc";
|
||||
|
||||
@@ -137,6 +143,27 @@ export async function GET(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// Apply gender filter
|
||||
if (gender) {
|
||||
customersWithDependants = customersWithDependants.filter((customer) =>
|
||||
customer.gender.toLowerCase() === gender.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
// Apply martial status filter
|
||||
if (martial) {
|
||||
customersWithDependants = customersWithDependants.filter((customer) =>
|
||||
customer.martial.toLowerCase() === martial.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
// Apply status filter
|
||||
if (status) {
|
||||
customersWithDependants = customersWithDependants.filter((customer) =>
|
||||
customer.status.toLowerCase() === status.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
customersWithDependants.sort((a, b) => {
|
||||
let aValue: string | number = a[sortBy as keyof typeof a] as string | number;
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Plus, Trash2, ArrowLeft, Save, Loader2 } from "lucide-react";
|
||||
@@ -45,6 +46,9 @@ export default function CustomerEditPage() {
|
||||
lastNameJp: "",
|
||||
firstNameJpKana: "",
|
||||
lastNameJpKana: "",
|
||||
gender: "",
|
||||
martial: "",
|
||||
status: "",
|
||||
},
|
||||
customerContact: {
|
||||
email: "",
|
||||
@@ -96,14 +100,14 @@ export default function CustomerEditPage() {
|
||||
form.setValue("customerInfo.lastNameJp", customerData.lastNameJp);
|
||||
form.setValue("customerInfo.firstNameJpKana", customerData.firstNameJpKana);
|
||||
form.setValue("customerInfo.lastNameJpKana", customerData.lastNameJpKana);
|
||||
form.setValue("customerInfo.firstNameJp", customerData.firstNameJp);
|
||||
form.setValue("customerInfo.lastNameJp", customerData.lastNameJp);
|
||||
form.setValue("customerInfo.firstNameJpKana", customerData.firstNameJpKana);
|
||||
form.setValue("customerInfo.lastNameJpKana", customerData.lastNameJpKana);
|
||||
form.setValue("customerInfo.gender", customerData.gender || "");
|
||||
form.setValue("customerInfo.martial", customerData.martial || "");
|
||||
form.setValue("customerInfo.status", customerData.status || "");
|
||||
form.setValue("customerContact.email", customerData.email);
|
||||
form.setValue("customerContact.mobile", customerData.mobile);
|
||||
form.setValue("customerContact.originAdd1", customerData.originAdd1);
|
||||
form.setValue("customerContact.localAdd1", customerData.localAdd1);
|
||||
form.setValue("customerContact.localAdd1", customerData.localAdd1);
|
||||
|
||||
// Convert dependants to the expected format
|
||||
const dependants: CustomerDependantForm[] = customerData.dependants.map((dep: {
|
||||
@@ -292,6 +296,13 @@ export default function CustomerEditPage() {
|
||||
const isValid = await form.trigger([
|
||||
"customerInfo.firstNameEn",
|
||||
"customerInfo.lastNameEn",
|
||||
"customerInfo.firstNameJp",
|
||||
"customerInfo.lastNameJp",
|
||||
"customerInfo.firstNameJpKana",
|
||||
"customerInfo.lastNameJpKana",
|
||||
"customerInfo.gender",
|
||||
"customerInfo.martial",
|
||||
"customerInfo.status"
|
||||
]);
|
||||
if (isValid) {
|
||||
setActiveTab("contact");
|
||||
@@ -330,8 +341,22 @@ export default function CustomerEditPage() {
|
||||
return (
|
||||
values.customerInfo?.firstNameEn?.trim() !== "" &&
|
||||
values.customerInfo?.lastNameEn?.trim() !== "" &&
|
||||
values.customerInfo?.firstNameJp?.trim() !== "" &&
|
||||
values.customerInfo?.lastNameJp?.trim() !== "" &&
|
||||
values.customerInfo?.firstNameJpKana?.trim() !== "" &&
|
||||
values.customerInfo?.lastNameJpKana?.trim() !== "" &&
|
||||
values.customerInfo?.gender?.trim() !== "" &&
|
||||
values.customerInfo?.martial?.trim() !== "" &&
|
||||
values.customerInfo?.status?.trim() !== "" &&
|
||||
!errors.customerInfo?.firstNameEn &&
|
||||
!errors.customerInfo?.lastNameEn
|
||||
!errors.customerInfo?.lastNameEn &&
|
||||
!errors.customerInfo?.firstNameJp &&
|
||||
!errors.customerInfo?.lastNameJp &&
|
||||
!errors.customerInfo?.firstNameJpKana &&
|
||||
!errors.customerInfo?.lastNameJpKana &&
|
||||
!errors.customerInfo?.gender &&
|
||||
!errors.customerInfo?.martial &&
|
||||
!errors.customerInfo?.status
|
||||
);
|
||||
} else if (tabName === "contact") {
|
||||
return (
|
||||
@@ -490,6 +515,73 @@ export default function CustomerEditPage() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerInfo.gender"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Gender</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select gender" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="male">Male</SelectItem>
|
||||
<SelectItem value="female">Female</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerInfo.martial"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Marital Status</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select marital status" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="single">Single</SelectItem>
|
||||
<SelectItem value="married">Married</SelectItem>
|
||||
<SelectItem value="divorced">Divorced</SelectItem>
|
||||
<SelectItem value="widowed">Widowed</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerInfo.status"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Status</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select status" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="active">Active</SelectItem>
|
||||
<SelectItem value="inactive">Inactive</SelectItem>
|
||||
<SelectItem value="pending">Pending</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button type="button" onClick={handleNextTab}>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Plus, Trash2, ArrowLeft } from "lucide-react";
|
||||
@@ -39,6 +40,9 @@ export default function CustomerAddPage() {
|
||||
lastNameJp: "",
|
||||
firstNameJpKana: "",
|
||||
lastNameJpKana: "",
|
||||
gender: "",
|
||||
martial: "",
|
||||
status: "",
|
||||
},
|
||||
customerContact: {
|
||||
email: "",
|
||||
@@ -154,7 +158,10 @@ export default function CustomerAddPage() {
|
||||
"customerInfo.firstNameJp",
|
||||
"customerInfo.lastNameJp",
|
||||
"customerInfo.firstNameJpKana",
|
||||
"customerInfo.lastNameJpKana"
|
||||
"customerInfo.lastNameJpKana",
|
||||
"customerInfo.gender",
|
||||
"customerInfo.martial",
|
||||
"customerInfo.status"
|
||||
]);
|
||||
if (isValid) {
|
||||
setActiveTab("contact");
|
||||
@@ -193,8 +200,22 @@ export default function CustomerAddPage() {
|
||||
return (
|
||||
values.customerInfo?.firstNameEn?.trim() !== "" &&
|
||||
values.customerInfo?.lastNameEn?.trim() !== "" &&
|
||||
values.customerInfo?.firstNameJp?.trim() !== "" &&
|
||||
values.customerInfo?.lastNameJp?.trim() !== "" &&
|
||||
values.customerInfo?.firstNameJpKana?.trim() !== "" &&
|
||||
values.customerInfo?.lastNameJpKana?.trim() !== "" &&
|
||||
values.customerInfo?.gender?.trim() !== "" &&
|
||||
values.customerInfo?.martial?.trim() !== "" &&
|
||||
values.customerInfo?.status?.trim() !== "" &&
|
||||
!errors.customerInfo?.firstNameEn &&
|
||||
!errors.customerInfo?.lastNameEn
|
||||
!errors.customerInfo?.lastNameEn &&
|
||||
!errors.customerInfo?.firstNameJp &&
|
||||
!errors.customerInfo?.lastNameJp &&
|
||||
!errors.customerInfo?.firstNameJpKana &&
|
||||
!errors.customerInfo?.lastNameJpKana &&
|
||||
!errors.customerInfo?.gender &&
|
||||
!errors.customerInfo?.martial &&
|
||||
!errors.customerInfo?.status
|
||||
);
|
||||
} else if (tabName === "contact") {
|
||||
return (
|
||||
@@ -347,6 +368,73 @@ export default function CustomerAddPage() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerInfo.gender"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Gender</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select gender" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="male">Male</SelectItem>
|
||||
<SelectItem value="female">Female</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerInfo.martial"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Marital Status</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select marital status" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="single">Single</SelectItem>
|
||||
<SelectItem value="married">Married</SelectItem>
|
||||
<SelectItem value="divorced">Divorced</SelectItem>
|
||||
<SelectItem value="widowed">Widowed</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerInfo.status"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Status</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select status" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="active">Active</SelectItem>
|
||||
<SelectItem value="inactive">Inactive</SelectItem>
|
||||
<SelectItem value="pending">Pending</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button type="button" onClick={handleNextTab}>
|
||||
|
||||
@@ -4,7 +4,10 @@ import { useState, useEffect, useCallback, useMemo } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Plus, Loader2, Users, RefreshCw } from "lucide-react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Plus, Loader2, Users, FileText, Download, FileSpreadsheet, Filter, X } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import axios from "axios";
|
||||
import { ServerDataTable } from "@/components/ui/server-data-table";
|
||||
@@ -31,11 +34,13 @@ export default function CustomerPage() {
|
||||
const router = useRouter();
|
||||
const [customers, setCustomers] = useState<Customer[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [query, setQuery] = useState({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
search: "",
|
||||
gender: "",
|
||||
martial: "",
|
||||
status: "",
|
||||
sortBy: "id",
|
||||
sortOrder: "asc" as "asc" | "desc",
|
||||
});
|
||||
@@ -47,8 +52,12 @@ export default function CustomerPage() {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
});
|
||||
// Initialize search and sorting state to match query
|
||||
const [searchValue, setSearchValue] = useState(query.search);
|
||||
// Individual filter states
|
||||
const [nameSearch, setNameSearch] = useState("");
|
||||
const [genderFilter, setGenderFilter] = useState("all");
|
||||
const [martialFilter, setMartialFilter] = useState("all");
|
||||
const [statusFilter, setStatusFilter] = useState("all");
|
||||
// Initialize sorting state to match query
|
||||
const [sorting, setSorting] = useState<{ id: string; desc: boolean }[]>(
|
||||
query.sortBy ? [{ id: query.sortBy, desc: query.sortOrder === "desc" }] : []
|
||||
);
|
||||
@@ -60,6 +69,9 @@ export default function CustomerPage() {
|
||||
page: query.page.toString(),
|
||||
pageSize: query.pageSize.toString(),
|
||||
search: query.search,
|
||||
gender: query.gender,
|
||||
martial: query.martial,
|
||||
status: query.status,
|
||||
sortBy: query.sortBy,
|
||||
sortOrder: query.sortOrder,
|
||||
});
|
||||
@@ -75,7 +87,6 @@ export default function CustomerPage() {
|
||||
toast.error("Failed to load customers");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
@@ -116,18 +127,50 @@ export default function CustomerPage() {
|
||||
}
|
||||
}, [fetchCustomers, customers]);
|
||||
|
||||
const handleRefresh = useCallback(async () => {
|
||||
setIsRefreshing(true);
|
||||
await fetchCustomers();
|
||||
toast.success("Customer list refreshed");
|
||||
}, [fetchCustomers]);
|
||||
|
||||
const handlePaginationChange = useCallback((page: number, pageSize: number) => {
|
||||
setQuery((prev) => ({ ...prev, page, pageSize }));
|
||||
}, []);
|
||||
|
||||
// Search handler - will be called from ServerDataTable after debounce
|
||||
// Filter handlers - only update local state, don't trigger search
|
||||
const handleNameSearchChange = useCallback((value: string) => {
|
||||
setNameSearch(value);
|
||||
}, []);
|
||||
|
||||
const handleGenderFilterChange = useCallback((value: string) => {
|
||||
setGenderFilter(value);
|
||||
}, []);
|
||||
|
||||
const handleMartialFilterChange = useCallback((value: string) => {
|
||||
setMartialFilter(value);
|
||||
}, []);
|
||||
|
||||
const handleStatusFilterChange = useCallback((value: string) => {
|
||||
setStatusFilter(value);
|
||||
}, []);
|
||||
|
||||
// Apply filters when filter button is clicked
|
||||
const handleApplyFilters = useCallback(() => {
|
||||
setQuery((prev) => ({
|
||||
...prev,
|
||||
search: nameSearch,
|
||||
gender: genderFilter === "all" ? "" : genderFilter,
|
||||
martial: martialFilter === "all" ? "" : martialFilter,
|
||||
status: statusFilter === "all" ? "" : statusFilter,
|
||||
page: 1
|
||||
}));
|
||||
}, [nameSearch, genderFilter, martialFilter, statusFilter]);
|
||||
|
||||
const handleClearFilters = useCallback(() => {
|
||||
setNameSearch("");
|
||||
setGenderFilter("all");
|
||||
setMartialFilter("all");
|
||||
setStatusFilter("all");
|
||||
setQuery((prev) => ({ ...prev, search: "", gender: "", martial: "", status: "", page: 1 }));
|
||||
}, []);
|
||||
|
||||
// Legacy search handler for ServerDataTable compatibility
|
||||
const handleSearchChange = useCallback((search: string) => {
|
||||
setNameSearch(search);
|
||||
setQuery((prev) => ({ ...prev, search, page: 1 }));
|
||||
}, []);
|
||||
|
||||
@@ -169,19 +212,47 @@ export default function CustomerPage() {
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
onClick={handleRefresh}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={isRefreshing}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${isRefreshing ? "animate-spin" : ""}`} />
|
||||
<span>Refresh</span>
|
||||
<FileText className="h-4 w-4" />
|
||||
<span>PDF Export</span>
|
||||
</Button>
|
||||
<Button onClick={handleAddCustomer} className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
<span>CSV Download</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<FileSpreadsheet className="h-4 w-4" />
|
||||
<span>Excel Download</span>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleAddCustomer}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
<span>Add Customer</span>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleApplyFilters}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<Filter className="h-4 w-4" />
|
||||
<span>Filter</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -193,20 +264,95 @@ export default function CustomerPage() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{/* Filter Section */}
|
||||
<div className="mb-6 p-4 bg-gray-50 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-semibold">Search & Filter</h3>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleClearFilters}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
<span>Clear All</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{/* Name Search */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="nameSearch">Search by Name</Label>
|
||||
<Input
|
||||
id="nameSearch"
|
||||
placeholder="Enter customer name..."
|
||||
value={nameSearch}
|
||||
onChange={(e) => handleNameSearchChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Gender Filter */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="genderFilter">Gender</Label>
|
||||
<Select value={genderFilter} onValueChange={handleGenderFilterChange}>
|
||||
<SelectTrigger id="genderFilter">
|
||||
<SelectValue placeholder="Select gender" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Genders</SelectItem>
|
||||
<SelectItem value="male">Male</SelectItem>
|
||||
<SelectItem value="female">Female</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Marital Status Filter */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="martialFilter">Marital Status</Label>
|
||||
<Select value={martialFilter} onValueChange={handleMartialFilterChange}>
|
||||
<SelectTrigger id="martialFilter">
|
||||
<SelectValue placeholder="Select marital status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Statuses</SelectItem>
|
||||
<SelectItem value="single">Single</SelectItem>
|
||||
<SelectItem value="married">Married</SelectItem>
|
||||
<SelectItem value="divorced">Divorced</SelectItem>
|
||||
<SelectItem value="widowed">Widowed</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Status Filter */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="statusFilter">Status</Label>
|
||||
<Select value={statusFilter} onValueChange={handleStatusFilterChange}>
|
||||
<SelectTrigger id="statusFilter">
|
||||
<SelectValue placeholder="Select status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Statuses</SelectItem>
|
||||
<SelectItem value="active">Active</SelectItem>
|
||||
<SelectItem value="inactive">Inactive</SelectItem>
|
||||
<SelectItem value="pending">Pending</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{pagination.total > 0 ? (
|
||||
<ServerDataTable
|
||||
columns={columns}
|
||||
data={customers}
|
||||
pagination={pagination}
|
||||
searchKey="name"
|
||||
searchPlaceholder="Search customers by name..."
|
||||
isLoading={isLoading}
|
||||
onPaginationChange={handlePaginationChange}
|
||||
onSearchChange={handleSearchChange}
|
||||
onSortingChange={handleSortingChange}
|
||||
searchValue={searchValue}
|
||||
searchValue={nameSearch}
|
||||
sorting={sorting}
|
||||
setSearchValue={setSearchValue}
|
||||
setSearchValue={setNameSearch}
|
||||
setSorting={setSorting}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -14,6 +14,9 @@ export interface Customer {
|
||||
mobile: string;
|
||||
originAdd1: string;
|
||||
localAdd1: string;
|
||||
gender: string;
|
||||
martial: string;
|
||||
status: string;
|
||||
dependants?: Array<{
|
||||
id: number;
|
||||
custId: number;
|
||||
@@ -109,6 +112,87 @@ export const createCustomerColumns = ({
|
||||
return <div className="font-mono">{row.getValue("mobile")}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "gender",
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
className="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Gender
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const gender = row.getValue("gender") as string;
|
||||
return (
|
||||
<Badge variant="outline" className="capitalize">
|
||||
{gender || "Not specified"}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "martial",
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
className="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Marital Status
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const martial = row.getValue("martial") as string;
|
||||
return (
|
||||
<Badge variant="outline" className="capitalize">
|
||||
{martial || "Not specified"}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
className="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Status
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const status = row.getValue("status") as string;
|
||||
const getStatusVariant = (status: string) => {
|
||||
switch (status?.toLowerCase()) {
|
||||
case "active":
|
||||
return "default";
|
||||
case "inactive":
|
||||
return "secondary";
|
||||
case "pending":
|
||||
return "outline";
|
||||
default:
|
||||
return "outline";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Badge variant={getStatusVariant(status)} className="capitalize">
|
||||
{status || "Not specified"}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "dependants",
|
||||
header: "Dependants",
|
||||
|
||||
@@ -27,8 +27,8 @@ export interface Customer {
|
||||
// localCountry: string;
|
||||
// preferLang: string;
|
||||
// dob: string; // ISO string
|
||||
// gender: string;
|
||||
// martial: string;
|
||||
gender: string;
|
||||
martial: string;
|
||||
// occupation: string;
|
||||
// ecName: string;
|
||||
// ecRelation: string;
|
||||
@@ -38,7 +38,7 @@ export interface Customer {
|
||||
// initialOutReachRemark?: string;
|
||||
// consentGiven?: string;
|
||||
// contentDate?: string;
|
||||
// status: string;
|
||||
status: string;
|
||||
// remarks: string;
|
||||
// potcustId?: string;
|
||||
// isDeleted: boolean;
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
"email": "john.smith@email.com",
|
||||
"mobile": "+1-555-0101",
|
||||
"originAdd1": "123 Main Street, New York, NY 10001, USA",
|
||||
"localAdd1": "456 Oak Avenue, Tokyo, Japan 150-0001"
|
||||
"localAdd1": "456 Oak Avenue, Tokyo, Japan 150-0001",
|
||||
"gender": "other",
|
||||
"martial": "married",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
@@ -24,7 +27,10 @@
|
||||
"email": "sarah.johnson@email.com",
|
||||
"mobile": "+1-555-0102",
|
||||
"originAdd1": "789 Pine Road, Los Angeles, CA 90210, USA",
|
||||
"localAdd1": "321 Sakura Street, Osaka, Japan 530-0001"
|
||||
"localAdd1": "321 Sakura Street, Osaka, Japan 530-0001",
|
||||
"gender": "female",
|
||||
"martial": "single",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
@@ -37,7 +43,10 @@
|
||||
"email": "michael.brown@email.com",
|
||||
"mobile": "+44-20-7946-0958",
|
||||
"originAdd1": "42 Victoria Street, London, UK SW1H 0TL",
|
||||
"localAdd1": "789 Shibuya Crossing, Tokyo, Japan 150-0002"
|
||||
"localAdd1": "789 Shibuya Crossing, Tokyo, Japan 150-0002",
|
||||
"gender": "male",
|
||||
"martial": "divorced",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
@@ -50,7 +59,10 @@
|
||||
"email": "emily.davis@email.com",
|
||||
"mobile": "+61-2-9876-5432",
|
||||
"originAdd1": "15 Collins Street, Melbourne, VIC 3000, Australia",
|
||||
"localAdd1": "654 Namba District, Osaka, Japan 542-0076"
|
||||
"localAdd1": "654 Namba District, Osaka, Japan 542-0076",
|
||||
"gender": "female",
|
||||
"martial": "married",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
@@ -63,7 +75,186 @@
|
||||
"email": "david.wilson@email.com",
|
||||
"mobile": "+1-416-555-0199",
|
||||
"originAdd1": "100 Queen Street West, Toronto, ON M5H 2N2, Canada",
|
||||
"localAdd1": "987 Ginza District, Tokyo, Japan 104-0061"
|
||||
"localAdd1": "987 Ginza District, Tokyo, Japan 104-0061",
|
||||
"gender": "male",
|
||||
"martial": "single",
|
||||
"status": "inactive"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"firstNameEn": "Maria",
|
||||
"lastNameEn": "Garcia",
|
||||
"firstNameJp": "マリア",
|
||||
"lastNameJp": "ガルシア",
|
||||
"firstNameJpKana": "マリア",
|
||||
"lastNameJpKana": "ガルシア",
|
||||
"email": "maria.garcia@email.com",
|
||||
"mobile": "+34-91-555-0123",
|
||||
"originAdd1": "Calle Gran Vía 45, Madrid, Spain 28013",
|
||||
"localAdd1": "246 Harajuku Street, Tokyo, Japan 150-0001",
|
||||
"gender": "female",
|
||||
"martial": "married",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"firstNameEn": "James",
|
||||
"lastNameEn": "Lee",
|
||||
"firstNameJp": "ジェームス",
|
||||
"lastNameJp": "リー",
|
||||
"firstNameJpKana": "ジェームス",
|
||||
"lastNameJpKana": "リー",
|
||||
"email": "james.lee@email.com",
|
||||
"mobile": "+65-6555-0187",
|
||||
"originAdd1": "1 Raffles Place, Singapore 048616",
|
||||
"localAdd1": "135 Roppongi Hills, Tokyo, Japan 106-6108",
|
||||
"gender": "male",
|
||||
"martial": "single",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"firstNameEn": "Anna",
|
||||
"lastNameEn": "Mueller",
|
||||
"firstNameJp": "アンナ",
|
||||
"lastNameJp": "ミュラー",
|
||||
"firstNameJpKana": "アンナ",
|
||||
"lastNameJpKana": "ミュラー",
|
||||
"email": "anna.mueller@email.com",
|
||||
"mobile": "+49-30-555-0234",
|
||||
"originAdd1": "Unter den Linden 77, Berlin, Germany 10117",
|
||||
"localAdd1": "567 Shinjuku District, Tokyo, Japan 160-0022",
|
||||
"gender": "female",
|
||||
"martial": "widowed",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"firstNameEn": "Robert",
|
||||
"lastNameEn": "Taylor",
|
||||
"firstNameJp": "ロバート",
|
||||
"lastNameJp": "テイラー",
|
||||
"firstNameJpKana": "ロバート",
|
||||
"lastNameJpKana": "テイラー",
|
||||
"email": "robert.taylor@email.com",
|
||||
"mobile": "+1-555-0145",
|
||||
"originAdd1": "890 Broadway, New York, NY 10003, USA",
|
||||
"localAdd1": "890 Akasaka District, Tokyo, Japan 107-0052",
|
||||
"gender": "male",
|
||||
"martial": "married",
|
||||
"status": "inactive"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"firstNameEn": "Lisa",
|
||||
"lastNameEn": "Anderson",
|
||||
"firstNameJp": "リサ",
|
||||
"lastNameJp": "アンダーソン",
|
||||
"firstNameJpKana": "リサ",
|
||||
"lastNameJpKana": "アンダーソン",
|
||||
"email": "lisa.anderson@email.com",
|
||||
"mobile": "+1-555-0167",
|
||||
"originAdd1": "456 Sunset Boulevard, Los Angeles, CA 90028, USA",
|
||||
"localAdd1": "234 Ikebukuro District, Tokyo, Japan 171-0021",
|
||||
"gender": "female",
|
||||
"martial": "divorced",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"firstNameEn": "Alex",
|
||||
"lastNameEn": "Chen",
|
||||
"firstNameJp": "アレックス",
|
||||
"lastNameJp": "チェン",
|
||||
"firstNameJpKana": "アレックス",
|
||||
"lastNameJpKana": "チェン",
|
||||
"email": "alex.chen@email.com",
|
||||
"mobile": "+86-21-555-0189",
|
||||
"originAdd1": "88 Nanjing Road, Shanghai, China 200001",
|
||||
"localAdd1": "678 Aoyama District, Tokyo, Japan 107-0061",
|
||||
"gender": "other",
|
||||
"martial": "single",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"firstNameEn": "Sophie",
|
||||
"lastNameEn": "Martin",
|
||||
"firstNameJp": "ソフィー",
|
||||
"lastNameJp": "マルタン",
|
||||
"firstNameJpKana": "ソフィー",
|
||||
"lastNameJpKana": "マルタン",
|
||||
"email": "sophie.martin@email.com",
|
||||
"mobile": "+33-1-555-0212",
|
||||
"originAdd1": "25 Champs-Élysées, Paris, France 75008",
|
||||
"localAdd1": "345 Ebisu District, Tokyo, Japan 150-0013",
|
||||
"gender": "female",
|
||||
"martial": "married",
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"firstNameEn": "Thomas",
|
||||
"lastNameEn": "White",
|
||||
"firstNameJp": "トーマス",
|
||||
"lastNameJp": "ホワイト",
|
||||
"firstNameJpKana": "トーマス",
|
||||
"lastNameJpKana": "ホワイト",
|
||||
"email": "thomas.white@email.com",
|
||||
"mobile": "+1-555-0234",
|
||||
"originAdd1": "789 Michigan Avenue, Chicago, IL 60611, USA",
|
||||
"localAdd1": "456 Kanda District, Tokyo, Japan 101-0047",
|
||||
"gender": "male",
|
||||
"martial": "single",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"firstNameEn": "Isabella",
|
||||
"lastNameEn": "Rodriguez",
|
||||
"firstNameJp": "イザベラ",
|
||||
"lastNameJp": "ロドリゲス",
|
||||
"firstNameJpKana": "イザベラ",
|
||||
"lastNameJpKana": "ロドリゲス",
|
||||
"email": "isabella.rodriguez@email.com",
|
||||
"mobile": "+52-55-555-0256",
|
||||
"originAdd1": "Avenida Reforma 123, Mexico City, Mexico 06600",
|
||||
"localAdd1": "789 Ueno District, Tokyo, Japan 110-0005",
|
||||
"gender": "female",
|
||||
"martial": "married",
|
||||
"status": "inactive"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"firstNameEn": "William",
|
||||
"lastNameEn": "Thompson",
|
||||
"firstNameJp": "ウィリアム",
|
||||
"lastNameJp": "トンプソン",
|
||||
"firstNameJpKana": "ウィリアム",
|
||||
"lastNameJpKana": "トンプソン",
|
||||
"email": "william.thompson@email.com",
|
||||
"mobile": "+1-555-0278",
|
||||
"originAdd1": "567 Market Street, San Francisco, CA 94105, USA",
|
||||
"localAdd1": "123 Sumida District, Tokyo, Japan 130-0013",
|
||||
"gender": "male",
|
||||
"martial": "divorced",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"firstNameEn": "Do",
|
||||
"lastNameEn": "Tung",
|
||||
"firstNameJp": "ádf",
|
||||
"lastNameJp": "ádf",
|
||||
"firstNameJpKana": "ádf",
|
||||
"lastNameJpKana": "ádf",
|
||||
"gender": "female",
|
||||
"martial": "married",
|
||||
"status": "active",
|
||||
"email": "dothanhtung196@gmail.com",
|
||||
"mobile": "0987417491",
|
||||
"originAdd1": "1231231231",
|
||||
"localAdd1": "asdfsdaf"
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
@@ -210,260 +401,160 @@
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"userId": 1,
|
||||
"permissionId": 9
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"userId": 1,
|
||||
"permissionId": 10
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"userId": 1,
|
||||
"permissionId": 11
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"userId": 1,
|
||||
"permissionId": 12
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"userId": 1,
|
||||
"permissionId": 13
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"userId": 1,
|
||||
"permissionId": 14
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"userId": 1,
|
||||
"permissionId": 15
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"userId": 2,
|
||||
"permissionId": 4
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"userId": 2,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"id": 10,
|
||||
"userId": 2,
|
||||
"permissionId": 6
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"userId": 2,
|
||||
"permissionId": 8
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"userId": 2,
|
||||
"permissionId": 11
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"id": 12,
|
||||
"userId": 3,
|
||||
"permissionId": 1
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"id": 13,
|
||||
"userId": 3,
|
||||
"permissionId": 2
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"id": 14,
|
||||
"userId": 3,
|
||||
"permissionId": 14
|
||||
"permissionId": 4
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"userId": 3,
|
||||
"id": 15,
|
||||
"userId": 4,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"userId": 4,
|
||||
"permissionId": 6
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"userId": 4,
|
||||
"permissionId": 7
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"userId": 4,
|
||||
"permissionId": 1
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"userId": 5,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"userId": 5,
|
||||
"permissionId": 6
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"userId": 5,
|
||||
"permissionId": 8
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"userId": 6,
|
||||
"permissionId": 1
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"userId": 6,
|
||||
"permissionId": 2
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"userId": 4,
|
||||
"userId": 6,
|
||||
"permissionId": 4
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"userId": 4,
|
||||
"permissionId": 1
|
||||
"userId": 6,
|
||||
"permissionId": 8
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"userId": 4,
|
||||
"userId": 7,
|
||||
"permissionId": 8
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"userId": 4,
|
||||
"permissionId": 9
|
||||
"userId": 7,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"userId": 4,
|
||||
"permissionId": 15
|
||||
"userId": 8,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"userId": 5,
|
||||
"permissionId": 4
|
||||
"userId": 8,
|
||||
"permissionId": 1
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"userId": 5,
|
||||
"userId": 9,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"userId": 5,
|
||||
"permissionId": 11
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"userId": 5,
|
||||
"permissionId": 12
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"userId": 6,
|
||||
"permissionId": 7
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"userId": 6,
|
||||
"permissionId": 10
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"userId": 6,
|
||||
"permissionId": 15
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"userId": 6,
|
||||
"permissionId": 1
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"userId": 6,
|
||||
"permissionId": 2
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"userId": 7,
|
||||
"permissionId": 11
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"userId": 7,
|
||||
"permissionId": 12
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"userId": 7,
|
||||
"permissionId": 13
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"userId": 7,
|
||||
"permissionId": 4
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"userId": 8,
|
||||
"permissionId": 4
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"userId": 8,
|
||||
"permissionId": 1
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"userId": 8,
|
||||
"permissionId": 11
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"userId": 9,
|
||||
"permissionId": 4
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"userId": 9,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"userId": 9,
|
||||
"permissionId": 6
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"id": 32,
|
||||
"userId": 9,
|
||||
"permissionId": 7
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"userId": 9,
|
||||
"permissionId": 8
|
||||
},
|
||||
{
|
||||
"id": 49,
|
||||
"userId": 9,
|
||||
"permissionId": 11
|
||||
},
|
||||
{
|
||||
"id": 50,
|
||||
"id": 34,
|
||||
"userId": 10,
|
||||
"permissionId": 4
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
"id": 35,
|
||||
"userId": 10,
|
||||
"permissionId": 1
|
||||
},
|
||||
{
|
||||
"id": 52,
|
||||
"userId": 10,
|
||||
"permissionId": 8
|
||||
},
|
||||
{
|
||||
"id": 53,
|
||||
"userId": 10,
|
||||
"permissionId": 9
|
||||
},
|
||||
{
|
||||
"id": 54,
|
||||
"userId": 11,
|
||||
"permissionId": 4
|
||||
},
|
||||
{
|
||||
"id": 55,
|
||||
"id": 36,
|
||||
"userId": 11,
|
||||
"permissionId": 5
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"id": 37,
|
||||
"userId": 11,
|
||||
"permissionId": 6
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"userId": 11,
|
||||
"permissionId": 8
|
||||
},
|
||||
{
|
||||
"id": 57,
|
||||
"userId": 11,
|
||||
"permissionId": 11
|
||||
},
|
||||
{
|
||||
"id": 58,
|
||||
"userId": 11,
|
||||
"permissionId": 12
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "user_read",
|
||||
"description": "Read user information",
|
||||
"description": "View user list and user details",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
@@ -480,106 +571,36 @@
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "customer_read",
|
||||
"description": "Read customer information",
|
||||
"name": "user_permissions",
|
||||
"description": "Manage user permissions",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "customer_read",
|
||||
"description": "View customer list and customer details",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "customer_write",
|
||||
"description": "Create and update customer information",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"id": 7,
|
||||
"name": "customer_delete",
|
||||
"description": "Delete customer records",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "admin_panel",
|
||||
"description": "Access administrative panel",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "reports_view",
|
||||
"description": "View system reports",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "reports_export",
|
||||
"description": "Export reports and data",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "system_settings",
|
||||
"description": "Modify system configuration",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "mail_template_read",
|
||||
"description": "Read mail templates",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "mail_template_write",
|
||||
"description": "Create and update mail templates",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "mail_template_delete",
|
||||
"description": "Delete mail templates",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "permission_manage",
|
||||
"description": "Manage user permissions",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "audit_log",
|
||||
"description": "View audit logs and system activities",
|
||||
"name": "mail_template_access",
|
||||
"description": "Access mail template configuration",
|
||||
"isActive": true
|
||||
}
|
||||
],
|
||||
"customerDependants": [
|
||||
{
|
||||
"id": 1,
|
||||
"custId": 1,
|
||||
"firstNameEn": "Jane",
|
||||
"lastNameEn": "Smith",
|
||||
"firstNameJp": "ジェーン",
|
||||
"lastNameJp": "スミス",
|
||||
"firstNameJpKana": "ジェーン",
|
||||
"lastNameJpKana": "スミス",
|
||||
"email": "jane.smith@email.com",
|
||||
"mobile": "+1-555-0111",
|
||||
"originAdd1": "123 Main Street, New York, NY 10001, USA",
|
||||
"localAdd1": "456 Oak Avenue, Tokyo, Japan 150-0001"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"custId": 1,
|
||||
"firstNameEn": "Tommy",
|
||||
"lastNameEn": "Smith",
|
||||
"firstNameJp": "トミー",
|
||||
"lastNameJp": "スミス",
|
||||
"firstNameJpKana": "トミー",
|
||||
"lastNameJpKana": "スミス",
|
||||
"email": "tommy.smith@email.com",
|
||||
"mobile": "+1-555-0112",
|
||||
"originAdd1": "123 Main Street, New York, NY 10001, USA",
|
||||
"localAdd1": "456 Oak Avenue, Tokyo, Japan 150-0001"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"custId": 2,
|
||||
@@ -677,6 +698,34 @@
|
||||
"mobile": "+1-416-555-0191",
|
||||
"originAdd1": "100 Queen Street West, Toronto, ON M5H 2N2, Canada",
|
||||
"localAdd1": "987 Ginza District, Tokyo, Japan 104-0061"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"custId": 1,
|
||||
"firstNameEn": "Jane",
|
||||
"lastNameEn": "Smith",
|
||||
"firstNameJp": "ジェーン",
|
||||
"lastNameJp": "スミス",
|
||||
"firstNameJpKana": "ジェーン",
|
||||
"lastNameJpKana": "スミス",
|
||||
"email": "jane.smith@email.com",
|
||||
"mobile": "+1-555-0111",
|
||||
"originAdd1": "123 Main Street, New York, NY 10001, USA",
|
||||
"localAdd1": "456 Oak Avenue, Tokyo, Japan 150-0001"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"custId": 1,
|
||||
"firstNameEn": "Tommy",
|
||||
"lastNameEn": "Smith",
|
||||
"firstNameJp": "トミー",
|
||||
"lastNameJp": "スミス",
|
||||
"firstNameJpKana": "トミー",
|
||||
"lastNameJpKana": "スミス",
|
||||
"email": "tommy.smith@email.com",
|
||||
"mobile": "+1-555-0112",
|
||||
"originAdd1": "123 Main Street, New York, NY 10001, USA",
|
||||
"localAdd1": "456 Oak Avenue, Tokyo, Japan 150-0001"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -13,6 +13,9 @@ export const sampleCustomers: Customer[] = [
|
||||
mobile: "+1-555-0101",
|
||||
originAdd1: "123 Main Street, New York, NY 10001, USA",
|
||||
localAdd1: "456 Oak Avenue, Tokyo, Japan 150-0001",
|
||||
gender: "male",
|
||||
martial: "married",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -26,6 +29,9 @@ export const sampleCustomers: Customer[] = [
|
||||
mobile: "+1-555-0102",
|
||||
originAdd1: "789 Pine Road, Los Angeles, CA 90210, USA",
|
||||
localAdd1: "321 Sakura Street, Osaka, Japan 530-0001",
|
||||
gender: "female",
|
||||
martial: "single",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@@ -39,6 +45,9 @@ export const sampleCustomers: Customer[] = [
|
||||
mobile: "+44-20-7946-0958",
|
||||
originAdd1: "42 Victoria Street, London, UK SW1H 0TL",
|
||||
localAdd1: "789 Shibuya Crossing, Tokyo, Japan 150-0002",
|
||||
gender: "male",
|
||||
martial: "divorced",
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
@@ -52,6 +61,9 @@ export const sampleCustomers: Customer[] = [
|
||||
mobile: "+61-2-9876-5432",
|
||||
originAdd1: "15 Collins Street, Melbourne, VIC 3000, Australia",
|
||||
localAdd1: "654 Namba District, Osaka, Japan 542-0076",
|
||||
gender: "female",
|
||||
martial: "married",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
@@ -65,5 +77,168 @@ export const sampleCustomers: Customer[] = [
|
||||
mobile: "+1-416-555-0199",
|
||||
originAdd1: "100 Queen Street West, Toronto, ON M5H 2N2, Canada",
|
||||
localAdd1: "987 Ginza District, Tokyo, Japan 104-0061",
|
||||
gender: "male",
|
||||
martial: "single",
|
||||
status: "inactive",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
firstNameEn: "Maria",
|
||||
lastNameEn: "Garcia",
|
||||
firstNameJp: "マリア",
|
||||
lastNameJp: "ガルシア",
|
||||
firstNameJpKana: "マリア",
|
||||
lastNameJpKana: "ガルシア",
|
||||
email: "maria.garcia@email.com",
|
||||
mobile: "+34-91-555-0123",
|
||||
originAdd1: "Calle Gran Vía 45, Madrid, Spain 28013",
|
||||
localAdd1: "246 Harajuku Street, Tokyo, Japan 150-0001",
|
||||
gender: "female",
|
||||
martial: "married",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
firstNameEn: "James",
|
||||
lastNameEn: "Lee",
|
||||
firstNameJp: "ジェームス",
|
||||
lastNameJp: "リー",
|
||||
firstNameJpKana: "ジェームス",
|
||||
lastNameJpKana: "リー",
|
||||
email: "james.lee@email.com",
|
||||
mobile: "+65-6555-0187",
|
||||
originAdd1: "1 Raffles Place, Singapore 048616",
|
||||
localAdd1: "135 Roppongi Hills, Tokyo, Japan 106-6108",
|
||||
gender: "male",
|
||||
martial: "single",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
firstNameEn: "Anna",
|
||||
lastNameEn: "Mueller",
|
||||
firstNameJp: "アンナ",
|
||||
lastNameJp: "ミュラー",
|
||||
firstNameJpKana: "アンナ",
|
||||
lastNameJpKana: "ミュラー",
|
||||
email: "anna.mueller@email.com",
|
||||
mobile: "+49-30-555-0234",
|
||||
originAdd1: "Unter den Linden 77, Berlin, Germany 10117",
|
||||
localAdd1: "567 Shinjuku District, Tokyo, Japan 160-0022",
|
||||
gender: "female",
|
||||
martial: "widowed",
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
firstNameEn: "Robert",
|
||||
lastNameEn: "Taylor",
|
||||
firstNameJp: "ロバート",
|
||||
lastNameJp: "テイラー",
|
||||
firstNameJpKana: "ロバート",
|
||||
lastNameJpKana: "テイラー",
|
||||
email: "robert.taylor@email.com",
|
||||
mobile: "+1-555-0145",
|
||||
originAdd1: "890 Broadway, New York, NY 10003, USA",
|
||||
localAdd1: "890 Akasaka District, Tokyo, Japan 107-0052",
|
||||
gender: "male",
|
||||
martial: "married",
|
||||
status: "inactive",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
firstNameEn: "Lisa",
|
||||
lastNameEn: "Anderson",
|
||||
firstNameJp: "リサ",
|
||||
lastNameJp: "アンダーソン",
|
||||
firstNameJpKana: "リサ",
|
||||
lastNameJpKana: "アンダーソン",
|
||||
email: "lisa.anderson@email.com",
|
||||
mobile: "+1-555-0167",
|
||||
originAdd1: "456 Sunset Boulevard, Los Angeles, CA 90028, USA",
|
||||
localAdd1: "234 Ikebukuro District, Tokyo, Japan 171-0021",
|
||||
gender: "female",
|
||||
martial: "divorced",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
firstNameEn: "Alex",
|
||||
lastNameEn: "Chen",
|
||||
firstNameJp: "アレックス",
|
||||
lastNameJp: "チェン",
|
||||
firstNameJpKana: "アレックス",
|
||||
lastNameJpKana: "チェン",
|
||||
email: "alex.chen@email.com",
|
||||
mobile: "+86-21-555-0189",
|
||||
originAdd1: "88 Nanjing Road, Shanghai, China 200001",
|
||||
localAdd1: "678 Aoyama District, Tokyo, Japan 107-0061",
|
||||
gender: "other",
|
||||
martial: "single",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
firstNameEn: "Sophie",
|
||||
lastNameEn: "Martin",
|
||||
firstNameJp: "ソフィー",
|
||||
lastNameJp: "マルタン",
|
||||
firstNameJpKana: "ソフィー",
|
||||
lastNameJpKana: "マルタン",
|
||||
email: "sophie.martin@email.com",
|
||||
mobile: "+33-1-555-0212",
|
||||
originAdd1: "25 Champs-Élysées, Paris, France 75008",
|
||||
localAdd1: "345 Ebisu District, Tokyo, Japan 150-0013",
|
||||
gender: "female",
|
||||
martial: "married",
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
firstNameEn: "Thomas",
|
||||
lastNameEn: "White",
|
||||
firstNameJp: "トーマス",
|
||||
lastNameJp: "ホワイト",
|
||||
firstNameJpKana: "トーマス",
|
||||
lastNameJpKana: "ホワイト",
|
||||
email: "thomas.white@email.com",
|
||||
mobile: "+1-555-0234",
|
||||
originAdd1: "789 Michigan Avenue, Chicago, IL 60611, USA",
|
||||
localAdd1: "456 Kanda District, Tokyo, Japan 101-0047",
|
||||
gender: "male",
|
||||
martial: "single",
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
firstNameEn: "Isabella",
|
||||
lastNameEn: "Rodriguez",
|
||||
firstNameJp: "イザベラ",
|
||||
lastNameJp: "ロドリゲス",
|
||||
firstNameJpKana: "イザベラ",
|
||||
lastNameJpKana: "ロドリゲス",
|
||||
email: "isabella.rodriguez@email.com",
|
||||
mobile: "+52-55-555-0256",
|
||||
originAdd1: "Avenida Reforma 123, Mexico City, Mexico 06600",
|
||||
localAdd1: "789 Ueno District, Tokyo, Japan 110-0005",
|
||||
gender: "female",
|
||||
martial: "married",
|
||||
status: "inactive",
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
firstNameEn: "William",
|
||||
lastNameEn: "Thompson",
|
||||
firstNameJp: "ウィリアム",
|
||||
lastNameJp: "トンプソン",
|
||||
firstNameJpKana: "ウィリアム",
|
||||
lastNameJpKana: "トンプソン",
|
||||
email: "william.thompson@email.com",
|
||||
mobile: "+1-555-0278",
|
||||
originAdd1: "567 Market Street, San Francisco, CA 94105, USA",
|
||||
localAdd1: "123 Sumida District, Tokyo, Japan 130-0013",
|
||||
gender: "male",
|
||||
martial: "divorced",
|
||||
status: "active",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -7,6 +7,9 @@ export const customerInfoSchema = z.object({
|
||||
lastNameJp: z.string().min(1, "Last name in Japanese is required"),
|
||||
firstNameJpKana: z.string().min(1, "First name in Japanese Kana is required"),
|
||||
lastNameJpKana: z.string().min(1, "Last name in Japanese Kana is required"),
|
||||
gender: z.string().min(1, "Gender is required"),
|
||||
martial: z.string().min(1, "Marital status is required"),
|
||||
status: z.string().min(1, "Status is required"),
|
||||
});
|
||||
|
||||
export const customerContactSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user