@7365admin1/layer-common 3.0.6 → 3.0.8

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.
@@ -1,406 +1,496 @@
1
1
  <template>
2
- <v-card width="100%" :loading="loading.processing" :disabled="loading.processing || getAnnouncementByIdPending"
3
- :key="`form-key-${formKey}`">
4
- <v-toolbar>
5
- <v-row no-gutters class="fill-height px-6" align="center">
6
- <span class="font-weight-bold text-h5 text-capitalize">
7
- {{ prop.mode }} Announcement
8
- </span>
2
+ <v-card
3
+ width="100%"
4
+ :loading="loading.processing"
5
+ :disabled="loading.processing || getAnnouncementByIdPending"
6
+ :key="`form-key-${formKey}`"
7
+ >
8
+ <v-toolbar>
9
+ <v-row no-gutters class="fill-height px-6" align="center">
10
+ <span class="font-weight-bold text-h5 text-capitalize">
11
+ {{ prop.mode }} Announcement
12
+ </span>
13
+ </v-row>
14
+ </v-toolbar>
15
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-6">
16
+ <v-form
17
+ ref="formRef"
18
+ v-model="validForm"
19
+ :disabled="loading.processing"
20
+ @click="errorMessage = ''"
21
+ >
22
+ <v-row no-gutters>
23
+ <v-col cols="12" class="font-weight-bold text-h6 mb-2"
24
+ >General Information</v-col
25
+ >
26
+ <v-col cols="12">
27
+ <InputLabel title="Recipient(s)" />
28
+ <v-select
29
+ v-model="bulletinForm.recipients"
30
+ :items="recipientList"
31
+ multiple
32
+ item-value="value"
33
+ placeholder="Select recipients"
34
+ >
35
+ <template v-slot:prepend-item>
36
+ <v-list-item
37
+ title="Select All"
38
+ @click="handleSelectAll"
39
+ class=""
40
+ >
41
+ <template v-slot:prepend>
42
+ <v-checkbox-btn
43
+ :color="
44
+ selectedSomeRecipients ? 'indigo-darken-4' : undefined
45
+ "
46
+ :indeterminate="
47
+ selectedSomeRecipients && !selectedAllRecipients
48
+ "
49
+ :model-value="selectedAllRecipients"
50
+ ></v-checkbox-btn>
51
+ </template>
52
+ </v-list-item>
53
+ </template>
54
+
55
+ <template v-slot:selection="{ item, index }">
56
+ <v-chip :text="item.title" class="py-2">
57
+ <template #append>
58
+ <v-btn
59
+ icon="mdi-close"
60
+ size="small"
61
+ density="compact"
62
+ color="transparent"
63
+ flat
64
+ class="ml-3 text-black"
65
+ @mousedown.stop
66
+ @click.stop="handleRemoveRecipient(item)"
67
+ />
68
+ </template>
69
+ </v-chip>
70
+ </template>
71
+ </v-select>
72
+ </v-col>
73
+ <v-col cols="12">
74
+ <InputLabel class="text-capitalize" title="Title" required />
75
+ <v-text-field
76
+ v-model.trim="bulletinForm.title"
77
+ density="compact"
78
+ :rules="[requiredRule]"
79
+ />
80
+ </v-col>
81
+ <v-col cols="12" class="my-4">
82
+ <InputLabel class="text-capitalize" title="Content" required />
83
+ <Editor v-model="bulletinForm.content" :readOnly="false" />
84
+ </v-col>
85
+
86
+ <v-col cols="12">
87
+ <v-checkbox
88
+ v-model="bulletinForm.noExpiration"
89
+ label="No Expiration"
90
+ />
91
+ </v-col>
92
+
93
+ <v-col cols="12">
94
+ <InputLabel class="text-capitalize" title="Start Date" required />
95
+ <InputDateTimePicker
96
+ v-model:utc="bulletinForm.startDate"
97
+ :rules="[validStartDateRule]"
98
+ />
99
+ </v-col>
100
+
101
+ <v-col cols="12" v-if="!bulletinForm.noExpiration">
102
+ <InputLabel class="text-capitalize" title="End Date" required />
103
+ <InputDateTimePicker
104
+ v-model:utc="bulletinForm.endDate"
105
+ :rules="[validExpiryDateRule]"
106
+ :min="today"
107
+ />
108
+ </v-col>
109
+
110
+ <v-col cols="12">
111
+ <v-col cols="12" class="font-weight-bold text-h6"
112
+ >Attachments</v-col
113
+ >
114
+ <v-col cols="12">
115
+ <v-btn
116
+ text="Upload"
117
+ class="text-capitalize"
118
+ color="primary-button"
119
+ variant="outlined"
120
+ min-width="150"
121
+ @click="handleUpload"
122
+ :loading="loading.uploading"
123
+ />
124
+ </v-col>
125
+ <v-row no-gutters class="px-5">
126
+ <v-col cols="12">Videos ({{ videosArray.length }})</v-col>
127
+ <AttachmentList
128
+ :list="videosArray"
129
+ show-checkbox
130
+ @set-preview="handleSetPreview"
131
+ @remove="handleRemoveFile"
132
+ />
133
+
134
+ <v-col cols="12" class="mt-5"
135
+ >Photos ({{ photosArray.length }})</v-col
136
+ >
137
+ <AttachmentList
138
+ :list="photosArray"
139
+ show-checkbox
140
+ @set-preview="handleSetPreview"
141
+ @remove="handleRemoveFile"
142
+ />
143
+
144
+ <v-col cols="12" class="mt-5"
145
+ >Files ({{ filesArray.length }})</v-col
146
+ >
147
+ <AttachmentList
148
+ :list="filesArray"
149
+ show-checkbox
150
+ @set-preview="handleSetPreview"
151
+ @remove="handleRemoveFile"
152
+ />
9
153
  </v-row>
