@tager/admin-banners 0.3.6 → 0.3.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tager/admin-banners",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.umd.js",
6
6
  "module": "dist/index.esm.js",
@@ -0,0 +1,48 @@
1
+ import {
2
+ computed,
3
+ onMounted,
4
+ Ref,
5
+ SetupContext,
6
+ watch,
7
+ } from '@vue/composition-api';
8
+
9
+ import { Nullable, ResourceRef, useResource } from '@tager/admin-services';
10
+
11
+ import { getZoneFields } from '../services/requests';
12
+ import { ZoneFields } from '../typings/zones';
13
+ import { BannerFormValues } from '../typings/banners';
14
+
15
+ export function useFetchZoneFields({
16
+ context,
17
+ values,
18
+ }: {
19
+ context: SetupContext;
20
+ values: Ref<BannerFormValues>;
21
+ }): ResourceRef<Nullable<ZoneFields>> {
22
+ const zoneId = computed(() => values.value.bannerZone?.value ?? '');
23
+
24
+ const [fetchZoneFields, resource] = useResource<Nullable<ZoneFields>>({
25
+ fetchResource: () => getZoneFields(zoneId.value),
26
+ initialValue: null,
27
+ context,
28
+ resourceName: 'Zone Fields',
29
+ });
30
+
31
+ onMounted(() => {
32
+ if (!zoneId.value) {
33
+ return;
34
+ }
35
+
36
+ fetchZoneFields();
37
+ });
38
+
39
+ watch(zoneId, () => {
40
+ if (!zoneId.value) {
41
+ return;
42
+ }
43
+
44
+ fetchZoneFields();
45
+ });
46
+
47
+ return resource;
48
+ }
@@ -1,6 +1,6 @@
1
1
  import { request, ResponseBody } from '@tager/admin-services';
2
2
 
3
- import { Zone } from '../typings/zones';
3
+ import { Zone, ZoneFields } from '../typings/zones';
4
4
  import { Banner, BannerPayload } from '../typings/banners';
5
5
 
6
6
  /** Zones **/
