EntityTable.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. // @ts-expect-error intentional conversion of filtered object to ApiResource for table display
  129. return newItem as ApiResource
  130. })
  131. }
  132. if (props.sortBy) {
  133. items = items.sort((a: ApiResource, b: ApiResource) => {
  134. return (a as unknown as Record<string, unknown>)[props.sortBy] >
  135. (b as unknown as Record<string, unknown>)[props.sortBy]
  136. ? 1
  137. : -1
  138. })
  139. }
  140. return items
  141. })
  142. const actionsRoute = computed(() => {
  143. return UrlUtils.join(props.baseActionsRoute, props.model.entity)
  144. })
  145. const showDeletionConfirmationDialog: Ref<boolean> = ref(false)
  146. const itemToDelete: Ref<ApiResource | null> = ref(null)
  147. /**
  148. * Redirect to the edition page for the given item
  149. * @param item
  150. */
  151. const onEditClicked = (item: ApiResource) => {
  152. navigateTo(UrlUtils.join(actionsRoute.value, item.id))
  153. }
  154. /**
  155. * Show the deletion confirmation dialog
  156. * @param item
  157. */
  158. const onDeleteClicked = (item: ApiResource) => {
  159. itemToDelete.value = em.cast(props.model, item)
  160. showDeletionConfirmationDialog.value = true
  161. }
  162. const onCancelClicked = () => {
  163. itemToDelete.value = null
  164. }
  165. /**
  166. * Deletion has be confirmed, perform
  167. */
  168. const onDeleteConfirmed = async () => {
  169. pageStore.loading = true
  170. await deleteItem(itemToDelete.value)
  171. pageStore.loading = false
  172. }
  173. /**
  174. * Redirect to the creation page for this model
  175. */
  176. const goToCreatePage = () => {
  177. navigateTo(UrlUtils.join(actionsRoute.value, 'new'))
  178. }
  179. // Nettoyer les données lors du démontage du composant
  180. onBeforeUnmount(() => {
  181. // Nettoyer les références du store si nécessaire
  182. if (import.meta.client) {
  183. clearNuxtData('/^' + props.model.entity + '_many_/')
  184. useRepo(props.model).flush()
  185. }
  186. })
  187. </script>
  188. <style scoped lang="scss"></style>