feat: add bulkDelete, tablefilter, rowEdit modal, add Admin modal
This commit is contained in:
parent
c0f022dc10
commit
d56e33ece0
@ -25,6 +25,7 @@
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"sass": "^1.86.0",
|
||||
"sonner": "^2.0.2",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@ -56,6 +56,9 @@ importers:
|
||||
sass:
|
||||
specifier: ^1.86.0
|
||||
version: 1.86.0
|
||||
sonner:
|
||||
specifier: ^2.0.2
|
||||
version: 2.0.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
zod:
|
||||
specifier: ^3.24.2
|
||||
version: 3.24.2
|
||||
@ -3468,6 +3471,12 @@ packages:
|
||||
snake-case@3.0.4:
|
||||
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
|
||||
|
||||
sonner@2.0.2:
|
||||
resolution: {integrity: sha512-xOeXErZ4blqQd11ZnlDmoRmg+ctUJBkTU8H+HVh9rnWi9Ke28xiL39r4iCTeDX31ODTe/s1MaiaY333dUzLCtA==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -7392,6 +7401,11 @@ snapshots:
|
||||
dot-case: 3.0.4
|
||||
tslib: 2.8.1
|
||||
|
||||
sonner@2.0.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
stable-hash@0.0.5: {}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import Form from "#/components/form/form"
|
||||
import { loginSchema } from "#/schema/loginSchema"
|
||||
import { loginSchema } from "#/schema"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { signIn } from "next-auth/react"
|
||||
import { useRouter } from "next/navigation";
|
||||
@ -25,6 +25,8 @@ export default function LoginPage() {
|
||||
: result.error;
|
||||
console.error(errorMessage)
|
||||
throw new Error(result.error)
|
||||
} else {
|
||||
router.push('/admin/home')
|
||||
}
|
||||
return result
|
||||
} catch (error: any) {
|
||||
@ -35,9 +37,6 @@ export default function LoginPage() {
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
onSuccess: () => {
|
||||
router.push('/admin/home')
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error(error.message)
|
||||
|
||||
@ -3,22 +3,27 @@
|
||||
import FloatingLabelInput from "#/components/floatingLabelInput"
|
||||
import { Modal } from "#/components/modal"
|
||||
import Table from "#/components/table/table"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import axios from "axios"
|
||||
import { table } from "console"
|
||||
import Image from "next/image"
|
||||
import { useSession } from "next-auth/react"
|
||||
|
||||
export interface Admin {
|
||||
id: string
|
||||
email: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
profile: string
|
||||
}
|
||||
import { DropdownMenu } from "radix-ui"
|
||||
import Link from "next/link"
|
||||
import { icons } from "#/assets/icons"
|
||||
import { useState } from "react"
|
||||
import Form from "#/components/form/form"
|
||||
import { adminSchema } from "#/schema"
|
||||
import { Admin } from "#/types"
|
||||
|
||||
export default function Admins (){
|
||||
const {data: session, status} = useSession()
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [openDeleteModal, setOpenDeleteModal] = useState(false);
|
||||
const [openEditModal, setOpenEditModal] = useState(false);
|
||||
const [selectedAdminId, setSelectedAdminId] = useState<string | null>(null);
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { data: users, refetch, isLoading} = useQuery({
|
||||
enabled: status === 'authenticated',
|
||||
@ -42,6 +47,135 @@ export default function Admins (){
|
||||
}
|
||||
})
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: async (data: { last_name: string; first_name: string; email: string }) => {
|
||||
try {
|
||||
|
||||
const result = await axios.post(
|
||||
`https://private-docs-api.intside.co/users/`,
|
||||
{
|
||||
last_name: data.last_name,
|
||||
first_name: data.first_name,
|
||||
email: data.email,
|
||||
user_type: "admin"
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${session?.user.access_token}`
|
||||
}
|
||||
})
|
||||
|
||||
if(result.status === 200 || result.status === 201) {
|
||||
console.log('ajout réussie !')
|
||||
setOpenModal(false)
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.message.includes("Network Error")) {
|
||||
console.error("Problème de connexion au serveur");
|
||||
}
|
||||
|
||||
console.error("Autre = ", error);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] })
|
||||
refetch()
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const mutationUpdate = useMutation({
|
||||
mutationFn: async (data: { id: string, last_name: string; first_name: string; email: string }) => {
|
||||
try {
|
||||
|
||||
const result = await axios.put(
|
||||
`https://private-docs-api.intside.co/users/${data.id}/`,
|
||||
{
|
||||
last_name: data.last_name,
|
||||
first_name: data.first_name,
|
||||
email: data.email,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${session?.user.access_token}`
|
||||
}
|
||||
})
|
||||
|
||||
if(result.status === 200 || result.status === 201) {
|
||||
console.log('modification réussie !')
|
||||
setOpenEditModal(false)
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.message.includes("Network Error")) {
|
||||
console.error("Problème de connexion au serveur");
|
||||
}
|
||||
|
||||
console.error("Autre = ", error);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] })
|
||||
refetch()
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
const bulkDeleteMutation = useMutation({
|
||||
mutationFn: async (ids: string[]) => {
|
||||
try {
|
||||
const deletePromises = ids.map(id =>
|
||||
axios.delete(`https://private-docs-api.intside.co/users/${id}/`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${session?.user.access_token}`
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(deletePromises);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
refetch();
|
||||
}
|
||||
});
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: async (id: string) => {
|
||||
try {
|
||||
const response = await axios.delete(
|
||||
`https://private-docs-api.intside.co/users/${id}/`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${session?.user.access_token}`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if(response.status === 200 || response.status === 201) {
|
||||
console.log('Suppresion réussie !')
|
||||
setOpenDeleteModal(false)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] })
|
||||
|
||||
refetch()
|
||||
}
|
||||
})
|
||||
|
||||
const columns: ColumnDef<Admin>[] = [
|
||||
{
|
||||
header: "Administrateurs",
|
||||
@ -90,52 +224,127 @@ export default function Admins (){
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// id: "delete",
|
||||
// cell: ({ cell }) => {
|
||||
// const id = String(cell.row.original.id)
|
||||
// return (
|
||||
// <div className="relative p-2 cursor-pointer"
|
||||
// // onClick={() => { mutate(id) }}
|
||||
// >
|
||||
// <Modal
|
||||
// open={open}
|
||||
// trigger={
|
||||
// <div onClick={() => setOpen(true)}>
|
||||
// <Image alt="" src={icons.trash} className="absolute right-2 top-[-50%] transform translate-middle-y hover:text-blue-500" />
|
||||
// </div>
|
||||
// }
|
||||
// title={
|
||||
// <p className="font-bold text-3xl">Supprimer une organisation</p>
|
||||
// }
|
||||
// content={
|
||||
// <div>
|
||||
// <p>Voulez-vous vraiment supprimer cette organisation ?</p>
|
||||
{
|
||||
id: "delete",
|
||||
cell: ({ cell }) => {
|
||||
const admin = cell.row.original
|
||||
return (
|
||||
<div className="flex justify-end p-2 cursor-pointer space-x-2"
|
||||
// onClick={() => { mutate(id) }}
|
||||
>
|
||||
|
||||
// <div className="grid grid-cols-2 gap-3 mt-3">
|
||||
// <button
|
||||
// className="bg-blue-100 text-blue-600 py-2 px-4 text-lg rounded-full text-center hover:bg-blue-200"
|
||||
// onClick={() => { setOpen(false) }}
|
||||
// >
|
||||
// Annuler
|
||||
// </button>
|
||||
// <button
|
||||
// className="bg-red-500 text-white py-2 px-4 text-lg rounded-full text-center hover:bg-red-600"
|
||||
// onClick={() => {
|
||||
// mutate(id)
|
||||
// setOpen(false)
|
||||
// }}
|
||||
// >
|
||||
// Supprimer
|
||||
// </button>
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
// />
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
<Modal
|
||||
open={openEditModal && selectedAdminId === admin.id}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (!isOpen) {
|
||||
setSelectedAdminId(null);
|
||||
setOpenEditModal(false);
|
||||
}
|
||||
}}
|
||||
trigger={
|
||||
<div onClick={() =>{
|
||||
setSelectedAdminId(admin.id);
|
||||
setOpenEditModal(true)
|
||||
}}>
|
||||
<Image alt="" width={24} height={24} src={icons.editIcon} className="hover:text-blue-500" />
|
||||
</div>
|
||||
}
|
||||
title={
|
||||
<p className="font-bold text-3xl">Modifier un admin</p>
|
||||
}
|
||||
content={
|
||||
<>
|
||||
<Form
|
||||
fields={[
|
||||
{
|
||||
name: "id", // Ajoutez un champ caché pour l'ID
|
||||
type: "hidden",
|
||||
defaultValue: admin.id
|
||||
},
|
||||
{
|
||||
name: "last_name",
|
||||
label: "Nom",
|
||||
type: "text",
|
||||
placeholder: "Entrer le nom de l'admin",
|
||||
defaultValue: admin.last_name
|
||||
},
|
||||
{
|
||||
name: "first_name",
|
||||
label: "Prénom",
|
||||
type: "text",
|
||||
placeholder: "Entrer le prénom de l'admin",
|
||||
defaultValue: admin.first_name
|
||||
},
|
||||
{
|
||||
name: "email",
|
||||
label: "Adresse e-mail",
|
||||
type: "text",
|
||||
placeholder: "Entrer l'email de l'admin",
|
||||
defaultValue: admin.email
|
||||
},
|
||||
// {
|
||||
// name: "statut",
|
||||
// label: "Adresse e-mail",
|
||||
// type: "select",
|
||||
// placeholder: "Entrer l'email de l'admin",
|
||||
// options
|
||||
// }
|
||||
]}
|
||||
submit={mutationUpdate.mutate}
|
||||
schema={adminSchema}
|
||||
child={<button onClick={() => setOpenModal(false)} type="submit" className="btn-auth">{isLoading ? "Chargement..." : "Modifier"}</button>}
|
||||
/></>
|
||||
}
|
||||
|
||||
/>
|
||||
|
||||
<Modal
|
||||
open={openDeleteModal && selectedAdminId === admin.id}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (!isOpen) {
|
||||
setSelectedAdminId(null);
|
||||
setOpenDeleteModal(false);
|
||||
}
|
||||
}}
|
||||
trigger={
|
||||
<div onClick={() => {
|
||||
setSelectedAdminId(admin.id);
|
||||
setOpenDeleteModal(true)
|
||||
}}>
|
||||
<Image alt="" src={icons.trash} className=" hover:text-blue-500" />
|
||||
</div>
|
||||
}
|
||||
title={
|
||||
<p className="font-bold text-3xl">Supprimer un admin</p>
|
||||
}
|
||||
content={
|
||||
<div>
|
||||
<p>Voulez-vous vraiment supprimer cet admin ?</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 mt-3">
|
||||
<button
|
||||
className="bg-blue-100 text-blue-600 py-2 px-4 text-lg rounded-full text-center hover:bg-blue-200"
|
||||
onClick={() => { setOpenDeleteModal(false) }}
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
className="bg-red-500 text-white py-2 px-4 text-lg rounded-full text-center hover:bg-red-600"
|
||||
onClick={() => {
|
||||
mutate(admin.id)
|
||||
setOpenDeleteModal(false)
|
||||
}}
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
@ -144,30 +353,82 @@ export default function Admins (){
|
||||
columns={columns}
|
||||
data={users || []}
|
||||
pageSize={5}
|
||||
header={(table) => (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 md:flex-row w-full">
|
||||
<div className="flex">
|
||||
<input checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && undefined)
|
||||
}
|
||||
header={(table) => {
|
||||
const ids = table.getRowModel().rows.filter((row) => row.getIsSelected()).map(row => row.original.id )
|
||||
if(bulkDeleteMutation.isSuccess) {
|
||||
table.toggleAllPageRowsSelected(false)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 md:flex-row w-full">
|
||||
<div className="flex items-center space-x-3">
|
||||
<input checked={table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && undefined)}
|
||||
onChange={(e) => table.toggleAllPageRowsSelected(e.target.checked)}
|
||||
type="checkbox" name="" id=""
|
||||
/>
|
||||
</div>
|
||||
type="checkbox" name="" id="" />
|
||||
|
||||
<div className="flex justify-between md:justify-end space-x-3">
|
||||
<Modal
|
||||
trigger={
|
||||
<button className="p-3 bg-blue-600 text-white rounded-full">
|
||||
Ajouter un admin
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
<FloatingLabelInput name="search" placeholder="Effectuer une recherche" type="text" onChange={(value) => table.setGlobalFilter(value)} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<p className="cursor-pointer rounded-full bg-gray-300 text-gray-500 p-2">
|
||||
Sélectionner une action
|
||||
</p>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content className="min-w-[150px] shadow-sm bg-white rounded-md p-1" sideOffset={5}>
|
||||
<DropdownMenu.Item onClick={() => bulkDeleteMutation.mutate(ids)} className="p-2 text-[14px] cursor-pointer hover:bg-blue-100 hover:border-blue-100 hover:text-blue-500 hover:rounded-md outline-none">
|
||||
Supprimer
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between md:justify-end space-x-3">
|
||||
<Modal
|
||||
title="Ajouter un admin"
|
||||
open={openModal}
|
||||
trigger={<div onClick={() => setOpenModal(true)} className="cursor-pointer p-3 bg-blue-600 text-white rounded-full">
|
||||
Ajouter un admin
|
||||
</div>}
|
||||
content={
|
||||
<>
|
||||
<Form
|
||||
fields={[
|
||||
{
|
||||
name: "last_name",
|
||||
label: "Nom",
|
||||
type: "text",
|
||||
placeholder: "Entrer le nom de l'admin"
|
||||
},
|
||||
{
|
||||
name: "first_name",
|
||||
label: "Prénom",
|
||||
type: "text",
|
||||
placeholder: "Entrer le prénom de l'admin"
|
||||
},
|
||||
{
|
||||
name: "email",
|
||||
label: "Adresse e-mail",
|
||||
type: "text",
|
||||
placeholder: "Entrer l'email de l'admin"
|
||||
}
|
||||
]}
|
||||
submit={mutation.mutate}
|
||||
schema={adminSchema}
|
||||
child={<button type="submit" className="btn-auth">{isLoading ? "Chargement..." : "Créer le compte"}</button>}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
/>
|
||||
|
||||
<FloatingLabelInput name="search" placeholder="Effectuer une recherche" type="text" onChange={(value) => table.setGlobalFilter(value)} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -135,6 +135,11 @@ export default function HomePage () {
|
||||
>
|
||||
<Modal
|
||||
open={open}
|
||||
onOpenChange={(isOpen)=>{
|
||||
if(!isOpen) {
|
||||
setOpen(isOpen)
|
||||
}
|
||||
}}
|
||||
trigger={
|
||||
<div onClick={() => setOpen(true)}>
|
||||
<Image alt="" src={icons.trash} className="absolute right-2 top-[-50%] transform translate-middle-y hover:text-blue-500" />
|
||||
|
||||
@ -32,8 +32,7 @@ export default function FloatingLabelInput({
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<select
|
||||
className="input-form focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
className="input-form focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
name={name}
|
||||
defaultValue={defaultValue}
|
||||
>
|
||||
@ -52,9 +51,8 @@ export default function FloatingLabelInput({
|
||||
name={name}
|
||||
type={showPassword ? "text" : "password"}
|
||||
placeholder={placeholder}
|
||||
className="input-form focus:ring-2 focus:ring-blue-500 pr-10"
|
||||
className="input-form focus:ring-2 focus:ring-blue-500 pr-10 outline-none"
|
||||
defaultValue={defaultValue}
|
||||
required
|
||||
|
||||
/>
|
||||
{showPasswordToggle && (
|
||||
@ -89,8 +87,7 @@ export default function FloatingLabelInput({
|
||||
<input
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
className="floating-label input-form focus:ring-2 focus:ring-blue-500"
|
||||
required
|
||||
className="input-form focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
name={name}
|
||||
defaultValue={defaultValue}
|
||||
onChange={handleChange}
|
||||
|
||||
@ -5,33 +5,38 @@ export function Modal({
|
||||
trigger,
|
||||
title,
|
||||
content,
|
||||
open
|
||||
open,
|
||||
onOpenChange
|
||||
}: {
|
||||
trigger: ReactNode;
|
||||
title: string | ReactNode;
|
||||
title?: string | ReactNode;
|
||||
content: ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}) {
|
||||
const [toggle, setToggle] = useState(open);
|
||||
|
||||
return (
|
||||
<Dialog.Root open={toggle}>
|
||||
<Dialog.Root open={open} onOpenChange={onOpenChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
{trigger}
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black/25 z-40" />
|
||||
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-md bg-white rounded-lg shadow-xl z-50 p-6">
|
||||
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-md bg-white rounded-lg shadow-xl z-50 p-6"
|
||||
// onPointerDownOutside={(e) => e.preventDefault()}
|
||||
>
|
||||
<Dialog.Title className="text-xl font-bold text-center my-4">
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
{content}
|
||||
|
||||
{/* <div className="absolute top-4 right-4 text-gray-500 hover:text-gray-700 cursor-pointer"
|
||||
onClick={() => {setToggle(false)}}
|
||||
<div className=" justify-center">
|
||||
{content}
|
||||
</div>
|
||||
|
||||
<div className="absolute top-4 right-4 text-gray-500 hover:text-gray-700 cursor-pointer"
|
||||
onClick={() => {onOpenChange?.(false)}}
|
||||
>
|
||||
Fermer
|
||||
</div> */}
|
||||
X
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
getFilteredRowModel,
|
||||
Table as TableType,
|
||||
} from "@tanstack/react-table"
|
||||
import { cloneElement, isValidElement, ReactNode, useState } from "react";
|
||||
import { cloneElement, isValidElement, ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import Image from "next/image";
|
||||
import { icons } from "#/assets/icons";
|
||||
@ -53,6 +53,22 @@ export default function Table<TData, TValue>({
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
})
|
||||
|
||||
const headerCheckboxRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (headerCheckboxRef.current) {
|
||||
headerCheckboxRef.current.indeterminate =
|
||||
table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected();
|
||||
}
|
||||
|
||||
console.log("SELECTED ALL = ", table.getSelectedRowModel().rows)
|
||||
console.log("SELECTED = ", table.getRowModel().rows.filter((row) => row.getIsSelected()).map(row => row.original))
|
||||
}, [
|
||||
table.getIsSomePageRowsSelected(),
|
||||
table.getIsAllPageRowsSelected(),
|
||||
table.getRowModel()
|
||||
]);
|
||||
|
||||
const totalPages = table.getPageCount()
|
||||
const currentPage = table.getState().pagination.pageIndex + 1
|
||||
|
||||
@ -85,18 +101,17 @@ export default function Table<TData, TValue>({
|
||||
<thead className="h-10">
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id} className="rounded-lg">
|
||||
<th className="bg-blue-300 p-3 text-start first:rounded-tl-lg">
|
||||
<input checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && undefined)
|
||||
}
|
||||
<th className="bg-[#E9F0FF] p-3 text-start first:rounded-tl-lg">
|
||||
<input
|
||||
ref={headerCheckboxRef}
|
||||
checked={!!table.getIsAllPageRowsSelected()}
|
||||
onChange={(e) => table.toggleAllPageRowsSelected(e.target.checked)}
|
||||
type="checkbox" name="" id=""
|
||||
/>
|
||||
</th>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return(
|
||||
<th key={header.id} className="bg-blue-300 p-3 text-start last:rounded-tr-lg">
|
||||
<th key={header.id} className="bg-[#E9F0FF] p-3 text-start last:rounded-tr-lg">
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
|
||||
@ -4,7 +4,7 @@ import { ZodSchema } from "zod";
|
||||
export interface FloatingLabelInputProps {
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
type: 'text' | 'password' | 'select' | 'email' | 'number';
|
||||
type: 'text' | 'password' | 'select' | 'email' | 'number' | 'hidden';
|
||||
options?: string[];
|
||||
button?: React.ReactNode;
|
||||
showPasswordToggle?: boolean;
|
||||
@ -17,7 +17,7 @@ export interface FormProps {
|
||||
title?: string,
|
||||
fields: FloatingLabelInputProps[],
|
||||
submit: (param: any) => unknown,
|
||||
className: string,
|
||||
className?: string,
|
||||
child: ReactNode,
|
||||
schema: ZodSchema
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user