| 
									
										
										
										
											2022-03-17 10:30:10 -08:00
										 |  |  | <template> | 
					
						
							|  |  |  |   <div> | 
					
						
							|  |  |  |     <v-card-actions> | 
					
						
							|  |  |  |       <v-menu v-if="tableConfig.hideColumns" offset-y bottom nudge-bottom="6" :close-on-content-click="false"> | 
					
						
							|  |  |  |         <template #activator="{ on, attrs }"> | 
					
						
							| 
									
										
										
										
											2022-07-31 15:31:20 -05:00
										 |  |  |           <v-btn color="accent" class="mr-2" dark v-bind="attrs" v-on="on"> | 
					
						
							| 
									
										
										
										
											2022-03-17 10:30:10 -08:00
										 |  |  |             <v-icon> | 
					
						
							|  |  |  |               {{ $globals.icons.cog }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							|  |  |  |           </v-btn> | 
					
						
							|  |  |  |         </template> | 
					
						
							|  |  |  |         <v-card> | 
					
						
							|  |  |  |           <v-card-text> | 
					
						
							|  |  |  |             <v-checkbox | 
					
						
							|  |  |  |               v-for="itemValue in headers" | 
					
						
							|  |  |  |               :key="itemValue.text + itemValue.show" | 
					
						
							|  |  |  |               v-model="filteredHeaders" | 
					
						
							|  |  |  |               :value="itemValue.value" | 
					
						
							|  |  |  |               dense | 
					
						
							|  |  |  |               flat | 
					
						
							|  |  |  |               inset | 
					
						
							|  |  |  |               :label="itemValue.text" | 
					
						
							|  |  |  |               hide-details | 
					
						
							|  |  |  |             ></v-checkbox> | 
					
						
							|  |  |  |           </v-card-text> | 
					
						
							|  |  |  |         </v-card> | 
					
						
							|  |  |  |       </v-menu> | 
					
						
							|  |  |  |       <BaseOverflowButton | 
					
						
							|  |  |  |         v-if="bulkActions.length > 0" | 
					
						
							|  |  |  |         :disabled="selected.length < 1" | 
					
						
							|  |  |  |         mode="event" | 
					
						
							|  |  |  |         color="info" | 
					
						
							|  |  |  |         :items="bulkActions" | 
					
						
							|  |  |  |         v-on="bulkActionListener" | 
					
						
							|  |  |  |       > | 
					
						
							|  |  |  |       </BaseOverflowButton> | 
					
						
							|  |  |  |       <slot name="button-row"> </slot> | 
					
						
							|  |  |  |     </v-card-actions> | 
					
						
							|  |  |  |     <div class="mx-2 clip-width"> | 
					
						
							| 
									
										
										
										
											2022-08-15 23:55:51 +02:00
										 |  |  |       <v-text-field v-model="search" :label="$t('search.search')"></v-text-field> | 
					
						
							| 
									
										
										
										
											2022-03-17 10:30:10 -08:00
										 |  |  |     </div> | 
					
						
							|  |  |  |     <v-data-table | 
					
						
							|  |  |  |       v-model="selected" | 
					
						
							|  |  |  |       item-key="id" | 
					
						
							|  |  |  |       :show-select="bulkActions.length > 0" | 
					
						
							|  |  |  |       :headers="activeHeaders" | 
					
						
							|  |  |  |       :items="data || []" | 
					
						
							|  |  |  |       :items-per-page="15" | 
					
						
							|  |  |  |       :search="search" | 
					
						
							|  |  |  |       class="elevation-2" | 
					
						
							|  |  |  |     > | 
					
						
							|  |  |  |       <template v-for="header in activeHeaders" #[`item.${header.value}`]="{ item }"> | 
					
						
							|  |  |  |         <slot :name="'item.' + header.value" v-bind="{ item }"> {{ item[header.value] }}</slot> | 
					
						
							|  |  |  |       </template> | 
					
						
							|  |  |  |       <template #item.actions="{ item }"> | 
					
						
							|  |  |  |         <BaseButtonGroup | 
					
						
							|  |  |  |           :buttons="[ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               icon: $globals.icons.edit, | 
					
						
							| 
									
										
										
										
											2022-08-15 23:55:51 +02:00
										 |  |  |               text: $t('general.edit'), | 
					
						
							| 
									
										
										
										
											2022-03-17 10:30:10 -08:00
										 |  |  |               event: 'edit', | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               icon: $globals.icons.delete, | 
					
						
							| 
									
										
										
										
											2022-08-15 23:55:51 +02:00
										 |  |  |               text: $t('general.delete'), | 
					
						
							| 
									
										
										
										
											2022-03-17 10:30:10 -08:00
										 |  |  |               event: 'delete', | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           ]" | 
					
						
							|  |  |  |           @delete="$emit('delete-one', item)" | 
					
						
							|  |  |  |           @edit="$emit('edit-one', item)" | 
					
						
							|  |  |  |         /> | 
					
						
							|  |  |  |       </template> | 
					
						
							|  |  |  |     </v-data-table> | 
					
						
							|  |  |  |     <v-card-actions class="justify-end"> | 
					
						
							|  |  |  |       <slot name="button-bottom"> </slot> | 
					
						
							|  |  |  |       <BaseButton color="info" @click="downloadAsJson(data, 'export.json')"> | 
					
						
							|  |  |  |         <template #icon> | 
					
						
							|  |  |  |           {{ $globals.icons.download }} | 
					
						
							|  |  |  |         </template> | 
					
						
							| 
									
										
										
										
											2022-08-15 23:55:51 +02:00
										 |  |  |         {{ $t("general.download") }} | 
					
						
							| 
									
										
										
										
											2022-03-17 10:30:10 -08:00
										 |  |  |       </BaseButton> | 
					
						
							|  |  |  |     </v-card-actions> | 
					
						
							|  |  |  |   </div> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script lang="ts"> | 
					
						
							|  |  |  | import { computed, defineComponent, ref } from "@nuxtjs/composition-api"; | 
					
						
							|  |  |  | import { downloadAsJson } from "~/composables/use-utils"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface TableConfig { | 
					
						
							|  |  |  |   hideColumns: boolean; | 
					
						
							|  |  |  |   canExport: boolean; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface TableHeaders { | 
					
						
							|  |  |  |   text: string; | 
					
						
							|  |  |  |   value: string; | 
					
						
							|  |  |  |   show: boolean; | 
					
						
							|  |  |  |   align?: string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface BulkAction { | 
					
						
							|  |  |  |   icon: string; | 
					
						
							|  |  |  |   text: string; | 
					
						
							|  |  |  |   event: string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default defineComponent({ | 
					
						
							|  |  |  |   props: { | 
					
						
							|  |  |  |     tableConfig: { | 
					
						
							|  |  |  |       type: Object as () => TableConfig, | 
					
						
							|  |  |  |       default: () => ({ | 
					
						
							|  |  |  |         hideColumns: false, | 
					
						
							|  |  |  |         canExport: false, | 
					
						
							|  |  |  |       }), | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     headers: { | 
					
						
							|  |  |  |       type: Array as () => TableHeaders[], | 
					
						
							|  |  |  |       required: true, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     data: { | 
					
						
							|  |  |  |       type: Array as () => any[], | 
					
						
							|  |  |  |       required: true, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     bulkActions: { | 
					
						
							|  |  |  |       type: Array as () => BulkAction[], | 
					
						
							|  |  |  |       default: () => [], | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   setup(props, context) { | 
					
						
							|  |  |  |     // ===========================================================
 | 
					
						
							|  |  |  |     // Reactive Headers
 | 
					
						
							|  |  |  |     const filteredHeaders = ref<string[]>([]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set default filtered
 | 
					
						
							|  |  |  |     filteredHeaders.value = (() => { | 
					
						
							|  |  |  |       const filtered: string[] = []; | 
					
						
							|  |  |  |       props.headers.forEach((element) => { | 
					
						
							|  |  |  |         if (element.show) { | 
					
						
							|  |  |  |           filtered.push(element.value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       return filtered; | 
					
						
							|  |  |  |     })(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const activeHeaders = computed(() => { | 
					
						
							|  |  |  |       const filtered = props.headers.filter((header) => filteredHeaders.value.includes(header.value)); | 
					
						
							|  |  |  |       filtered.push({ text: "", value: "actions", show: true, align: "right" }); | 
					
						
							|  |  |  |       return filtered; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const selected = ref<any[]>([]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // ===========================================================
 | 
					
						
							|  |  |  |     // Bulk Action Event Handler
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const bulkActionListener = computed(() => { | 
					
						
							|  |  |  |       const handlers: { [key: string]: () => void } = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       props.bulkActions.forEach((action) => { | 
					
						
							|  |  |  |         handlers[action.event] = () => { | 
					
						
							|  |  |  |           context.emit(action.event, selected.value); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return handlers; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const search = ref(""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       selected, | 
					
						
							|  |  |  |       filteredHeaders, | 
					
						
							|  |  |  |       activeHeaders, | 
					
						
							|  |  |  |       bulkActionListener, | 
					
						
							|  |  |  |       search, | 
					
						
							|  |  |  |       downloadAsJson, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <style> | 
					
						
							|  |  |  | .clip-width { | 
					
						
							|  |  |  |   max-width: 400px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | </style> |