EntityTable.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <!--
  2. A data table for the parameters page
  3. -->
  4. <template>
  5. <div class="container">
  6. <UiLoadingPanel v-if="pending" />
  7. <div v-else>
  8. <LayoutParametersTable
  9. :items="items"
  10. :title="title"
  11. :columns-definitions="columns"
  12. :actions="actions"
  13. :actions-route="actionsRoute"
  14. @edit-clicked="onEditClicked"
  15. @delete-clicked="onDeleteClicked"
  16. @add-clicked="goToCreatePage"
  17. />
  18. <UiFormDeletionConfirmationDialog
  19. v-model="showDeletionConfirmationDialog"
  20. @delete-clicked="onDeleteConfirmed"
  21. @cancel-clicked="onCancelClicked"
  22. />
  23. </div>
  24. </div>
  25. </template>
  26. <script setup lang="ts">
  27. import { TABLE_ACTION } from '~/types/enum/enums'
  28. import UrlUtils from '~/services/utils/urlUtils'
  29. import type ApiResource from '~/models/ApiResource'
  30. import { useEntityFetch } from '~/composables/data/useEntityFetch'
  31. import type { AssociativeArray } from '~/types/data'
  32. import type { ColumnDefinition } from '~/types/interfaces'
  33. import { useDeleteItem } from '~/composables/form/useDeleteItem'
  34. import { useEntityManager } from '~/composables/data/useEntityManager'
  35. const props = defineProps({
  36. /**
  37. * The model whom entities shall be to fetch
  38. */
  39. model: {
  40. type: Object as PropType<typeof ApiResource>,
  41. required: true,
  42. },
  43. /** Titre du tableau */
  44. title: {
  45. type: String,
  46. required: false,
  47. default: null,
  48. },
  49. /**
  50. * If provided, define the columns to show.
  51. * Else, all the entity's props are shown.
  52. *
  53. * Ex: [
  54. * { property: 'id', label : 'Identifier'},
  55. * { property: 'name', label : 'Full name'},
  56. * ]
  57. */
  58. columnsDefinitions: {
  59. type: Array as PropType<Array<ColumnDefinition> | null>,
  60. required: false,
  61. default: null,
  62. },
  63. /**
  64. * List of the actions available for each record
  65. */
  66. actions: {
  67. type: Array as PropType<Array<TABLE_ACTION>>,
  68. required: false,
  69. default: () => [TABLE_ACTION.EDIT, TABLE_ACTION.DELETE, TABLE_ACTION.ADD],
  70. },
  71. /**
  72. * The base URL for the edit / create pages
  73. * The resulting url will be constructed this way :
  74. *
  75. * Edition : ({baseUrl}/){apiResource.entity}/{id}
  76. * Creation : ({baseUrl}/){apiResource.entity}/new
  77. */
  78. baseActionsRoute: {
  79. type: String,
  80. required: false,
  81. default: '/parameters',
  82. },
  83. /**
  84. * If provided, sort the record by the given property
  85. */
  86. sortBy: {
  87. type: String as PropType<string>,
  88. required: false,
  89. default: 'id',
  90. },
  91. })
  92. const i18n = useI18n()
  93. const { em } = useEntityManager()
  94. const { fetchCollection } = useEntityFetch()
  95. const { data: collection, pending } = fetchCollection(props.model)
  96. const { deleteItem } = useDeleteItem()
  97. const pageStore = usePageStore()
  98. /**
  99. * Return the properties to display in the table, or all the
  100. * props of the model if not specified.
  101. */
  102. const columns: ComputedRef<Array<ColumnDefinition>> = computed(() => {
  103. return (
  104. props.columnsDefinitions ??
  105. Object.getOwnPropertyNames(new props.model()).map((prop) => {
  106. return { property: prop }
  107. })
  108. )
  109. })
  110. /**
  111. * Fetch the collection of ApiResources of the given model, then
  112. * map it according to the configuration.
  113. */
  114. const items: ComputedRef<Array<ApiResource> | null> = computed(() => {
  115. if (pending.value || collection.value === null) {
  116. return null
  117. }
  118. let items: Array<ApiResource> = collection.value!.items
  119. if (props.columnsDefinitions !== null) {
  120. // Filter the columns to show
  121. items = items.map((item) => {
  122. const newItem: ApiResource = { id: item.id }
  123. for (const col of props.columnsDefinitions!) {
  124. newItem[col.property] = item[col.property]
  125. }
  126. return newItem
  127. })
  128. }
  129. if (props.sortBy) {
  130. items = items.sort((a: AssociativeArray, b: AssociativeArray) => {
  131. return a[props.sortBy as keyof typeof a] >
  132. b[props.sortBy as keyof typeof b]
  133. ? 1
  134. : -1
  135. })
  136. }
  137. return items
  138. })
  139. const actionsRoute = computed(() => {
  140. return UrlUtils.join(props.baseActionsRoute, props.model.entity)
  141. })
  142. const showDeletionConfirmationDialog: Ref<boolean> = ref(false)
  143. const itemToDelete: Ref<ApiResource | null> = ref(null)
  144. /**
  145. * Redirect to the edition page for the given item
  146. * @param item
  147. */
  148. const onEditClicked = (item: ApiResource) => {
  149. navigateTo(UrlUtils.join(actionsRoute.value, item.id))
  150. }
  151. /**
  152. * Show the deletion confirmation dialog
  153. * @param item
  154. */
  155. const onDeleteClicked = (item: ApiResource) => {
  156. itemToDelete.value = em.cast(props.model, item)
  157. showDeletionConfirmationDialog.value = true
  158. }
  159. const onCancelClicked = () => {
  160. itemToDelete.value = null
  161. }
  162. /**
  163. * Deletion has be confirmed, perform
  164. */
  165. const onDeleteConfirmed = async () => {
  166. pageStore.loading = true
  167. await deleteItem(itemToDelete.value)
  168. pageStore.loading = false
  169. }
  170. /**
  171. * Redirect to the creation page for this model
  172. */
  173. const goToCreatePage = () => {
  174. navigateTo(UrlUtils.join(actionsRoute.value, 'new'))
  175. }
  176. </script>
  177. <style scoped lang="scss"></style>