@tager/admin-banners 0.1.6 → 0.3.1

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 (77) hide show
  1. package/dist/constants/menu.d.ts +4 -1
  2. package/dist/constants/paths.d.ts +8 -15
  3. package/dist/constants/routes.d.ts +3 -4
  4. package/dist/hooks/index.d.ts +2 -0
  5. package/dist/hooks/useFetchBanner.d.ts +8 -0
  6. package/dist/hooks/useFetchBanners.d.ts +6 -0
  7. package/dist/hooks/useFetchZones.d.ts +6 -0
  8. package/dist/index.esm.js +3202 -875
  9. package/dist/index.umd.js +3216 -886
  10. package/dist/locales/en.d.ts +39 -0
  11. package/dist/locales/ru.d.ts +3 -0
  12. package/dist/{index.d.ts → main.d.ts} +1 -0
  13. package/dist/router/index.d.ts +4 -0
  14. package/dist/services/requests.d.ts +22 -35
  15. package/dist/typings/banners.d.ts +40 -0
  16. package/dist/typings/common.d.ts +1 -0
  17. package/dist/typings/model.d.ts +103 -15
  18. package/dist/typings/zones.d.ts +5 -0
  19. package/dist/utils/common.d.ts +2 -0
  20. package/dist/views/Banners/BannerForm/BannerForm.helpers.d.ts +5 -0
  21. package/dist/views/Banners/BannerForm/BannerForm.vue.d.ts +81 -0
  22. package/dist/views/Banners/BannerForm/index.d.ts +1 -0
  23. package/dist/views/Banners/BannerList/BannerList.helpers.d.ts +5 -0
  24. package/dist/views/Banners/BannerList/BannerList.vue.d.ts +55 -0
  25. package/dist/views/Banners/BannerList/hooks/index.d.ts +1 -0
  26. package/dist/views/Banners/BannerList/hooks/useAdvancedSearch.d.ts +21 -0
  27. package/dist/views/Banners/BannerList/index.d.ts +1 -0
  28. package/dist/views/Zones/ZoneList/ZoneList.helpers.d.ts +5 -0
  29. package/dist/views/Zones/ZoneList/ZoneList.vue.d.ts +28 -0
  30. package/dist/views/Zones/ZoneList/index.d.ts +1 -0
  31. package/package.json +32 -23
  32. package/src/config/config.json +1 -1
  33. package/src/constants/menu.ts +24 -10
  34. package/src/constants/paths.ts +10 -27
  35. package/src/constants/routes.ts +27 -49
  36. package/src/dev.ts +22 -34
  37. package/src/hooks/index.ts +2 -0
  38. package/src/hooks/useFetchBanner.ts +41 -0
  39. package/src/hooks/useFetchBanners.ts +23 -0
  40. package/src/hooks/useFetchZones.ts +23 -0
  41. package/src/locales/en.ts +43 -0
  42. package/src/locales/ru.ts +45 -0
  43. package/src/main.ts +12 -0
  44. package/src/router/index.ts +38 -0
  45. package/src/services/requests.ts +35 -86
  46. package/src/typings/banners.ts +44 -0
  47. package/src/typings/common.ts +1 -0
  48. package/src/typings/model.ts +119 -16
  49. package/src/typings/shims-vue.d.ts +1 -0
  50. package/src/typings/translate.d.ts +10 -0
  51. package/src/typings/zones.ts +5 -0
  52. package/src/utils/common.ts +13 -0
  53. package/src/views/App.vue +7 -7
  54. package/src/views/Banners/BannerForm/BannerForm.helpers.ts +38 -0
  55. package/src/views/Banners/BannerForm/BannerForm.vue +236 -0
  56. package/src/views/Banners/BannerForm/index.ts +1 -0
  57. package/src/views/Banners/BannerList/BannerList.helpers.ts +93 -0
  58. package/src/views/Banners/BannerList/BannerList.vue +233 -0
  59. package/src/views/Banners/BannerList/hooks/index.ts +1 -0
  60. package/src/views/Banners/BannerList/hooks/useAdvancedSearch.ts +152 -0
  61. package/src/views/Banners/BannerList/index.ts +1 -0
  62. package/src/views/Home.vue +37 -0
  63. package/src/views/Zones/ZoneList/ZoneList.helpers.ts +34 -0
  64. package/src/views/Zones/ZoneList/ZoneList.vue +75 -0
  65. package/src/views/Zones/ZoneList/index.ts +1 -0
  66. package/dist/views/BannerAreaForm.vue.d.ts +0 -22
  67. package/dist/views/BannerAreaList.vue.d.ts +0 -17
  68. package/dist/views/BannerItemForm.vue.d.ts +0 -27
  69. package/dist/views/BannerItemList.vue.d.ts +0 -22
  70. package/src/index.ts +0 -3
  71. package/src/locales/en.js +0 -3
  72. package/src/locales/ru.js +0 -3
  73. package/src/views/BannerAreaForm.vue +0 -173
  74. package/src/views/BannerAreaList.vue +0 -176
  75. package/src/views/BannerItemForm.vue +0 -231
  76. package/src/views/BannerItemList.vue +0 -221
  77. package/typings/index.d.ts +0 -15
