@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
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
- import { request, useResource, notEmpty, isNotNullish, useResourceDelete, createId, convertRequestErrorToMap, urlTranslit, i18n } from '@tager/admin-services';
2
- import { onMounted, computed, ref, watch, defineComponent } from '@vue/composition-api';
3
- import { getFilterParamAsStringArray, getFilterParamAsString, getFilterParams, useDataTable, ShortCodeConstructor, FormFooter, createTabErrorFinder } from '@tager/admin-ui';
1
+ import { request, useResource, FETCH_STATUSES, notEmpty, isNotNullish, useResourceDelete, useResourceClone, createId, convertRequestErrorToMap, urlTranslit, getWebsiteOrigin, i18n } from '@tager/admin-services';
2
+ import { onMounted, watch, computed, ref, defineComponent } from '@vue/composition-api';
3
+ import { getFilterParamAsStringArray, getFilterParamAsString, getFilterParams, useTranslation, useDataTable, ShortCodeConstructor, FormFooter, createTabErrorFinder } from '@tager/admin-ui';
4
4
  import { compile } from 'path-to-regexp';
5
5
  import { universalFieldUtils, DynamicField } from '@tager/admin-dynamic-field';
6
6
 
@@ -37,7 +37,7 @@ const EN = {
37
37
  actions: 'Actions',
38
38
  updatePost: 'Update post',
39
39
  title: 'Title',
40
- URLAlias: 'URL alias',
40
+ link: 'Link',
41
41
  excerpt: 'Excerpt',
42
42
  body: 'Body',
43
43
  defaultCategory: 'Default Category',
@@ -68,6 +68,15 @@ const EN = {
68
68
  blogCategoryWasSuccessfullyUpdated: 'Blog category was successfully updated',
69
69
  blogCategoryCreationWasFailed: 'Blog category creation was failed',
70
70
  blogCategoryUpdateWasFailed: 'Blog category update was failed',
71
+ clone: 'Clone',
72
+ cloneConfirm: 'Are you sure you want to clone the page?',
73
+ cloneSuccess: 'Page have copied successfully',
74
+ cloneFailure: 'Page copy failure',
75
+ tabs: {
76
+ common: 'Common',
77
+ seo: 'SEO',
78
+ },
79
+ parentCategory: 'Parent category',
71
80
  };
72
81
 
73
82
  const RU = {
@@ -89,7 +98,7 @@ const RU = {
89
98
  edit: 'Редактировать',
90
99
  remove: 'Удалить',
91
100
  postsByCategory: 'Посты по категориям',
92
- name: 'Имя',
101
+ name: 'Название',
93
102
  image: 'Изображение',
94
103
  language: 'Язык',
95
104
  languages: 'Языки',
@@ -103,7 +112,7 @@ const RU = {
103
112
  actions: 'Действия',
104
113
  updatePost: 'Обновить пост',
105
114
  title: 'Заголовок',
106
- URLAlias: 'URL',
115
+ link: 'Ссылка',
107
116
  excerpt: 'Краткое описание',
108
117
  defaultCategory: 'Категория по-умолчанию',
109
118
  body: 'Содержание',
@@ -134,6 +143,15 @@ const RU = {
134
143
  blogCategoryWasSuccessfullyUpdated: 'Категория блога успешно обновлена',
135
144
  blogCategoryCreationWasFailed: 'Не удалось создать категорию блога',
136
145
  blogCategoryUpdateWasFailed: 'Не удалось обновить категорию блога',
146
+ clone: 'Клонировать',
147
+ cloneConfirm: 'Вы уверены, что хотите создать копию страницы?',
148
+ cloneSuccess: 'Копия страницы успешно создана',
149
+ cloneFailure: 'Ошибка копирования страницы',
150
+ tabs: {
151
+ common: 'Общие',
152
+ seo: 'SEO',
153
+ },
154
+ parentCategory: 'Родительская категория',
137
155
  };
138
156
 
139
157
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
@@ -2003,203 +2021,848 @@ function stubFalse() {
2003
2021
  module.exports = isEqual;
2004
2022
  });
2005
2023
 
2006
- /** Blog Posts */
2007
- function getBlogPostList(params) {
2008
- return request.get({ path: '/admin/blog/posts', params });
2009
- }
2010
- function getBlogPostCount() {
2011
- return request.get({ path: '/admin/blog/posts/count' });
2012
- }
2013
- function getBlogPost(postId) {
2014
- return request.get({ path: `/admin/blog/posts/${postId}` });
2015
- }
2016
- function createBlogPost(payload) {
2017
- return request.post({ path: '/admin/blog/posts', body: payload });
2018
- }
2019
- function updateBlogPost(postId, payload) {
2020
- return request.put({ path: `/admin/blog/posts/${postId}`, body: payload });
2021
- }
2022
- function deleteBlogPost(postId) {
2023
- return request.delete({ path: `/admin/blog/posts/${postId}` });
2024
- }
2025
- /** Blog Categories */
2026
- function getBlogCategoryList(params) {
2027
- return request.get({ path: '/admin/blog/categories', params });
2028
- }
2029
- function getBlogCategory(categoryId) {
2030
- return request.get({ path: `/admin/blog/categories/${categoryId}` });
2031
- }
2032
- function createBlogCategory(payload) {
2033
- return request.post({ path: '/admin/blog/categories', body: payload });
2034
- }
2035
- function updateBlogCategory(categoryId, payload) {
2036
- return request.put({
2037
- path: `/admin/blog/categories/${categoryId}`,
2038
- body: payload,
2039
- });
2040
- }
2041
- function deleteBlogCategory(categoryId) {
2042
- return request.delete({
2043
- path: `/admin/blog/categories/${categoryId}`,
2044
- });
2045
- }
2046
- function moveBlogCategory(categoryId, direction) {
2047
- return request.post({
2048
- path: `/admin/blog/categories/move/${categoryId}/${direction}`,
2049
- });
2050
- }
2051
- function getBlogModuleConfig() {
2052
- return request.get({
2053
- path: `/admin/blog/module-info`,
2054
- });
2024
+ /**
2025
+ * lodash (Custom Build) <https://lodash.com/>
2026
+ * Build: `lodash modularize exports="npm" -o ./`
2027
+ * Copyright jQuery Foundation and other contributors <https://jquery.org/>
2028
+ * Released under MIT license <https://lodash.com/license>
2029
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
2030
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
2031
+ */
2032
+
2033
+ /** Used as references for various `Number` constants. */
2034
+ var INFINITY = 1 / 0,
2035
+ MAX_SAFE_INTEGER = 9007199254740991;
2036
+
2037
+ /** `Object#toString` result references. */
2038
+ var argsTag = '[object Arguments]',
2039
+ funcTag = '[object Function]',
2040
+ genTag = '[object GeneratorFunction]',
2041
+ symbolTag = '[object Symbol]';
2042
+
2043
+ /** Detect free variable `global` from Node.js. */
2044
+ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
2045
+
2046
+ /** Detect free variable `self`. */
2047
+ var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
2048
+
2049
+ /** Used as a reference to the global object. */
2050
+ var root = freeGlobal || freeSelf || Function('return this')();
2051
+
2052
+ /**
2053
+ * A faster alternative to `Function#apply`, this function invokes `func`
2054
+ * with the `this` binding of `thisArg` and the arguments of `args`.
2055
+ *
2056
+ * @private
2057
+ * @param {Function} func The function to invoke.
2058
+ * @param {*} thisArg The `this` binding of `func`.
2059
+ * @param {Array} args The arguments to invoke `func` with.
2060
+ * @returns {*} Returns the result of `func`.
2061
+ */
2062
+ function apply(func, thisArg, args) {
2063
+ switch (args.length) {
2064
+ case 0: return func.call(thisArg);
2065
+ case 1: return func.call(thisArg, args[0]);
2066
+ case 2: return func.call(thisArg, args[0], args[1]);
2067
+ case 3: return func.call(thisArg, args[0], args[1], args[2]);
2068
+ }
2069
+ return func.apply(thisArg, args);
2055
2070
  }
2056
2071
 
2057
- const BLOG_ROUTE_PATHS = {
2058
- POST_LIST: '/blog/posts',
2059
- POST_FORM: '/blog/posts/:postId',
2060
- CATEGORY_LIST: '/blog/categories',
2061
- CATEGORY_FORM: '/blog/categories/:categoryId',
2062
- };
2063
- function getBlogPostListUrl() {
2064
- return BLOG_ROUTE_PATHS.POST_LIST;
2065
- }
2066
- function getBlogPostFormUrl(params) {
2067
- return compile(BLOG_ROUTE_PATHS.POST_FORM)({ postId: params.postId });
2068
- }
2069
- function getBlogCategoryListUrl() {
2070
- return BLOG_ROUTE_PATHS.CATEGORY_LIST;
2071
- }
2072
- function getBlogCategoryFormUrl(params) {
2073
- return compile(BLOG_ROUTE_PATHS.CATEGORY_FORM)({
2074
- categoryId: params.categoryId,
2075
- });
2072
+ /**
2073
+ * A specialized version of `_.map` for arrays without support for iteratee
2074
+ * shorthands.
2075
+ *
2076
+ * @private
2077
+ * @param {Array} [array] The array to iterate over.
2078
+ * @param {Function} iteratee The function invoked per iteration.
2079
+ * @returns {Array} Returns the new mapped array.
2080
+ */
2081
+ function arrayMap(array, iteratee) {
2082
+ var index = -1,
2083
+ length = array ? array.length : 0,
2084
+ result = Array(length);
2085
+
2086
+ while (++index < length) {
2087
+ result[index] = iteratee(array[index], index, array);
2088
+ }
2089
+ return result;
2076
2090
  }
2077
2091
 
2078
- function useModuleConfig(params) {
2079
- const [fetchModuleConfig, resource] = useResource({
2080
- fetchResource: getBlogModuleConfig,
2081
- initialValue: null,
2082
- context: params.context,
2083
- resourceName: 'Module configuration',
2084
- });
2085
- onMounted(() => {
2086
- fetchModuleConfig();
2087
- });
2088
- return resource;
2092
+ /**
2093
+ * Appends the elements of `values` to `array`.
2094
+ *
2095
+ * @private
2096
+ * @param {Array} array The array to modify.
2097
+ * @param {Array} values The values to append.
2098
+ * @returns {Array} Returns `array`.
2099
+ */
2100
+ function arrayPush(array, values) {
2101
+ var index = -1,
2102
+ length = values.length,
2103
+ offset = array.length;
2104
+
2105
+ while (++index < length) {
2106
+ array[offset + index] = values[index];
2107
+ }
2108
+ return array;
2089
2109
  }
2090
2110
 
2091
- function useBlogPostList(params) {
2092
- const [fetchBlogPostList, resource] = useResource({
2093
- fetchResource: () => getBlogPostList({ pageNumber: 1, pageSize: 99999 }),
2094
- initialValue: [],
2095
- context: params.context,
2096
- resourceName: 'Blog post list',
2097
- });
2098
- onMounted(() => {
2099
- fetchBlogPostList();
2100
- });
2101
- return resource;
2111
+ /** Used for built-in method references. */
2112
+ var objectProto = Object.prototype;
2113
+
2114
+ /** Used to check objects for own properties. */
2115
+ var hasOwnProperty = objectProto.hasOwnProperty;
2116
+
2117
+ /**
2118
+ * Used to resolve the
2119
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
2120
+ * of values.
2121
+ */
2122
+ var objectToString = objectProto.toString;
2123
+
2124
+ /** Built-in value references. */
2125
+ var Symbol = root.Symbol,
2126
+ propertyIsEnumerable = objectProto.propertyIsEnumerable,
2127
+ spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;
2128
+
2129
+ /* Built-in method references for those with the same name as other `lodash` methods. */
2130
+ var nativeMax = Math.max;
2131
+
2132
+ /**
2133
+ * The base implementation of `_.flatten` with support for restricting flattening.
2134
+ *
2135
+ * @private
2136
+ * @param {Array} array The array to flatten.
2137
+ * @param {number} depth The maximum recursion depth.
2138
+ * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
2139
+ * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
2140
+ * @param {Array} [result=[]] The initial result value.
2141
+ * @returns {Array} Returns the new flattened array.
2142
+ */
2143
+ function baseFlatten(array, depth, predicate, isStrict, result) {
2144
+ var index = -1,
2145
+ length = array.length;
2146
+
2147
+ predicate || (predicate = isFlattenable);
2148
+ result || (result = []);
2149
+
2150
+ while (++index < length) {
2151
+ var value = array[index];
2152
+ if (depth > 0 && predicate(value)) {
2153
+ if (depth > 1) {
2154
+ // Recursively flatten arrays (susceptible to call stack limits).
2155
+ baseFlatten(value, depth - 1, predicate, isStrict, result);
2156
+ } else {
2157
+ arrayPush(result, value);
2158
+ }
2159
+ } else if (!isStrict) {
2160
+ result[result.length] = value;
2161
+ }
2162
+ }
2163
+ return result;
2102
2164
  }
2103
2165
 
2104
- function useBlogCategoryList(params) {
2105
- const [fetchBlogCategoryList, resource] = useResource({
2106
- fetchResource: getBlogCategoryList,
2107
- initialValue: [],
2108
- context: params.context,
2109
- resourceName: 'Blog category list',
2110
- });
2111
- onMounted(() => {
2112
- fetchBlogCategoryList();
2113
- });
2114
- return resource;
2166
+ /**
2167
+ * The base implementation of `_.pick` without support for individual
2168
+ * property identifiers.
2169
+ *
2170
+ * @private
2171
+ * @param {Object} object The source object.
2172
+ * @param {string[]} props The property identifiers to pick.
2173
+ * @returns {Object} Returns the new object.
2174
+ */
2175
+ function basePick(object, props) {
2176
+ object = Object(object);
2177
+ return basePickBy(object, props, function(value, key) {
2178
+ return key in object;
2179
+ });
2115
2180
  }
2116
2181
 
2117
- function convertPostList(postList, selectedCategory, languages) {
2118
- return postList
2119
- .filter((post) => selectedCategory
2120
- ? post.categories.some(({ id }) => id === selectedCategory.id)
2121
- : true)
2122
- .map((post) => {
2123
- if (languages.length > 0) {
2124
- const foundLanguage = languages.find(({ id }) => id === post.language);
2125
- return Object.assign(Object.assign({}, post), { language: foundLanguage ? foundLanguage.name : post.language });
2126
- }
2127
- return post;
2128
- });
2129
- }
2130
- function getPostTableColumnDefs(moduleConfig, t) {
2131
- const isLangSpecific = moduleConfig
2132
- ? moduleConfig.languages.length > 0
2133
- : false;
2134
- const COLUMN_DEFS = [
2135
- {
2136
- id: 1,
2137
- name: 'ID',
2138
- field: 'id',
2139
- style: { width: '50px', textAlign: 'center' },
2140
- headStyle: { width: '50px', textAlign: 'center' },
2141
- },
2142
- {
2143
- id: 2,
2144
- name: t('blog:name'),
2145
- field: 'name',
2146
- type: 'name',
2147
- format: ({ row }) => ({
2148
- adminLink: {
2149
- text: row.title,
2150
- url: getBlogPostFormUrl({ postId: row.id }),
2151
- },
2152
- websiteLink: {
2153
- text: row.url,
2154
- url: [
2155
- process.env.VUE_APP_WEBSITE_URL || window.location.origin,
2156
- row.url,
2157
- ].join(''),
2158
- },
2159
- }),
2160
- },
2161
- {
2162
- id: 3,
2163
- name: t('blog:image'),
2164
- field: 'image',
2165
- type: 'image',
2166
- style: { width: '250px' },
2167
- headStyle: { width: '250px' },
2168
- },
2169
- isLangSpecific
2170
- ? { id: 4, name: t('blog:language'), field: 'language' }
2171
- : null,
2172
- { id: 5, name: t('blog:date'), field: 'date', type: 'date' },
2173
- {
2174
- id: 6,
2175
- name: t('blog:categories'),
2176
- field: 'categories',
2177
- format: ({ row }) => row.categories.map((category) => category.name).join(', '),
2178
- },
2179
- {
2180
- id: 7,
2181
- name: t('blog:actions'),
2182
- field: 'actions',
2183
- style: { whiteSpace: 'nowrap', width: '120px' },
2184
- headStyle: { whiteSpace: 'nowrap', width: '120px' },
2185
- },
2186
- ];
2187
- return COLUMN_DEFS.filter(notEmpty);
2182
+ /**
2183
+ * The base implementation of `_.pickBy` without support for iteratee shorthands.
2184
+ *
2185
+ * @private
2186
+ * @param {Object} object The source object.
2187
+ * @param {string[]} props The property identifiers to pick from.
2188
+ * @param {Function} predicate The function invoked per property.
2189
+ * @returns {Object} Returns the new object.
2190
+ */
2191
+ function basePickBy(object, props, predicate) {
2192
+ var index = -1,
2193
+ length = props.length,
2194
+ result = {};
2195
+
2196
+ while (++index < length) {
2197
+ var key = props[index],
2198
+ value = object[key];
2199
+
2200
+ if (predicate(value, key)) {
2201
+ result[key] = value;
2202
+ }
2203
+ }
2204
+ return result;
2188
2205
  }
2189
2206
 
2190
- var FilterTypes;
2191
- (function (FilterTypes) {
2192
- FilterTypes["CATEGORY"] = "category";
2193
- FilterTypes["LANGUAGE"] = "language";
2194
- FilterTypes["FROM_DATE"] = "from-date";
2195
- FilterTypes["TO_DATE"] = "to-date";
2196
- })(FilterTypes || (FilterTypes = {}));
2207
+ /**
2208
+ * The base implementation of `_.rest` which doesn't validate or coerce arguments.
2209
+ *
2210
+ * @private
2211
+ * @param {Function} func The function to apply a rest parameter to.
2212
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
2213
+ * @returns {Function} Returns the new function.
2214
+ */
2215
+ function baseRest(func, start) {
2216
+ start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
2217
+ return function() {
2218
+ var args = arguments,
2219
+ index = -1,
2220
+ length = nativeMax(args.length - start, 0),
2221
+ array = Array(length);
2222
+
2223
+ while (++index < length) {
2224
+ array[index] = args[start + index];
2225
+ }
2226
+ index = -1;
2227
+ var otherArgs = Array(start + 1);
2228
+ while (++index < start) {
2229
+ otherArgs[index] = args[index];
2230
+ }
2231
+ otherArgs[start] = array;
2232
+ return apply(func, this, otherArgs);
2233
+ };
2234
+ }
2235
+
2236
+ /**
2237
+ * Checks if `value` is a flattenable `arguments` object or array.
2238
+ *
2239
+ * @private
2240
+ * @param {*} value The value to check.
2241
+ * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
2242
+ */
2243
+ function isFlattenable(value) {
2244
+ return isArray(value) || isArguments(value) ||
2245
+ !!(spreadableSymbol && value && value[spreadableSymbol]);
2246
+ }
2247
+
2248
+ /**
2249
+ * Converts `value` to a string key if it's not a string or symbol.
2250
+ *
2251
+ * @private
2252
+ * @param {*} value The value to inspect.
2253
+ * @returns {string|symbol} Returns the key.
2254
+ */
2255
+ function toKey(value) {
2256
+ if (typeof value == 'string' || isSymbol(value)) {
2257
+ return value;
2258
+ }
2259
+ var result = (value + '');
2260
+ return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
2261
+ }
2262
+
2263
+ /**
2264
+ * Checks if `value` is likely an `arguments` object.
2265
+ *
2266
+ * @static
2267
+ * @memberOf _
2268
+ * @since 0.1.0
2269
+ * @category Lang
2270
+ * @param {*} value The value to check.
2271
+ * @returns {boolean} Returns `true` if `value` is an `arguments` object,
2272
+ * else `false`.
2273
+ * @example
2274
+ *
2275
+ * _.isArguments(function() { return arguments; }());
2276
+ * // => true
2277
+ *
2278
+ * _.isArguments([1, 2, 3]);
2279
+ * // => false
2280
+ */
2281
+ function isArguments(value) {
2282
+ // Safari 8.1 makes `arguments.callee` enumerable in strict mode.
2283
+ return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
2284
+ (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
2285
+ }
2286
+
2287
+ /**
2288
+ * Checks if `value` is classified as an `Array` object.
2289
+ *
2290
+ * @static
2291
+ * @memberOf _
2292
+ * @since 0.1.0
2293
+ * @category Lang
2294
+ * @param {*} value The value to check.
2295
+ * @returns {boolean} Returns `true` if `value` is an array, else `false`.
2296
+ * @example
2297
+ *
2298
+ * _.isArray([1, 2, 3]);
2299
+ * // => true
2300
+ *
2301
+ * _.isArray(document.body.children);
2302
+ * // => false
2303
+ *
2304
+ * _.isArray('abc');
2305
+ * // => false
2306
+ *
2307
+ * _.isArray(_.noop);
2308
+ * // => false
2309
+ */
2310
+ var isArray = Array.isArray;
2311
+
2312
+ /**
2313
+ * Checks if `value` is array-like. A value is considered array-like if it's
2314
+ * not a function and has a `value.length` that's an integer greater than or
2315
+ * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
2316
+ *
2317
+ * @static
2318
+ * @memberOf _
2319
+ * @since 4.0.0
2320
+ * @category Lang
2321
+ * @param {*} value The value to check.
2322
+ * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
2323
+ * @example
2324
+ *
2325
+ * _.isArrayLike([1, 2, 3]);
2326
+ * // => true
2327
+ *
2328
+ * _.isArrayLike(document.body.children);
2329
+ * // => true
2330
+ *
2331
+ * _.isArrayLike('abc');
2332
+ * // => true
2333
+ *
2334
+ * _.isArrayLike(_.noop);
2335
+ * // => false
2336
+ */
2337
+ function isArrayLike(value) {
2338
+ return value != null && isLength(value.length) && !isFunction(value);
2339
+ }
2340
+
2341
+ /**
2342
+ * This method is like `_.isArrayLike` except that it also checks if `value`
2343
+ * is an object.
2344
+ *
2345
+ * @static
2346
+ * @memberOf _
2347
+ * @since 4.0.0
2348
+ * @category Lang
2349
+ * @param {*} value The value to check.
2350
+ * @returns {boolean} Returns `true` if `value` is an array-like object,
2351
+ * else `false`.
2352
+ * @example
2353
+ *
2354
+ * _.isArrayLikeObject([1, 2, 3]);
2355
+ * // => true
2356
+ *
2357
+ * _.isArrayLikeObject(document.body.children);
2358
+ * // => true
2359
+ *
2360
+ * _.isArrayLikeObject('abc');
2361
+ * // => false
2362
+ *
2363
+ * _.isArrayLikeObject(_.noop);
2364
+ * // => false
2365
+ */
2366
+ function isArrayLikeObject(value) {
2367
+ return isObjectLike(value) && isArrayLike(value);
2368
+ }
2369
+
2370
+ /**
2371
+ * Checks if `value` is classified as a `Function` object.
2372
+ *
2373
+ * @static
2374
+ * @memberOf _
2375
+ * @since 0.1.0
2376
+ * @category Lang
2377
+ * @param {*} value The value to check.
2378
+ * @returns {boolean} Returns `true` if `value` is a function, else `false`.
2379
+ * @example
2380
+ *
2381
+ * _.isFunction(_);
2382
+ * // => true
2383
+ *
2384
+ * _.isFunction(/abc/);
2385
+ * // => false
2386
+ */
2387
+ function isFunction(value) {
2388
+ // The use of `Object#toString` avoids issues with the `typeof` operator
2389
+ // in Safari 8-9 which returns 'object' for typed array and other constructors.
2390
+ var tag = isObject(value) ? objectToString.call(value) : '';
2391
+ return tag == funcTag || tag == genTag;
2392
+ }
2393
+
2394
+ /**
2395
+ * Checks if `value` is a valid array-like length.
2396
+ *
2397
+ * **Note:** This method is loosely based on
2398
+ * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
2399
+ *
2400
+ * @static
2401
+ * @memberOf _
2402
+ * @since 4.0.0
2403
+ * @category Lang
2404
+ * @param {*} value The value to check.
2405
+ * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
2406
+ * @example
2407
+ *
2408
+ * _.isLength(3);
2409
+ * // => true
2410
+ *
2411
+ * _.isLength(Number.MIN_VALUE);
2412
+ * // => false
2413
+ *
2414
+ * _.isLength(Infinity);
2415
+ * // => false
2416
+ *
2417
+ * _.isLength('3');
2418
+ * // => false
2419
+ */
2420
+ function isLength(value) {
2421
+ return typeof value == 'number' &&
2422
+ value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
2423
+ }
2424
+
2425
+ /**
2426
+ * Checks if `value` is the
2427
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
2428
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
2429
+ *
2430
+ * @static
2431
+ * @memberOf _
2432
+ * @since 0.1.0
2433
+ * @category Lang
2434
+ * @param {*} value The value to check.
2435
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
2436
+ * @example
2437
+ *
2438
+ * _.isObject({});
2439
+ * // => true
2440
+ *
2441
+ * _.isObject([1, 2, 3]);
2442
+ * // => true
2443
+ *
2444
+ * _.isObject(_.noop);
2445
+ * // => true
2446
+ *
2447
+ * _.isObject(null);
2448
+ * // => false
2449
+ */
2450
+ function isObject(value) {
2451
+ var type = typeof value;
2452
+ return !!value && (type == 'object' || type == 'function');
2453
+ }
2454
+
2455
+ /**
2456
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
2457
+ * and has a `typeof` result of "object".
2458
+ *
2459
+ * @static
2460
+ * @memberOf _
2461
+ * @since 4.0.0
2462
+ * @category Lang
2463
+ * @param {*} value The value to check.
2464
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
2465
+ * @example
2466
+ *
2467
+ * _.isObjectLike({});
2468
+ * // => true
2469
+ *
2470
+ * _.isObjectLike([1, 2, 3]);
2471
+ * // => true
2472
+ *
2473
+ * _.isObjectLike(_.noop);
2474
+ * // => false
2475
+ *
2476
+ * _.isObjectLike(null);
2477
+ * // => false
2478
+ */
2479
+ function isObjectLike(value) {
2480
+ return !!value && typeof value == 'object';
2481
+ }
2482
+
2483
+ /**
2484
+ * Checks if `value` is classified as a `Symbol` primitive or object.
2485
+ *
2486
+ * @static
2487
+ * @memberOf _
2488
+ * @since 4.0.0
2489
+ * @category Lang
2490
+ * @param {*} value The value to check.
2491
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
2492
+ * @example
2493
+ *
2494
+ * _.isSymbol(Symbol.iterator);
2495
+ * // => true
2496
+ *
2497
+ * _.isSymbol('abc');
2498
+ * // => false
2499
+ */
2500
+ function isSymbol(value) {
2501
+ return typeof value == 'symbol' ||
2502
+ (isObjectLike(value) && objectToString.call(value) == symbolTag);
2503
+ }
2504
+
2505
+ /**
2506
+ * Creates an object composed of the picked `object` properties.
2507
+ *
2508
+ * @static
2509
+ * @since 0.1.0
2510
+ * @memberOf _
2511
+ * @category Object
2512
+ * @param {Object} object The source object.
2513
+ * @param {...(string|string[])} [props] The property identifiers to pick.
2514
+ * @returns {Object} Returns the new object.
2515
+ * @example
2516
+ *
2517
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
2518
+ *
2519
+ * _.pick(object, ['a', 'c']);
2520
+ * // => { 'a': 1, 'c': 3 }
2521
+ */
2522
+ var pick = baseRest(function(object, props) {
2523
+ return object == null ? {} : basePick(object, arrayMap(baseFlatten(props, 1), toKey));
2524
+ });
2525
+
2526
+ var lodash_pick = pick;
2527
+
2528
+ function getUserProfile() {
2529
+ return request.get({ path: '/admin/self' });
2530
+ }
2531
+ /** Blog Posts */
2532
+ function getPosts(params) {
2533
+ return request.get({ path: '/admin/blog/posts', params });
2534
+ }
2535
+ function getPostsCount() {
2536
+ return request.get({ path: '/admin/blog/posts/count' });
2537
+ }
2538
+ function getPost(postId) {
2539
+ return request.get({ path: `/admin/blog/posts/${postId}` });
2540
+ }
2541
+ function clonePost(postId) {
2542
+ return request.post({ path: `/admin/blog/posts/${postId}/clone` });
2543
+ }
2544
+ function createBlogPost(payload) {
2545
+ return request.post({ path: '/admin/blog/posts', body: payload });
2546
+ }
2547
+ function updateBlogPost(postId, payload) {
2548
+ return request.put({ path: `/admin/blog/posts/${postId}`, body: payload });
2549
+ }
2550
+ function deleteBlogPost(postId) {
2551
+ return request.delete({ path: `/admin/blog/posts/${postId}` });
2552
+ }
2553
+ /** Blog Categories */
2554
+ function getCategories(params) {
2555
+ return request.get({ path: '/admin/blog/categories', params });
2556
+ }
2557
+ function getCategory(categoryId) {
2558
+ return request.get({ path: `/admin/blog/categories/${categoryId}` });
2559
+ }
2560
+ function createCategory(payload) {
2561
+ return request.post({ path: '/admin/blog/categories', body: payload });
2562
+ }
2563
+ function updateCategory(categoryId, payload) {
2564
+ return request.put({
2565
+ path: `/admin/blog/categories/${categoryId}`,
2566
+ body: payload,
2567
+ });
2568
+ }
2569
+ function deleteCategory(categoryId) {
2570
+ return request.delete({
2571
+ path: `/admin/blog/categories/${categoryId}`,
2572
+ });
2573
+ }
2574
+ function moveCategory(categoryId, direction) {
2575
+ return request.post({
2576
+ path: `/admin/blog/categories/move/${categoryId}/${direction}`,
2577
+ });
2578
+ }
2579
+ function getModuleConfig() {
2580
+ return request.get({
2581
+ path: `/admin/blog/module-info`,
2582
+ });
2583
+ }
2584
+
2585
+ const BLOG_ROUTE_PATHS = {
2586
+ POST_LIST: '/blog/posts',
2587
+ POST_FORM: '/blog/posts/:postId',
2588
+ CATEGORY_LIST: '/blog/categories',
2589
+ CATEGORY_FORM: '/blog/categories/:categoryId',
2590
+ };
2591
+ function getBlogPostListUrl() {
2592
+ return BLOG_ROUTE_PATHS.POST_LIST;
2593
+ }
2594
+ function getBlogPostFormUrl(params) {
2595
+ return compile(BLOG_ROUTE_PATHS.POST_FORM)({ postId: params.postId });
2596
+ }
2597
+ function getBlogCategoryListUrl() {
2598
+ return BLOG_ROUTE_PATHS.CATEGORY_LIST;
2599
+ }
2600
+ function getBlogCategoryFormUrl(params) {
2601
+ return compile(BLOG_ROUTE_PATHS.CATEGORY_FORM)({
2602
+ categoryId: params.categoryId,
2603
+ });
2604
+ }
2605
+
2606
+ function useFetchPosts(params) {
2607
+ const [fetchPosts, resource] = useResource({
2608
+ fetchResource: () => getPosts({ pageNumber: 1, pageSize: 99999 }),
2609
+ initialValue: [],
2610
+ context: params.context,
2611
+ resourceName: 'Blog posts',
2612
+ });
2613
+ onMounted(() => {
2614
+ fetchPosts();
2615
+ });
2616
+ return resource;
2617
+ }
2618
+
2619
+ function useFetchCategory({ context, categoryId, isCreation, }) {
2620
+ const [fetchCategory, resource] = useResource({
2621
+ fetchResource: () => getCategory(categoryId.value),
2622
+ initialValue: null,
2623
+ context,
2624
+ resourceName: 'Blog category',
2625
+ });
2626
+ onMounted(() => {
2627
+ if (isCreation.value) {
2628
+ return;
2629
+ }
2630
+ fetchCategory();
2631
+ });
2632
+ watch(categoryId, () => {
2633
+ if (isCreation.value) {
2634
+ return;
2635
+ }
2636
+ fetchCategory();
2637
+ });
2638
+ return resource;
2639
+ }
2640
+
2641
+ function useFetchCategories({ context, }) {
2642
+ const [fetchCategories, resource] = useResource({
2643
+ fetchResource: getCategories,
2644
+ initialValue: [],
2645
+ context,
2646
+ resourceName: 'Blog categories',
2647
+ });
2648
+ onMounted(() => {
2649
+ fetchCategories();
2650
+ });
2651
+ return resource;
2652
+ }
2653
+
2654
+ function useFetchModuleConfig({ context, }) {
2655
+ const [fetchModuleConfig, resource] = useResource({
2656
+ fetchResource: getModuleConfig,
2657
+ initialValue: null,
2658
+ context: context,
2659
+ resourceName: 'Module configuration',
2660
+ });
2661
+ onMounted(() => {
2662
+ fetchModuleConfig();
2663
+ });
2664
+ return resource;
2665
+ }
2666
+
2667
+ function useStore(context) {
2668
+ return context.root.$store;
2669
+ }
2670
+
2671
+ const Scope = {
2672
+ All: '*',
2673
+ OrdersCreate: 'orders.create',
2674
+ OrdersView: 'orders.view',
2675
+ OrdersDelete: 'orders.delete',
2676
+ RbacViewRoles: 'rbac.view-roles',
2677
+ RbacEditRoles: 'rbac.edit-roles',
2678
+ AdministratorsView: 'administrators.view',
2679
+ AdministratorsEdit: 'administrators.edit',
2680
+ AdministratorsDelete: 'administrators.delete',
2681
+ };
2682
+
2683
+ const USER_MODULE_NAMESPACE = 'user';
2684
+ const USER_MUTATION_TYPES = {
2685
+ PROFILE_REQUEST_STARTED: 'profileRequestStarted',
2686
+ PROFILE_REQUEST_FULFILLED: 'profileRequestFulfilled',
2687
+ PROFILE_REQUEST_REJECTED: 'profileRequestRejected',
2688
+ };
2689
+ const USER_ACTION_TYPES = {
2690
+ FETCH_USER_PROFILE: 'fetchUserProfile',
2691
+ };
2692
+ const userGetters = {
2693
+ userProfile(state) {
2694
+ return state.profile;
2695
+ },
2696
+ userScopes(state) {
2697
+ const roles = state.profile ? state.profile.roles : [];
2698
+ const scopes = [];
2699
+ roles.forEach((role) => {
2700
+ scopes.push(...role.scopes);
2701
+ });
2702
+ return scopes;
2703
+ },
2704
+ userHasPermission: (state, getters) => (scope) => {
2705
+ const scopes = getters.userScopes;
2706
+ if (scopes.includes(Scope.All))
2707
+ return true;
2708
+ return scopes.includes(scope);
2709
+ },
2710
+ };
2711
+ const userModule = {
2712
+ namespaced: true,
2713
+ state: {
2714
+ profile: null,
2715
+ status: FETCH_STATUSES.IDLE,
2716
+ },
2717
+ mutations: {
2718
+ [USER_MUTATION_TYPES.PROFILE_REQUEST_STARTED](state) {
2719
+ if (state.status === FETCH_STATUSES.IDLE) {
2720
+ state.status = FETCH_STATUSES.LOADING;
2721
+ }
2722
+ },
2723
+ [USER_MUTATION_TYPES.PROFILE_REQUEST_FULFILLED](state, user) {
2724
+ if (state.status === FETCH_STATUSES.LOADING) {
2725
+ state.status = FETCH_STATUSES.SUCCESS;
2726
+ state.profile = user;
2727
+ }
2728
+ },
2729
+ [USER_MUTATION_TYPES.PROFILE_REQUEST_REJECTED](state) {
2730
+ if (state.status === FETCH_STATUSES.LOADING) {
2731
+ state.status = FETCH_STATUSES.FAILURE;
2732
+ state.profile = null;
2733
+ }
2734
+ },
2735
+ },
2736
+ actions: {
2737
+ [USER_ACTION_TYPES.FETCH_USER_PROFILE](store) {
2738
+ if (store.state.status !== FETCH_STATUSES.IDLE)
2739
+ return;
2740
+ store.commit(USER_MUTATION_TYPES.PROFILE_REQUEST_STARTED);
2741
+ getUserProfile()
2742
+ .then((response) => {
2743
+ store.commit(USER_MUTATION_TYPES.PROFILE_REQUEST_FULFILLED, response.data);
2744
+ })
2745
+ .catch((error) => {
2746
+ console.error(error);
2747
+ store.commit(USER_MUTATION_TYPES.PROFILE_REQUEST_REJECTED);
2748
+ });
2749
+ },
2750
+ },
2751
+ getters: userGetters,
2752
+ };
2753
+
2754
+ function namespace(namespace, name) {
2755
+ return `${namespace}/${name}`;
2756
+ }
2757
+ function userNamespace(name) {
2758
+ return namespace(USER_MODULE_NAMESPACE, name);
2759
+ }
2760
+ function generateNumberArray(length) {
2761
+ return Array.from({ length }, (_, index) => index);
2762
+ }
2763
+ function getNameWithDepth(name, depth) {
2764
+ return (generateNumberArray(depth)
2765
+ .map(() => '—')
2766
+ .join(' ') +
2767
+ ' ' +
2768
+ name);
2769
+ }
2770
+
2771
+ function useUserPermission(context, scope) {
2772
+ const store = useStore(context);
2773
+ return computed(() => {
2774
+ return store.getters[userNamespace('userHasPermission')](scope);
2775
+ });
2776
+ }
2777
+
2778
+ function convertPostList(postList, selectedCategory, languages) {
2779
+ return postList
2780
+ .filter((post) => selectedCategory
2781
+ ? post.categories.some(({ id }) => id === selectedCategory.id)
2782
+ : true)
2783
+ .map((post) => {
2784
+ if (languages.length > 0) {
2785
+ const foundLanguage = languages.find(({ id }) => id === post.language);
2786
+ return Object.assign(Object.assign({}, post), { language: foundLanguage ? foundLanguage.name : post.language });
2787
+ }
2788
+ return post;
2789
+ });
2790
+ }
2791
+ function getPostTableColumnDefs(moduleConfig, t, canViewAdministrators) {
2792
+ const isLangSpecific = moduleConfig
2793
+ ? moduleConfig.languages.length > 0
2794
+ : false;
2795
+ const COLUMN_DEFS = [
2796
+ {
2797
+ id: 1,
2798
+ name: 'ID',
2799
+ field: 'id',
2800
+ style: { width: '50px', textAlign: 'center' },
2801
+ headStyle: { width: '50px', textAlign: 'center' },
2802
+ },
2803
+ {
2804
+ id: 2,
2805
+ name: t('blog:name'),
2806
+ field: 'name',
2807
+ type: 'name',
2808
+ format: ({ row }) => ({
2809
+ adminLink: {
2810
+ text: row.title,
2811
+ url: getBlogPostFormUrl({ postId: row.id }),
2812
+ },
2813
+ websiteLink: {
2814
+ text: row.url,
2815
+ url: [
2816
+ process.env.VUE_APP_WEBSITE_URL || window.location.origin,
2817
+ row.url,
2818
+ ].join(''),
2819
+ },
2820
+ }),
2821
+ },
2822
+ {
2823
+ id: 3,
2824
+ name: t('blog:image'),
2825
+ field: 'image',
2826
+ type: 'image',
2827
+ style: { width: '250px' },
2828
+ headStyle: { width: '250px' },
2829
+ },
2830
+ isLangSpecific
2831
+ ? { id: 4, name: t('blog:language'), field: 'language' }
2832
+ : null,
2833
+ { id: 5, name: t('blog:date'), field: 'date', type: 'date' },
2834
+ {
2835
+ id: 6,
2836
+ name: t('blog:categories'),
2837
+ field: 'categories',
2838
+ format: ({ row }) => row.categories.map((category) => category.name).join(', '),
2839
+ },
2840
+ canViewAdministrators
2841
+ ? {
2842
+ id: 7,
2843
+ name: t('blog:actions'),
2844
+ field: 'actions',
2845
+ style: { whiteSpace: 'nowrap', width: '120px' },
2846
+ headStyle: { whiteSpace: 'nowrap', width: '120px' },
2847
+ }
2848
+ : null,
2849
+ ];
2850
+ return COLUMN_DEFS.filter(notEmpty);
2851
+ }
2852
+
2853
+ var FilterTypes;
2854
+ (function (FilterTypes) {
2855
+ FilterTypes["CATEGORY"] = "category";
2856
+ FilterTypes["LANGUAGE"] = "language";
2857
+ FilterTypes["FROM_DATE"] = "from-date";
2858
+ FilterTypes["TO_DATE"] = "to-date";
2859
+ })(FilterTypes || (FilterTypes = {}));
2197
2860
  const dateFormat = (date) => date.split('-').reverse().join('.');
2198
2861
  function useAdvancedSearch({ context, categoryList, languageList, }) {
2199
2862
  /** Category **/
2200
- const categoryOptionList = computed(() => categoryList.value.map(({ id, name }) => ({
2863
+ const categoryOptionList = computed(() => categoryList.value.map(({ id, name, depth }) => ({
2201
2864
  value: String(id),
2202
- label: name,
2865
+ label: getNameWithDepth(name, depth),
2203
2866
  })));
2204
2867
  const initialCategoryFilter = computed(() => {
2205
2868
  const queryValue = getFilterParamAsStringArray(context.root.$route.query, FilterTypes.CATEGORY);
@@ -2302,9 +2965,9 @@ function useAdvancedSearch({ context, categoryList, languageList, }) {
2302
2965
  return categoryOptionList.value;
2303
2966
  }
2304
2967
  const selectedList = categoryList.value.filter(({ language }) => languageFilter.value.some(({ value }) => value === language));
2305
- return selectedList.map(({ id, name }) => ({
2968
+ return selectedList.map(({ id, name, depth }) => ({
2306
2969
  value: String(id),
2307
- label: name,
2970
+ label: getNameWithDepth(name, depth),
2308
2971
  }));
2309
2972
  });
2310
2973
  return {
@@ -2321,18 +2984,20 @@ function useAdvancedSearch({ context, categoryList, languageList, }) {
2321
2984
  }
2322
2985
 
2323
2986
  var script = defineComponent({
2324
- name: 'BlogPostList',
2987
+ name: 'Posts',
2325
2988
  setup(props, context) {
2989
+ const { t } = useTranslation(context);
2990
+ const canViewAdministrators = useUserPermission(context, Scope.AdministratorsView);
2326
2991
  const categoryId = computed(() => {
2327
2992
  var _a;
2328
2993
  return Array.isArray(context.root.$route.query.category)
2329
2994
  ? (_a = context.root.$route.query.category[0]) !== null && _a !== void 0 ? _a : '' : context.root.$route.query.category;
2330
2995
  });
2331
- /** Fetch module config */
2332
- const { data: moduleConfig, loading: isModuleConfigLoading, } = useModuleConfig({ context });
2996
+ /** Fetch module config **/
2997
+ const { data: moduleConfig, loading: isModuleConfigLoading, } = useFetchModuleConfig({ context });
2333
2998
  const languageList = computed(() => { var _a, _b; return (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.languages) !== null && _b !== void 0 ? _b : []; });
2334
- /** Fetch category list */
2335
- const { data: categoryList, loading: isCategoryListLoading, } = useBlogCategoryList({ context });
2999
+ /** Fetch category list **/
3000
+ const { data: categoryList, loading: isCategoryListLoading, } = useFetchCategories({ context });
2336
3001
  const selectedCategory = computed(() => {
2337
3002
  var _a;
2338
3003
  return categoryId.value
@@ -2350,20 +3015,19 @@ var script = defineComponent({
2350
3015
  languageList,
2351
3016
  });
2352
3017
  const isLangSpecific = computed(() => languageOptionList.value.length > 1);
2353
- /** Fetch Post list */
3018
+ /** Fetch Post list **/
2354
3019
  const { fetchEntityList: fetchPostList, isLoading: isPostLoading, rowData: postList, errorMessage, searchQuery, handleChange, pageNumber, pageCount, pageSize, } = useDataTable({
2355
- fetchEntityList: (params) => getBlogPostList(Object.assign({ query: params.searchQuery, pageNumber: params.pageNumber, pageSize: params.pageSize }, filterParams.value)),
3020
+ fetchEntityList: (params) => getPosts(Object.assign({ query: params.searchQuery, pageNumber: params.pageNumber, pageSize: params.pageSize }, filterParams.value)),
2356
3021
  initialValue: [],
2357
3022
  context,
2358
3023
  resourceName: 'Blog post list',
2359
3024
  pageSize: 100,
2360
3025
  });
2361
3026
  watch(filterParams, () => {
2362
- var _a;
2363
3027
  if (isModuleConfigLoading.value || isCategoryListLoading.value) {
2364
3028
  return;
2365
3029
  }
2366
- const newQuery = Object.assign(Object.assign({}, filterParams.value), { query: ((_a = context.root.$route.query.query) !== null && _a !== void 0 ? _a : '') });
3030
+ const newQuery = Object.assign(Object.assign({}, lodash_pick(context.root.$route.query, ['query', 'pageNumber'])), filterParams.value);
2367
3031
  if (!lodash_isequal(context.root.$route.query, newQuery)) {
2368
3032
  context.root.$router.replace({ query: newQuery });
2369
3033
  fetchPostList();
@@ -2373,16 +3037,27 @@ var script = defineComponent({
2373
3037
  var _a, _b;
2374
3038
  return convertPostList(postList.value, selectedCategory.value, (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.languages) !== null && _b !== void 0 ? _b : []);
2375
3039
  });
2376
- const { handleResourceDelete, isDeleting } = useResourceDelete({
3040
+ const isRowDataLoading = computed(() => isCategoryListLoading.value ||
3041
+ isPostLoading.value ||
3042
+ isModuleConfigLoading.value);
3043
+ const { isDeleting, handleResourceDelete } = useResourceDelete({
2377
3044
  deleteResource: deleteBlogPost,
2378
3045
  resourceName: 'Post',
2379
3046
  onSuccess: fetchPostList,
2380
3047
  context,
2381
3048
  });
2382
- const isRowDataLoading = computed(() => isCategoryListLoading.value ||
2383
- isPostLoading.value ||
2384
- isModuleConfigLoading.value);
2385
- const columnDefs = computed(() => getPostTableColumnDefs(moduleConfig.value, context.root.$t));
3049
+ const { isCloning, handleResourceClone } = useResourceClone({
3050
+ cloneResource: clonePost,
3051
+ confirmMessage: t('blog:cloneConfirm'),
3052
+ successMessage: t('blog:cloneSuccess'),
3053
+ failureMessage: t('blog:cloneFailure'),
3054
+ onSuccessRedirectTo: (data) => `/posts/${data.id}`,
3055
+ context,
3056
+ });
3057
+ function isBusy(postId) {
3058
+ return isDeleting(postId) || isCloning(postId) || isRowDataLoading.value;
3059
+ }
3060
+ const columnDefs = computed(() => getPostTableColumnDefs(moduleConfig.value, context.root.$t, canViewAdministrators.value));
2386
3061
  return {
2387
3062
  columnDefs,
2388
3063
  rowData: displayedPostList,
@@ -2408,6 +3083,11 @@ var script = defineComponent({
2408
3083
  toDateFilter,
2409
3084
  tags,
2410
3085
  tagRemovalHandler,
3086
+ // Permissions
3087
+ canViewAdministrators,
3088
+ // Clone
3089
+ handleResourceClone,
3090
+ isBusy,
2411
3091
  };
2412
3092
  },
2413
3093
  });
@@ -2552,11 +3232,13 @@ var __vue_render__ = function () {
2552
3232
  attrs: {
2553
3233
  title: _vm.pageTitle,
2554
3234
  "header-buttons": [
2555
- {
2556
- text: _vm.$t("blog:newPost"),
2557
- href: _vm.getBlogPostFormUrl({ postId: "create" }),
2558
- },
2559
- ],
3235
+ _vm.canViewAdministrators
3236
+ ? {
3237
+ text: _vm.$t("blog:newPost"),
3238
+ href: _vm.getBlogPostFormUrl({ postId: "create" }),
3239
+ }
3240
+ : null,
3241
+ ].filter(Boolean),
2560
3242
  },
2561
3243
  scopedSlots: _vm._u([
2562
3244
  {
@@ -2699,7 +3381,7 @@ var __vue_render__ = function () {
2699
3381
  attrs: {
2700
3382
  variant: "icon",
2701
3383
  title: _vm.$t("blog:edit"),
2702
- disabled: _vm.isDeleting(row.id),
3384
+ disabled: _vm.isBusy(row.id),
2703
3385
  href: _vm.getBlogPostFormUrl({ postId: row.id }),
2704
3386
  },
2705
3387
  },
@@ -2707,13 +3389,31 @@ var __vue_render__ = function () {
2707
3389
  1
2708
3390
  ),
2709
3391
  _vm._v(" "),
3392
+ _c(
3393
+ "base-button",
3394
+ {
3395
+ attrs: {
3396
+ variant: "icon",
3397
+ title: _vm.$t("blog:clone"),
3398
+ disabled: _vm.isBusy(row.id),
3399
+ },
3400
+ on: {
3401
+ click: function ($event) {
3402
+ return _vm.handleResourceClone(row.id)
3403
+ },
3404
+ },
3405
+ },
3406
+ [_c("svg-icon", { attrs: { name: "contentCopy" } })],
3407
+ 1
3408
+ ),
3409
+ _vm._v(" "),
2710
3410
  _c(
2711
3411
  "base-button",
2712
3412
  {
2713
3413
  attrs: {
2714
3414
  variant: "icon",
2715
3415
  title: _vm.$t("blog:remove"),
2716
- disabled: _vm.isDeleting(row.id),
3416
+ disabled: _vm.isBusy(row.id),
2717
3417
  },
2718
3418
  on: {
2719
3419
  click: function ($event) {
@@ -2742,11 +3442,11 @@ __vue_render__._withStripped = true;
2742
3442
  /* style */
2743
3443
  const __vue_inject_styles__ = function (inject) {
2744
3444
  if (!inject) return
2745
- inject("data-v-e7aab2dc_0", { source: ".filters[data-v-e7aab2dc] {\n display: flex;\n margin: 0 -10px;\n}\n.filters[data-v-e7aab2dc]:not(:first-child) {\n margin-top: 10px;\n}\n.filters .filter[data-v-e7aab2dc] {\n padding: 10px 10px 0;\n width: 50%;\n margin: 0;\n}\n.date-label[data-v-e7aab2dc] {\n margin-bottom: 0.5rem;\n}\n.date-content[data-v-e7aab2dc] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n column-gap: 10px;\n}\n\n/*# sourceMappingURL=PostList.vue.map */", map: {"version":3,"sources":["/home/runner/work/tager-admin-blog/tager-admin-blog/src/views/Posts/PostList/PostList.vue","PostList.vue"],"names":[],"mappings":"AAmRA;EACA,aAAA;EACA,eAAA;AClRA;ADoRA;EACA,gBAAA;AClRA;ADqRA;EACA,oBAAA;EACA,UAAA;EACA,SAAA;ACnRA;ADuRA;EACA,qBAAA;ACpRA;ADuRA;EACA,aAAA;EACA,qCAAA;EACA,gBAAA;ACpRA;;AAEA,uCAAuC","file":"PostList.vue","sourcesContent":["<template>\n <page\n :title=\"pageTitle\"\n :header-buttons=\"[\n {\n text: $t('blog:newPost'),\n href: getBlogPostFormUrl({ postId: 'create' }),\n },\n ]\"\n >\n <template v-slot:content>\n <data-table\n :column-defs=\"columnDefs\"\n :row-data=\"rowData\"\n :loading=\"isRowDataLoading\"\n :error-message=\"errorMessage\"\n :search-query=\"searchQuery\"\n :pagination=\"{\n pageNumber,\n pageCount,\n pageSize,\n disabled: isRowDataLoading,\n }\"\n data-table=\"blog-post\"\n @change=\"handleChange\"\n >\n <template v-slot:filters>\n <advanced-search :tags=\"tags\" @click:tag=\"tagRemovalHandler\">\n <div class=\"filters\">\n <form-field-multi-select\n v-model=\"categoryFilter\"\n :options=\"categoryOptionList\"\n name=\"categoryFilter\"\n :searchable=\"true\"\n :label=\"$t('blog:category')\"\n class=\"filter\"\n />\n\n <form-field-multi-select\n v-if=\"isLangSpecific\"\n v-model=\"languageFilter\"\n :options=\"languageOptionList\"\n name=\"languageFilter\"\n :searchable=\"true\"\n :label=\"$t('blog:language')\"\n class=\"filter\"\n />\n\n <div class=\"filter\">\n <div class=\"date-label\">\n {{ $t('blog:dateOfPublication') }}\n </div>\n\n <div class=\"date-content\">\n <form-field\n v-model=\"fromDateFilter\"\n :label=\"$t('blog:From')\"\n name=\"fromDateFilter\"\n type=\"date\"\n :max=\"toDateFilter\"\n />\n\n <form-field\n v-model=\"toDateFilter\"\n :label=\"$t('blog:To')\"\n name=\"toDateFilter\"\n type=\"date\"\n :min=\"fromDateFilter\"\n />\n </div>\n </div>\n </div>\n </advanced-search>\n </template>\n\n <template v-slot:cell(actions)=\"{ row }\">\n <base-button\n variant=\"icon\"\n :title=\"$t('blog:edit')\"\n :disabled=\"isDeleting(row.id)\"\n :href=\"getBlogPostFormUrl({ postId: row.id })\"\n >\n <svg-icon name=\"edit\"></svg-icon>\n </base-button>\n <base-button\n variant=\"icon\"\n :title=\"$t('blog:remove')\"\n :disabled=\"isDeleting(row.id)\"\n @click=\"handleResourceDelete(row.id)\"\n >\n <svg-icon name=\"delete\"></svg-icon>\n </base-button>\n </template>\n </data-table>\n </template>\n </page>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, watch } from '@vue/composition-api';\nimport isEqual from 'lodash.isequal';\n\nimport { ColumnDefinition, useDataTable } from '@tager/admin-ui';\nimport { Nullable, useResourceDelete } from '@tager/admin-services';\n\nimport { Category, Language, PostShort } from '../../../typings/model';\nimport { deleteBlogPost, getBlogPostList } from '../../../services/requests';\nimport { getBlogPostFormUrl } from '../../../constants/paths';\nimport { useModuleConfig, useBlogCategoryList } from '../../../hooks';\n\nimport { convertPostList, getPostTableColumnDefs } from './PostList.helpers';\nimport { useAdvancedSearch } from './hooks';\n\nexport default defineComponent({\n name: 'BlogPostList',\n setup(props, context) {\n const categoryId = computed<string>(() => {\n return Array.isArray(context.root.$route.query.category)\n ? context.root.$route.query.category[0] ?? ''\n : context.root.$route.query.category;\n });\n\n /** Fetch module config */\n const {\n data: moduleConfig,\n loading: isModuleConfigLoading,\n } = useModuleConfig({ context });\n\n const languageList = computed<Language[]>(\n () => moduleConfig.value?.languages ?? []\n );\n\n /** Fetch category list */\n const {\n data: categoryList,\n loading: isCategoryListLoading,\n } = useBlogCategoryList({ context });\n\n const selectedCategory = computed<Nullable<Category>>(() =>\n categoryId.value\n ? categoryList.value.find(\n ({ id }) => String(id) === String(categoryId.value)\n ) ?? null\n : null\n );\n\n const pageTitle = computed<string>(() => {\n return selectedCategory.value\n ? `${context.root.$t('blog:postsByCategory')} \"${\n selectedCategory.value.name\n }\"`\n : context.root.$t('blog:posts');\n });\n\n /** Advanced search **/\n\n const {\n categoryFilter,\n categoryOptionList,\n languageFilter,\n languageOptionList,\n fromDateFilter,\n toDateFilter,\n filterParams,\n tags,\n tagRemovalHandler,\n } = useAdvancedSearch({\n context,\n categoryList,\n languageList,\n });\n\n const isLangSpecific = computed<boolean>(\n () => languageOptionList.value.length > 1\n );\n\n /** Fetch Post list */\n const {\n fetchEntityList: fetchPostList,\n isLoading: isPostLoading,\n rowData: postList,\n errorMessage,\n searchQuery,\n handleChange,\n pageNumber,\n pageCount,\n pageSize,\n } = useDataTable<PostShort>({\n fetchEntityList: (params) =>\n getBlogPostList({\n query: params.searchQuery,\n pageNumber: params.pageNumber,\n pageSize: params.pageSize,\n ...filterParams.value,\n }),\n initialValue: [],\n context,\n resourceName: 'Blog post list',\n pageSize: 100,\n });\n\n watch(filterParams, () => {\n if (isModuleConfigLoading.value || isCategoryListLoading.value) {\n return;\n }\n\n const newQuery = {\n ...filterParams.value,\n query: (context.root.$route.query.query ?? '') as string,\n };\n\n if (!isEqual(context.root.$route.query, newQuery)) {\n context.root.$router.replace({ query: newQuery });\n fetchPostList();\n }\n });\n\n const displayedPostList = computed<PostShort[]>(() =>\n convertPostList(\n postList.value,\n selectedCategory.value,\n moduleConfig.value?.languages ?? []\n )\n );\n\n const { handleResourceDelete, isDeleting } = useResourceDelete({\n deleteResource: deleteBlogPost,\n resourceName: 'Post',\n onSuccess: fetchPostList,\n context,\n });\n\n const isRowDataLoading = computed<boolean>(\n () =>\n isCategoryListLoading.value ||\n isPostLoading.value ||\n isModuleConfigLoading.value\n );\n\n const columnDefs = computed<ColumnDefinition<PostShort>[]>(() =>\n getPostTableColumnDefs(moduleConfig.value, context.root.$t)\n );\n\n return {\n columnDefs,\n rowData: displayedPostList,\n handleResourceDelete,\n isDeleting,\n categoryList,\n isRowDataLoading,\n errorMessage,\n getBlogPostFormUrl,\n pageTitle,\n searchQuery,\n handleChange,\n pageNumber,\n pageCount,\n pageSize,\n\n // Advanced search\n categoryFilter,\n categoryOptionList,\n isLangSpecific,\n languageFilter,\n languageOptionList,\n fromDateFilter,\n toDateFilter,\n tags,\n tagRemovalHandler,\n };\n },\n});\n</script>\n\n<style scoped lang=\"scss\">\n.filters {\n display: flex;\n margin: 0 -10px;\n\n &:not(:first-child) {\n margin-top: 10px;\n }\n\n .filter {\n padding: 10px 10px 0;\n width: 50%;\n margin: 0;\n }\n}\n\n.date-label {\n margin-bottom: 0.5rem;\n}\n\n.date-content {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n column-gap: 10px;\n}\n</style>\n",".filters {\n display: flex;\n margin: 0 -10px;\n}\n.filters:not(:first-child) {\n margin-top: 10px;\n}\n.filters .filter {\n padding: 10px 10px 0;\n width: 50%;\n margin: 0;\n}\n\n.date-label {\n margin-bottom: 0.5rem;\n}\n\n.date-content {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n column-gap: 10px;\n}\n\n/*# sourceMappingURL=PostList.vue.map */"]}, media: undefined });
3445
+ inject("data-v-7862c502_0", { source: ".filters[data-v-7862c502] {\n display: flex;\n margin: 0 -10px;\n}\n.filters[data-v-7862c502]:not(:first-child) {\n margin-top: 10px;\n}\n.filters .filter[data-v-7862c502] {\n padding: 10px 10px 0;\n width: 50%;\n margin: 0;\n}\n.date-label[data-v-7862c502] {\n margin-bottom: 0.5rem;\n}\n.date-content[data-v-7862c502] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n column-gap: 10px;\n}\n\n/*# sourceMappingURL=PostList.vue.map */", map: {"version":3,"sources":["/home/runner/work/tager-admin-blog/tager-admin-blog/src/views/Posts/PostList/PostList.vue","PostList.vue"],"names":[],"mappings":"AAiVA;EACA,aAAA;EACA,eAAA;AChVA;ADkVA;EACA,gBAAA;AChVA;ADmVA;EACA,oBAAA;EACA,UAAA;EACA,SAAA;ACjVA;ADqVA;EACA,qBAAA;AClVA;ADqVA;EACA,aAAA;EACA,qCAAA;EACA,gBAAA;AClVA;;AAEA,uCAAuC","file":"PostList.vue","sourcesContent":["<template>\n <page\n :title=\"pageTitle\"\n :header-buttons=\"\n [\n canViewAdministrators\n ? {\n text: $t('blog:newPost'),\n href: getBlogPostFormUrl({ postId: 'create' }),\n }\n : null,\n ].filter(Boolean)\n \"\n >\n <template v-slot:content>\n <data-table\n :column-defs=\"columnDefs\"\n :row-data=\"rowData\"\n :loading=\"isRowDataLoading\"\n :error-message=\"errorMessage\"\n :search-query=\"searchQuery\"\n :pagination=\"{\n pageNumber,\n pageCount,\n pageSize,\n disabled: isRowDataLoading,\n }\"\n data-table=\"blog-post\"\n @change=\"handleChange\"\n >\n <template v-slot:filters>\n <advanced-search :tags=\"tags\" @click:tag=\"tagRemovalHandler\">\n <div class=\"filters\">\n <form-field-multi-select\n v-model=\"categoryFilter\"\n :options=\"categoryOptionList\"\n name=\"categoryFilter\"\n :searchable=\"true\"\n :label=\"$t('blog:category')\"\n class=\"filter\"\n />\n\n <form-field-multi-select\n v-if=\"isLangSpecific\"\n v-model=\"languageFilter\"\n :options=\"languageOptionList\"\n name=\"languageFilter\"\n :searchable=\"true\"\n :label=\"$t('blog:language')\"\n class=\"filter\"\n />\n\n <div class=\"filter\">\n <div class=\"date-label\">\n {{ $t('blog:dateOfPublication') }}\n </div>\n\n <div class=\"date-content\">\n <form-field\n v-model=\"fromDateFilter\"\n :label=\"$t('blog:From')\"\n name=\"fromDateFilter\"\n type=\"date\"\n :max=\"toDateFilter\"\n />\n\n <form-field\n v-model=\"toDateFilter\"\n :label=\"$t('blog:To')\"\n name=\"toDateFilter\"\n type=\"date\"\n :min=\"fromDateFilter\"\n />\n </div>\n </div>\n </div>\n </advanced-search>\n </template>\n\n <template v-slot:cell(actions)=\"{ row }\">\n <base-button\n variant=\"icon\"\n :title=\"$t('blog:edit')\"\n :disabled=\"isBusy(row.id)\"\n :href=\"getBlogPostFormUrl({ postId: row.id })\"\n >\n <svg-icon name=\"edit\" />\n </base-button>\n\n <base-button\n variant=\"icon\"\n :title=\"$t('blog:clone')\"\n :disabled=\"isBusy(row.id)\"\n @click=\"handleResourceClone(row.id)\"\n >\n <svg-icon name=\"contentCopy\" />\n </base-button>\n\n <base-button\n variant=\"icon\"\n :title=\"$t('blog:remove')\"\n :disabled=\"isBusy(row.id)\"\n @click=\"handleResourceDelete(row.id)\"\n >\n <svg-icon name=\"delete\" />\n </base-button>\n </template>\n </data-table>\n </template>\n </page>\n</template>\n\n<script lang=\"ts\">\nimport { computed, defineComponent, watch } from '@vue/composition-api';\nimport isEqual from 'lodash.isequal';\nimport pick from 'lodash.pick';\n\nimport {\n ColumnDefinition,\n useDataTable,\n useTranslation,\n} from '@tager/admin-ui';\nimport {\n Nullable,\n useResourceClone,\n useResourceDelete,\n} from '@tager/admin-services';\n\nimport { Category, Language, PostFull, PostShort } from '@/typings/model';\nimport { clonePost, deleteBlogPost, getPosts } from '@/services/requests';\nimport { getBlogPostFormUrl } from '@/constants/paths';\nimport {\n useFetchModuleConfig,\n useFetchCategories,\n useUserPermission,\n} from '@/hooks';\nimport { Scope } from '@/constants/scopes';\n\nimport { convertPostList, getPostTableColumnDefs } from './PostList.helpers';\nimport { useAdvancedSearch } from './hooks';\n\nexport default defineComponent({\n name: 'Posts',\n setup(props, context) {\n const { t } = useTranslation(context);\n\n const canViewAdministrators = useUserPermission(\n context,\n Scope.AdministratorsView\n );\n\n const categoryId = computed<string>(() => {\n return Array.isArray(context.root.$route.query.category)\n ? context.root.$route.query.category[0] ?? ''\n : context.root.$route.query.category;\n });\n\n /** Fetch module config **/\n\n const {\n data: moduleConfig,\n loading: isModuleConfigLoading,\n } = useFetchModuleConfig({ context });\n\n const languageList = computed<Language[]>(\n () => moduleConfig.value?.languages ?? []\n );\n\n /** Fetch category list **/\n\n const {\n data: categoryList,\n loading: isCategoryListLoading,\n } = useFetchCategories({ context });\n\n const selectedCategory = computed<Nullable<Category>>(() =>\n categoryId.value\n ? categoryList.value.find(\n ({ id }) => String(id) === String(categoryId.value)\n ) ?? null\n : null\n );\n\n const pageTitle = computed<string>(() => {\n return selectedCategory.value\n ? `${context.root.$t('blog:postsByCategory')} \"${\n selectedCategory.value.name\n }\"`\n : context.root.$t('blog:posts');\n });\n\n /** Advanced search **/\n\n const {\n categoryFilter,\n categoryOptionList,\n languageFilter,\n languageOptionList,\n fromDateFilter,\n toDateFilter,\n filterParams,\n tags,\n tagRemovalHandler,\n } = useAdvancedSearch({\n context,\n categoryList,\n languageList,\n });\n\n const isLangSpecific = computed<boolean>(\n () => languageOptionList.value.length > 1\n );\n\n /** Fetch Post list **/\n\n const {\n fetchEntityList: fetchPostList,\n isLoading: isPostLoading,\n rowData: postList,\n errorMessage,\n searchQuery,\n handleChange,\n pageNumber,\n pageCount,\n pageSize,\n } = useDataTable<PostShort>({\n fetchEntityList: (params) =>\n getPosts({\n query: params.searchQuery,\n pageNumber: params.pageNumber,\n pageSize: params.pageSize,\n ...filterParams.value,\n }),\n initialValue: [],\n context,\n resourceName: 'Blog post list',\n pageSize: 100,\n });\n\n watch(filterParams, () => {\n if (isModuleConfigLoading.value || isCategoryListLoading.value) {\n return;\n }\n\n const newQuery = {\n ...pick(context.root.$route.query, ['query', 'pageNumber']),\n ...filterParams.value,\n };\n\n if (!isEqual(context.root.$route.query, newQuery)) {\n context.root.$router.replace({ query: newQuery });\n fetchPostList();\n }\n });\n\n const displayedPostList = computed<PostShort[]>(() =>\n convertPostList(\n postList.value,\n selectedCategory.value,\n moduleConfig.value?.languages ?? []\n )\n );\n\n const isRowDataLoading = computed<boolean>(\n () =>\n isCategoryListLoading.value ||\n isPostLoading.value ||\n isModuleConfigLoading.value\n );\n\n const { isDeleting, handleResourceDelete } = useResourceDelete({\n deleteResource: deleteBlogPost,\n resourceName: 'Post',\n onSuccess: fetchPostList,\n context,\n });\n\n const { isCloning, handleResourceClone } = useResourceClone({\n cloneResource: clonePost,\n confirmMessage: t('blog:cloneConfirm'),\n successMessage: t('blog:cloneSuccess'),\n failureMessage: t('blog:cloneFailure'),\n onSuccessRedirectTo: (data: PostFull) => `/posts/${data.id}`,\n context,\n });\n\n function isBusy(postId: number): boolean {\n return isDeleting(postId) || isCloning(postId) || isRowDataLoading.value;\n }\n\n const columnDefs = computed<ColumnDefinition<PostShort>[]>(() =>\n getPostTableColumnDefs(\n moduleConfig.value,\n context.root.$t,\n canViewAdministrators.value\n )\n );\n\n return {\n columnDefs,\n rowData: displayedPostList,\n handleResourceDelete,\n isDeleting,\n categoryList,\n isRowDataLoading,\n errorMessage,\n getBlogPostFormUrl,\n pageTitle,\n searchQuery,\n handleChange,\n pageNumber,\n pageCount,\n pageSize,\n\n // Advanced search\n categoryFilter,\n categoryOptionList,\n isLangSpecific,\n languageFilter,\n languageOptionList,\n fromDateFilter,\n toDateFilter,\n tags,\n tagRemovalHandler,\n\n // Permissions\n canViewAdministrators,\n\n // Clone\n handleResourceClone,\n isBusy,\n };\n },\n});\n</script>\n\n<style scoped lang=\"scss\">\n.filters {\n display: flex;\n margin: 0 -10px;\n\n &:not(:first-child) {\n margin-top: 10px;\n }\n\n .filter {\n padding: 10px 10px 0;\n width: 50%;\n margin: 0;\n }\n}\n\n.date-label {\n margin-bottom: 0.5rem;\n}\n\n.date-content {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n column-gap: 10px;\n}\n</style>\n",".filters {\n display: flex;\n margin: 0 -10px;\n}\n.filters:not(:first-child) {\n margin-top: 10px;\n}\n.filters .filter {\n padding: 10px 10px 0;\n width: 50%;\n margin: 0;\n}\n\n.date-label {\n margin-bottom: 0.5rem;\n}\n\n.date-content {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n column-gap: 10px;\n}\n\n/*# sourceMappingURL=PostList.vue.map */"]}, media: undefined });
2746
3446
 
2747
3447
  };
2748
3448
  /* scoped */
2749
- const __vue_scope_id__ = "data-v-e7aab2dc";
3449
+ const __vue_scope_id__ = "data-v-7862c502";
2750
3450
  /* module identifier */
2751
3451
  const __vue_module_identifier__ = undefined;
2752
3452
  /* functional template */
@@ -2856,7 +3556,10 @@ function convertFormValuesToUpdatePayload(values) {
2856
3556
  function convertCategoryListToOptions(categoryList, languageId) {
2857
3557
  return categoryList
2858
3558
  .filter(({ language }) => (languageId ? language === languageId : true))
2859
- .map(({ id, name }) => ({ value: id, label: name }));
3559
+ .map(({ id, name, depth }) => ({
3560
+ value: id,
3561
+ label: getNameWithDepth(name, depth),
3562
+ }));
2860
3563
  }
2861
3564
 
2862
3565
  var script$1 = defineComponent({
@@ -2867,8 +3570,8 @@ var script$1 = defineComponent({
2867
3570
  const postId = computed(() => context.root.$route.params.postId);
2868
3571
  const isCreation = computed(() => postId.value === 'create');
2869
3572
  const urlAliasChanged = ref(false);
2870
- /** Fetch module config */
2871
- const { data: moduleConfig, loading: isModuleConfigLoading, } = useModuleConfig({ context });
3573
+ /** Fetch module config **/
3574
+ const { data: moduleConfig, loading: isModuleConfigLoading, } = useFetchModuleConfig({ context });
2872
3575
  const languageList = computed(() => { var _a, _b; return (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.languages) !== null && _b !== void 0 ? _b : []; });
2873
3576
  const languageOptionList = computed(() => languageList.value.map(({ id, name }) => ({
2874
3577
  value: id,
@@ -2876,10 +3579,10 @@ var script$1 = defineComponent({
2876
3579
  })));
2877
3580
  const isLangSpecific = computed(() => languageOptionList.value.length > 0);
2878
3581
  /** Fetch category list */
2879
- const { data: categoryList, loading: isCategoryListLoading, } = useBlogCategoryList({ context });
3582
+ const { data: categoryList, loading: isCategoryListLoading, } = useFetchCategories({ context });
2880
3583
  /** Fetch Post */
2881
3584
  const [fetchPost, { data: post, loading: isPostLoading }] = useResource({
2882
- fetchResource: () => getBlogPost(postId.value),
3585
+ fetchResource: () => getPost(postId.value),
2883
3586
  initialValue: null,
2884
3587
  context,
2885
3588
  resourceName: 'Post',
@@ -2895,7 +3598,7 @@ var script$1 = defineComponent({
2895
3598
  fetchPost();
2896
3599
  });
2897
3600
  /** Fetch post list */
2898
- const { data: postList, loading: isPostListLoading } = useBlogPostList({
3601
+ const { data: postList, loading: isPostListLoading } = useFetchPosts({
2899
3602
  context,
2900
3603
  });
2901
3604
  const postOptionList = computed(() => postList.value
@@ -2904,10 +3607,10 @@ var script$1 = defineComponent({
2904
3607
  value: post.id,
2905
3608
  label: post.title,
2906
3609
  })));
2907
- /** Form State */
3610
+ /** Form State **/
3611
+ const isSubmitting = ref(false);
2908
3612
  const values = ref(convertPostToFormValues(post.value, languageOptionList.value, postOptionList.value, (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : []));
2909
3613
  const errors = ref({});
2910
- const isSubmitting = ref(false);
2911
3614
  watch([post, languageOptionList, postOptionList, moduleConfig], () => {
2912
3615
  var _a, _b;
2913
3616
  values.value = convertPostToFormValues(post.value, languageOptionList.value, postOptionList.value, (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : []);
@@ -3073,6 +3776,29 @@ var script$1 = defineComponent({
3073
3776
  }
3074
3777
  return result;
3075
3778
  });
3779
+ const handleCategoriesChange = (selectedOptions) => {
3780
+ const newSelectedOptions = [];
3781
+ selectedOptions.forEach((selectedOption) => {
3782
+ const findTreeSelectedCategory = (categoryId) => {
3783
+ const foundSelectedCategory = categoryList.value.find(({ id }) => id === categoryId);
3784
+ if (!foundSelectedCategory) {
3785
+ return;
3786
+ }
3787
+ const newSelectedOption = {
3788
+ value: foundSelectedCategory.id,
3789
+ label: foundSelectedCategory.name,
3790
+ };
3791
+ if (!newSelectedOptions.some(({ value }) => value === newSelectedOption.value)) {
3792
+ newSelectedOptions.push(newSelectedOption);
3793
+ }
3794
+ if (foundSelectedCategory.parent) {
3795
+ findTreeSelectedCategory(foundSelectedCategory.parent.id);
3796
+ }
3797
+ };
3798
+ findTreeSelectedCategory(selectedOption.value);
3799
+ });
3800
+ values.value.categories = newSelectedOptions;
3801
+ };
3076
3802
  return {
3077
3803
  isCreation,
3078
3804
  postId,
@@ -3096,6 +3822,7 @@ var script$1 = defineComponent({
3096
3822
  handleTitleChange,
3097
3823
  handleAliasChange,
3098
3824
  urlAliasTemplate,
3825
+ handleCategoriesChange,
3099
3826
  };
3100
3827
  },
3101
3828
  });
@@ -3190,7 +3917,7 @@ var __vue_render__$1 = function () {
3190
3917
  attrs: {
3191
3918
  id: "urlAlias",
3192
3919
  name: "urlAlias",
3193
- label: _vm.$t("blog:URLAlias"),
3920
+ label: _vm.$t("blog:link"),
3194
3921
  "url-template": _vm.urlAliasTemplate,
3195
3922
  error: _vm.errors.urlAlias,
3196
3923
  },
@@ -3340,18 +4067,13 @@ var __vue_render__$1 = function () {
3340
4067
  _vm._v(" "),
3341
4068
  _c("form-field-multi-select", {
3342
4069
  attrs: {
4070
+ "selected-options": _vm.values.categories,
3343
4071
  name: "categories",
3344
4072
  label: _vm.$t("blog:categories"),
3345
4073
  options: _vm.categoryOptionList,
3346
4074
  error: _vm.errors.categories,
3347
4075
  },
3348
- model: {
3349
- value: _vm.values.categories,
3350
- callback: function ($$v) {
3351
- _vm.$set(_vm.values, "categories", $$v);
3352
- },
3353
- expression: "values.categories",
3354
- },
4076
+ on: { change: _vm.handleCategoriesChange },
3355
4077
  }),
3356
4078
  _vm._v(" "),
3357
4079
  _c("form-field-multi-select", {
@@ -3472,52 +4194,58 @@ function convertCategoryList(categoryList, languages) {
3472
4194
  return category;
3473
4195
  });
3474
4196
  }
3475
- function getCategoryTableColumnDefs(moduleConfig, t) {
4197
+ function getCategoryTableColumnDefs(moduleConfig, t, canViewAdministrators) {
3476
4198
  const isLangSpecific = moduleConfig
3477
4199
  ? moduleConfig.languages.length > 0
3478
4200
  : false;
3479
4201
  const COLUMN_DEFS = [
3480
4202
  {
3481
- id: 2,
4203
+ id: 1,
3482
4204
  name: t('blog:name'),
3483
4205
  field: 'name',
3484
4206
  type: 'name',
3485
- format: ({ row }) => {
3486
- return {
3487
- adminLink: {
3488
- url: getBlogCategoryFormUrl({ categoryId: row.id }),
3489
- text: row.name,
3490
- },
3491
- websiteLink: {
3492
- url: [
3493
- process.env.VUE_APP_WEBSITE_URL || window.location.origin,
3494
- row.url,
3495
- ].join(''),
3496
- text: row.url,
3497
- },
3498
- paramList: row.isDefault
3499
- ? [{ name: t('blog:defaultCategory'), value: t('blog:yes') }]
3500
- : [],
3501
- };
4207
+ format: ({ row }) => ({
4208
+ adminLink: {
4209
+ url: getBlogCategoryFormUrl({ categoryId: row.id }),
4210
+ text: getNameWithDepth(row.name, row.depth),
4211
+ },
4212
+ paramList: row.isDefault
4213
+ ? [{ name: t('blog:defaultCategory'), value: t('blog:yes') }]
4214
+ : [],
4215
+ }),
4216
+ },
4217
+ {
4218
+ id: 2,
4219
+ name: t('blog:link'),
4220
+ field: 'path',
4221
+ type: 'link',
4222
+ format: ({ row }) => ({
4223
+ url: `${getWebsiteOrigin()}${row.url}`,
4224
+ text: row.url,
4225
+ }),
4226
+ options: {
4227
+ shouldOpenNewTab: true,
3502
4228
  },
3503
4229
  },
3504
4230
  isLangSpecific
3505
- ? { id: 4, name: t('blog:language'), field: 'language' }
4231
+ ? { id: 3, name: t('blog:language'), field: 'language' }
3506
4232
  : null,
3507
4233
  {
3508
- id: 3,
4234
+ id: 4,
3509
4235
  name: t('blog:posts'),
3510
4236
  field: 'linkToPosts',
3511
4237
  style: { whiteSpace: 'nowrap', width: '130px' },
3512
4238
  },
3513
- {
3514
- id: 4,
3515
- name: t('blog:actions'),
3516
- field: 'actions',
3517
- style: { whiteSpace: 'nowrap', width: '205px' },
3518
- headStyle: { whiteSpace: 'nowrap', width: '205px' },
3519
- class: 'actions-cell',
3520
- },
4239
+ canViewAdministrators
4240
+ ? {
4241
+ id: 5,
4242
+ name: t('blog:actions'),
4243
+ field: 'actions',
4244
+ style: { whiteSpace: 'nowrap', width: '170px' },
4245
+ headStyle: { whiteSpace: 'nowrap', width: '170px' },
4246
+ class: 'actions-cell',
4247
+ }
4248
+ : null,
3521
4249
  ];
3522
4250
  return COLUMN_DEFS.filter(notEmpty);
3523
4251
  }
@@ -3571,18 +4299,19 @@ function useAdvancedSearch$1({ context, languageList }) {
3571
4299
  var script$2 = defineComponent({
3572
4300
  name: 'BlogCategoryList',
3573
4301
  setup(props, context) {
3574
- /** Fetch module config */
3575
- const { data: moduleConfig, loading: isModuleConfigLoading, } = useModuleConfig({ context });
4302
+ const canViewAdministrators = useUserPermission(context, Scope.AdministratorsView);
4303
+ /** Fetch module config **/
4304
+ const { data: moduleConfig, loading: isModuleConfigLoading, } = useFetchModuleConfig({ context });
3576
4305
  const languageList = computed(() => { var _a, _b; return (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.languages) !== null && _b !== void 0 ? _b : []; });
3577
4306
  /** Advanced search **/
3578
4307
  const { languageFilter, languageOptionList, filterParams, tags, tagRemovalHandler, } = useAdvancedSearch$1({ context, languageList });
3579
4308
  const isLangSpecific = computed(() => languageOptionList.value.length > 1);
3580
4309
  /** Fetch category list */
3581
4310
  const { fetchEntityList: fetchCategoryList, isLoading: isCategoryListLoading, rowData: categoryList, errorMessage, searchQuery, handleChange, pageNumber, pageCount, pageSize, } = useDataTable({
3582
- fetchEntityList: (params) => getBlogCategoryList(Object.assign({ query: params.searchQuery, pageNumber: params.pageNumber, pageSize: params.pageSize }, filterParams.value)),
4311
+ fetchEntityList: (params) => getCategories(Object.assign({ query: params.searchQuery, pageNumber: params.pageNumber, pageSize: params.pageSize }, filterParams.value)),
3583
4312
  initialValue: [],
3584
4313
  context,
3585
- resourceName: 'Blog category list',
4314
+ resourceName: 'Blog categories',
3586
4315
  pageSize: 100,
3587
4316
  });
3588
4317
  watch(filterParams, () => {
@@ -3596,16 +4325,16 @@ var script$2 = defineComponent({
3596
4325
  fetchCategoryList();
3597
4326
  }
3598
4327
  });
3599
- const { handleResourceDelete: handleCategoryDelete, isDeleting, } = useResourceDelete({
3600
- deleteResource: deleteBlogCategory,
4328
+ const { isDeleting, handleResourceDelete: handleCategoryDelete, } = useResourceDelete({
4329
+ deleteResource: deleteCategory,
3601
4330
  resourceName: 'Post',
3602
4331
  onSuccess: fetchCategoryList,
3603
4332
  context,
3604
4333
  });
3605
4334
  const isCategoryMoving = ref(false);
3606
- function moveCategory(categoryId, direction) {
4335
+ function startMoveCategory(categoryId, direction) {
3607
4336
  isCategoryMoving.value = true;
3608
- moveBlogCategory(categoryId, direction)
4337
+ moveCategory(categoryId, direction)
3609
4338
  .then((response) => {
3610
4339
  if (response.success) {
3611
4340
  return fetchCategoryList();
@@ -3619,7 +4348,7 @@ var script$2 = defineComponent({
3619
4348
  /** Misc */
3620
4349
  const isRowDataLoading = computed(() => isCategoryListLoading.value || isModuleConfigLoading.value);
3621
4350
  const displayedCategoryList = computed(() => convertCategoryList(categoryList.value, languageList.value));
3622
- const columnDefs = computed(() => getCategoryTableColumnDefs(moduleConfig.value, context.root.$t));
4351
+ const columnDefs = computed(() => getCategoryTableColumnDefs(moduleConfig.value, context.root.$t, canViewAdministrators.value));
3623
4352
  return {
3624
4353
  columnDefs,
3625
4354
  rowData: displayedCategoryList,
@@ -3630,7 +4359,7 @@ var script$2 = defineComponent({
3630
4359
  isDeleting,
3631
4360
  getBlogCategoryFormUrl,
3632
4361
  getLinkToPostsByCategory,
3633
- moveCategory,
4362
+ startMoveCategory,
3634
4363
  isCategoryMoving,
3635
4364
  searchQuery,
3636
4365
  handleChange,
@@ -3641,6 +4370,8 @@ var script$2 = defineComponent({
3641
4370
  languageOptionList,
3642
4371
  tags,
3643
4372
  tagRemovalHandler,
4373
+ // Permissions
4374
+ canViewAdministrators,
3644
4375
  };
3645
4376
  },
3646
4377
  });
@@ -3657,11 +4388,13 @@ var __vue_render__$2 = function () {
3657
4388
  attrs: {
3658
4389
  title: _vm.$t("blog:blogCategories"),
3659
4390
  "header-buttons": [
3660
- {
3661
- text: _vm.$t("blog:newCategory"),
3662
- href: _vm.getBlogCategoryFormUrl({ categoryId: "create" }),
3663
- },
3664
- ],
4391
+ _vm.canViewAdministrators
4392
+ ? {
4393
+ text: _vm.$t("blog:newCategory"),
4394
+ href: _vm.getBlogCategoryFormUrl({ categoryId: "create" }),
4395
+ }
4396
+ : null,
4397
+ ].filter(Boolean),
3665
4398
  },
3666
4399
  scopedSlots: _vm._u([
3667
4400
  {
@@ -3785,7 +4518,7 @@ var __vue_render__$2 = function () {
3785
4518
  },
3786
4519
  on: {
3787
4520
  click: function ($event) {
3788
- return _vm.moveCategory(row.id, "up")
4521
+ return _vm.startMoveCategory(row.id, "up")
3789
4522
  },
3790
4523
  },
3791
4524
  },
@@ -3805,7 +4538,7 @@ var __vue_render__$2 = function () {
3805
4538
  },
3806
4539
  on: {
3807
4540
  click: function ($event) {
3808
- return _vm.moveCategory(row.id, "down")
4541
+ return _vm.startMoveCategory(row.id, "down")
3809
4542
  },
3810
4543
  },
3811
4544
  },
@@ -3879,92 +4612,87 @@ __vue_render__$2._withStripped = true;
3879
4612
  );
3880
4613
 
3881
4614
  function convertCategoryToFormValues(category, languageOptionList) {
3882
- var _a, _b;
4615
+ var _a, _b, _c, _d, _e;
3883
4616
  const currentLangOption = languageOptionList.find(({ value }) => value === (category === null || category === void 0 ? void 0 : category.language));
3884
4617
  if (!category) {
3885
4618
  return {
4619
+ language: currentLangOption !== null && currentLangOption !== void 0 ? currentLangOption : null,
3886
4620
  name: '',
3887
- isDefault: false,
4621
+ parent: null,
4622
+ urlAlias: '',
3888
4623
  pageTitle: '',
3889
4624
  pageDescription: '',
4625
+ pageKeywords: '',
4626
+ openGraphTitle: '',
4627
+ openGraphDescription: '',
3890
4628
  openGraphImage: null,
3891
- urlAlias: '',
3892
- language: currentLangOption !== null && currentLangOption !== void 0 ? currentLangOption : null,
3893
4629
  };
3894
4630
  }
3895
4631
  return {
4632
+ language: currentLangOption !== null && currentLangOption !== void 0 ? currentLangOption : null,
3896
4633
  name: category.name,
3897
- isDefault: category.isDefault,
4634
+ parent: category.parent
4635
+ ? { value: category.parent.id, label: category.parent.name }
4636
+ : null,
4637
+ urlAlias: category.urlAlias,
3898
4638
  pageTitle: (_a = category.pageTitle) !== null && _a !== void 0 ? _a : '',
3899
4639
  pageDescription: (_b = category.pageDescription) !== null && _b !== void 0 ? _b : '',
4640
+ pageKeywords: (_c = category.pageKeywords) !== null && _c !== void 0 ? _c : '',
4641
+ openGraphTitle: (_d = category.openGraphTitle) !== null && _d !== void 0 ? _d : '',
4642
+ openGraphDescription: (_e = category.openGraphDescription) !== null && _e !== void 0 ? _e : '',
3900
4643
  openGraphImage: category.openGraphImage
3901
4644
  ? { id: createId(), file: category.openGraphImage }
3902
4645
  : null,
3903
- urlAlias: category.urlAlias,
3904
- language: currentLangOption !== null && currentLangOption !== void 0 ? currentLangOption : null,
3905
4646
  };
3906
4647
  }
3907
- function convertCategoryFormValuesToCreationPayload(values) {
3908
- var _a, _b, _c, _d;
4648
+ function convertCategoryFormValuesToPayload(values) {
4649
+ var _a, _b, _c, _d, _e, _f;
3909
4650
  return {
4651
+ language: (_b = (_a = values.language) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null,
3910
4652
  name: values.name,
3911
- isDefault: values.isDefault,
4653
+ parent: (_d = (_c = values.parent) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : null,
4654
+ urlAlias: values.urlAlias,
4655
+ isDefault: false,
3912
4656
  pageTitle: values.pageTitle,
3913
4657
  pageDescription: values.pageDescription,
3914
- openGraphImage: (_b = (_a = values.openGraphImage) === null || _a === void 0 ? void 0 : _a.file.id) !== null && _b !== void 0 ? _b : null,
3915
- language: (_d = (_c = values.language) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : null,
4658
+ openGraphImage: (_f = (_e = values.openGraphImage) === null || _e === void 0 ? void 0 : _e.file.id) !== null && _f !== void 0 ? _f : null,
3916
4659
  };
3917
- }
3918
- function convertCategoryFormValuesToUpdatePayload(values) {
3919
- return Object.assign(Object.assign({}, convertCategoryFormValuesToCreationPayload(values)), { urlAlias: values.urlAlias });
3920
4660
  }
3921
4661
 
3922
4662
  var script$3 = defineComponent({
3923
- name: 'BlogCategoryForm',
4663
+ name: 'CategoryForm',
3924
4664
  components: { FormFooter },
3925
4665
  setup(props, context) {
4666
+ const { t } = useTranslation(context);
3926
4667
  const categoryId = computed(() => context.root.$route.params.categoryId);
3927
4668
  const isCreation = computed(() => categoryId.value === 'create');
3928
4669
  /** Fetch module config */
3929
- const { data: moduleConfig, loading: isModuleConfigLoading, } = useModuleConfig({ context });
4670
+ const { data: moduleConfig, loading: isModuleConfigLoading, } = useFetchModuleConfig({ context });
3930
4671
  const languageList = computed(() => { var _a, _b; return (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.languages) !== null && _b !== void 0 ? _b : []; });
3931
4672
  const languageOptionList = computed(() => languageList.value.map(({ id, name }) => ({ value: id, label: name })));
3932
4673
  const isLangSpecific = computed(() => languageOptionList.value.length > 0);
4674
+ const imageScenario = computed(() => { var _a, _b, _c; return (_c = (_b = (_a = moduleConfig.value) === null || _a === void 0 ? void 0 : _a.fileScenarios) === null || _b === void 0 ? void 0 : _b.openGraph) !== null && _c !== void 0 ? _c : null; });
4675
+ /** Fetch Categories */
4676
+ const { data: categories, loading: isCategoriesLoading, } = useFetchCategories({ context });
3933
4677
  /** Fetch Category */
3934
- const [fetchCategory, { data: category, loading: isCategoryLoading },] = useResource({
3935
- fetchResource: () => getBlogCategory(categoryId.value),
3936
- initialValue: null,
4678
+ const { data: category, loading: isCategoryLoading } = useFetchCategory({
3937
4679
  context,
3938
- resourceName: 'Category',
3939
- });
3940
- onMounted(() => {
3941
- if (isCreation.value)
3942
- return;
3943
- fetchCategory();
3944
- });
3945
- watch(categoryId, () => {
3946
- if (isCreation.value)
3947
- return;
3948
- fetchCategory();
4680
+ categoryId,
4681
+ isCreation,
3949
4682
  });
3950
4683
  /** Form State */
4684
+ const isSubmitting = ref(false);
3951
4685
  const values = ref(convertCategoryToFormValues(category.value, languageOptionList.value));
3952
4686
  const errors = ref({});
3953
- const isSubmitting = ref(false);
3954
4687
  watch(category, () => {
3955
4688
  values.value = convertCategoryToFormValues(category.value, languageOptionList.value);
3956
4689
  });
3957
4690
  function submitForm(event) {
3958
- var _a, _b;
3959
4691
  isSubmitting.value = true;
3960
- const creationBody = convertCategoryFormValuesToCreationPayload(values.value);
3961
- const updateBody = convertCategoryFormValuesToUpdatePayload(values.value);
3962
- if (!isCreation.value) {
3963
- updateBody.language = ((_a = category.value) === null || _a === void 0 ? void 0 : _a.language) ? (_b = category.value) === null || _b === void 0 ? void 0 : _b.language : null;
3964
- }
4692
+ const body = convertCategoryFormValuesToPayload(values.value);
3965
4693
  const requestPromise = isCreation.value
3966
- ? createBlogCategory(creationBody)
3967
- : updateBlogCategory(categoryId.value, updateBody);
4694
+ ? createCategory(body)
4695
+ : updateCategory(categoryId.value, body);
3968
4696
  requestPromise
3969
4697
  .then(({ data }) => {
3970
4698
  errors.value = {};
@@ -4000,13 +4728,32 @@ var script$3 = defineComponent({
4000
4728
  isSubmitting.value = false;
4001
4729
  });
4002
4730
  }
4731
+ /** Category Options */
4732
+ const categoryOptionList = computed(() => {
4733
+ var _a, _b;
4734
+ return convertCategoryListToOptions(categories.value, (_b = (_a = values.value.language) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null);
4735
+ });
4003
4736
  /** Misc */
4004
- const isContentLoading = computed(() => isCategoryLoading.value || isModuleConfigLoading.value);
4737
+ const isContentLoading = computed(() => isCategoryLoading.value ||
4738
+ isModuleConfigLoading.value ||
4739
+ isCategoriesLoading.value);
4005
4740
  const categoryPagePath = computed(() => {
4006
4741
  var _a, _b;
4007
- const origin = process.env.VUE_APP_WEBSITE_URL || window.location.origin;
4008
- return origin + ((_b = (_a = category.value) === null || _a === void 0 ? void 0 : _a.urlTemplate) !== null && _b !== void 0 ? _b : '');
4742
+ return `${getWebsiteOrigin()}${(_b = (_a = category.value) === null || _a === void 0 ? void 0 : _a.urlTemplate) !== null && _b !== void 0 ? _b : ''}`;
4009
4743
  });
4744
+ /** SEO **/
4745
+ function handleSeoFieldGroupChange({ title, description, keywords, image, }) {
4746
+ values.value.pageTitle = title;
4747
+ values.value.pageDescription = description;
4748
+ values.value.pageKeywords = keywords;
4749
+ values.value.openGraphImage = image;
4750
+ }
4751
+ /** Tabs */
4752
+ const tabList = computed(() => [
4753
+ { id: 'common', label: t('blog:tabs.common') },
4754
+ { id: 'seo', label: t('blog:tabs.seo') },
4755
+ ]);
4756
+ const selectedTabId = ref(tabList.value[0].id);
4010
4757
  return {
4011
4758
  isCreation,
4012
4759
  isContentLoading: isContentLoading,
@@ -4019,6 +4766,13 @@ var script$3 = defineComponent({
4019
4766
  submitForm,
4020
4767
  isSubmitting,
4021
4768
  moduleConfig,
4769
+ categoryOptionList,
4770
+ // SEO
4771
+ imageScenario,
4772
+ handleSeoFieldGroupChange,
4773
+ // Tabs
4774
+ tabList,
4775
+ selectedTabId,
4022
4776
  };
4023
4777
  },
4024
4778
  });
@@ -4055,123 +4809,182 @@ var __vue_render__$3 = function () {
4055
4809
  },
4056
4810
  },
4057
4811
  [
4058
- _vm.isCreation && _vm.isLangSpecific
4059
- ? _c("form-field-select", {
4060
- attrs: {
4061
- name: "language",
4062
- label: _vm.$t("blog:language"),
4063
- options: _vm.languageOptionList,
4064
- error: _vm.errors.language,
4065
- },
4066
- model: {
4067
- value: _vm.values.language,
4068
- callback: function ($$v) {
4069
- _vm.$set(_vm.values, "language", $$v);
4812
+ _vm.isCreation
4813
+ ? [
4814
+ _vm.isLangSpecific
4815
+ ? _c("form-field-select", {
4816
+ attrs: {
4817
+ name: "language",
4818
+ label: _vm.$t("blog:language"),
4819
+ options: _vm.languageOptionList,
4820
+ error: _vm.errors.language,
4821
+ },
4822
+ model: {
4823
+ value: _vm.values.language,
4824
+ callback: function ($$v) {
4825
+ _vm.$set(_vm.values, "language", $$v);
4826
+ },
4827
+ expression: "values.language",
4828
+ },
4829
+ })
4830
+ : _vm._e(),
4831
+ _vm._v(" "),
4832
+ _c("form-field", {
4833
+ attrs: {
4834
+ name: "name",
4835
+ label: _vm.$t("blog:name"),
4836
+ error: _vm.errors.name,
4070
4837
  },
4071
- expression: "values.language",
4072
- },
4073
- })
4074
- : _vm._e(),
4075
- _vm._v(" "),
4076
- _c("form-field", {
4077
- attrs: {
4078
- name: "name",
4079
- label: _vm.$t("blog:name"),
4080
- error: _vm.errors.name,
4081
- },
4082
- model: {
4083
- value: _vm.values.name,
4084
- callback: function ($$v) {
4085
- _vm.$set(_vm.values, "name", $$v);
4086
- },
4087
- expression: "values.name",
4088
- },
4089
- }),
4090
- _vm._v(" "),
4091
- _c("form-field-checkbox", {
4092
- attrs: {
4093
- name: "isDefault",
4094
- label: _vm.$t("blog:defaultCategory"),
4095
- error: _vm.errors.isDefault,
4096
- },
4097
- model: {
4098
- value: _vm.values.isDefault,
4099
- callback: function ($$v) {
4100
- _vm.$set(_vm.values, "isDefault", $$v);
4101
- },
4102
- expression: "values.isDefault",
4103
- },
4104
- }),
4105
- _vm._v(" "),
4106
- !_vm.isCreation
4107
- ? _c("form-field-url-alias-input", {
4108
- attrs: {
4109
- id: "urlAlias",
4110
- name: "urlAlias",
4111
- label: _vm.$t("blog:URLAlias"),
4112
- "url-template": _vm.pagePath,
4113
- error: _vm.errors.urlAlias,
4114
- },
4115
- model: {
4116
- value: _vm.values.urlAlias,
4117
- callback: function ($$v) {
4118
- _vm.$set(_vm.values, "urlAlias", $$v);
4838
+ model: {
4839
+ value: _vm.values.name,
4840
+ callback: function ($$v) {
4841
+ _vm.$set(_vm.values, "name", $$v);
4842
+ },
4843
+ expression: "values.name",
4119
4844
  },
4120
- expression: "values.urlAlias",
4121
- },
4122
- })
4123
- : _vm._e(),
4124
- _vm._v(" "),
4125
- _c("form-field", {
4126
- attrs: {
4127
- name: "pageTitle",
4128
- label: _vm.$t("blog:pageTitle"),
4129
- error: _vm.errors.pageTitle,
4130
- },
4131
- model: {
4132
- value: _vm.values.pageTitle,
4133
- callback: function ($$v) {
4134
- _vm.$set(_vm.values, "pageTitle", $$v);
4135
- },
4136
- expression: "values.pageTitle",
4137
- },
4138
- }),
4139
- _vm._v(" "),
4140
- _c("form-field", {
4141
- attrs: {
4142
- name: "pageDescription",
4143
- label: _vm.$t("blog:pageDescription"),
4144
- type: "textarea",
4145
- error: _vm.errors.pageDescription,
4146
- },
4147
- model: {
4148
- value: _vm.values.pageDescription,
4149
- callback: function ($$v) {
4150
- _vm.$set(_vm.values, "pageDescription", $$v);
4151
- },
4152
- expression: "values.pageDescription",
4153
- },
4154
- }),
4155
- _vm._v(" "),
4156
- _c("form-field-file-input", {
4157
- attrs: {
4158
- label: _vm.$t("blog:openGraphImage"),
4159
- name: "openGraphImage",
4160
- "file-type": "image",
4161
- "image-scenario": _vm.moduleConfig
4162
- ? _vm.moduleConfig.fileScenarios.openGraph
4163
- : null,
4164
- },
4165
- model: {
4166
- value: _vm.values.openGraphImage,
4167
- callback: function ($$v) {
4168
- _vm.$set(_vm.values, "openGraphImage", $$v);
4169
- },
4170
- expression: "values.openGraphImage",
4171
- },
4172
- }),
4845
+ }),
4846
+ _vm._v(" "),
4847
+ _c("form-field-select", {
4848
+ attrs: {
4849
+ name: "parentCategory",
4850
+ label: _vm.$t("blog:parentCategory"),
4851
+ options: _vm.categoryOptionList,
4852
+ error: _vm.errors.parent,
4853
+ },
4854
+ model: {
4855
+ value: _vm.values.parent,
4856
+ callback: function ($$v) {
4857
+ _vm.$set(_vm.values, "parent", $$v);
4858
+ },
4859
+ expression: "values.parent",
4860
+ },
4861
+ }),
4862
+ _vm._v(" "),
4863
+ _c("form-field-url-alias-input", {
4864
+ attrs: {
4865
+ id: "urlAlias",
4866
+ name: "urlAlias",
4867
+ label: _vm.$t("blog:link"),
4868
+ "url-template": _vm.pagePath,
4869
+ error: _vm.errors.urlAlias,
4870
+ },
4871
+ model: {
4872
+ value: _vm.values.urlAlias,
4873
+ callback: function ($$v) {
4874
+ _vm.$set(_vm.values, "urlAlias", $$v);
4875
+ },
4876
+ expression: "values.urlAlias",
4877
+ },
4878
+ }),
4879
+ ]
4880
+ : [
4881
+ _c("tab-list", {
4882
+ attrs: {
4883
+ "tab-list": _vm.tabList,
4884
+ "selected-tab-id": _vm.selectedTabId,
4885
+ },
4886
+ on: {
4887
+ "tab:update": function ($event) {
4888
+ _vm.selectedTabId = $event.tabId;
4889
+ },
4890
+ },
4891
+ }),
4892
+ _vm._v(" "),
4893
+ _vm.selectedTabId === "common"
4894
+ ? [
4895
+ _vm.isLangSpecific
4896
+ ? _c("form-field-select", {
4897
+ attrs: {
4898
+ name: "language",
4899
+ label: _vm.$t("blog:language"),
4900
+ options: _vm.languageOptionList,
4901
+ error: _vm.errors.language,
4902
+ },
4903
+ model: {
4904
+ value: _vm.values.language,
4905
+ callback: function ($$v) {
4906
+ _vm.$set(_vm.values, "language", $$v);
4907
+ },
4908
+ expression: "values.language",
4909
+ },
4910
+ })
4911
+ : _vm._e(),
4912
+ _vm._v(" "),
4913
+ _c("form-field", {
4914
+ attrs: {
4915
+ name: "name",
4916
+ label: _vm.$t("blog:name"),
4917
+ error: _vm.errors.name,
4918
+ },
4919
+ model: {
4920
+ value: _vm.values.name,
4921
+ callback: function ($$v) {
4922
+ _vm.$set(_vm.values, "name", $$v);
4923
+ },
4924
+ expression: "values.name",
4925
+ },
4926
+ }),
4927
+ _vm._v(" "),
4928
+ _c("form-field-select", {
4929
+ attrs: {
4930
+ name: "parentCategory",
4931
+ label: _vm.$t("blog:parentCategory"),
4932
+ options: _vm.categoryOptionList,
4933
+ error: _vm.errors.parent,
4934
+ },
4935
+ model: {
4936
+ value: _vm.values.parent,
4937
+ callback: function ($$v) {
4938
+ _vm.$set(_vm.values, "parent", $$v);
4939
+ },
4940
+ expression: "values.parent",
4941
+ },
4942
+ }),
4943
+ _vm._v(" "),
4944
+ _c("form-field-url-alias-input", {
4945
+ attrs: {
4946
+ id: "urlAlias",
4947
+ name: "urlAlias",
4948
+ label: _vm.$t("blog:link"),
4949
+ "url-template": _vm.pagePath,
4950
+ error: _vm.errors.urlAlias,
4951
+ },
4952
+ model: {
4953
+ value: _vm.values.urlAlias,
4954
+ callback: function ($$v) {
4955
+ _vm.$set(_vm.values, "urlAlias", $$v);
4956
+ },
4957
+ expression: "values.urlAlias",
4958
+ },
4959
+ }),
4960
+ ]
4961
+ : _vm._e(),
4962
+ _vm._v(" "),
4963
+ _vm.selectedTabId === "seo"
4964
+ ? [
4965
+ _c("seo-field-group", {
4966
+ attrs: {
4967
+ title: _vm.values.pageTitle,
4968
+ "title-error-message": _vm.errors.pageTitle,
4969
+ description: _vm.values.pageDescription,
4970
+ "description-error-message":
4971
+ _vm.errors.pageDescription,
4972
+ "should-display-keywords": false,
4973
+ keywords: _vm.values.pageKeywords,
4974
+ "keywords-error-message":
4975
+ _vm.errors.pageKeywords,
4976
+ image: _vm.values.openGraphImage,
4977
+ "image-scenario": _vm.imageScenario,
4978
+ "image-error-message":
4979
+ _vm.errors.openGraphImage,
4980
+ },
4981
+ on: { change: _vm.handleSeoFieldGroupChange },
4982
+ }),
4983
+ ]
4984
+ : _vm._e(),
4985
+ ],
4173
4986
  ],
4174
- 1
4987
+ 2
4175
4988
  ),
4176
4989
  ]
4177
4990
  },
@@ -4299,4 +5112,4 @@ function getBlogMenuItem(params) {
4299
5112
  i18n.addTranslations('en', 'blog', EN);
4300
5113
  i18n.addTranslations('ru', 'blog', RU);
4301
5114
 
4302
- export { BLOG_CATEGORY_FORM_ROUTE, BLOG_CATEGORY_LIST_ROUTE, BLOG_POST_FORM_ROUTE, BLOG_POST_LIST_ROUTE, BLOG_ROUTE_PATHS, createBlogCategory, createBlogPost, deleteBlogCategory, deleteBlogPost, getBlogCategory, getBlogCategoryFormUrl, getBlogCategoryList, getBlogCategoryListUrl, getBlogMenuItem, getBlogModuleConfig, getBlogPost, getBlogPostCount, getBlogPostFormUrl, getBlogPostList, getBlogPostListUrl, moveBlogCategory, updateBlogCategory, updateBlogPost };
5115
+ export { BLOG_CATEGORY_FORM_ROUTE, BLOG_CATEGORY_LIST_ROUTE, BLOG_POST_FORM_ROUTE, BLOG_POST_LIST_ROUTE, BLOG_ROUTE_PATHS, clonePost, createBlogPost, createCategory, deleteBlogPost, deleteCategory, getBlogCategoryFormUrl, getBlogCategoryListUrl, getBlogMenuItem, getBlogPostFormUrl, getBlogPostListUrl, getCategories, getCategory, getModuleConfig, getPost, getPosts, getPostsCount, getUserProfile, moveCategory, updateBlogPost, updateCategory };