@tager/admin-banners 0.1.5 → 0.3.0

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 (80) hide show
  1. package/CHANGELOG.md +13 -13
  2. package/dist/constants/menu.d.ts +4 -1
  3. package/dist/constants/paths.d.ts +8 -15
  4. package/dist/constants/routes.d.ts +3 -4
  5. package/dist/hooks/index.d.ts +2 -0
  6. package/dist/hooks/useFetchBanner.d.ts +8 -0
  7. package/dist/hooks/useFetchBanners.d.ts +6 -0
  8. package/dist/hooks/useFetchZones.d.ts +6 -0
  9. package/dist/index.esm.js +3200 -875
  10. package/dist/index.umd.js +3213 -886
  11. package/dist/locales/en.d.ts +39 -0
  12. package/dist/locales/ru.d.ts +3 -0
  13. package/dist/{index.d.ts → main.d.ts} +1 -0
  14. package/dist/router/index.d.ts +4 -0
  15. package/dist/services/requests.d.ts +19 -35
  16. package/dist/typings/banners.d.ts +40 -0
  17. package/dist/typings/common.d.ts +1 -0
  18. package/dist/typings/model.d.ts +103 -15
  19. package/dist/typings/zones.d.ts +5 -0
  20. package/dist/utils/common.d.ts +2 -0
  21. package/dist/views/Banners/BannerForm/BannerForm.helpers.d.ts +5 -0
  22. package/dist/views/Banners/BannerForm/BannerForm.vue.d.ts +81 -0
  23. package/dist/views/Banners/BannerForm/index.d.ts +1 -0
  24. package/dist/views/Banners/BannerList/BannerList.helpers.d.ts +5 -0
  25. package/dist/views/Banners/BannerList/BannerList.vue.d.ts +55 -0
  26. package/dist/views/Banners/BannerList/hooks/index.d.ts +1 -0
  27. package/dist/views/Banners/BannerList/hooks/useAdvancedSearch.d.ts +21 -0
  28. package/dist/views/Banners/BannerList/index.d.ts +1 -0
  29. package/dist/views/Zones/ZoneList/ZoneList.helpers.d.ts +5 -0
  30. package/dist/views/Zones/ZoneList/ZoneList.vue.d.ts +28 -0
  31. package/dist/views/Zones/ZoneList/index.d.ts +1 -0
  32. package/package.json +76 -67
  33. package/src/config/config.json +19 -19
  34. package/src/constants/menu.ts +26 -12
  35. package/src/constants/paths.ts +19 -36
  36. package/src/constants/routes.ts +44 -66
  37. package/src/dev.ts +22 -34
  38. package/src/hooks/index.ts +2 -0
  39. package/src/hooks/useFetchBanner.ts +41 -0
  40. package/src/hooks/useFetchBanners.ts +23 -0
  41. package/src/hooks/useFetchZones.ts +23 -0
  42. package/src/locales/en.ts +43 -0
  43. package/src/locales/ru.ts +45 -0
  44. package/src/main.ts +12 -0
  45. package/src/router/index.ts +38 -0
  46. package/src/services/requests.ts +52 -107
  47. package/src/typings/banners.ts +44 -0
  48. package/src/typings/common.ts +1 -0
  49. package/src/typings/model.ts +121 -18
  50. package/src/typings/sfc.d.ts +12 -12
  51. package/src/typings/shims-tsx.d.ts +13 -13
  52. package/src/typings/shims-vue.d.ts +11 -10
  53. package/src/typings/translate.d.ts +10 -0
  54. package/src/typings/zones.ts +5 -0
  55. package/src/utils/common.ts +13 -0
  56. package/src/views/App.vue +22 -22
  57. package/src/views/Banners/BannerForm/BannerForm.helpers.ts +38 -0
  58. package/src/views/Banners/BannerForm/BannerForm.vue +236 -0
  59. package/src/views/Banners/BannerForm/index.ts +1 -0
  60. package/src/views/Banners/BannerList/BannerList.helpers.ts +93 -0
  61. package/src/views/Banners/BannerList/BannerList.vue +233 -0
  62. package/src/views/Banners/BannerList/hooks/index.ts +1 -0
  63. package/src/views/Banners/BannerList/hooks/useAdvancedSearch.ts +152 -0
  64. package/src/views/Banners/BannerList/index.ts +1 -0
  65. package/src/views/Home.vue +37 -0
  66. package/src/views/Zones/ZoneList/ZoneList.helpers.ts +34 -0
  67. package/src/views/Zones/ZoneList/ZoneList.vue +75 -0
  68. package/src/views/Zones/ZoneList/index.ts +1 -0
  69. package/dist/views/BannerAreaForm.vue.d.ts +0 -22
  70. package/dist/views/BannerAreaList.vue.d.ts +0 -17
  71. package/dist/views/BannerItemForm.vue.d.ts +0 -27
  72. package/dist/views/BannerItemList.vue.d.ts +0 -22
  73. package/src/index.ts +0 -3
  74. package/src/locales/en.js +0 -3
  75. package/src/locales/ru.js +0 -3
  76. package/src/views/BannerAreaForm.vue +0 -173
  77. package/src/views/BannerAreaList.vue +0 -176
  78. package/src/views/BannerItemForm.vue +0 -230
  79. package/src/views/BannerItemList.vue +0 -221
  80. package/typings/index.d.ts +0 -15
