adminizer 4.4.0-build.153 → 4.4.0-build.155

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.
@@ -73,7 +73,8 @@ export class MediaManagerAdapter {
73
73
  "No variants found": "",
74
74
  "Actions": "",
75
75
  "Yes": "",
76
- "No": ""
76
+ "No": "",
77
+ 'File type are not supported': ""
77
78
  };
78
79
  return Object.fromEntries(Object.keys(messages).map(key => [key, req.i18n.__(key)]));
79
80
  }
@@ -33,6 +33,7 @@ export interface MediaManagerWidgetClientItem extends MediaManagerWidgetItem {
33
33
  */
34
34
  mimeType: string;
35
35
  variants: MediaManagerItem[];
36
+ filename: string;
36
37
  url?: string;
37
38
  }
38
39
  export interface MediaManagerWidgetJSON {
@@ -92,6 +92,7 @@ export class DefaultMediaManager extends AbstractMediaManager {
92
92
  widgetItems.push({
93
93
  id: file[fieldName].id,
94
94
  mimeType: file[fieldName].mimeType,
95
+ filename: file[fieldName].filename,
95
96
  url: file[fieldName].url,
96
97
  variants: []
97
98
  });
@@ -8,7 +8,7 @@ export declare class WaterlineModel<T> extends AbstractModel<T> {
8
8
  /** Custom populate all function */
9
9
  private populateAll;
10
10
  protected _findOne(criteria: Partial<T>): Promise<T | null>;
11
- protected _find(criteria?: Partial<T>, options?: FindOptions): Promise<T[]>;
11
+ protected _find(criteria?: Partial<any>, options?: FindOptions): Promise<T[]>;
12
12
  protected _updateOne(criteria: Partial<T>, data: Partial<T>): Promise<T | null>;
13
13
  protected _update(criteria: Partial<T>, data: Partial<T>): Promise<T[]>;
14
14
  protected _destroyOne(criteria: Partial<T>): Promise<T | null>;
@@ -36,9 +36,20 @@ export class WaterlineModel extends AbstractModel {
36
36
  // Hack
37
37
  const attributes = this.model.attributes;
38
38
  const associations = Object.keys(attributes).filter(attr => attributes[attr].model || attributes[attr].collection);
39
- //@ts-ignore
40
- if (associations.includes(criteria?.sort?.split(' ')[0]) && JSON.stringify(criteria.where) === '{}')
41
- criteria.sort = undefined;
39
+ if (JSON.stringify(criteria.where) === '{}') {
40
+ let sortField;
41
+ if (criteria?.sort) {
42
+ if (typeof criteria.sort === 'string') {
43
+ sortField = criteria.sort.split(' ')[0];
44
+ }
45
+ else if (Array.isArray(criteria.sort)) {
46
+ sortField = criteria.sort[0];
47
+ }
48
+ }
49
+ if (sortField && associations.includes(sortField)) {
50
+ criteria.sort = undefined;
51
+ }
52
+ }
42
53
  let query = this.model.find(criteria);
43
54
  if (options.populate && Array.isArray(options.populate) && options.populate.length > 0) {
44
55
  options.populate.forEach(([field, populateCriteria]) => {
@@ -161,4 +161,29 @@ export declare abstract class AbstractNotificationService extends EventEmitter {
161
161
  * @returns {Promise<void>} A promise that resolves when the operation is complete.
162
162
  */
163
163
  markAllAsRead(userId: number): Promise<void>;
164
+ /**
165
+ * Returns the count of user notifications: all notifications or only unread ones.
166
+ * If unreadOnly is true, returns the number of unread notifications.
167
+ * If unreadOnly is false, returns the total number of notifications (read + unread) associated with the user.
168
+ *
169
+ * @param {number} userId - The ID of the user whose notification count is requested.
170
+ * @param {boolean} [unreadOnly=false] - If true, counts only unread notifications; if false, counts all notifications.
171
+ * @returns {Promise<number>} A promise that resolves with the number of matching notifications.
172
+ *
173
+ * @example
174
+ * const total = await service.getNotificationsCount(123); // all notifications
175
+ * const unreadCount = await service.getNotificationsCount(123, true); // only unread
176
+ */
177
+ getNotificationsCount(userId: number, unreadOnly?: boolean): Promise<number>;
178
+ /**
179
+ * Convenience method to get the number of unread notifications for a user.
180
+ * Equivalent to calling getNotificationsCount(userId, true).
181
+ *
182
+ * @param {number} userId - The ID of the user.
183
+ * @returns {Promise<number>} A promise that resolves with the number of unread notifications.
184
+ *
185
+ * @example
186
+ * const unread = await service.getUnreadNotificationsCount(123);
187
+ */
188
+ getUnreadNotificationsCount(userId: number): Promise<number>;
164
189
  }
@@ -350,4 +350,61 @@ export class AbstractNotificationService extends EventEmitter {
350
350
  Adminizer.log.error('Error marking all notifications as read:', error);
351
351
  }
352
352
  }
353
+ /**
354
+ * Returns the count of user notifications: all notifications or only unread ones.
355
+ * If unreadOnly is true, returns the number of unread notifications.
356
+ * If unreadOnly is false, returns the total number of notifications (read + unread) associated with the user.
357
+ *
358
+ * @param {number} userId - The ID of the user whose notification count is requested.
359
+ * @param {boolean} [unreadOnly=false] - If true, counts only unread notifications; if false, counts all notifications.
360
+ * @returns {Promise<number>} A promise that resolves with the number of matching notifications.
361
+ *
362
+ * @example
363
+ * const total = await service.getNotificationsCount(123); // all notifications
364
+ * const unreadCount = await service.getNotificationsCount(123, true); // only unread
365
+ */
366
+ async getNotificationsCount(userId, unreadOnly = false) {
367
+ try {
368
+ // Формируем условие фильтрации по статусу прочтения
369
+ const whereClause = { userId: userId };
370
+ if (unreadOnly) {
371
+ whereClause.read = false; // только непрочитанные
372
+ }
373
+ // если unreadOnly = false — получаем ВСЕ записи (и прочитанные, и нет)
374
+ // Получаем все user-notification связи
375
+ const userNotifications = await this.adminizer.modelHandler.model.get('usernotificationap')["_find"]({
376
+ where: whereClause
377
+ }, { populate: [['notificationId', {}]] });
378
+ if (userNotifications.length === 0)
379
+ return 0;
380
+ // Фильтруем по классу уведомлений через notificationId
381
+ const validNotificationIds = userNotifications
382
+ .map((un) => un.notificationId.id)
383
+ .filter((id) => id != null);
384
+ if (validNotificationIds.length === 0)
385
+ return 0;
386
+ // Пересчитываем только те, у которых notificationClass совпадает
387
+ return await this.adminizer.modelHandler.model.get('notificationap')["_count"]({
388
+ id: validNotificationIds,
389
+ notificationClass: this.notificationClass
390
+ });
391
+ }
392
+ catch (error) {
393
+ Adminizer.log.error(`Error counting ${unreadOnly ? 'unread' : 'all'} notifications:`, error);
394
+ return 0;
395
+ }
396
+ }
397
+ /**
398
+ * Convenience method to get the number of unread notifications for a user.
399
+ * Equivalent to calling getNotificationsCount(userId, true).
400
+ *
401
+ * @param {number} userId - The ID of the user.
402
+ * @returns {Promise<number>} A promise that resolves with the number of unread notifications.
403
+ *
404
+ * @example
405
+ * const unread = await service.getUnreadNotificationsCount(123);
406
+ */
407
+ async getUnreadNotificationsCount(userId) {
408
+ return this.getNotificationsCount(userId, true);
409
+ }
353
410
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "adminizer",
3
3
  "type": "module",
4
- "version": "4.4.0-build.153",
4
+ "version": "4.4.0-build.155",
5
5
  "main": "index.js",
6
6
  "exports": {
7
7
  ".": "./index.js",
@@ -3,10 +3,10 @@
3
3
  "Actions": "Actions",
4
4
  "All": "All",
5
5
  "Are you sure?": "Are you sure?",
6
- "Back": "Back",
7
- "Back to Login": "Back to Login",
8
- "Additional login page": "Additional login page",
9
- "Clean": "Clean",
6
+ "Back": "Back",
7
+ "Back to Login": "Back to Login",
8
+ "Additional login page": "Additional login page",
9
+ "Clean": "Clean",
10
10
  "Confirm Password": "Confirm Password",
11
11
  "Confirm Password*": "Confirm Password*",
12
12
  "Create": "Create",
@@ -139,5 +139,6 @@
139
139
  "W x H (orig.)": "W x H (orig.)",
140
140
  "Sizes/Ver": "Sizes/Ver",
141
141
  "Locales": "Locales",
142
- "Date": "Date"
143
- }
142
+ "Date": "Date",
143
+ "File type are not supported": "File type are not supported"
144
+ }
@@ -119,5 +119,6 @@
119
119
  "W x H (orig.)": "W x H (orig.)",
120
120
  "Sizes/Ver": "Sizes/Ver",
121
121
  "Locales": "Locales",
122
- "Leave this field empty if you don't want to change the password": "Оставьте это поле пустым, если не хотите менять пароль"
122
+ "Leave this field empty if you don't want to change the password": "Оставьте это поле пустым, если не хотите менять пароль",
123
+ "File type are not supported": "Этот файл не поддерживается"
123
124
  }