@tager/admin-blog 0.3.23 → 0.3.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/constants/scopes.d.ts +11 -0
  2. package/dist/dev.d.ts +0 -2
  3. package/dist/hooks/index.d.ts +7 -3
  4. package/dist/hooks/{useBlogCategoryList.d.ts → useFetchCategories.d.ts} +2 -2
  5. package/dist/hooks/useFetchCategory.d.ts +8 -0
  6. package/dist/hooks/{useModuleConfig.d.ts → useFetchModuleConfig.d.ts} +2 -2
  7. package/dist/hooks/{useBlogPostList.d.ts → useFetchPosts.d.ts} +2 -2
  8. package/dist/hooks/useStore.d.ts +4 -0
  9. package/dist/hooks/useUserPermission.d.ts +3 -0
  10. package/dist/hooks/useUserProfile.d.ts +3 -0
  11. package/dist/index.esm.js +1245 -432
  12. package/dist/index.umd.js +1253 -438
  13. package/dist/locales/en.d.ts +10 -1
  14. package/dist/router/index.d.ts +4 -0
  15. package/dist/services/requests.d.ts +13 -22
  16. package/dist/store/index.d.ts +6 -0
  17. package/dist/store/user.d.ts +14 -0
  18. package/dist/typings/common.d.ts +1 -0
  19. package/dist/typings/model.d.ts +45 -1
  20. package/dist/utils/common.d.ts +4 -0
  21. package/dist/views/Categories/CategoryForm/CategoryForm.helpers.d.ts +3 -14
  22. package/dist/views/Categories/CategoryForm/CategoryForm.vue.d.ts +45 -22
  23. package/dist/views/Categories/CategoryList/CategoryList.helpers.d.ts +2 -2
  24. package/dist/views/Categories/CategoryList/CategoryList.vue.d.ts +6 -4
  25. package/dist/views/Categories/CategoryList/hooks/useAdvancedSearch.d.ts +1 -1
  26. package/dist/views/Posts/PostForm/PostForm.helpers.d.ts +2 -2
  27. package/dist/views/Posts/PostForm/PostForm.vue.d.ts +2 -0
  28. package/dist/views/Posts/PostList/PostList.helpers.d.ts +2 -2
  29. package/dist/views/Posts/PostList/PostList.vue.d.ts +8 -2
  30. package/dist/views/Posts/PostList/hooks/useAdvancedSearch.d.ts +1 -1
  31. package/package.json +9 -5
  32. package/src/constants/scopes.ts +11 -0
  33. package/src/dev.ts +11 -42
  34. package/src/hooks/index.ts +7 -3
  35. package/src/hooks/useFetchCategories.ts +25 -0
  36. package/src/hooks/useFetchCategory.ts +41 -0
  37. package/src/hooks/{useModuleConfig.ts → useFetchModuleConfig.ts} +7 -5
  38. package/src/hooks/useFetchPosts.ts +23 -0
  39. package/src/hooks/useStore.ts +8 -0
  40. package/src/hooks/useUserPermission.ts +16 -0
  41. package/src/hooks/useUserProfile.ts +13 -0
  42. package/src/locales/en.ts +13 -1
  43. package/src/locales/ru.ts +14 -2
  44. package/src/router/index.ts +39 -0
  45. package/src/services/requests.ts +30 -26
  46. package/src/store/index.ts +21 -0
  47. package/src/store/user.ts +109 -0
  48. package/src/typings/common.ts +1 -0
  49. package/src/typings/model.ts +54 -1
  50. package/src/utils/common.ts +23 -0
  51. package/src/views/App.vue +10 -2
  52. package/src/views/Categories/CategoryForm/CategoryForm.helpers.ts +26 -35
  53. package/src/views/Categories/CategoryForm/CategoryForm.vue +180 -113
  54. package/src/views/Categories/CategoryList/CategoryList.helpers.ts +41 -36
  55. package/src/views/Categories/CategoryList/CategoryList.vue +42 -25
  56. package/src/views/Categories/CategoryList/hooks/useAdvancedSearch.ts +1 -1
  57. package/src/views/Home.vue +1 -1
  58. package/src/views/Posts/PostForm/PostForm.helpers.ts +7 -6
  59. package/src/views/Posts/PostForm/PostForm.vue +57 -22
  60. package/src/views/Posts/PostList/PostList.helpers.ts +13 -15
  61. package/src/views/Posts/PostList/PostList.vue +96 -34
  62. package/src/views/Posts/PostList/hooks/useAdvancedSearch.ts +6 -5
  63. package/src/hooks/useBlogCategoryList.ts +0 -23
  64. package/src/hooks/useBlogPostList.ts +0 -23
