@tager/admin-blog 0.3.21 → 0.3.25

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 (86) hide show
  1. package/dist/index.esm.js +3452 -612
  2. package/dist/index.umd.js +3454 -619
  3. package/package.json +13 -7
  4. package/src/constants/menu.ts +0 -2
  5. package/src/constants/paths.ts +0 -5
  6. package/src/constants/routes.ts +5 -18
  7. package/src/constants/scopes.ts +11 -0
  8. package/src/dev.ts +11 -44
  9. package/src/hooks/index.ts +6 -0
  10. package/src/hooks/useBlogCategoryList.ts +3 -5
  11. package/src/hooks/useBlogPostList.ts +2 -4
  12. package/src/hooks/useModuleConfig.ts +4 -8
  13. package/src/hooks/useStore.ts +8 -0
  14. package/src/hooks/useUserPermission.ts +16 -0
  15. package/src/hooks/useUserProfile.ts +13 -0
  16. package/src/locales/en.ts +17 -5
  17. package/src/locales/ru.ts +17 -4
  18. package/src/router/index.ts +39 -0
  19. package/src/services/requests.ts +33 -43
  20. package/src/store/index.ts +21 -0
  21. package/src/store/user.ts +109 -0
  22. package/src/typings/common.ts +1 -0
  23. package/src/typings/model.ts +62 -38
  24. package/src/utils/common.ts +23 -0
  25. package/src/views/App.vue +10 -4
  26. package/src/views/{BlogCategoryForm/BlogCategoryForm.helpers.ts → Categories/CategoryForm/CategoryForm.helpers.ts} +22 -7
  27. package/src/views/{BlogCategoryForm/BlogCategoryForm.vue → Categories/CategoryForm/CategoryForm.vue} +140 -63
  28. package/src/views/Categories/CategoryForm/index.ts +1 -0
  29. package/src/views/Categories/CategoryList/CategoryList.helpers.ts +91 -0
  30. package/src/views/Categories/CategoryList/CategoryList.vue +269 -0
  31. package/src/views/Categories/CategoryList/hooks/index.ts +1 -0
  32. package/src/views/Categories/CategoryList/hooks/useAdvancedSearch.ts +95 -0
  33. package/src/views/Categories/CategoryList/index.ts +1 -0
  34. package/src/views/Home.vue +2 -9
  35. package/src/views/Posts/PostForm/PostForm.helpers.ts +150 -0
  36. package/src/views/Posts/PostForm/PostForm.vue +550 -0
  37. package/src/views/{BlogPostForm → Posts/PostForm}/components/TabList/TabList.types.ts +0 -0
  38. package/src/views/{BlogPostForm → Posts/PostForm}/components/TabList/TabList.vue +1 -1
  39. package/src/views/{BlogPostForm → Posts/PostForm}/components/TabList/index.ts +0 -0
  40. package/src/views/Posts/PostForm/index.ts +1 -0
  41. package/src/views/{BlogPostList/BlogPostList.helpers.ts → Posts/PostList/PostList.helpers.ts} +10 -18
  42. package/src/views/Posts/PostList/PostList.vue +361 -0
  43. package/src/views/Posts/PostList/hooks/index.ts +1 -0
  44. package/src/views/Posts/PostList/hooks/useAdvancedSearch.ts +250 -0
  45. package/src/views/Posts/PostList/index.ts +1 -0
  46. package/dist/constants/menu.d.ts +0 -5
  47. package/dist/constants/paths.d.ts +0 -16
  48. package/dist/constants/routes.d.ts +0 -6
  49. package/dist/dev.d.ts +0 -3
  50. package/dist/hooks/useBlogCategoryList.d.ts +0 -7
  51. package/dist/hooks/useBlogPostList.d.ts +0 -7
  52. package/dist/hooks/useModuleConfig.d.ts +0 -7
  53. package/dist/locales/en.d.ts +0 -63
  54. package/dist/locales/ru.d.ts +0 -3
  55. package/dist/main.d.ts +0 -4
  56. package/dist/services/requests.d.ts +0 -68
  57. package/dist/typings/model.d.ts +0 -74
  58. package/dist/views/BlogCategoryForm/BlogCategoryForm.helpers.d.ts +0 -16
  59. package/dist/views/BlogCategoryForm/BlogCategoryForm.vue.d.ts +0 -86
  60. package/dist/views/BlogCategoryForm/index.d.ts +0 -1
  61. package/dist/views/BlogCategoryList/BlogCategoryList.helpers.d.ts +0 -6
  62. package/dist/views/BlogCategoryList/BlogCategoryList.vue.d.ts +0 -3
  63. package/dist/views/BlogCategoryList/index.d.ts +0 -1
  64. package/dist/views/BlogPostForm/BlogPostForm.helpers.d.ts +0 -27
  65. package/dist/views/BlogPostForm/BlogPostForm.vue.d.ts +0 -9320
  66. package/dist/views/BlogPostForm/components/TabList/TabList.types.d.ts +0 -5
  67. package/dist/views/BlogPostForm/components/TabList/index.d.ts +0 -2
  68. package/dist/views/BlogPostForm/index.d.ts +0 -1
  69. package/dist/views/BlogPostList/BlogPostList.helpers.d.ts +0 -6
  70. package/dist/views/BlogPostList/BlogPostList.vue.d.ts +0 -45
  71. package/dist/views/BlogPostList/index.d.ts +0 -1
  72. package/dist/views/BlogSettings/BlogSettings.helpers.d.ts +0 -6
  73. package/dist/views/BlogSettings/BlogSettings.vue.d.ts +0 -9132
  74. package/dist/views/BlogSettings/index.d.ts +0 -1
  75. package/src/views/BlogCategoryForm/index.ts +0 -1
  76. package/src/views/BlogCategoryList/BlogCategoryList.helpers.ts +0 -82
  77. package/src/views/BlogCategoryList/BlogCategoryList.vue +0 -196
  78. package/src/views/BlogCategoryList/index.ts +0 -1
  79. package/src/views/BlogPostForm/BlogPostForm.helpers.ts +0 -152
  80. package/src/views/BlogPostForm/BlogPostForm.vue +0 -534
  81. package/src/views/BlogPostForm/index.ts +0 -1
  82. package/src/views/BlogPostList/BlogPostList.vue +0 -174
  83. package/src/views/BlogPostList/index.ts +0 -1
  84. package/src/views/BlogSettings/BlogSettings.helpers.ts +0 -25
  85. package/src/views/BlogSettings/BlogSettings.vue +0 -110
  86. package/src/views/BlogSettings/index.ts +0 -1
