EntityTable.vue 5.4 KB

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