@@ -1,12 +1,16 @@
1
1
  <template>
2
2
  <page
3
3
  :title="$t('blog:blogCategories')"
4
- :header-buttons="[
5
- {
6
- text: $t('blog:newCategory'),
7
- href: getBlogCategoryFormUrl({ categoryId: 'create' }),
8
- },
9
- ]"
4
+ :header-buttons="
5
+ [
6
+ canViewAdministrators
7
+ ? {
8
+ text: $t('blog:newCategory'),
9
+ href: getBlogCategoryFormUrl({ categoryId: 'create' }),
10
+ }
11
+ : null,
12
+ ].filter(Boolean)
13
+ "
10
14
  >
11
15
  <template v-slot:content>
12
16
  <data-table
@@ -63,7 +67,7 @@
63
67
  variant="icon"
64
68
  :title="$t('blog:moveUp')"
65
69
  :disabled="rowIndex === 0 || isCategoryMoving"
66
- @click="moveCategory(row.id, 'up')"
70
+ @click="startMoveCategory(row.id, 'up')"
67
71
  >
68
72
  <svg-icon name="north" />
69
73
  </base-button>
@@ -72,7 +76,7 @@
72
76
  variant="icon"
73
77
  :title="$t('blog:moveDown')"
74
78
  :disabled="rowIndex === rowData.length - 1 || isCategoryMoving"
75
- @click="moveCategory(row.id, 'down')"
79
+ @click="startMoveCategory(row.id, 'down')"
76
80
  >
77
81
  <svg-icon name="south" />
78
82
  </base-button>
@@ -98,14 +102,15 @@ import isEqual from 'lodash.isequal';
98
102
  import { useResourceDelete } from '@tager/admin-services';
99
103
  import { useDataTable } from '@tager/admin-ui';
100
104
 
101
- import { Category, Language } from '../../../typings/model';
105
+ import { Scope } from '@/constants/scopes';
106
+ import { Category, Language } from '@/typings/model';
102
107
  import {
103
- deleteBlogCategory,
104
- getBlogCategoryList,
105
- moveBlogCategory,
106
- } from '../../../services/requests';
107
- import { getBlogCategoryFormUrl } from '../../../constants/paths';
108
- import { useModuleConfig } from '../../../hooks';
108
+ deleteCategory,
109
+ getCategories,
110
+ moveCategory,
111
+ } from '@/services/requests';
112
+ import { getBlogCategoryFormUrl } from '@/constants/paths';
113
+ import { useFetchModuleConfig, useUserPermission } from '@/hooks';
109
114
 