10
- </v-toolbar>
11
- <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-6">
12
- <v-form ref="formRef" v-model="validForm" :disabled="loading.processing" @click="errorMessage = ''">
13
- <v-row no-gutters>
14
- <v-col cols="12" class="font-weight-bold text-h6 mb-2">General Information</v-col>
15
- <v-col cols="12">
16
- <InputLabel title="Recipient(s)" />
17
- <v-select v-model="bulletinForm.recipients" :items="recipientList" multiple item-value="value"
18
- placeholder="Select recipients">
19
- <template v-slot:prepend-item>
20
- <v-list-item title="Select All" @click="handleSelectAll" class="">
21
- <template v-slot:prepend>
22
- <v-checkbox-btn :color="selectedSomeRecipients ? 'indigo-darken-4' : undefined"
23
- :indeterminate="selectedSomeRecipients && !selectedAllRecipients"
24
- :model-value="selectedAllRecipients"></v-checkbox-btn>
25
- </template>
26
- </v-list-item>
27
- </template>
28
-
29
- <template v-slot:selection="{ item, index }">
30
- <v-chip :text="item.title" class="py-2">
31
- <template #append>
32
- <v-btn icon="mdi-close" size="small" density="compact" color="transparent" flat
33
- class="ml-3 text-black" @mousedown.stop
34
- @click.stop="handleRemoveRecipient(item)" />
35
- </template>
36
- </v-chip>
37
- </template>
38
- </v-select>
39
- </v-col>
40
- <v-col cols="12">
41
- <InputLabel class="text-capitalize" title="Title" required />
42
- <v-text-field v-model.trim="bulletinForm.title" density="compact" :rules="[requiredRule]" />
43
- </v-col>
44
- <v-col cols="12" class="my-4">
45
- <InputLabel class="text-capitalize" title="Content" required />
46
- <Editor v-model="bulletinForm.content" />
47
- </v-col>
48
-
49
- <v-col cols="12">
50
- <v-checkbox v-model="bulletinForm.noExpiration" label="No Expiration" />
51
- </v-col>
52
-
53
- <v-col cols="12">
54
- <InputLabel class="text-capitalize" title="Start Date" required />
55
- <InputDateTimePicker v-model:utc="bulletinForm.startDate" :rules="[validStartDateRule]"/>
56
- </v-col>
57
-
58
- <v-col cols="12" v-if="!bulletinForm.noExpiration">
59
- <InputLabel class="text-capitalize" title="End Date" required />
60
- <InputDateTimePicker v-model:utc="bulletinForm.endDate" :rules="[validExpiryDateRule]" :min="today" />
61
- </v-col>
62
-
63
- <v-col cols="12">
64
- <v-col cols="12" class="font-weight-bold text-h6">Attachments</v-col>
65
- <v-col cols="12">
66
- <v-btn text="Upload" class="text-capitalize" color="primary-button" variant="outlined"
67
- min-width="150" @click="handleUpload" :loading="loading.uploading" />
68
- </v-col>
69
- <v-row no-gutters class="px-5">
70
- <v-col cols="12">Videos ({{ videosArray.length }})</v-col>
71
- <AttachmentList :list="videosArray" show-checkbox @set-preview="handleSetPreview"
72
- @remove="handleRemoveFile" />
73
-
74
- <v-col cols="12" class="mt-5">Photos ({{ photosArray.length }})</v-col>
75
- <AttachmentList :list="photosArray" show-checkbox @set-preview="handleSetPreview"
76
- @remove="handleRemoveFile" />
77
-
78
- <v-col cols="12" class="mt-5">Files ({{ filesArray.length }})</v-col>
79
- <AttachmentList :list="filesArray" show-checkbox @set-preview="handleSetPreview"
80
- @remove="handleRemoveFile" />
81
- </v-row>
82
- </v-col>
83
- </v-row>
84
- </v-form>
85
- </v-card-text>
86
-
87
- <v-row no-gutters class="w-100" v-if="errorMessage">
88
- <p class="text-error w-100 text-center text-subtitle-2">{{ errorMessage }}</p>
154
+ </v-col>
89
155
  </v-row>
