11<script setup lang="ts">
22import SecondaryButton from ' @/packages/ui/src/Buttons/SecondaryButton.vue' ;
33import { UserCircleIcon } from ' @heroicons/vue/24/solid' ;
4- import { PlusIcon } from ' @heroicons/vue/16/solid' ;
5- import { type Component , ref } from ' vue' ;
4+ import {
5+ ChevronUpDownIcon ,
6+ ChevronDownIcon ,
7+ ChevronUpIcon ,
8+ PlusIcon ,
9+ } from ' @heroicons/vue/16/solid' ;
10+ import { type Component , computed , h , ref , watchEffect } from ' vue' ;
611import { type Client } from ' @/packages/api/src' ;
7- import ClientTableRow from ' @/Components/Common/Client/ClientTableRow.vue' ;
812import ClientCreateModal from ' @/Components/Common/Client/ClientCreateModal.vue' ;
9- import ClientTableHeading from ' @/Components/Common/Client/ClientTableHeading.vue' ;
1013import { canCreateClients } from ' @/utils/permissions' ;
1114
12- defineProps <{
15+ const props = defineProps <{
1316 clients: Client [];
1417}>();
1518const createClient = ref (false );
19+
20+ import {
21+ FlexRender ,
22+ getCoreRowModel ,
23+ useVueTable ,
24+ createColumnHelper ,
25+ type SortingState ,
26+ getSortedRowModel ,
27+ } from ' @tanstack/vue-table' ;
28+ import { storeToRefs } from ' pinia' ;
29+ import { useProjectsStore } from ' @/utils/useProjects' ;
30+ import { CheckCircleIcon } from ' @heroicons/vue/20/solid' ;
31+ import TableHeading from ' @/Components/Common/TableHeading.vue' ;
32+ import ClientMoreOptionsDropdown from ' @/Components/Common/Client/ClientMoreOptionsDropdown.vue' ;
33+ import { useClientsStore } from ' @/utils/useClients' ;
34+ import ClientEditModal from ' @/Components/Common/Client/ClientEditModal.vue' ;
35+ import TableRow from ' @/Components/TableRow.vue' ;
36+ import TableCell from ' @/Components/TableCell.vue' ;
37+
38+ const columnHelper = createColumnHelper <Client >();
39+ const { projects } = storeToRefs (useProjectsStore ());
40+
41+ const columns = computed (() => [
42+ columnHelper .accessor ((row ) => row .name , {
43+ id: ' name' ,
44+ cell : (info ) => info .getValue (),
45+ header : () => ' Name' ,
46+ }),
47+ columnHelper .accessor ((row ) => row , {
48+ id: ' projects' ,
49+ sortingFn : (a , b ) => {
50+ return (
51+ projects .value .filter (
52+ (projects ) => projects .client_id === a .original .id
53+ ).length -
54+ projects .value .filter (
55+ (projects ) => projects .client_id === b .original .id
56+ ).length
57+ );
58+ },
59+ cell : (info ) =>
60+ h (' div' , {
61+ innerHTML:
62+ projects .value .filter (
63+ (projects ) => projects .client_id === info .getValue ().id
64+ ).length + ' Projects' ,
65+ }),
66+ header : () => ' Projects' ,
67+ }),
68+ columnHelper .accessor ((row ) => row , {
69+ id: ' status' ,
70+ enableSorting: false ,
71+ cell : (info ) =>
72+ h (
73+ ' div' ,
74+ {
75+ class: ' flex space-x-1 items-center' ,
76+ },
77+ [
78+ h (CheckCircleIcon , {
79+ class: ' w-5' ,
80+ }),
81+ h (' span' , {
82+ innerHTML: info .getValue ().is_archived
83+ ? ' Archived'
84+ : ' Active' ,
85+ }),
86+ ]
87+ ),
88+ header : () => ' Status' ,
89+ }),
90+ columnHelper .display ({
91+ id: ' actions' ,
92+ cell : (info ) => {
93+ const showEditModal = ref (false );
94+ return h (
95+ ' div' ,
96+ {
97+ class: ' flex space-x-1 items-center' ,
98+ },
99+ [
100+ h (ClientEditModal , {
101+ client: info .row .original ,
102+ show: showEditModal .value ,
103+ }),
104+ h (ClientMoreOptionsDropdown , {
105+ class: ' w-5' ,
106+ client: info .row .original ,
107+ onEdit : () => (showEditModal .value = true ),
108+ onArchive : () => {
109+ useClientsStore ().updateClient (
110+ info .row .original .id ,
111+ {
112+ ... info .row .original ,
113+ is_archived: ! info .row .original .is_archived ,
114+ }
115+ );
116+ },
117+ onDelete : () => {
118+ useClientsStore ().deleteClient (
119+ info .row .original .id
120+ );
121+ },
122+ }),
123+ ]
124+ );
125+ },
126+ }),
127+ ]);
128+
129+ const data = ref (props .clients );
130+
131+ watchEffect (() => {
132+ data .value = props .clients ;
133+ });
134+
135+ const table = useVueTable ({
136+ get data() {
137+ return data .value ;
138+ },
139+ onSortingChange : (updaterOrValue ) => {
140+ sorting .value =
141+ typeof updaterOrValue === ' function'
142+ ? updaterOrValue (sorting .value )
143+ : updaterOrValue ;
144+ },
145+ getCoreRowModel: getCoreRowModel (),
146+ getSortedRowModel: getSortedRowModel (),
147+ state: {
148+ get sorting() {
149+ return sorting .value ;
150+ },
151+ },
152+ columns: columns .value ,
153+ });
154+ const sorting = ref <SortingState >([]);
16155 </script >
17156
18157<template >
@@ -23,7 +162,43 @@ const createClient = ref(false);
23162 data-testid =" client_table"
24163 class =" grid min-w-full"
25164 style =" grid-template-columns : 1fr 150px 200px 80px " >
26- <ClientTableHeading ></ClientTableHeading >
165+ <TableHeading >
166+ <TableCell
167+ v-for =" header in table.getHeaderGroups()[0].headers"
168+ :key =" header.id"
169+ :class ="
170+ header.column.getCanSort()
171+ ? 'cursor-pointer select-none'
172+ : ''
173+ "
174+ @click ="
175+ header.column.getToggleSortingHandler()?.($event)
176+ "
177+ :cell =" header" >
178+ <FlexRender
179+ v-if =" !header.isPlaceholder"
180+ :render =" header.column.columnDef.header"
181+ :props =" header.getContext()" />
182+ <div class =" px-1" v-if =" header.column.getCanSort()" >
183+ <ChevronUpDownIcon
184+ class =" h-4 text-text-tertiary"
185+ v-if ="
186+ header.column.getIsSorted() === false
187+ " ></ChevronUpDownIcon >
188+ <ChevronDownIcon
189+ class =" h-4 text-accent-300"
190+ v-if ="
191+ header.column.getIsSorted() === 'desc'
192+ " ></ChevronDownIcon >
193+ <ChevronUpIcon
194+ class =" h-4 text-accent-300"
195+ v-if ="
196+ header.column.getIsSorted() === 'asc'
197+ " ></ChevronUpIcon >
198+ </div >
199+ </TableCell >
200+ </TableHeading >
201+
27202 <div
28203 class =" col-span-2 py-24 text-center"
29204 v-if =" clients.length === 0" >
@@ -40,9 +215,16 @@ const createClient = ref(false);
40215 >Create your First Client
41216 </SecondaryButton >
42217 </div >
43- <template v-for =" client in clients " :key =" client .id " >
44- <ClientTableRow :client =" client" ></ClientTableRow >
45- </template >
218+ <TableRow v-for =" row in table.getRowModel().rows" :key =" row.id" >
219+ <TableCell
220+ v-for =" cell in row.getVisibleCells()"
221+ :key =" cell.id"
222+ :cell =" cell" >
223+ <FlexRender
224+ :render =" cell.column.columnDef.cell"
225+ :props =" cell.getContext()" />
226+ </TableCell >
227+ </TableRow >
46228 </div >
47229 </div >
48230 </div >
0 commit comments