110
115
  import {
111
116
  convertCategoryList,
@@ -117,12 +122,17 @@ import { useAdvancedSearch } from './hooks';
117
122
  export default defineComponent({
118
123
  name: 'BlogCategoryList',
119
124
  setup(props, context) {
120
- /** Fetch module config */
125
+ const canViewAdministrators = useUserPermission(
126
+ context,
127
+ Scope.AdministratorsView
128
+ );
129
+
130
+ /** Fetch module config **/
121
131
 
122
132
  const {
123
133
  data: moduleConfig,
124
134
  loading: isModuleConfigLoading,
125
- } = useModuleConfig({ context });
135
+ } = useFetchModuleConfig({ context });
126
136
 
127
137
  const languageList = computed<Language[]>(
128
138
  () => moduleConfig.value?.languages ?? []
@@ -156,7 +166,7 @@ export default defineComponent({
156
166
  pageSize,
157
167
  } = useDataTable<Category>({
158
168
  fetchEntityList: (params) =>
159
- getBlogCategoryList({
169
+ getCategories({
160
170
  query: params.searchQuery,
161
171
  pageNumber: params.pageNumber,
162
172
  pageSize: params.pageSize,
@@ -164,7 +174,7 @@ export default defineComponent({
164
174
  }),
165
175
  initialValue: [],
166
176
  context,
167
- resourceName: 'Blog category list',
177
+ resourceName: 'Blog categories',
168
178
  pageSize: 100,
169
179
  });
170
180
 
@@ -185,10 +195,10 @@ export default defineComponent({
185
195
  });
186
196
 
187
197
  const {
188
- handleResourceDelete: handleCategoryDelete,
189
198
  isDeleting,
199
+ handleResourceDelete: handleCategoryDelete,
190
200
  } = useResourceDelete({
191
- deleteResource: deleteBlogCategory,
201
+ deleteResource: deleteCategory,
192
202
  resourceName: 'Post',
193
203
  onSuccess: fetchCategoryList,
194
204
  context,
@@ -196,10 +206,10 @@ export default defineComponent({
196
206
 
197
207
  const isCategoryMoving = ref<boolean>(false);
198
208
 
199
- function moveCategory(categoryId: number, direction: 'up' | 'down') {
209
+ function startMoveCategory(categoryId: number, direction: 'up' | 'down') {
200
210
  isCategoryMoving.value = true;
201
211
 
202
- moveBlogCategory(categoryId, direction)
212
+ moveCategory(categoryId, direction)
203
213
  .then((response) => {
204
214
  if (response.success) {
205
215
  return fetchCategoryList();
@@ -222,7 +232,11 @@ export default defineComponent({
222
232
  );
223
233
 
224
234
  const columnDefs = computed(() =>
225
- getCategoryTableColumnDefs(moduleConfig.value, context.root.$t)
235
+ getCategoryTableColumnDefs(
236
+ moduleConfig.value,
237
+ context.root.$t,
238
+ canViewAdministrators.value
239
+ )
226
240
  );
227
241
 
228
242
  return {
@@ -235,7 +249,7 @@ export default defineComponent({
235
249
  isDeleting,
236
250
  getBlogCategoryFormUrl,
237
251
  getLinkToPostsByCategory,
238
- moveCategory,
252
+ startMoveCategory,
239
253
  isCategoryMoving,
240
254
 
241
255
  searchQuery,
@@ -248,6 +262,9 @@ export default defineComponent({
248
262
  languageOptionList,
249
263
  tags,
250
264
  tagRemovalHandler,
265
+
266
+ // Permissions
267
+ canViewAdministrators,
251
268
  };
252
269
  },
253
270
  });
@@ -14,7 +14,7 @@ import {
14
14
  OptionType,
15
15
  } from '@tager/admin-ui';
16
16
 
17
- import { Language } from '../../../../typings/model';
17
+ import { Language } from '@/typings/model';
18
18
 
19
19
  interface Params {
20
20
  context: SetupContext;
@@ -7,7 +7,7 @@ import { defineComponent, ref } from '@vue/composition-api';
7
7
 
8
8
  import { NavigationGridItem } from '@tager/admin-ui';
9
9
 
10
- import { getBlogCategoryListUrl, getBlogPostListUrl } from '../constants/paths';
10
+ import { getBlogCategoryListUrl, getBlogPostListUrl } from '@/constants/paths';
11
11
 
12
12
  export default defineComponent({
13
13
  name: 'Home',
@@ -6,11 +6,9 @@ import {
6
6
  universalFieldUtils,
7
7
  } from '@tager/admin-dynamic-field';
8
8
 
9
- import { Category, PostFull } from '../../../typings/model';
10
- import {
11
- PostCreationPayload,
12
- PostUpdatePayload,
13
- } from '../../../services/requests';
9
+ import { Category, PostFull } from '@/typings/model';
10
+ import { PostCreationPayload, PostUpdatePayload } from '@/services/requests';
11
+ import { getNameWithDepth } from '@/utils/common';
14
12
 
15
13
  export interface FormValues {
16
14
  title: string;
@@ -149,5 +147,8 @@ export function convertCategoryListToOptions(
149
147
  ): OptionType<number>[] {
150
148
  return categoryList
151
149
  .filter(({ language }) => (languageId ? language === languageId : true))
152
- .map(({ id, name }) => ({ value: id, label: name }));
150
+ .map(({ id, name, depth }) => ({
151
+ value: id,
152
+ label: getNameWithDepth(name, depth),
153
+ }));
153
154
  }
@@ -39,7 +39,7 @@
39
39
  id="urlAlias"
40
40
  v-model="values.urlAlias"
41
41
  name="urlAlias"
42
- :label="$t('blog:URLAlias')"
42
+ :label="$t('blog:link')"
43
43
  :url-template="urlAliasTemplate"
44
44
  :error="errors.urlAlias"
45
45
  @change="handleAliasChange"
@@ -111,11 +111,12 @@
111
111
  />
112
112
 
113
113
  <form-field-multi-select
114
- v-model="values.categories"
114
+ :selected-options="values.categories"
115
115
  name="categories"
116
116
  :label="$t('blog:categories')"
117
117
  :options="categoryOptionList"
118
118
  :error="errors.categories"
119
+ @change="handleCategoriesChange"
119
120
  />
120
121
 
121
122
  <form-field-multi-select
@@ -195,20 +196,14 @@ import {
195
196
  } from '@tager/admin-ui';
196
197
  import { DynamicField } from '@tager/admin-dynamic-field';
197
198
 
199
+ import { createBlogPost, getPost, updateBlogPost } from '@/services/requests';
200
+ import { Language, PostFull } from '@/typings/model';
201
+ import { getBlogPostFormUrl, getBlogPostListUrl } from '@/constants/paths';
202
+
198
203
  import {
199
- createBlogPost,
200
- getBlogPost,
201
- updateBlogPost,
202
- } from '../../../services/requests';
203
- import { Language, PostFull } from '../../../typings/model';
204
- import {
205
- getBlogPostFormUrl,
206
- getBlogPostListUrl,
207
- } from '../../../constants/paths';
208
- import {
209
- useModuleConfig,
210
- useBlogCategoryList,
211
- useBlogPostList,
204
+ useFetchModuleConfig,
205
+ useFetchCategories,
206
+ useFetchPosts,
212
207
  } from '../../../hooks';
213
208
 
214
209
  import {
@@ -228,11 +223,12 @@ export default defineComponent({
228
223
 
229
224
  const urlAliasChanged = ref<boolean>(false);
230
225
 
231
- /** Fetch module config */
226
+ /** Fetch module config **/
227
+
232
228
  const {
233
229
  data: moduleConfig,
234
230
  loading: isModuleConfigLoading,
235
- } = useModuleConfig({ context });
231
+ } = useFetchModuleConfig({ context });
236
232
 
237
233
  const languageList = computed<Language[]>(
238
234
  () => moduleConfig.value?.languages ?? []
@@ -248,17 +244,18 @@ export default defineComponent({
248
244
  const isLangSpecific = computed(() => languageOptionList.value.length > 0);
249
245
 
250
246
  /** Fetch category list */
247
+
251
248
  const {
252
249
  data: categoryList,
253
250
  loading: isCategoryListLoading,
254
- } = useBlogCategoryList({ context });
251
+ } = useFetchCategories({ context });
255
252
 
256
253
  /** Fetch Post */
257
254
 
258
255
  const [fetchPost, { data: post, loading: isPostLoading }] = useResource<
259
256
  Nullable<PostFull>
260
257
  >({
261
- fetchResource: () => getBlogPost(postId.value),
258
+ fetchResource: () => getPost(postId.value),
262
259
  initialValue: null,
263
260
  context,
264
261
  resourceName: 'Post',
@@ -277,7 +274,7 @@ export default defineComponent({
277
274
  });
278
275
 
279
276
  /** Fetch post list */
280
- const { data: postList, loading: isPostListLoading } = useBlogPostList({
277
+ const { data: postList, loading: isPostListLoading } = useFetchPosts({
281
278
  context,
282
279
  });
283
280
 
@@ -290,7 +287,8 @@ export default defineComponent({
290
287
  }))
291
288
  );
292
289
 
293
- /** Form State */
290
+ /** Form State **/
291
+ const isSubmitting = ref<boolean>(false);
294
292
  const values = ref<FormValues>(
295
293
  convertPostToFormValues(
296
294
  post.value,
@@ -300,7 +298,6 @@ export default defineComponent({
300
298
  )
301
299
  );
302
300
  const errors = ref<Record<string, string>>({});
303
- const isSubmitting = ref<boolean>(false);
304
301
 
305
302
  watch([post, languageOptionList, postOptionList, moduleConfig], () => {
306
303
  values.value = convertPostToFormValues(
@@ -523,6 +520,43 @@ export default defineComponent({
523
520
  return result;
524
521
  });
525
522
 
523
+ const handleCategoriesChange = (selectedOptions: OptionType<number>[]) => {
524
+ const newSelectedOptions: OptionType<number>[] = [];
525
+
526
+ selectedOptions.forEach((selectedOption) => {
527
+ const findTreeSelectedCategory = (categoryId: number) => {
528
+ const foundSelectedCategory = categoryList.value.find(
529
+ ({ id }) => id === categoryId
530
+ );
531
+
532
+ if (!foundSelectedCategory) {
533
+ return;
534
+ }
535
+
536
+ const newSelectedOption: OptionType<number> = {
537
+ value: foundSelectedCategory.id,
538
+ label: foundSelectedCategory.name,
539
+ };
540
+
541
+ if (
542
+ !newSelectedOptions.some(
543
+ ({ value }) => value === newSelectedOption.value
544
+ )
545
+ ) {
546
+ newSelectedOptions.push(newSelectedOption);
547
+ }
548
+
549
+ if (foundSelectedCategory.parent) {
550
+ findTreeSelectedCategory(foundSelectedCategory.parent.id);
551
+ }
552
+ };
553
+
554
+ findTreeSelectedCategory(selectedOption.value);
555
+ });
556
+
557
+ values.value.categories = newSelectedOptions;
558
+ };
559
+
526
560
  return {
527
561
  isCreation,
528
562
  postId,
@@ -546,6 +580,7 @@ export default defineComponent({
546
580
  handleTitleChange,
547
581
  handleAliasChange,
548
582
  urlAliasTemplate,
583
+ handleCategoriesChange,
549
584
  };
550
585
  },
551
586
  });
@@ -3,13 +3,8 @@ import { TFunction } from 'i18next';
3
3
  import { notEmpty, Nullable } from '@tager/admin-services';
4
4
  import { ColumnDefinition } from '@tager/admin-ui';
5
5
 
6
- import {
7
- Category,
8
- ModuleConfig,
9
- Language,
10
- PostShort,
11
- } from '../../../typings/model';
12
- import { getBlogPostFormUrl } from '../../../constants/paths';
6
+ import { Category, ModuleConfig, Language, PostShort } from '@/typings/model';
7
+ import { getBlogPostFormUrl } from '@/constants/paths';
13
8
 
14
9
  export function convertPostList(
15
10
  postList: PostShort[],
@@ -38,7 +33,8 @@ export function convertPostList(
38
33
 
39
34
  export function getPostTableColumnDefs(
40
35
  moduleConfig: Nullable<ModuleConfig>,
41
- t: TFunction
36
+ t: TFunction,
37
+ canViewAdministrators: boolean
42
38
  ): ColumnDefinition<PostShort>[] {
43
39
  const isLangSpecific = moduleConfig
44
40
  ? moduleConfig.languages.length > 0
@@ -90,13 +86,15 @@ export function getPostTableColumnDefs(
90
86
  format: ({ row }) =>
91
87
  row.categories.map((category) => category.name).join(', '),
92
88
  },
93
- {
94
- id: 7,
95
- name: t('blog:actions'),
96
- field: 'actions',
97
- style: { whiteSpace: 'nowrap', width: '120px' },
98
- headStyle: { whiteSpace: 'nowrap', width: '120px' },
99
- },
89
+ canViewAdministrators
90
+ ? {
91
+ id: 7,
92
+ name: t('blog:actions'),
93
+ field: 'actions',
94
+ style: { whiteSpace: 'nowrap', width: '120px' },
95
+ headStyle: { whiteSpace: 'nowrap', width: '120px' },
96
+ }
97
+ : null,
100
98
  ];
101
99
 
102
100
  return COLUMN_DEFS.filter(notEmpty);
@@ -1,12 +1,16 @@
1
1
  <template>
2
2
  <page
3
3
  :title="pageTitle"
4
- :header-buttons="[
5
- {
6
- text: $t('blog:newPost'),
7
- href: getBlogPostFormUrl({ postId: 'create' }),
8
- },
9
- ]"
4
+ :header-buttons="
5
+ [
6
+ canViewAdministrators
7
+ ? {
8
+ text: $t('blog:newPost'),
9
+ href: getBlogPostFormUrl({ postId: 'create' }),
10
+ }
11
+ : null,
12
+ ].filter(Boolean)
13
+ "
10
14
  >
11
15
  <template v-slot:content>
12
16
  <data-table
@@ -77,18 +81,28 @@
77
81
  <base-button
78
82
  variant="icon"
79
83
  :title="$t('blog:edit')"
80
- :disabled="isDeleting(row.id)"
84
+ :disabled="isBusy(row.id)"
81
85
  :href="getBlogPostFormUrl({ postId: row.id })"
82
86
  >
83
- <svg-icon name="edit"></svg-icon>
87
+ <svg-icon name="edit" />
84
88
  </base-button>
89
+
90
+ <base-button
91
+ variant="icon"
92
+ :title="$t('blog:clone')"
93
+ :disabled="isBusy(row.id)"
94
+ @click="handleResourceClone(row.id)"
95
+ >
96
+ <svg-icon name="contentCopy" />
97
+ </base-button>
98
+
85
99
  <base-button
86
100
  variant="icon"
87
101
  :title="$t('blog:remove')"
88
- :disabled="isDeleting(row.id)"
102
+ :disabled="isBusy(row.id)"
89
103
  @click="handleResourceDelete(row.id)"
90
104
  >
91
- <svg-icon name="delete"></svg-icon>
105
+ <svg-icon name="delete" />
92
106
  </base-button>
93
107
  </template>
94
108
  </data-table>
@@ -99,42 +113,65 @@
99
113
  <script lang="ts">
100
114
  import { computed, defineComponent, watch } from '@vue/composition-api';
101
115
  import isEqual from 'lodash.isequal';
102
-
103
- import { ColumnDefinition, useDataTable } from '@tager/admin-ui';
104
- import { Nullable, useResourceDelete } from '@tager/admin-services';
105
-
106
- import { Category, Language, PostShort } from '../../../typings/model';
107
- import { deleteBlogPost, getBlogPostList } from '../../../services/requests';
108
- import { getBlogPostFormUrl } from '../../../constants/paths';
109
- import { useModuleConfig, useBlogCategoryList } from '../../../hooks';
116
+ import pick from 'lodash.pick';
117
+
118
+ import {
119
+ ColumnDefinition,
120
+ useDataTable,
121
+ useTranslation,
122
+ } from '@tager/admin-ui';
123
+ import {
124
+ Nullable,
125
+ useResourceClone,
126
+ useResourceDelete,
127
+ } from '@tager/admin-services';
128
+
129
+ import { Category, Language, PostFull, PostShort } from '@/typings/model';
130
+ import { clonePost, deleteBlogPost, getPosts } from '@/services/requests';
131
+ import { getBlogPostFormUrl } from '@/constants/paths';
132
+ import {
133
+ useFetchModuleConfig,
134
+ useFetchCategories,
135
+ useUserPermission,
136
+ } from '@/hooks';
137
+ import { Scope } from '@/constants/scopes';
110
138
 
111
139
  import { convertPostList, getPostTableColumnDefs } from './PostList.helpers';
112
140
  import { useAdvancedSearch } from './hooks';
113
141
 
114
142
  export default defineComponent({
115
- name: 'BlogPostList',
143
+ name: 'Posts',
116
144
  setup(props, context) {
145
+ const { t } = useTranslation(context);
146
+
147
+ const canViewAdministrators = useUserPermission(
148
+ context,
149
+ Scope.AdministratorsView
150
+ );
151
+
117
152
  const categoryId = computed<string>(() => {
118
153
  return Array.isArray(context.root.$route.query.category)
119
154
  ? context.root.$route.query.category[0] ?? ''
120
155
  : context.root.$route.query.category;
121
156
  });
122
157
 
123
- /** Fetch module config */
158
+ /** Fetch module config **/
159
+
124
160
  const {
125
161
  data: moduleConfig,
126
162
  loading: isModuleConfigLoading,
127
- } = useModuleConfig({ context });
163
+ } = useFetchModuleConfig({ context });
128
164
 
129
165
  const languageList = computed<Language[]>(
130
166
  () => moduleConfig.value?.languages ?? []
131
167
  );
132
168
 
133
- /** Fetch category list */
169
+ /** Fetch category list **/
170
+
134
171
  const {
135
172
  data: categoryList,
136
173
  loading: isCategoryListLoading,
137
- } = useBlogCategoryList({ context });
174
+ } = useFetchCategories({ context });
138
175
 
139
176
  const selectedCategory = computed<Nullable<Category>>(() =>
140
177
  categoryId.value
@@ -174,7 +211,8 @@ export default defineComponent({
174
211
  () => languageOptionList.value.length > 1
175
212
  );
176
213
 
177
- /** Fetch Post list */
214
+ /** Fetch Post list **/
215
+
178
216
  const {
179
217
  fetchEntityList: fetchPostList,
180
218
  isLoading: isPostLoading,
@@ -187,7 +225,7 @@ export default defineComponent({
187
225
  pageSize,
188
226
  } = useDataTable<PostShort>({
189
227
  fetchEntityList: (params) =>
190
- getBlogPostList({
228
+ getPosts({
191
229
  query: params.searchQuery,
192
230
  pageNumber: params.pageNumber,
193
231
  pageSize: params.pageSize,
@@ -205,8 +243,8 @@ export default defineComponent({
205
243
  }
206
244
 
207
245
  const newQuery = {
246
+ ...pick(context.root.$route.query, ['query', 'pageNumber']),
208
247
  ...filterParams.value,
209
- query: (context.root.$route.query.query ?? '') as string,
210
248
  };
211
249
 
212
250
  if (!isEqual(context.root.$route.query, newQuery)) {
@@ -223,13 +261,6 @@ export default defineComponent({
223
261
  )
224
262
  );
225
263
 
226
- const { handleResourceDelete, isDeleting } = useResourceDelete({
227
- deleteResource: deleteBlogPost,
228
- resourceName: 'Post',
229
- onSuccess: fetchPostList,
230
- context,
231
- });
232
-
233
264
  const isRowDataLoading = computed<boolean>(
234
265
  () =>
235
266
  isCategoryListLoading.value ||
@@ -237,8 +268,32 @@ export default defineComponent({
237
268
  isModuleConfigLoading.value
238
269
  );
239
270
 
271
+ const { isDeleting, handleResourceDelete } = useResourceDelete({
272
+ deleteResource: deleteBlogPost,
273
+ resourceName: 'Post',
274
+ onSuccess: fetchPostList,
275
+ context,
276
+ });
277
+
278
+ const { isCloning, handleResourceClone } = useResourceClone({
279
+ cloneResource: clonePost,
280
+ confirmMessage: t('blog:cloneConfirm'),
281
+ successMessage: t('blog:cloneSuccess'),
282
+ failureMessage: t('blog:cloneFailure'),
283
+ onSuccessRedirectTo: (data: PostFull) => `/posts/${data.id}`,
284
+ context,
285
+ });
286
+
287
+ function isBusy(postId: number): boolean {
288
+ return isDeleting(postId) || isCloning(postId) || isRowDataLoading.value;
289
+ }
290
+
240
291
  const columnDefs = computed<ColumnDefinition<PostShort>[]>(() =>
241
- getPostTableColumnDefs(moduleConfig.value, context.root.$t)
292
+ getPostTableColumnDefs(
293
+ moduleConfig.value,
294
+ context.root.$t,
295
+ canViewAdministrators.value
296
+ )
242
297
  );
243
298
 
244
299
  return {
@@ -267,6 +322,13 @@ export default defineComponent({
267
322
  toDateFilter,
268
323
  tags,
269
324
  tagRemovalHandler,
325
+
326
+ // Permissions
327
+ canViewAdministrators,
328
+
329
+ // Clone
330
+ handleResourceClone,
331
+ isBusy,
270
332
  };
271
333
  },
272
334
  });
@@ -16,7 +16,8 @@ import {
16
16
  } from '@tager/admin-ui';
17
17
  import { isNotNullish } from '@tager/admin-services';
18
18
 
19
- import { Category, Language } from '../../../../typings/model';
19
+ import { Category, Language } from '@/typings/model';
20
+ import { getNameWithDepth } from '@/utils/common';
20
21
 
21
22
  interface Params {
22
23
  context: SetupContext;
@@ -53,9 +54,9 @@ export function useAdvancedSearch({
53
54
  /** Category **/
54
55
 
55
56
  const categoryOptionList = computed<OptionType[]>(() =>
56
- categoryList.value.map(({ id, name }) => ({
57
+ categoryList.value.map(({ id, name, depth }) => ({
57
58
  value: String(id),
58
- label: name,
59
+ label: getNameWithDepth(name, depth),
59
60
  }))
60
61
  );
61
62
 
@@ -229,9 +230,9 @@ export function useAdvancedSearch({
229
230
  languageFilter.value.some(({ value }) => value === language)
230
231
  );
231
232
 
232
- return selectedList.map(({ id, name }) => ({
233
+ return selectedList.map(({ id, name, depth }) => ({
233
234
  value: String(id),
234
- label: name,
235
+ label: getNameWithDepth(name, depth),
235
236
  }));
236
237
  });
237
238