90
- <v-toolbar density="compact">
91
- <v-row no-gutters>
92
- <v-col cols="6">
93
- <v-btn tile block variant="text" class="text-none" size="48" @click="close" text="Close" />
94
- </v-col>
95
- <v-col cols="6">
96
- <v-btn tile block variant="flat" color="primary-button" class="text-none" size="48"
97
- :text="prop.mode === 'add' ? 'Add Bulletin' : 'Save'"
98
- :disabled="loading.processing || !validForm" @click="onSubmit" />
99
- </v-col>
100
- </v-row>
101
- </v-toolbar>
102
- </v-card>
156
+ </v-form>
157
+ </v-card-text>
158
+
159
+ <v-row no-gutters class="w-100" v-if="errorMessage">
160
+ <p class="text-error w-100 text-center text-subtitle-2">
161
+ {{ errorMessage }}
162
+ </p>
163
+ </v-row>
164
+ <v-toolbar density="compact">
165
+ <v-row no-gutters>
166
+ <v-col cols="6">
167
+ <v-btn
168
+ tile
169
+ block
170
+ variant="text"
171
+ class="text-none"
172
+ size="48"
173
+ @click="close"
174
+ text="Close"
175
+ />
176
+ </v-col>
177
+ <v-col cols="6">
178
+ <v-btn
179
+ tile
180
+ block
181
+ variant="flat"
182
+ color="primary-button"
183
+ class="text-none"
184
+ size="48"
185
+ :text="prop.mode === 'add' ? 'Add Bulletin' : 'Save'"
186
+ :disabled="loading.processing || !validForm"
187
+ @click="onSubmit"
188
+ />
189
+ </v-col>
190
+ </v-row>
191
+ </v-toolbar>
192
+ </v-card>
103
193
  </template>
104
194
 
105
195
  <script setup lang="ts">
106
196
  const today = computed(() => {
107
- const d = new Date()
108
- const yyyy = d.getFullYear()
109
- const mm = String(d.getMonth() + 1).padStart(2, '0')
110
- const dd = String(d.getDate()).padStart(2, '0')
111
- console.log('${yyyy}-${mm}-${dd}',`${yyyy}-${mm}-${dd}`)
112
- return `${yyyy}-${mm}-${dd}`
113
- })
197
+ const d = new Date();
198
+ const yyyy = d.getFullYear();
199
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
200
+ const dd = String(d.getDate()).padStart(2, "0");
201
+ console.log("${yyyy}-${mm}-${dd}", `${yyyy}-${mm}-${dd}`);
202
+ return `${yyyy}-${mm}-${dd}`;
203
+ });
114
204
 
115
205
  const prop = defineProps({
116
- siteId: {
117
- type: String,
118
- required: true
119
- },
120
- mode: {
121
- type: String as PropType<'edit' | 'add'>,
122
- default: "add"
123
- },
124
- activeId: {
125
- type: String,
126
- default: ""
127
- }
128
- })
206
+ siteId: {
207
+ type: String,
208
+ required: true,
209
+ },
210
+ mode: {
211
+ type: String as PropType<"edit" | "add">,
212
+ default: "add",
213
+ },
214
+ activeId: {
215
+ type: String,
216
+ default: "",
217
+ },
218
+ });
129
219
 