@@ -1,18 +1,121 @@
1
1
  import { Nullable, FileType } from '@tager/admin-services';
2
+ import {
3
+ FieldConfigUnion,
4
+ FieldShortType,
5
+ IncomingValueUnion,
6
+ } from '@tager/admin-dynamic-field';
7
+ import {
8
+ OptionType,
9
+ ShortCodeItemType,
10
+ SingleFileInputValueType,
11
+ } from '@tager/admin-ui';
2
12
 
3
- export type BannerArea = {
4
- id: number;
5
- alias: string;
6
- label: string;
7
- scenario: string;
8
- };
9
-
10
- export type BannerItem = {
11
- id: number;
12
- title: string;
13
- text: string;
14
- image: Nullable<FileType>;
15
- buttonLabel: string;
16
- buttonLink: Nullable<string>;
17
- buttonIsNewTab: boolean;
18
- };
13
+ export interface SeoInfo {
14
+ readonly pageTitle: Nullable<string>;
15
+ readonly pageDescription: Nullable<string>;
16
+ readonly pageKeywords: Nullable<string>;
17
+ readonly openGraphTitle: Nullable<string>;
18
+ readonly openGraphDescription: Nullable<string>;
19
+ readonly openGraphImage: Nullable<FileType>;
20
+ }
21
+
22
+ export interface Category extends SeoInfo {
23
+ readonly id: number;
24
+ readonly name: string;
25
+ readonly isDefault: boolean;
26
+ readonly language: string;
27
+ readonly url: string;
28
+ readonly urlTemplate: string;
29
+ readonly urlAlias: string;
30
+ readonly postsCount: string;
31
+ readonly parent: Nullable<{ id: Nullable<number>; name: string }>;
32
+ readonly depth: number;
33
+ }
34
+
35
+ export interface PostShort {
36
+ readonly id: number;
37
+ readonly title: string;
38
+ readonly url: string;
39
+ readonly excerpt: string;
40
+ readonly status: string;
41
+ readonly language: string;
42
+ readonly categories: Category[];
43
+ readonly datetime: string;
44
+ readonly image: Nullable<FileType>;
45
+ }
46
+
47
+ export interface PostFull extends SeoInfo {
48
+ readonly id: number;
49
+ readonly language: string;
50
+ readonly title: string;
51
+ readonly url: string;
52
+ readonly urlTemplate: string;
53
+ readonly urlAlias: string;
54
+ readonly datetime: string|null;
55
+ readonly status: string;
56
+ readonly excerpt: string;
57
+ readonly image: Nullable<FileType>;
58
+ readonly imageMobile: Nullable<FileType>;
59
+ readonly categories: Category[];
60
+ readonly body: string;
61
+ readonly coverImage: Nullable<FileType>;
62
+ readonly relatedPosts: Pick<PostShort, 'id' | 'title'>[];
63
+ readonly tags: string[];
64
+ readonly additionalFields: FieldShortType<IncomingValueUnion>[];
65
+ }
66
+
67
+ export interface Language {
68
+ readonly id: string;
69
+ readonly name: string;
70
+ }
71
+
72
+ export interface DefaultCategory {
73
+ readonly id: number;
74
+ readonly name: string;
75
+ readonly language: string;
76
+ }
77
+
78
+ export interface FileScenarios {
79
+ readonly cover: Nullable<string>;
80
+ readonly image: Nullable<string>;
81
+ readonly imageMobile: Nullable<string>;
82
+ readonly content: Nullable<string>;
83
+ readonly openGraph: Nullable<string>;
84
+ }
85
+
86
+ export interface ModuleConfig {
87
+ readonly urlPostTemplate: Nullable<string>;
88
+ readonly urlCategoryTemplate: Nullable<string>;
89
+ readonly languages: Language[];
90
+ readonly defaultCategories: DefaultCategory[];
91
+ readonly fields: FieldConfigUnion[];
92
+ readonly shortcodes: ShortCodeItemType[];
93
+ readonly fileScenarios: FileScenarios;
94
+ }
95
+
96
+ export interface CategoryFormValues {
97
+ language: Nullable<OptionType>;
98
+ name: string;
99
+ parent: OptionType<Nullable<number>>;
100
+ urlAlias: string;
101
+ isDefault: boolean;
102
+
103
+ pageTitle: Nullable<string>;
104
+ pageDescription: Nullable<string>;
105
+ pageKeywords: Nullable<string>;
106
+ openGraphTitle: Nullable<string>;
107
+ openGraphDescription: Nullable<string>;
108
+ openGraphImage: Nullable<SingleFileInputValueType>;
109
+ }
110
+
111
+ export interface CategoryPayload {
112
+ language: Nullable<string>;
113
+ name: string;
114
+ parent: Nullable<number>;
115
+ urlAlias: Nullable<string>;
116
+ isDefault: boolean;
117
+
118
+ pageTitle: Nullable<string>;
119
+ pageDescription: Nullable<string>;
120
+ openGraphImage: Nullable<string>;
121
+ }
@@ -1,4 +1,5 @@
1
1
  declare module '*.vue' {
2
+ // eslint-disable-next-line import/order
2
3
  import Vue from 'vue';
3
4
  export default Vue;
4
5
  }