@@ -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
+ }
@@ -0,0 +1,233 @@
1
+ <template>
2
+ <page
3
+ :title="$t('banners:banners')"
4
+ :header-buttons="[
5
+ {
6
+ text: $t('banners:createBanner'),
7
+ href: getBannersBannerFormUrl({ bannerId: 'create' }),
8
+ },
9
+ ]"
10
+ >
11
+ <template v-slot:content>
12
+ <data-table
13
+ :column-defs="columnDefs"
14
+ :row-data="rowData"
15
+ :loading="isRowDataLoading"
16
+ :error-message="errorMessage"
17
+ data-table="banners"
18
+ :use-search="true"
19
+ :use-pagination="true"
20
+ :search-query="searchQuery"
21
+ :pagination="{
22
+ pageNumber,
23
+ pageCount,
24
+ pageSize,
25
+ disabled: isRowDataLoading,
26
+ }"
27
+ @change="handleChange"
28
+ >
29
+ <template v-slot:filters>
30
+ <advanced-search :tags="tags" @click:tag="tagRemovalHandler">
31
+ <div class="filters">
32
+ <form-field-multi-select
33
+ v-model="zoneFilter"
34
+ :options="zonesOptionList"
35
+ name="zoneFilter"
36
+ :searchable="true"
37
+ :label="$t('banners:zone')"
38
+ class="filter"
39
+ />
40
+
41
+ <form-field
42
+ v-model="dateFilter"
43
+ :label="$t('banners:date')"
44
+ name="dateFilter"
45
+ type="date"
46
+ class="filter"
47
+ />
48
+
49
+ <form-field-multi-select
50
+ v-model="statusFilter"
51
+ :options="statusesOptionList"
52
+ name="statusFilter"
53
+ :searchable="true"
54
+ :label="$t('banners:status')"
55
+ class="filter"
56
+ />
57
+ </div>
58
+ </advanced-search>
59
+ </template>
60
+
61
+ <template v-slot:cell(actions)="{ row }">
62
+ <base-button
63
+ variant="icon"
64
+ :title="$t('banners:edit')"
65
+ :disabled="isBusy(row.id)"
66
+ :href="getBannersBannerFormUrl({ bannerId: row.id })"
67
+ >
68
+ <svg-icon name="edit" />
69
+ </base-button>
70
+
71
+ <base-button
72
+ variant="icon"
73
+ :title="$t('banners:remove')"
74
+ :disabled="isBusy(row.id)"
75
+ @click="handleResourceDelete(row.id)"
76
+ >
77
+ <svg-icon name="delete" />
78
+ </base-button>
79
+ </template>
80
+ </data-table>
81
+ </template>
82
+ </page>
83
+ </template>
84
+
85
+ <script lang="ts">
86
+ import {
87
+ computed,
88
+ defineComponent,
89
+ SetupContext,
90
+ watch,
91
+ } from '@vue/composition-api';
92
+ import isEqual from 'lodash.isequal';
93
+ import pick from 'lodash.pick';
94
+
95
+ import {
96
+ ColumnDefinition,
97
+ useDataTable,
98
+ useTranslation,
99
+ } from '@tager/admin-ui';
100
+ import { useResourceDelete } from '@tager/admin-services';
101
+
102
+ import { deleteBanner, getBanners } from '../../../services/requests';
103
+ import { Banner } from '../../../typings/banners';
104
+ import { getBannersBannerFormUrl } from '../../../constants/paths';
105
+ import { useFetchZones } from '../../../hooks';
106
+
107
+ import { getColumnDefs } from './BannerList.helpers';
108
+ import { useAdvancedSearch } from './hooks';
109
+
110
+ export default defineComponent({
111
+ name: 'BannerList',
112
+ setup(props, context: SetupContext) {
113
+ const { t } = useTranslation(context);
114
+ const { loading: isZonesLoading, data: zoneList } = useFetchZones({
115
+ context,
116
+ });
117
+
118
+ const {
119
+ zoneFilter,
120
+ zonesOptionList,
121
+ dateFilter,
122
+ statusFilter,
123
+ statusesOptionList,
124
+ filterParams,
125
+ tags,
126
+ tagRemovalHandler,
127
+ } = useAdvancedSearch({
128
+ context,
129
+ t,
130
+ zoneList,
131
+ });
132
+
133
+ const {
134
+ fetchEntityList: fetchBanners,
135
+ isLoading: isBannersLoading,
136
+ rowData,
137
+ errorMessage,
138
+ handleChange,
139
+ searchQuery,
140
+ pageNumber,
141
+ pageCount,
142
+ pageSize,
143
+ } = useDataTable<Banner>({
144
+ fetchEntityList: (params) =>
145
+ getBanners({
146
+ query: params.searchQuery,
147
+ pageNumber: params.pageNumber,
148
+ pageSize: params.pageSize,
149
+ ...filterParams.value,
150
+ }),
151
+ initialValue: [],
152
+ context,
153
+ resourceName: 'Banners',
154
+ });
155
+
156
+ watch(filterParams, () => {
157
+ if (isZonesLoading.value) {
158
+ return;
159
+ }
160
+
161
+ const newQuery = {
162
+ ...pick(context.root.$route.query, ['query', 'pageNumber']),
163
+ ...filterParams.value,
164
+ };
165
+
166
+ if (!isEqual(context.root.$route.query, newQuery)) {
167
+ context.root.$router.replace({ query: newQuery });
168
+ fetchBanners();
169
+ }
170
+ });
171
+
172
+ const { isDeleting, handleResourceDelete } = useResourceDelete({
173
+ deleteResource: deleteBanner,
174
+ resourceName: 'Banner',
175
+ onSuccess: fetchBanners,
176
+ context,
177
+ });
178
+
179
+ const isBusy = (bannerId: number): boolean => {
180
+ return isDeleting(bannerId) || isBannersLoading.value;
181
+ };
182
+
183
+ const columnDefs = computed<ColumnDefinition<Banner>[]>(() =>
184
+ getColumnDefs(context.root.$t)
185
+ );
186
+
187
+ const isRowDataLoading = computed<boolean>(
188
+ () => isBannersLoading.value || isZonesLoading.value
189
+ );
190
+
191
+ return {
192
+ columnDefs,
193
+ isRowDataLoading,
194
+ rowData,
195
+ errorMessage,
196
+ handleChange,
197
+ getBannersBannerFormUrl,
198
+ searchQuery,
199
+ pageNumber,
200
+ pageCount,
201
+ pageSize,
202
+ isBusy,
203
+ handleResourceDelete,
204
+
205
+ // Advanced search
206
+ zoneFilter,
207
+ zonesOptionList,
208
+ dateFilter,
209
+ statusFilter,
210
+ statusesOptionList,
211
+ tags,
212
+ tagRemovalHandler,
213
+ };
214
+ },
215
+ });
216
+ </script>
217
+
218
+ <style lang="scss">
219
+ .filters {
220
+ display: flex;
221
+ margin: 0 -10px;
222
+
223
+ &:not(:first-child) {
224
+ margin-top: 10px;
225
+ }
226
+
227
+ .filter {
228
+ padding: 10px 10px 0;
229
+ width: 50%;
230
+ margin: 0;
231
+ }
232
+ }
233
+ </style>
@@ -0,0 +1 @@
1
+ export { useAdvancedSearch } from './useAdvancedSearch';