130
- const { requiredRule } = useUtils()
131
- const { addFile, urlToFile, getFileUrl } = useFile()
132
- const { add, update, recipientList, getBulletinById } = useBulletin()
220
+ const { requiredRule } = useUtils();
221
+ const { addFile, urlToFile, getFileUrl } = useFile();
222
+ const { add, update, recipientList, getBulletinById } = useBulletin();
133
223
 
134
224
  const bulletinForm = reactive<TCreateAnnouncementPayload>({
135
- recipients: [],
136
- title: "",
137
- content: "",
138
- site: prop.siteId,
139
- noExpiration: false,
140
- startDate: "",
141
- endDate: "",
142
- file: []
143
- })
144
-
145
- const emit = defineEmits(['close', 'done'])
225
+ recipients: [],
226
+ title: "",
227
+ content: "",
228
+ site: prop.siteId,
229
+ noExpiration: false,
230
+ startDate: "",
231
+ endDate: "",
232
+ file: [],
233
+ });
234
+
235
+ const emit = defineEmits(["close", "done"]);
146
236
 
147
237
  const validForm = ref(false);
148
- const formRef = ref<HTMLFormElement>()
149
- const formKey = ref(0)
150
- const errorMessage = ref('')
238
+ const formRef = ref<HTMLFormElement>();
239
+ const formKey = ref(0);
240
+ const errorMessage = ref("");
151
241
 
152
- const rawFileArray = ref<{ file: File, _id: string, preview: boolean }[]>([])
242
+ const rawFileArray = ref<{ file: File; _id: string; preview: boolean }[]>([]);
153
243
 
154
244
  const loading = reactive({
155
- uploading: false,
156
- processing: false
157
- })
158
-
159
-
160
-
161
-
162
- const photosArray = getByType('image/')
163
- const videosArray = getByType('video/')
164
- const filesArray = getByType('other')
165
-
166
- function getByType(prefix: 'image/' | 'video/' | 'other') {
167
- return computed(() => {
168
- return rawFileArray.value.filter(x => {
169
- const type = x?.file?.type
170
- if (typeof type !== 'string') return false
245
+ uploading: false,
246
+ processing: false,
247
+ });
171
248
 
172
- if (prefix === 'other') {
173
- return !type.startsWith('image/') && !type.startsWith('video/')
174
- }
175
- return type.startsWith(prefix)
176
- })
177
- })
249
+ const photosArray = getByType("image/");
250
+ const videosArray = getByType("video/");
251
+ const filesArray = getByType("other");
252
+
253
+ function getByType(prefix: "image/" | "video/" | "other") {
254
+ return computed(() => {
255
+ return rawFileArray.value.filter((x) => {
256
+ const type = x?.file?.type;
257
+ if (typeof type !== "string") return false;
258
+
259
+ if (prefix === "other") {
260
+ return !type.startsWith("image/") && !type.startsWith("video/");
261
+ }
262
+ return type.startsWith(prefix);
263
+ });
264
+ });
178
265
  }
179
266
 