@@ -13,6 +13,12 @@ export function getZones(params?: {
13
13
  return request.get({ path: '/admin/adv/zones', params });
14
14
  }
15
15
 
16
+ export function getZoneFields(
17
+ zoneId: string
18
+ ): Promise<ResponseBody<ZoneFields>> {
19
+ return request.get({ path: `/admin/adv/zones/${zoneId}` });
20
+ }
21
+
16
22
  /** Banners **/
17
23
 
18
24
  export function getBanners(params?: {
@@ -1,5 +1,11 @@
1
1
  import { FileType, Nullable } from '@tager/admin-services';
2
2
  import { OptionType, SingleFileInputValueType } from '@tager/admin-ui';
3
+ import {
4
+ FieldShortType,
5
+ FieldUnion,
6
+ IncomingValueUnion,
7
+ OutgoingValueUnion,
8
+ } from '@tager/admin-dynamic-field';
3
9
 
4
10
  export enum Status {
5
11
  Waiting = 'WAITING',
@@ -20,18 +26,20 @@ export interface Banner {
20
26
  dateEnd: Nullable<string>;
21
27
  disabled: boolean;
22
28
  comment: Nullable<string>;
29
+ fields: FieldShortType<IncomingValueUnion>[];
23
30
  }
24
31
 
25
32
  export interface BannerFormValues {
26
33
  link: Nullable<string>;
27
34
  image: Nullable<SingleFileInputValueType>;
28
35
  bannerZone: Nullable<OptionType>;
29
- priority: number;
36
+ priority: string;
30
37
  openNewTab: boolean;
31
38
  dateStart: string;
32
39
  dateEnd: string;
33
40
  disabled: boolean;
34
41
  comment: string;
42
+ fields: FieldUnion[];
35
43
  }
36
44
 
37
45
  export interface BannerPayload {
@@ -44,4 +52,5 @@ export interface BannerPayload {
44
52
  dateEnd: Nullable<string>;
45
53
  disabled: boolean;
46
54
  comment: string;
55
+ fields: FieldShortType<OutgoingValueUnion>[];
47
56
  }
@@ -1,5 +1,11 @@
1
+ import { FieldConfigUnion } from '@tager/admin-dynamic-field';
2
+
1
3
  export interface Zone {
2
4
  id: string;
3
5
  name: string;
4
6
  bannersCount: number;
5
7
  }
8
+
9
+ export interface ZoneFields extends Zone {
10
+ fields: FieldConfigUnion[];
11
+ }
@@ -1,5 +1,6 @@
1
1
  import { OptionType } from '@tager/admin-ui';
2
2
  import { createId, Nullable } from '@tager/admin-services';
3
+ import { universalFieldUtils } from '@tager/admin-dynamic-field';
3
4
 
4
5
  import {
5
6
  Banner,
@@ -16,7 +17,7 @@ export function convertBannerToFormValues(
16
17
  );
17
18
 
18
19
  return {
19
- priority: banner?.priority ?? 1,
20
+ priority: String(banner?.priority ?? 1),
20
21
  link: banner?.link ?? '',
21
22
  image: banner?.image ? { id: createId(), file: banner.image } : null,
22
23
  bannerZone: foundZone ?? null,
@@ -25,6 +26,7 @@ export function convertBannerToFormValues(
25
26
  dateEnd: banner?.dateEnd ?? '',
26
27
  disabled: banner?.disabled ?? false,
27
28
  comment: banner?.comment ?? '',
29
+ fields: [],
28
30
  };
29
31
  }
30
32
 
@@ -38,5 +40,9 @@ export function convertBannerFormValuesToPayload(
38
40
  dateEnd: values.dateEnd ? values.dateEnd : null,
39
41
  image: values.image?.file.id ?? null,
40
42
  bannerZone: values.bannerZone?.value ?? '',
43
+ fields: values.fields.map((field) => ({
44
+ name: field.config.name,
45
+ value: universalFieldUtils.getOutgoingValue(field),
46
+ })),
41
47
  };
42
48
  }
@@ -1,26 +1,26 @@
1
1
  <template>
2
2
  <page
3
- :title="
3
+ :title="
4
4
  isCreation ? $t('banners:createBanner') : $t('banners:updateBanner')
5
5
  "
6
- :is-content-loading="isContentLoading"
6
+ :is-content-loading="isContentLoading"
7
7
  >
8
8
  <template v-slot:content>
9
9
  <form novalidate @submit.prevent="submitForm">
10
10
  <div class="zone">
11
11
  <form-field-select
12
- v-model="values.bannerZone"
13
- name="bannerZone"
14
- :label="$t('banners:zone')"
15
- :options="zonesOptionList"
16
- :error="errors.bannerZone"
12
+ v-model="values.bannerZone"
13
+ name="bannerZone"
14
+ :label="$t('banners:zone')"
15
+ :options="zonesOptionList"
16
+ :error="errors.bannerZone"
17
17
  />
18
18
 
19
19
  <form-field
20
- v-model="values.priority"
21
- name="priority"
22
- :label="$t('banners:priority')"
23
- :error="errors.priority"
20
+ v-model="values.priority"
21
+ name="priority"
22
+ :label="$t('banners:priority')"
23
+ :error="errors.priority"
24
24
  />
25
25
  </div>
26
26
 
@@ -28,70 +28,76 @@
28
28
 
29
29
  <div class="date">
30
30
  <form-field
31
- v-model="values.dateStart"
32
- :label="$t('banners:from')"
33
- name="dateStart"
34
- type="date"
35
- :max="values.dateEnd"
31
+ v-model="values.dateStart"
32
+ :label="$t('banners:from')"
33
+ name="dateStart"
34
+ type="date"
35
+ :max="values.dateEnd"
36
36
  />
37
37
 
38
38
  <form-field
39
- v-model="values.dateEnd"
40
- :label="$t('banners:to')"
41
- name="dateEnd"
42
- type="date"
43
- :min="values.dateStart"
39
+ v-model="values.dateEnd"
40
+ :label="$t('banners:to')"
41
+ name="dateEnd"
42
+ type="date"
43
+ :min="values.dateStart"
44
44
  />
45
45
  </div>
46
46
 
47
47
  <form-field-checkbox
48
- v-model="values.disabled"
49
- name="disabled"
50
- :label="$t('banners:disabled')"
51
- :error="errors.disabled"
48
+ v-model="values.disabled"
49
+ name="disabled"
50
+ :label="$t('banners:disabled')"
51
+ :error="errors.disabled"
52
52
  />
53
53
 
54
54
  <div class="link">
55
55
  <form-field
56
- v-model="values.link"
57
- name="link"
58
- :label="$t('banners:link')"
59
- :error="errors.name"
56
+ v-model="values.link"
57
+ name="link"
58
+ :label="$t('banners:link')"
59
+ :error="errors.name"
60
60
  />
61
61
 
62
62
  <form-field-checkbox
63
- v-model="values.openNewTab"
64
- name="openNewTab"
65
- :label="$t('banners:openNewTab')"
66
- :error="errors.openNewTab"
63
+ v-model="values.openNewTab"
64
+ name="openNewTab"
65
+ :label="$t('banners:openNewTab')"
66
+ :error="errors.openNewTab"
67
67
  />
68
68
  </div>
69
69
 
70
70
  <form-field-file-input
71
- v-model="values.image"
72
- name="image"
73
- file-type="image"
74
- :label="$t('banners:image')"
75
- :error="errors.file"
71
+ v-model="values.image"
72
+ name="image"
73
+ file-type="image"
74
+ :label="$t('banners:image')"
75
+ :error="errors.file"
76
+ />
77
+
78
+ <DynamicField
79
+ v-for="field of values.fields"
80
+ :key="field.name"
81
+ :field="field"
76
82
  />
77
83
 
78
84
  <form-field
79
- v-model="values.comment"
80
- name="comment"
81
- type="textarea"
82
- :label="$t('banners:comment')"
83
- :error="errors.comment"
85
+ v-model="values.comment"
86
+ name="comment"
87
+ type="textarea"
88
+ :label="$t('banners:comment')"
89
+ :error="errors.comment"
84
90
  />
85
91
  </form>
86
92
  </template>
87
93
 
88
94
  <template v-slot:footer>
89
95
  <FormFooter
90
- :back-href="bannerListUrl"
91
- :on-submit="submitForm"
92
- :is-submitting="isSubmitting"
93
- :is-creation="isCreation"
94
- :can-create-another="isCreation"
96
+ :back-href="bannerListUrl"
97
+ :on-submit="submitForm"
98
+ :is-submitting="isSubmitting"
99
+ :is-creation="isCreation"
100
+ :can-create-another="isCreation"
95
101
  />
96
102
  </template>
97
103
  </page>
@@ -106,17 +112,19 @@ import {
106
112
  watch,
107
113
  } from '@vue/composition-api';
108
114
 
109
- import {FormFooter, OptionType, TagerFormSubmitEvent} from '@tager/admin-ui';
110
- import {convertRequestErrorToMap} from '@tager/admin-services';
115
+ import { FormFooter, OptionType, TagerFormSubmitEvent } from '@tager/admin-ui';
116
+ import { DynamicField, universalFieldUtils } from '@tager/admin-dynamic-field';
117
+ import { convertRequestErrorToMap } from '@tager/admin-services';
111
118
 
112
- import {useFetchZones} from '../../../hooks';
113
- import {BannerFormValues} from '../../../typings/banners';
114
- import {useFetchBanner} from '../../../hooks/useFetchBanner';
119
+ import { useFetchZones } from '../../../hooks';
120
+ import { BannerFormValues } from '../../../typings/banners';
121
+ import { useFetchBanner } from '../../../hooks/useFetchBanner';
115
122
  import {
116
123
  getBannersBannerFormUrl,
117
124
  getBannersBannerListUrl,
118
125
  } from '../../../constants/paths';
119
- import {createBanner, updateBanner} from '../../../services/requests';
126
+ import { createBanner, updateBanner } from '../../../services/requests';
127
+ import { useFetchZoneFields } from '../../../hooks/useFetchZoneFields';
120
128
 
121
129
  import {
122
130
  convertBannerFormValuesToPayload,
@@ -125,27 +133,27 @@ import {
125
133
 
126
134
  export default defineComponent({
127
135
  name: 'BannerForm',
128
- components: {FormFooter},
136
+ components: { FormFooter, DynamicField },
129
137
  setup(props, context: SetupContext) {
130
138
  const bannerId = computed<string>(
131
- () => context.root.$route.params.bannerId
139
+ () => context.root.$route.params.bannerId
132
140
  );
133
141
  const isCreation = computed<boolean>(() => bannerId.value === 'create');
134
142
 
135
143
  /** Zones **/
136
144
 
137
- const {data: zones, loading: isZonesLoading} = useFetchZones({context});
145
+ const { data: zones, loading: isZonesLoading } = useFetchZones({ context });
138
146
 
139
147
  const zonesOptionList = computed<OptionType[]>(() =>
140
- zones.value.map<OptionType>((zone) => ({
141
- value: zone.id,
142
- label: zone.name,
143
- }))
148
+ zones.value.map<OptionType>((zone) => ({
149
+ value: zone.id,
150
+ label: zone.name,
151
+ }))
144
152
  );
145
153
 
146
154
  /** Banner **/
147
155
 
148
- const {data: banner, loading: isBannerLoading} = useFetchBanner({
156
+ const { data: banner, loading: isBannerLoading } = useFetchBanner({
149
157
  context,
150
158
  bannerId,
151
159
  isCreation,
@@ -155,14 +163,32 @@ export default defineComponent({
155
163
 
156
164
  const isSubmitting = ref<boolean>(false);
157
165
  const values = ref<BannerFormValues>(
158
- convertBannerToFormValues(banner.value, zonesOptionList.value)
166
+ convertBannerToFormValues(banner.value, zonesOptionList.value)
159
167
  );
160
168
  const errors = ref<Record<string, string>>({});
161
169
 
162
170
  watch([banner, zones], () => {
163
171
  values.value = convertBannerToFormValues(
164
- banner.value,
165
- zonesOptionList.value
172
+ banner.value,
173
+ zonesOptionList.value
174
+ );
175
+ });
176
+
177
+ /** Zone Fields **/
178
+
179
+ const {
180
+ loading: isZoneFieldsLoading,
181
+ data: zoneFields,
182
+ } = useFetchZoneFields({ context, values });
183
+
184
+ watch(zoneFields, () => {
185
+ values.value.fields = (
186
+ zoneFields.value?.fields ?? []
187
+ ).map((fieldConfig, index) =>
188
+ universalFieldUtils.createFormField(
189
+ fieldConfig,
190
+ banner.value ? banner.value.fields[index].value ?? null : null
191
+ )
166
192
  );
167
193
  });
168
194
 
@@ -172,56 +198,59 @@ export default defineComponent({
172
198
  const body = convertBannerFormValuesToPayload(values.value);
173
199
 
174
200
  const requestPromise = isCreation.value
175
- ? createBanner(body)
176
- : updateBanner(bannerId.value, body);
201
+ ? createBanner(body)
202
+ : updateBanner(bannerId.value, body);
177
203
 
178
204
  requestPromise
179
- .then(({data}) => {
180
- errors.value = {};
181
-
182
- if (event.type === 'create') {
183
- context.root.$router.push(
184
- getBannersBannerFormUrl({bannerId: String(data.id)})
185
- );
186
- }
187
-
188
- if (event.type === 'create_exit' || event.type === 'save_exit') {
189
- context.root.$router.push(getBannersBannerListUrl());
190
- }
191
-
192
- if (event.type === 'create_create-another') {
193
- values.value = convertBannerToFormValues(
194
- null,
195
- zonesOptionList.value
196
- );
197
- }
198
-
199
- context.root.$toast({
200
- variant: 'success',
201
- title: context.root.$t('banners:success'),
202
- body: isCreation.value
203
- ? context.root.$t('banners:bannerWasSuccessfullyCreated')
204
- : context.root.$t('banners:bannerWasSuccessfullyUpdated'),
205
- });
206
- })
207
- .catch((error) => {
208
- console.error(error);
209
- errors.value = convertRequestErrorToMap(error);
210
- context.root.$toast({
211
- variant: 'danger',
212
- title: context.root.$t('banners:error'),
213
- body: isCreation.value
214
- ? context.root.$t('banners:bannerCreationWasFailed')
215
- : context.root.$t('banners:bannerUpdateWasFailed'),
216
- });
217
- })
218
- .finally(() => {
219
- isSubmitting.value = false;
205
+ .then(({ data }) => {
206
+ errors.value = {};
207
+
208
+ if (event.type === 'create') {
209
+ context.root.$router.push(
210
+ getBannersBannerFormUrl({ bannerId: String(data.id) })
211
+ );
212
+ }
213
+
214
+ if (event.type === 'create_exit' || event.type === 'save_exit') {
215
+ context.root.$router.push(getBannersBannerListUrl());
216
+ }
217
+
218
+ if (event.type === 'create_create-another') {
219
+ values.value = convertBannerToFormValues(
220
+ null,
221
+ zonesOptionList.value
222
+ );
223
+ }
224
+
225
+ context.root.$toast({
226
+ variant: 'success',
227
+ title: context.root.$t('banners:success'),
228
+ body: isCreation.value
229
+ ? context.root.$t('banners:bannerWasSuccessfullyCreated')
230
+ : context.root.$t('banners:bannerWasSuccessfullyUpdated'),
231
+ });
232
+ })
233
+ .catch((error) => {
234
+ console.error(error);
235
+ errors.value = convertRequestErrorToMap(error);
236
+ context.root.$toast({
237
+ variant: 'danger',
238
+ title: context.root.$t('banners:error'),
239
+ body: isCreation.value
240
+ ? context.root.$t('banners:bannerCreationWasFailed')
241
+ : context.root.$t('banners:bannerUpdateWasFailed'),
220
242
  });
243
+ })
244
+ .finally(() => {
245
+ isSubmitting.value = false;
246
+ });
221
247
  };
222
248
 
223
249
  const isContentLoading = computed<boolean>(
224
- () => isBannerLoading.value || isZonesLoading.value
250
+ () =>
251
+ isBannerLoading.value ||
252
+ isZonesLoading.value ||
253
+ isZoneFieldsLoading.value
225
254
  );
226
255
 
227
256
  return {