@@ -0,0 +1,91 @@
1
+ import { TFunction } from 'i18next';
2
+
3
+ import { getWebsiteOrigin, notEmpty, Nullable } from '@tager/admin-services';
4
+ import { ColumnDefinition } from '@tager/admin-ui';
5
+
6
+ import { Category, Language, ModuleConfig } from '@/typings/model';
7
+ import { getBlogCategoryFormUrl, getBlogPostListUrl } from '@/constants/paths';
8
+ import { getNameWithDepth } from '@/utils/common';
9
+
10
+ export function convertCategoryList(
11
+ categoryList: Category[],
12
+ languages: Language[]
13
+ ): Category[] {
14
+ return categoryList.map((category) => {
15
+ if (languages.length > 0) {
16
+ const foundLanguage = languages.find(
17
+ ({ id }) => category.language === id
18
+ );
19
+
20
+ return {
21
+ ...category,
22
+ language: foundLanguage ? foundLanguage.name : category.language,
23
+ };
24
+ }
25
+
26
+ return category;
27
+ });
28
+ }
29
+
30
+ export function getCategoryTableColumnDefs(
31
+ moduleConfig: Nullable<ModuleConfig>,
32
+ t: TFunction
33
+ ): ColumnDefinition<Category>[] {
34
+ const isLangSpecific = moduleConfig
35
+ ? moduleConfig.languages.length > 0
36
+ : false;
37
+
38
+ const COLUMN_DEFS: (ColumnDefinition<Category> | null)[] = [
39
+ {
40
+ id: 1,
41
+ name: t('blog:name'),
42
+ field: 'name',
43
+ type: 'name',
44
+ format: ({ row }) => ({
45
+ adminLink: {
46
+ url: getBlogCategoryFormUrl({ categoryId: row.id }),
47
+ text: getNameWithDepth(row.name, row.depth),
48
+ },
49
+ paramList: row.isDefault
50
+ ? [{ name: t('blog:defaultCategory'), value: t('blog:yes') }]
51
+ : [],
52
+ }),
53
+ },
54
+ {
55
+ id: 2,
56
+ name: t('pages:path'),
57
+ field: 'path',
58
+ type: 'link',
59
+ format: ({ row }) => ({
60
+ url: `${getWebsiteOrigin()}${row.url}`,
61
+ text: row.url,
62
+ }),
63
+ options: {
64
+ shouldOpenNewTab: true,
65
+ },
66
+ },
67
+ isLangSpecific
68
+ ? { id: 3, name: t('blog:language'), field: 'language' }
69
+ : null,
70
+ {
71
+ id: 4,
72
+ name: t('blog:posts'),
73
+ field: 'linkToPosts',
74
+ style: { whiteSpace: 'nowrap', width: '130px' },
75
+ },
76
+ {
77
+ id: 5,
78
+ name: t('blog:actions'),
79
+ field: 'actions',
80
+ style: { whiteSpace: 'nowrap', width: '205px' },
81
+ headStyle: { whiteSpace: 'nowrap', width: '205px' },
82
+ class: 'actions-cell',
83
+ },
84
+ ];
85
+
86
+ return COLUMN_DEFS.filter(notEmpty);
87
+ }
88
+
89
+ export function getLinkToPostsByCategory(categoryId: number): string {
90
+ return `${getBlogPostListUrl()}?filter[category]=${categoryId}`;
91
+ }
@@ -0,0 +1,269 @@
1
+ <template>
2
+ <page
3
+ :title="$t('blog:blogCategories')"
4
+ :header-buttons="
5
+ [
6
+ canViewAdministrators
7
+ ? {
8
+ text: $t('blog:newCategory'),
9
+ href: getBlogCategoryFormUrl({ categoryId: 'create' }),
10
+ }
11
+ : null,
12
+ ].filter(Boolean)
13
+ "
14
+ >
15
+ <template v-slot:content>
16
+ <data-table
17
+ :column-defs="columnDefs"
18
+ :row-data="rowData"
19
+ :loading="isRowDataLoading"
20
+ :error-message="errorMessage"
21
+ :search-query="searchQuery"
22
+ :pagination="{
23
+ pageNumber,
24
+ pageCount,
25
+ pageSize,
26
+ disabled: isRowDataLoading,
27
+ }"
28
+ data-table="blog-category"
29
+ @change="handleChange"
30
+ >
31
+ <template v-if="isLangSpecific" v-slot:filters>
32
+ <advanced-search :tags="tags" @click:tag="tagRemovalHandler">
33
+ <div class="filters">
34
+ <form-field-multi-select
35
+ v-model="languageFilter"
36
+ :options="languageOptionList"
37
+ name="languageFilter"
38
+ :searchable="true"
39
+ :label="$t('blog:language')"
40
+ class="filter"
41
+ />
42
+ </div>
43
+ </advanced-search>
44
+ </template>
45
+
46
+ <template v-slot:cell(link-to-posts)="{ row }">
47
+ <count-button
48
+ :href="getLinkToPostsByCategory(row.id)"
49
+ variant="outline-secondary"
50
+ :count="row.postsCount"
51
+ >
52
+ {{ $t('blog:posts') }}
53
+ </count-button>
54
+ </template>
55
+
56
+ <template v-slot:cell(actions)="{ row, rowIndex }">
57
+ <base-button
58
+ v-if="canViewAdministrators"
59
+ variant="icon"
60
+ :title="$t('blog:edit')"
61
+ :disabled="isDeleting(row.id)"
62
+ :href="getBlogCategoryFormUrl({ categoryId: row.id })"
63
+ >
64
+ <svg-icon name="edit" />
65
+ </base-button>
66
+
67
+ <base-button
68
+ variant="icon"
69
+ :title="$t('blog:moveUp')"
70
+ :disabled="rowIndex === 0 || isCategoryMoving"
71
+ @click="moveCategory(row.id, 'up')"
72
+ >
73
+ <svg-icon name="north" />
74
+ </base-button>
75
+
76
+ <base-button
77
+ variant="icon"
78
+ :title="$t('blog:moveDown')"
79
+ :disabled="rowIndex === rowData.length - 1 || isCategoryMoving"
80
+ @click="moveCategory(row.id, 'down')"
81
+ >
82
+ <svg-icon name="south" />
83
+ </base-button>
84
+
85
+ <base-button
86
+ v-if="canViewAdministrators"
87
+ variant="icon"
88
+ :title="$t('blog:remove')"
89
+ :disabled="isDeleting(row.id) || row.postsCount > 0"
90
+ @click="handleCategoryDelete(row.id)"
91
+ >
92
+ <svg-icon name="delete" />
93
+ </base-button>
94
+ </template>
95
+ </data-table>
96
+ </template>
97
+ </page>
98
+ </template>
99
+
100
+ <script lang="ts">
101
+ import { computed, ref, defineComponent, watch } from '@vue/composition-api';
102
+ import isEqual from 'lodash.isequal';
103
+
104
+ import { useResourceDelete } from '@tager/admin-services';
105
+ import { useDataTable } from '@tager/admin-ui';
106
+
107
+ import { Scope } from '@/constants/scopes';
108
+ import { Category, Language } from '@/typings/model';
109
+ import {
110
+ deleteBlogCategory,
111
+ getBlogCategoryList,
112
+ moveBlogCategory,
113
+ } from '@/services/requests';
114
+ import { getBlogCategoryFormUrl } from '@/constants/paths';
115
+ import { useModuleConfig, useUserPermission } from '@/hooks';
116
+
117
+ import {
118
+ convertCategoryList,
119
+ getCategoryTableColumnDefs,
120
+ getLinkToPostsByCategory,
121
+ } from './CategoryList.helpers';
122
+ import { useAdvancedSearch } from './hooks';
123
+
124
+ export default defineComponent({
125
+ name: 'BlogCategoryList',
126
+ setup(props, context) {
127
+ const canViewAdministrators = useUserPermission(
128
+ context,
129
+ Scope.AdministratorsView
130
+ );
131
+
132
+ /** Fetch module config */
133
+
134
+ const {
135
+ data: moduleConfig,
136
+ loading: isModuleConfigLoading,
137
+ } = useModuleConfig({ context });
138
+
139
+ const languageList = computed<Language[]>(
140
+ () => moduleConfig.value?.languages ?? []
141
+ );
142
+
143
+ /** Advanced search **/
144
+
145
+ const {
146
+ languageFilter,
147
+ languageOptionList,
148
+ filterParams,
149
+ tags,
150
+ tagRemovalHandler,
151
+ } = useAdvancedSearch({ context, languageList });
152
+
153
+ const isLangSpecific = computed<boolean>(
154
+ () => languageOptionList.value.length > 1
155
+ );
156
+
157
+ /** Fetch category list */
158
+
159
+ const {
160
+ fetchEntityList: fetchCategoryList,
161
+ isLoading: isCategoryListLoading,
162
+ rowData: categoryList,
163
+ errorMessage,
164
+ searchQuery,
165
+ handleChange,
166
+ pageNumber,
167
+ pageCount,
168
+ pageSize,
169
+ } = useDataTable<Category>({
170
+ fetchEntityList: (params) =>
171
+ getBlogCategoryList({
172
+ query: params.searchQuery,
173
+ pageNumber: params.pageNumber,
174
+ pageSize: params.pageSize,
175
+ ...filterParams.value,
176
+ }),
177
+ initialValue: [],
178
+ context,
179
+ resourceName: 'Blog category list',
180
+ pageSize: 100,
181
+ });
182
+
183
+ watch(filterParams, () => {
184
+ if (isModuleConfigLoading.value || isCategoryListLoading.value) {
185
+ return;
186
+ }
187
+
188
+ const newQuery = {
189
+ ...filterParams.value,
190
+ query: (context.root.$route.query.query ?? '') as string,
191
+ };
192
+
193
+ if (!isEqual(context.root.$route.query, newQuery)) {
194
+ context.root.$router.replace({ query: newQuery });
195
+ fetchCategoryList();
196
+ }
197
+ });
198
+
199
+ const {
200
+ isDeleting,
201
+ handleResourceDelete: handleCategoryDelete,
202
+ } = useResourceDelete({
203
+ deleteResource: deleteBlogCategory,
204
+ resourceName: 'Post',
205
+ onSuccess: fetchCategoryList,
206
+ context,
207
+ });
208
+
209
+ const isCategoryMoving = ref<boolean>(false);
210
+
211
+ function moveCategory(categoryId: number, direction: 'up' | 'down') {
212
+ isCategoryMoving.value = true;
213
+
214
+ moveBlogCategory(categoryId, direction)
215
+ .then((response) => {
216
+ if (response.success) {
217
+ return fetchCategoryList();
218
+ }
219
+ })
220
+ .catch(console.error)
221
+ .finally(() => {
222
+ isCategoryMoving.value = false;
223
+ });
224
+ }
225
+
226
+ /** Misc */
227
+
228
+ const isRowDataLoading = computed<boolean>(
229
+ () => isCategoryListLoading.value || isModuleConfigLoading.value
230
+ );
231
+
232
+ const displayedCategoryList = computed<Category[]>(() =>
233
+ convertCategoryList(categoryList.value, languageList.value)
234
+ );
235
+
236
+ const columnDefs = computed(() =>
237
+ getCategoryTableColumnDefs(moduleConfig.value, context.root.$t)
238
+ );
239
+
240
+ return {
241
+ columnDefs,
242
+ rowData: displayedCategoryList,
243
+ isRowDataLoading,
244
+ errorMessage,
245
+ isLangSpecific,
246
+ handleCategoryDelete,
247
+ isDeleting,
248
+ getBlogCategoryFormUrl,
249
+ getLinkToPostsByCategory,
250
+ moveCategory,
251
+ isCategoryMoving,
252
+
253
+ searchQuery,
254
+ handleChange,
255
+ pageNumber,
256
+ pageCount,
257
+ pageSize,
258
+
259
+ languageFilter,
260
+ languageOptionList,
261
+ tags,
262
+ tagRemovalHandler,
263
+
264
+ // Permissions
265
+ canViewAdministrators,
266
+ };
267
+ },
268
+ });
269
+ </script>
@@ -0,0 +1 @@
1
+ export { useAdvancedSearch } from './useAdvancedSearch';
@@ -0,0 +1,95 @@
1
+ import {
2
+ computed,
3
+ ComputedRef,
4
+ Ref,
5
+ ref,
6
+ SetupContext,
7
+ watch,
8
+ } from '@vue/composition-api';
9
+
10
+ import {
11
+ FilterTagType,
12
+ getFilterParamAsStringArray,
13
+ getFilterParams,
14
+ OptionType,
15
+ } from '@tager/admin-ui';
16
+
17
+ import { Language } from '@/typings/model';
18
+
19
+ interface Params {
20
+ context: SetupContext;
21
+ languageList: ComputedRef<Language[]>;
22
+ }
23
+
24
+ interface State {
25
+ languageFilter: Ref<OptionType[]>;
26
+ languageOptionList: ComputedRef<OptionType[]>;
27
+ filterParams: ComputedRef<Record<string, string | string[]>>;
28
+ tags: ComputedRef<FilterTagType[]>;
29
+ tagRemovalHandler(event: FilterTagType): void;
30
+ }
31
+
32
+ enum FilterTypes {
33
+ LANGUAGE = 'language',
34
+ }
35
+
36
+ export function useAdvancedSearch({ context, languageList }: Params): State {
37
+ /** Language **/
38
+
39
+ const languageOptionList = computed<OptionType[]>(() =>
40
+ languageList.value.map(({ id, name }) => ({ value: id, label: name }))
41
+ );
42
+
43
+ const initialLanguageFilter = computed<OptionType[]>(() => {
44
+ const queryValue = getFilterParamAsStringArray(
45
+ context.root.$route.query,
46
+ FilterTypes.LANGUAGE
47
+ );
48
+ return languageOptionList.value.filter(({ value }) =>
49
+ queryValue.some((selected) => selected === value)
50
+ );
51
+ });
52
+
53
+ const languageFilter = ref<OptionType[]>(initialLanguageFilter.value);
54
+
55
+ watch(initialLanguageFilter, () => {
56
+ languageFilter.value = initialLanguageFilter.value;
57
+ });
58
+
59
+ /** Params **/
60
+
61
+ const filterParams = computed(() =>
62
+ getFilterParams({
63
+ [FilterTypes.LANGUAGE]: languageFilter.value.map(({ value }) => value),
64
+ })
65
+ );
66
+
67
+ /** Tag removal handler **/
68
+
69
+ function tagRemovalHandler(event: FilterTagType) {
70
+ if (event.name === FilterTypes.LANGUAGE) {
71
+ languageFilter.value = languageFilter.value.filter(
72
+ ({ value }) => value !== event.value
73
+ );
74
+ }
75
+ }
76
+
77
+ /** Tags **/
78
+
79
+ const tags = computed<FilterTagType[]>(() => [
80
+ ...languageFilter.value.map(({ value, label }) => ({
81
+ value,
82
+ label,
83
+ name: FilterTypes.LANGUAGE,
84
+ title: context.root.$t('blog:language'),
85
+ })),
86
+ ]);
87
+
88
+ return {
89
+ languageFilter,
90
+ languageOptionList,
91
+ tagRemovalHandler,
92
+ tags: tags,
93
+ filterParams,
94
+ };
95
+ }
@@ -0,0 +1 @@
1
+ export { default } from './CategoryList.vue';
@@ -7,16 +7,12 @@ import { defineComponent, ref } from '@vue/composition-api';
7
7
 