180
-
181
267
  const {
182
- data: getAnnouncementByIdReq,
183
- refresh: getAnnouncementByIdRefresh,
184
- pending: getAnnouncementByIdPending,
185
- error: getAnnouncementByIdError
268
+ data: getAnnouncementByIdReq,
269
+ refresh: getAnnouncementByIdRefresh,
270
+ pending: getAnnouncementByIdPending,
271
+ error: getAnnouncementByIdError,
186
272
  } = await useLazyAsyncData(
187
- `get-announcements-${prop.activeId}`,
188
- () => {
189
- if(prop.mode === 'add') return Promise.resolve(null) // ignore when add mode
190
- const id = prop?.activeId
191
- if (!id) {
192
- throw new Error("Invalid activeId or mode. Cannot fetch announcement.")
193
- }
194
- return getBulletinById(id)
195
- },
196
- {
197
- watch: [() => prop.activeId],
273
+ `get-announcements-${prop.activeId}`,
274
+ () => {
275
+ if (prop.mode === "add") return Promise.resolve(null); // ignore when add mode
276
+ const id = prop?.activeId;
277
+ if (!id) {
278
+ throw new Error("Invalid activeId or mode. Cannot fetch announcement.");
198
279
  }
280
+ return getBulletinById(id);
281
+ },
282
+ {
283
+ watch: [() => prop.activeId],
284
+ }
199
285
  );
200
286
 
201
- watch([getAnnouncementByIdReq, getAnnouncementByIdError], async ([newData, newError]) => {
287
+ watch(
288
+ [getAnnouncementByIdReq, getAnnouncementByIdError],
289
+ async ([newData, newError]) => {
202
290
  if (newData) {
203
- bulletinForm.title = newData?.title
204
- bulletinForm.content = newData?.content
205
- bulletinForm.noExpiration = newData?.noExpiration
206
- bulletinForm.startDate = newData?.startDate
207
- bulletinForm.endDate = newData?.endDate
208
- bulletinForm.file = newData?.file
209
- bulletinForm.recipients = newData?.recipients
210
-
211
- // mapped rawFileArray
212
-
213
- rawFileArray.value = await Promise.all(newData?.file?.map(async (item: TAnnouncementFile) => {
214
- const fileUrl = getFileUrl(item?._id)
215
- const file = await urlToFile(fileUrl, item?.name)
216
- return {
217
- file,
218
- _id: item?._id,
219
- preview: item.preview ?? false
220
-
221
- }
222
- }))
223
-
291
+ bulletinForm.title = newData?.title;
292
+ bulletinForm.content = newData?.content;
293
+ bulletinForm.noExpiration = newData?.noExpiration;
294
+ bulletinForm.startDate = newData?.startDate;
295
+ bulletinForm.endDate = newData?.endDate;
296
+ bulletinForm.file = newData?.file;
297
+ bulletinForm.recipients = newData?.recipients;
298
+
299
+ // mapped rawFileArray
300
+
301
+ rawFileArray.value = await Promise.all(
302
+ newData?.file?.map(async (item: TAnnouncementFile) => {
303
+ const fileUrl = getFileUrl(item?._id);
304
+ const file = await urlToFile(fileUrl, item?.name);
305
+ return {
306
+ file,
307
+ _id: item?._id,
308
+ preview: item.preview ?? false,
309
+ };
310
+ })
311
+ );
224
312
  }
225
313
 
226
314
  if (newError) {
227
- const errMessage = newError?.message
228
- console.error("Failed to load announcement:", errMessage)
229
- errorMessage.value = errMessage || ("Cannot fetch announcement" + newError)
315
+ const errMessage = newError?.message;
316
+ console.error("Failed to load announcement:", errMessage);
317
+ errorMessage.value = errMessage || "Cannot fetch announcement" + newError;
230
318
  }
231
- });
319
+ }
320
+ );
232
321
 
233
- const allRecipientValues: TAnnouncementRecipients[] = recipientList.map(r => r.value)
322
+ const allRecipientValues: TAnnouncementRecipients[] = recipientList.map(
323
+ (r) => r.value
324
+ );
234
325
 
235
326
  const selectedAllRecipients = computed(() => {
236
- return bulletinForm.recipients.length === allRecipientValues.length &&
237
- bulletinForm.recipients.every((x) => allRecipientValues.includes(x))
238
- })
239
-
240
-
327
+ return (
328
+ bulletinForm.recipients.length === allRecipientValues.length &&
329
+ bulletinForm.recipients.every((x) => allRecipientValues.includes(x))
330
+ );
331
+ });
241
332
 
242
333
  const selectedSomeRecipients = computed(() => {
243
- return bulletinForm.recipients?.length > 0
244
- })
245
-
246
-
334
+ return bulletinForm.recipients?.length > 0;
335
+ });
247
336
 
248
337
  function handleRemoveRecipient(item: any) {
249
- const recipientVal = item?.value
250
- const filtered = bulletinForm.recipients.filter(x => x !== recipientVal)
251
- bulletinForm.recipients = filtered
338
+ const recipientVal = item?.value;
339
+ const filtered = bulletinForm.recipients.filter((x) => x !== recipientVal);
340
+ bulletinForm.recipients = filtered;
252
341
  }
253
342
 
254
343
  function handleSelectAll() {
255
- if (selectedAllRecipients.value) {
256
- bulletinForm.recipients = []
257
- } else {
258
- bulletinForm.recipients = allRecipientValues
259
- }
344
+ if (selectedAllRecipients.value) {
345
+ bulletinForm.recipients = [];
346
+ } else {
347
+ bulletinForm.recipients = allRecipientValues;
348
+ }
260
349
  }
