EntityTable.vue 4.8 KB

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