8
8
  import { NavigationGridItem } from '@tager/admin-ui';
9
9
 
10
- import {
11
- getBlogCategoryListUrl,
12
- getBlogPostListUrl,
13
- getBlogSettingsUrl,
14
- } from '../constants/paths';
10
+ import { getBlogCategoryListUrl, getBlogPostListUrl } from '@/constants/paths';
15
11
 
16
12
  export default defineComponent({
17
13
  name: 'Home',
18
14
  setup(props, context) {
19
- const navItemList = ref<Array<NavigationGridItem>>([
15
+ const navItemList = ref<NavigationGridItem[]>([
20
16
  {
21
17
  name: context.root.$t('blog:blog'),
22
18
  linkList: [
@@ -25,7 +21,6 @@ export default defineComponent({
25
21
  url: getBlogCategoryListUrl(),
26
22
  text: context.root.$t('blog:categories'),
27
23
  },
28
- { url: getBlogSettingsUrl(), text: context.root.$t('blog:settings') },
29
24
  ],
30
25
  },
31
26
  ]);
@@ -34,5 +29,3 @@ export default defineComponent({
34
29
  },
35
30
  });
36
31
  </script>
37
-
38
- <style scoped lang="scss"></style>
@@ -0,0 +1,150 @@
1
+ import { createId, Nullable } from '@tager/admin-services';
2
+ import { OptionType, SingleFileInputValueType } from '@tager/admin-ui';
3
+ import {
4
+ FieldConfigUnion,
5
+ FieldUnion,
6
+ universalFieldUtils,
7
+ } from '@tager/admin-dynamic-field';
8
+
9
+ import { Category, PostFull } from '@/typings/model';
10
+ import { PostCreationPayload, PostUpdatePayload } from '@/services/requests';
11
+
12
+ export interface FormValues {
13
+ title: string;
14
+ excerpt: string;
15
+ body: string;
16
+ date: string;
17
+ coverImage: Nullable<SingleFileInputValueType>;
18
+ image: Nullable<SingleFileInputValueType>;
19
+ imageMobile: Nullable<SingleFileInputValueType>;
20
+ pageTitle: string;
21
+ pageDescription: string;
22
+ openGraphImage: Nullable<SingleFileInputValueType>;
23
+ urlAlias: string;
24
+ categories: OptionType<number>[];
25
+ language: Nullable<OptionType>;
26
+ relatedPosts: OptionType<number>[];
27
+ tags: string;
28
+ additionalFields: FieldUnion[];
29
+ }
30
+
31
+ export function convertPostToFormValues(
32
+ post: Nullable<PostFull>,
33
+ languageOptionList: OptionType[],
34
+ postOptionList: OptionType<number>[],
35
+ additionalFieldList: FieldConfigUnion[]
36
+ ): FormValues {
37
+ if (!post) {
38
+ return {
39
+ title: '',
40
+ excerpt: '',
41
+ body: '',
42
+ date: '',
43
+ coverImage: null,
44
+ image: null,
45
+ imageMobile: null,
46
+ pageTitle: '',
47
+ pageDescription: '',
48
+ openGraphImage: null,
49
+ urlAlias: '',
50
+ categories: [],
51
+ language: null,
52
+ relatedPosts: [],
53
+ tags: '',
54
+ additionalFields: additionalFieldList.map((fieldConfig) =>
55
+ universalFieldUtils.createFormField(fieldConfig, null)
56
+ ),
57
+ };
58
+ }
59
+
60
+ const currentLangOption = languageOptionList.find(
61
+ (option) => option.value === post.language
62
+ );
63
+
64
+ const selectedPostOptionList = postOptionList.filter((option) =>
65
+ post.relatedPosts.some((relatedPost) => relatedPost.id === option.value)
66
+ );
67
+
68
+ return {
69
+ title: post.title,
70
+ excerpt: post.excerpt,
71
+ body: post.body,
72
+ date: post.date,
73
+ coverImage: post.coverImage
74
+ ? { id: createId(), file: post.coverImage }
75
+ : null,
76
+ image: post.image ? { id: createId(), file: post.image } : null,
77
+ imageMobile: post.imageMobile
78
+ ? { id: createId(), file: post.imageMobile }
79
+ : null,
80
+ pageTitle: post.pageTitle ?? '',
81
+ pageDescription: post.pageDescription ?? '',
82
+ openGraphImage: post.openGraphImage
83
+ ? { id: createId(), file: post.openGraphImage }
84
+ : null,
85
+ urlAlias: post.urlAlias,
86
+ categories: post.categories.map((category) => ({
87
+ value: category.id,
88
+ label: category.name,
89
+ })),
90
+ language: currentLangOption ?? null,
91
+ relatedPosts: selectedPostOptionList,
92
+ tags: post.tags.join(','),
93
+ additionalFields: additionalFieldList.map((fieldConfig, index) => {
94
+ const postAdditionalField = post.additionalFields.find(
95
+ (item) => item.name === fieldConfig.name
96
+ );
97
+ return universalFieldUtils.createFormField(
98
+ fieldConfig,
99
+ postAdditionalField?.value
100
+ );
101
+ }),
102
+ };
103
+ }
104
+
105
+ export function convertFormValuesToCreationPayload(
106
+ values: FormValues
107
+ ): PostCreationPayload {
108
+ return {
109
+ title: values.title,
110
+ excerpt: values.excerpt,
111
+ urlAlias: values.urlAlias,
112
+ body: values.body,
113
+ date: values.date,
114
+ coverImage: values.coverImage?.file.id ?? null,
115
+ image: values.image?.file.id ?? null,
116
+ imageMobile: values.imageMobile?.file.id ?? null,
117
+ pageTitle: values.pageTitle,
118
+ pageDescription: values.pageDescription,
119
+ openGraphImage: values.openGraphImage?.file.id ?? null,
120
+ status: 'PUBLISHED',
121
+ categories: values.categories.map((option) => option.value),
122
+ language: values.language?.value ?? null,
123
+ relatedPosts: values.relatedPosts.map(
124
+ (relatedPostOption) => relatedPostOption.value
125
+ ),
126
+ tags: values.tags.split(',').filter((tag) => tag.trim().length > 0),
127
+ additionalFields: values.additionalFields.map((field) => ({
128
+ name: field.config.name,
129
+ value: universalFieldUtils.getOutgoingValue(field),
130
+ })),
131
+ };
132
+ }
133
+
134
+ export function convertFormValuesToUpdatePayload(
135
+ values: FormValues
136
+ ): PostUpdatePayload {
137
+ return {
138
+ ...convertFormValuesToCreationPayload(values),
139
+ urlAlias: values.urlAlias,
140
+ };
141
+ }
142
+
143
+ export function convertCategoryListToOptions(
144
+ categoryList: Category[],
145
+ languageId: Nullable<string>
146
+ ): OptionType<number>[] {
147
+ return categoryList
148
+ .filter(({ language }) => (languageId ? language === languageId : true))
149
+ .map(({ id, name }) => ({ value: id, label: name }));
150
+ }