261
350
 
262
-
263
351
  function validStartDateRule(value: string) {
264
- if (!value) {
265
- return 'Start Date is required';
266
- }
352
+ if (!value) {
353
+ return "Start Date is required";
354
+ }
267
355
 
268
- return true;
356
+ return true;
269
357
  }
270
358
 
271
359
  function validExpiryDateRule() {
272
- const noExpiration = bulletinForm.noExpiration
273
- if (noExpiration) return true;
274
- const startDateISO = bulletinForm.startDate;
275
- const endDateISO = bulletinForm.endDate;
276
- if (!endDateISO) {
277
- return 'End Date is required';
278
- }
279
-
280
- if (endDateISO && startDateISO) {
281
- const expiry = new Date(endDateISO);
282
- const start = new Date(startDateISO as string);
283
- return expiry >= start || 'End date must be equal or later than start date';
284
- }
285
- return true;
360
+ const noExpiration = bulletinForm.noExpiration;
361
+ if (noExpiration) return true;
362
+ const startDateISO = bulletinForm.startDate;
363
+ const endDateISO = bulletinForm.endDate;
364
+ if (!endDateISO) {
365
+ return "End Date is required";
366
+ }
367
+
368
+ if (endDateISO && startDateISO) {
369
+ const expiry = new Date(endDateISO);
370
+ const start = new Date(startDateISO as string);
371
+ return expiry >= start || "End date must be equal or later than start date";
372
+ }
373
+ return true;
286
374
  }
287
375
 
288
- watch(() => bulletinForm.noExpiration, (newVal) => {
376
+ watch(
377
+ () => bulletinForm.noExpiration,
378
+ (newVal) => {
289
379
  if (newVal === true) {
290
- setTimeout(() => {
291
- bulletinForm.endDate = ""
292
- }, 100)
380
+ setTimeout(() => {
381
+ bulletinForm.endDate = "";
382
+ }, 100);
293
383
  }
294
- })
295
-
384
+ }
385
+ );
296
386
 