@@ -0,0 +1,10 @@
1
+ import { TFunction } from 'i18next';
2
+
3
+ declare module 'vue/types/vue' {
4
+ interface Vue {
5
+ $t: TFunction;
6
+ }
7
+ interface VueConstructor {
8
+ $t: TFunction;
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ export interface Zone {
2
+ id: string;
3
+ name: string;
4
+ bannersCount: number;
5
+ }
@@ -0,0 +1,13 @@
1
+ export function generateNumberArray(length: number): Array<number> {
2
+ return Array.from({ length }, (_, index) => index);
3
+ }
4
+
5
+ export function getNameWithDepth(name: string, depth: number): string {
6
+ return (
7
+ generateNumberArray(depth)
8
+ .map(() => '—')
9
+ .join(' ') +
10
+ ' ' +
11
+ name
12
+ );
13
+ }
package/src/views/App.vue CHANGED
@@ -5,18 +5,18 @@
5
5
  </template>
6
6
 
7
7
  <script lang="ts">
8
- import Vue from 'vue';
8
+ import { defineComponent, SetupContext } from '@vue/composition-api';
9
9
 
10
- import { BANNER_MENU_ITEM } from '../constants/menu';
10
+ import { getBannersMenuItem } from '../constants/menu';
11
11
 
12
- export default Vue.extend({
12
+ export default defineComponent({
13
13
  name: 'App',
14
- data() {
14
+ setup(props, context: SetupContext) {
15
+ const menu = getBannersMenuItem({ t: context.root.$t });
16
+
15
17
  return {
16
- sidebarMenuList: [BANNER_MENU_ITEM],
18
+ sidebarMenuList: [menu],
17
19
  };
18
20
  },
19
21
  });
20
22
  </script>
21
-
22
- <style scoped lang="scss"></style>
@@ -0,0 +1,38 @@
1
+ import { OptionType } from '@tager/admin-ui';
2
+ import { createId, Nullable } from '@tager/admin-services';
3
+
4
+ import {
5
+ Banner,
6
+ BannerFormValues,
7
+ BannerPayload,
8
+ } from '../../../typings/banners';
9
+
10
+ export function convertBannerToFormValues(
11
+ banner: Nullable<Banner>,
12
+ zonesOptionList: OptionType[]
13
+ ): BannerFormValues {
14
+ const foundZone = zonesOptionList.find(
15
+ ({ value }) => value === banner?.bannerZone
16
+ );
17
+
18
+ return {
19
+ link: banner?.link ?? '',
20
+ image: banner?.image ? { id: createId(), file: banner.image } : null,
21
+ bannerZone: foundZone ?? null,
22
+ openNewTab: banner?.openNewTab ?? false,
23
+ dateStart: banner?.dateStart ?? '',
24
+ dateEnd: banner?.dateEnd ?? '',
25
+ disabled: banner?.disabled ?? false,
26
+ comment: banner?.comment ?? '',
27
+ };
28
+ }
29
+
30
+ export function convertBannerFormValuesToPayload(
31
+ values: BannerFormValues
32
+ ): BannerPayload {
33
+ return {
34
+ ...values,
35
+ image: values.image?.file.id ?? null,
36
+ bannerZone: values.bannerZone?.value ?? '',
37
+ };
38
+ }
@@ -0,0 +1,236 @@
1
+ <template>
2
+ <page
3
+ :title="
4
+ isCreation ? $t('banners:createBanner') : $t('banners:updateBanner')
5
+ "
6
+ :is-content-loading="isContentLoading"
7
+ >
8
+ <template v-slot:content>
9
+ <form novalidate @submit.prevent="submitForm">
10
+ <form-field-select
11
+ v-model="values.bannerZone"
12
+ name="bannerZone"
13
+ :label="$t('banners:zone')"
14
+ :options="zonesOptionList"
15
+ :error="errors.bannerZone"
16
+ />
17
+
18
+ <form-field-file-input
19
+ v-model="values.image"
20
+ name="image"
21
+ file-type="image"
22
+ :label="$t('banners:image')"
23
+ :error="errors.file"
24
+ />
25
+
26
+ <form-field
27
+ v-model="values.link"
28
+ name="link"
29
+ :label="$t('banners:link')"
30
+ :error="errors.name"
31
+ />
32
+
33
+ <form-field-checkbox
34
+ v-model="values.openNewTab"
35
+ name="openNewTab"
36
+ :label="$t('banners:openNewTab')"
37
+ :error="errors.openNewTab"
38
+ />
39
+
40
+ <input-label>{{ $t('banners:period') }}</input-label>
41
+
42
+ <div class="date">
43
+ <form-field
44
+ v-model="values.dateStart"
45
+ :label="$t('banners:from')"
46
+ name="dateStart"
47
+ type="date"
48
+ :max="values.dateEnd"
49
+ />
50
+
51
+ <form-field
52
+ v-model="values.dateEnd"
53
+ :label="$t('banners:to')"
54
+ name="dateEnd"
55
+ type="date"
56
+ :min="values.dateStart"
57
+ />
58
+ </div>
59
+
60
+ <form-field-checkbox
61
+ v-model="values.disabled"
62
+ name="disabled"
63
+ :label="$t('banners:disabled')"
64
+ :error="errors.disabled"
65
+ />
66
+
67
+ <form-field
68
+ v-model="values.comment"
69
+ name="comment"
70
+ type="textarea"
71
+ :label="$t('banners:comment')"
72
+ :error="errors.comment"
73
+ />
74
+ </form>
75
+ </template>
76
+
77
+ <template v-slot:footer>
78
+ <FormFooter
79
+ :back-href="bannerListUrl"
80
+ :on-submit="submitForm"
81
+ :is-submitting="isSubmitting"
82
+ :is-creation="isCreation"
83
+ :can-create-another="isCreation"
84
+ />
85
+ </template>
86
+ </page>
87
+ </template>
88
+
89
+ <script lang="ts">
90
+ import {
91
+ computed,
92
+ defineComponent,
93
+ ref,
94
+ SetupContext,
95
+ watch,
96
+ } from '@vue/composition-api';
97
+
98
+ import { FormFooter, OptionType, TagerFormSubmitEvent } from '@tager/admin-ui';
99
+ import { convertRequestErrorToMap } from '@tager/admin-services';
100
+
101
+ import { useFetchZones } from '../../../hooks';
102
+ import { BannerFormValues } from '../../../typings/banners';
103
+ import { useFetchBanner } from '../../../hooks/useFetchBanner';
104
+ import {
105
+ getBannersBannerFormUrl,
106
+ getBannersBannerListUrl,
107
+ } from '../../../constants/paths';
108
+ import { createBanner, updateBanner } from '../../../services/requests';
109
+
110
+ import {
111
+ convertBannerFormValuesToPayload,
112
+ convertBannerToFormValues,
113
+ } from './BannerForm.helpers';
114
+
115
+ export default defineComponent({
116
+ name: 'BannerForm',
117
+ components: { FormFooter },
118
+ setup(props, context: SetupContext) {
119
+ const bannerId = computed<string>(
120
+ () => context.root.$route.params.bannerId
121
+ );
122
+ const isCreation = computed<boolean>(() => bannerId.value === 'create');
123
+
124
+ /** Zones **/
125
+
126
+ const { data: zones, loading: isZonesLoading } = useFetchZones({ context });
127
+
128
+ const zonesOptionList = computed<OptionType[]>(() =>
129
+ zones.value.map<OptionType>((zone) => ({
130
+ value: zone.id,
131
+ label: zone.name,
132
+ }))
133
+ );
134
+
135
+ /** Banner **/
136
+
137
+ const { data: banner, loading: isBannerLoading } = useFetchBanner({
138
+ context,
139
+ bannerId,
140
+ isCreation,
141
+ });
142
+
143
+ /** Form State **/
144
+
145
+ const isSubmitting = ref<boolean>(false);
146
+ const values = ref<BannerFormValues>(
147
+ convertBannerToFormValues(banner.value, zonesOptionList.value)
148
+ );
149
+ const errors = ref<Record<string, string>>({});
150
+
151
+ watch([banner, zones], () => {
152
+ values.value = convertBannerToFormValues(
153
+ banner.value,
154
+ zonesOptionList.value
155
+ );
156
+ });
157
+
158
+ const submitForm = (event: TagerFormSubmitEvent) => {
159
+ isSubmitting.value = true;
160
+
161
+ const body = convertBannerFormValuesToPayload(values.value);
162
+
163
+ const requestPromise = isCreation.value
164
+ ? createBanner(body)
165
+ : updateBanner(bannerId.value, body);
166
+
167
+ requestPromise
168
+ .then(({ data }) => {
169
+ errors.value = {};
170
+
171
+ if (event.type === 'create') {
172
+ context.root.$router.push(
173
+ getBannersBannerFormUrl({ bannerId: String(data.id) })
174
+ );
175
+ }
176
+
177
+ if (event.type === 'create_exit' || event.type === 'save_exit') {
178
+ context.root.$router.push(getBannersBannerListUrl());
179
+ }
180
+
181
+ if (event.type === 'create_create-another') {
182
+ values.value = convertBannerToFormValues(
183
+ null,
184
+ zonesOptionList.value
185
+ );
186
+ }
187
+
188
+ context.root.$toast({
189
+ variant: 'success',
190
+ title: context.root.$t('banners:success'),
191
+ body: isCreation.value
192
+ ? context.root.$t('banners:bannerWasSuccessfullyCreated')
193
+ : context.root.$t('banners:bannerWasSuccessfullyUpdated'),
194
+ });
195
+ })
196
+ .catch((error) => {
197
+ console.error(error);
198
+ errors.value = convertRequestErrorToMap(error);
199
+ context.root.$toast({
200
+ variant: 'danger',
201
+ title: context.root.$t('banners:error'),
202
+ body: isCreation.value
203
+ ? context.root.$t('banners:bannerCreationWasFailed')
204
+ : context.root.$t('banners:bannerUpdateWasFailed'),
205
+ });
206
+ })
207
+ .finally(() => {
208
+ isSubmitting.value = false;
209
+ });
210
+ };
211
+
212
+ const isContentLoading = computed<boolean>(
213
+ () => isBannerLoading.value || isZonesLoading.value
214
+ );
215
+
216
+ return {
217
+ isCreation,
218
+ isContentLoading,
219
+ isSubmitting,
220
+ values,
221
+ errors,
222
+ submitForm,
223
+ bannerListUrl: getBannersBannerListUrl(),
224
+ zonesOptionList,
225
+ };
226
+ },
227
+ });
228
+ </script>
229
+
230
+ <style lang="scss">
231
+ .date {
232
+ display: grid;
233
+ grid-template-columns: repeat(2, 1fr);
234
+ column-gap: 10px;
235
+ }
236
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from './BannerForm.vue';
@@ -0,0 +1,93 @@
1
+ import { TFunction } from 'i18next';
2
+
3
+ import { ColumnDefinition } from '@tager/admin-ui';
4
+
5
+ import { Banner, Status } from '../../../typings/banners';
6
+
7
+ export function getStatus(type: Status, t: TFunction) {
8
+ switch (type) {
9
+ case Status.Waiting:
10
+ return t('banners:statuses.waiting');
11
+ case Status.Archived:
12
+ return t('banners:statuses.archived');
13
+ case Status.Active:
14
+ return t('banners:statuses.active');
15
+ case Status.Disabled:
16
+ return t('banners:statuses.disabled');
17
+
18
+ default:
19
+ return '';
20
+ }
21
+ }
22
+
23
+ export function getColumnDefs(t: TFunction): ColumnDefinition<Banner>[] {
24
+ return [
25
+ {
26
+ id: 0,
27
+ name: '#',
28
+ field: 'id',
29
+ style: { width: '50px', textAlign: 'center' },
30
+ headStyle: { width: '50px', textAlign: 'center' },
31
+ },
32
+ {
33
+ id: 1,
34
+ name: t('banners:zone'),
35
+ field: 'bannerZone',
36
+ format: ({ row }) =>
37
+ row.bannerZone ? row.bannerZone : t('banners:bannerZoneIsDeleted'),
38
+ },
39
+ {
40
+ id: 2,
41
+ name: t('banners:status'),
42
+ field: 'status',
43
+ format: ({ row }) => getStatus(row.status, t),
44
+ style: { width: '120px' },
45
+ headStyle: { width: '120px' },
46
+ },
47
+ {
48
+ id: 3,
49
+ name: t('banners:period'),
50
+ field: 'dateStart',
51
+ format: ({ row }) =>
52
+ `${row.dateStart ? `${t('banners:from')} ${row.dateStart}` : ''} ${
53
+ row.dateEnd ? `${t('banners:to')} ${row.dateEnd}` : ''
54
+ }`,
55
+ },
56
+ {
57
+ id: 4,
58
+ name: t('banners:image'),
59
+ field: 'image',
60
+ type: 'image',
61
+ },
62
+ {
63
+ id: 5,
64
+ name: t('banners:link'),
65
+ field: 'link',
66
+ type: 'name',
67
+ format: ({ row }) => ({
68
+ adminLink: {
69
+ url: row.link ?? '',
70
+ text: row.link ?? '',
71
+ },
72
+ paramList: [
73
+ {
74
+ name: t('banners:newTab'),
75
+ value: row.openNewTab ? t('banners:yes') : t('banners:no'),
76
+ },
77
+ ],
78
+ }),
79
+ },
80
+ {
81
+ id: 6,
82
+ name: t('banners:comment'),
83
+ field: 'comment',
84
+ },
85
+ {
86
+ id: 7,
87
+ name: t('banners:actions'),
88
+ field: 'actions',
89
+ style: { whiteSpace: 'nowrap', width: '100px' },
90
+ headStyle: { whiteSpace: 'nowrap', width: '100px' },
91
+ },
92
+ ];
93
+ }