FolderRenameDialog.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <template lang="pug">
  2. q-dialog(ref='dialogRef', @hide='onDialogHide')
  3. q-card(style='min-width: 650px;')
  4. q-card-section.card-header
  5. q-icon(name='img:/_assets/icons/fluent-rename.svg', left, size='sm')
  6. span {{t(`fileman.folderRename`)}}
  7. q-form.q-py-sm(ref='renameFolderForm', @submit='rename')
  8. q-item
  9. blueprint-icon(icon='folder')
  10. q-item-section
  11. q-input(
  12. outlined
  13. v-model='state.title'
  14. dense
  15. :rules='titleValidation'
  16. hide-bottom-space
  17. :label='t(`fileman.folderTitle`)'
  18. :aria-label='t(`fileman.folderTitle`)'
  19. lazy-rules='ondemand'
  20. autofocus
  21. ref='iptTitle'
  22. @keyup.enter='rename'
  23. )
  24. q-item
  25. blueprint-icon.self-start(icon='file-submodule')
  26. q-item-section
  27. q-input(
  28. outlined
  29. v-model='state.path'
  30. dense
  31. :rules='pathValidation'
  32. hide-bottom-space
  33. :label='t(`fileman.folderFileName`)'
  34. :aria-label='t(`fileman.folderFileName`)'
  35. :hint='t(`fileman.folderFileNameHint`)'
  36. lazy-rules='ondemand'
  37. @focus='state.pathDirty = true'
  38. @keyup.enter='rename'
  39. )
  40. q-card-actions.card-actions
  41. q-space
  42. q-btn.acrylic-btn(
  43. flat
  44. :label='t(`common.actions.cancel`)'
  45. color='grey'
  46. padding='xs md'
  47. @click='onDialogCancel'
  48. )
  49. q-btn(
  50. unelevated
  51. :label='t(`common.actions.rename`)'
  52. color='primary'
  53. padding='xs md'
  54. @click='rename'
  55. :loading='state.loading > 0'
  56. )
  57. q-inner-loading(:showing='state.loading > 0')
  58. q-spinner(color='accent', size='lg')
  59. </template>
  60. <script setup>
  61. import gql from 'graphql-tag'
  62. import { useI18n } from 'vue-i18n'
  63. import { useDialogPluginComponent, useQuasar } from 'quasar'
  64. import { onMounted, reactive, ref, watch } from 'vue'
  65. import slugify from 'slugify'
  66. import { useSiteStore } from 'src/stores/site'
  67. // PROPS
  68. const props = defineProps({
  69. folderId: {
  70. type: String,
  71. required: true
  72. }
  73. })
  74. // EMITS
  75. defineEmits([
  76. ...useDialogPluginComponent.emits
  77. ])
  78. // QUASAR
  79. const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
  80. const $q = useQuasar()
  81. // STORES
  82. const siteStore = useSiteStore()
  83. // I18N
  84. const { t } = useI18n()
  85. // DATA
  86. const state = reactive({
  87. path: '',
  88. title: '',
  89. pathDirty: false,
  90. loading: false
  91. })
  92. // REFS
  93. const renameFolderForm = ref(null)
  94. const iptTitle = ref(null)
  95. // VALIDATION RULES
  96. const titleValidation = [
  97. val => val.length > 0 || t('fileman.folderTitleMissing'),
  98. val => /^[^<>"]+$/.test(val) || t('fileman.folderTitleInvalidChars')
  99. ]
  100. const pathValidation = [
  101. val => val.length > 0 || t('fileman.folderFileNameMissing'),
  102. val => /^[a-z0-9-]+$/.test(val) || t('fileman.folderFileNameInvalid')
  103. ]
  104. // WATCHERS
  105. watch(() => state.title, (newValue) => {
  106. if (state.pathDirty && !state.path) {
  107. state.pathDirty = false
  108. }
  109. if (!state.pathDirty) {
  110. state.path = slugify(newValue, { lower: true, strict: true })
  111. }
  112. })
  113. // METHODS
  114. async function rename () {
  115. state.loading++
  116. try {
  117. const isFormValid = await renameFolderForm.value.validate(true)
  118. if (!isFormValid) {
  119. throw new Error(t('fileman.renameFolderInvalidData'))
  120. }
  121. const resp = await APOLLO_CLIENT.mutate({
  122. mutation: gql`
  123. mutation renameFolder (
  124. $folderId: UUID!
  125. $pathName: String!
  126. $title: String!
  127. ) {
  128. renameFolder (
  129. folderId: $folderId
  130. pathName: $pathName
  131. title: $title
  132. ) {
  133. operation {
  134. succeeded
  135. message
  136. }
  137. }
  138. }
  139. `,
  140. variables: {
  141. folderId: props.folderId,
  142. pathName: state.path,
  143. title: state.title
  144. }
  145. })
  146. if (resp?.data?.renameFolder?.operation?.succeeded) {
  147. $q.notify({
  148. type: 'positive',
  149. message: t('fileman.renameFolderSuccess')
  150. })
  151. onDialogOK()
  152. } else {
  153. throw new Error(resp?.data?.renameFolder?.operation?.message || 'An unexpected error occured.')
  154. }
  155. } catch (err) {
  156. $q.notify({
  157. type: 'negative',
  158. message: err.message
  159. })
  160. }
  161. state.loading--
  162. }
  163. // MOUNTED
  164. onMounted(async () => {
  165. state.loading++
  166. try {
  167. const resp = await APOLLO_CLIENT.query({
  168. query: gql`
  169. query fetchFolderForRename (
  170. $id: UUID!
  171. ) {
  172. folderById (
  173. id: $id
  174. ) {
  175. id
  176. folderPath
  177. fileName
  178. title
  179. }
  180. }
  181. `,
  182. variables: {
  183. id: props.folderId
  184. }
  185. })
  186. if (resp?.data?.folderById?.id !== props.folderId) {
  187. throw new Error('Failed to fetch folder data.')
  188. }
  189. state.path = resp.data.folderById.fileName
  190. state.title = resp.data.folderById.title
  191. state.pathDirty = true
  192. } catch (err) {
  193. $q.notify({
  194. type: 'negative',
  195. message: err.message
  196. })
  197. onDialogCancel()
  198. }
  199. state.loading--
  200. })
  201. </script>