297
387
  async function onSubmit() {
298
- errorMessage.value = ""
299
- await formRef.value?.validate()
300
- if (!validForm.value) {
301
- errorMessage.value = "Please complete required fields"
302
- return;
303
- }
304
-
305
- let payload: Partial<TCreateAnnouncementPayload> = {}
306
- if (prop.mode === 'add') {
307
- payload = { ...bulletinForm }
308
- } else if (prop.mode === 'edit') {
309
- const { site, ...rest } = bulletinForm
310
- payload = rest
388
+ errorMessage.value = "";
389
+ await formRef.value?.validate();
390
+ if (!validForm.value) {
391
+ errorMessage.value = "Please complete required fields";
392
+ return;
393
+ }
394
+
395
+ let payload: Partial<TCreateAnnouncementPayload> = {};
396
+ if (prop.mode === "add") {
397
+ payload = { ...bulletinForm };
398
+ } else if (prop.mode === "edit") {
399
+ const { site, ...rest } = bulletinForm;
400
+ payload = rest;
401
+ }
402
+
403
+ try {
404
+ loading.processing = true;
405
+ if (prop.mode === "add") {
406
+ await add(payload);
407
+ } else if (prop.mode === "edit") {
408
+ const id = prop.activeId;
409
+ if (!id) throw new Error("Missing activeId prop");
410
+ await update(prop.activeId, payload);
411
+ } else {
412
+ throw new Error("Missing mode prop!");
311
413
  }
312
414
 
313
- try {
314
- loading.processing = true
315
- if (prop.mode === 'add') {
316
- await add(payload)
317
- } else if (prop.mode === 'edit') {
318
- const id = prop.activeId
319
- if (!id) throw new Error('Missing activeId prop')
320
- await update(prop.activeId, payload)
321
- } else {
322
- throw new Error('Missing mode prop!')
323
- }
324
-
325
- emit('done')
326
-
327
- } catch (error: any) {
328
- const err = error?.data?.message
329
- errorMessage.value = err || `Failed to ${prop.mode === 'add' ? 'add' : 'update'}. Please try again.`;
330
-
331
- } finally {
332
- loading.processing = false
333
- }
415
+ emit("done");
416
+ } catch (error: any) {
417
+ const err = error?.data?.message;
418
+ errorMessage.value =
419
+ err ||
420
+ `Failed to ${prop.mode === "add" ? "add" : "update"}. Please try again.`;
421
+ } finally {
422
+ loading.processing = false;
423
+ }
334
424
  }
335
425
 
336
426
  function close() {
337
- emit('close')
427
+ emit("close");
338
428
  }
339
429
 
340
430
  function handleUpload() {
341
-
342
- const input = document.createElement("input")
343
- input.type = "file"
344
- input.multiple = true
345
-
346
- input.onchange = async (event: Event) => {
347
- const target = event.target as HTMLInputElement
348
- const filesArray = target.files || []
349
- const MAX_SIZE = 25 * 1024 * 1024 // 25MB
350
- for (const file of filesArray) {
351
- loading.uploading = true
352
-
353
- if (file.size > MAX_SIZE) {
354
- errorMessage.value = `Skipping File "${file.name}", exceeds 25MB limit.`
355
- continue
356
- }
357
-
358
- try {
359
- const res = await addFile(file)
360
- const fileId = res?.id
361
- const fileName = file?.name || `file-${fileId}`
362
- if (fileId) {
363
- rawFileArray.value.push({ file, _id: fileId, preview: false })
364
- // bulletinForm.file.push({ name: fileName, preview: false, _id: fileId })
365
- }
366
- }
367
- catch (err: any) {
368
- console.error("Upload failed:", err)
369
- const message = err?.data?.message
370
- errorMessage.value = "Failed to upload image. Please try again."
371
- } finally {
372
- loading.uploading = false
373
- }
374
-
431
+ const input = document.createElement("input");
432
+ input.type = "file";
433
+ input.multiple = true;
434
+
435
+ input.onchange = async (event: Event) => {
436
+ const target = event.target as HTMLInputElement;
437
+ const filesArray = target.files || [];
438
+ const MAX_SIZE = 25 * 1024 * 1024; // 25MB
439
+ for (const file of filesArray) {
440
+ loading.uploading = true;
441
+
442
+ if (file.size > MAX_SIZE) {
443
+ errorMessage.value = `Skipping File "${file.name}", exceeds 25MB limit.`;
444
+ continue;
445
+ }
446
+
447
+ try {
448
+ const res = await addFile(file);
449
+ const fileId = res?.id;
450
+ const fileName = file?.name || `file-${fileId}`;
451
+ if (fileId) {
452
+ rawFileArray.value.push({ file, _id: fileId, preview: false });
453
+ // bulletinForm.file.push({ name: fileName, preview: false, _id: fileId })
375
454
  }
455
+ } catch (err: any) {
456
+ console.error("Upload failed:", err);
457
+ const message = err?.data?.message;
458
+ errorMessage.value = "Failed to upload image. Please try again.";
459
+ } finally {
460
+ loading.uploading = false;
461
+ }
376
462
  }
377
- input.click()
463
+ };
464
+ input.click();
378
465
  }
379
466
 
380
467
  function handleSetPreview(fileId: string) {
381
- rawFileArray.value.forEach(item => {
382
- if (item._id !== fileId) {
383
- item.preview = false
384
- }
385
- })
468
+ rawFileArray.value.forEach((item) => {
469
+ if (item._id !== fileId) {
470
+ item.preview = false;
471
+ }
472
+ });
386
473
  }
387
474
 
388
475
  function handleRemoveFile(fileId: string) {
389
- rawFileArray.value = rawFileArray.value.filter(item => item?._id !== fileId)
476
+ rawFileArray.value = rawFileArray.value.filter(
477
+ (item) => item?._id !== fileId
478
+ );
390
479
  }
391
480
 
392
- watch(rawFileArray, (newArr) => {
393
- bulletinForm.file = newArr.map(item => {
394
- return {
395
- _id: item?._id,
396
- name: item?.file?.name,
397
- preview: item?.preview,
398
- }
399
- })
400
-
401
- }, { deep: true })
402
-
403
-
481
+ watch(
482
+ rawFileArray,
483
+ (newArr) => {
484
+ bulletinForm.file = newArr.map((item) => {
485
+ return {
486
+ _id: item?._id,
487
+ name: item?.file?.name,
488
+ preview: item?.preview,
489
+ };
490
+ });
491
+ },
492
+ { deep: true }
493
+ );
404
494
  </script>
405
495
 
406
- <style scoped></style>
496
+ <style scoped></style>