fontastic 1.0.0 → 1.0.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 (47) hide show
  1. package/.github/workflows/macos.yml +0 -7
  2. package/.github/workflows/ubuntu.yml +0 -7
  3. package/.github/workflows/windows.yml +0 -7
  4. package/README.md +1 -0
  5. package/app/core/ConnectionManager.js +7 -1
  6. package/app/core/ConnectionManager.ts +11 -3
  7. package/app/core/MessageHandler.js +24 -0
  8. package/app/core/MessageHandler.ts +29 -0
  9. package/app/core/menu/templates/DarwinTemplate.js +15 -0
  10. package/app/core/menu/templates/DarwinTemplate.ts +15 -0
  11. package/app/core/menu/templates/SystemTemplate.js +15 -0
  12. package/app/core/menu/templates/SystemTemplate.ts +15 -0
  13. package/app/database/entity/SmartCollection.schema.js +66 -0
  14. package/app/database/entity/SmartCollection.schema.ts +39 -0
  15. package/app/database/entity/index.js +1 -0
  16. package/app/database/entity/index.ts +2 -1
  17. package/app/database/repository/SmartCollection.repository.js +47 -0
  18. package/app/database/repository/SmartCollection.repository.ts +30 -0
  19. package/app/database/repository/Store.repository.js +107 -0
  20. package/app/database/repository/Store.repository.ts +106 -0
  21. package/app/database/repository/index.js +1 -0
  22. package/app/database/repository/index.ts +2 -1
  23. package/app/enums/ChannelType.js +5 -0
  24. package/app/enums/ChannelType.ts +6 -0
  25. package/app/enums/StorageType.js +1 -0
  26. package/app/enums/StorageType.ts +1 -0
  27. package/app/package.json +1 -1
  28. package/app/types/FontMetrics.js +3 -0
  29. package/app/types/SmartCollection.js +3 -0
  30. package/app/types/SmartCollection.ts +5 -0
  31. package/app/types/index.js +2 -0
  32. package/app/types/index.ts +1 -0
  33. package/package.json +1 -1
  34. package/src/app/core/services/database/database.service.ts +70 -1
  35. package/src/app/core/services/message/message.service.ts +23 -0
  36. package/src/app/core/services/presentation/presentation.service.ts +47 -0
  37. package/src/app/layout/header/header.component.html +1 -1
  38. package/src/app/layout/layout.component.html +1 -1
  39. package/src/app/layout/navigation/navigation.component.html +70 -4
  40. package/src/app/layout/navigation/navigation.component.ts +129 -25
  41. package/src/app/shared/components/datagrid/datagrid.component.html +82 -2
  42. package/src/app/shared/components/index.ts +1 -0
  43. package/src/app/shared/components/rule-builder/rule-builder.component.html +94 -0
  44. package/src/app/shared/components/rule-builder/rule-builder.component.ts +136 -0
  45. package/src/app/shared/components/toolbar/toolbar.component.html +0 -81
  46. package/src/app/shared/components/toolbar/toolbar.component.ts +1 -2
  47. package/src/styles/base/variables.css +16 -0
@@ -68,10 +68,3 @@ jobs:
68
68
 
69
69
  - name: Run headless e2e test
70
70
  run: npm run e2e
71
-
72
- - name: Build the app
73
- uses: nick-fields/retry@v3
74
- with:
75
- timeout_minutes: 15
76
- max_attempts: 3
77
- command: npm run electron:build
@@ -78,10 +78,3 @@ jobs:
78
78
  uses: GabrielBB/xvfb-action@v1
79
79
  with:
80
80
  run: npm run e2e
81
-
82
- - name: Build the app
83
- uses: nick-fields/retry@v3
84
- with:
85
- timeout_minutes: 15
86
- max_attempts: 3
87
- command: npm run electron:build
@@ -72,10 +72,3 @@ jobs:
72
72
 
73
73
  - name: Run headless e2e test
74
74
  run: npm run e2e
75
-
76
- - name: Build the app
77
- uses: nick-fields/retry@v3
78
- with:
79
- timeout_minutes: 15
80
- max_attempts: 3
81
- command: npm run electron:build
package/README.md CHANGED
@@ -17,6 +17,7 @@ Fontastic is a cross-platform font management and cataloging application built w
17
17
  ## Features
18
18
 
19
19
  - Nested collections — organize fonts into hierarchical groups with drag-and-drop
20
+ - Smart collections — auto-populate collections using rule-based filters on font metadata
20
21
  - Advanced search — quickly find fonts with powerful search and filtering
21
22
  - Glyph inspector — browse and examine individual characters and Unicode points
22
23
  - Waterfall preview — compare text rendering across multiple sizes at a glance
@@ -15,7 +15,7 @@ const repository_1 = require("../database/repository");
15
15
  const StorageType_1 = require("../enums/StorageType");
16
16
  class ConnectionManager {
17
17
  constructor(configManager) {
18
- this.schemas = [entity_1.Collection, entity_1.Store, entity_1.Logger];
18
+ this.schemas = [entity_1.Collection, entity_1.Store, entity_1.Logger, entity_1.SmartCollection];
19
19
  this.subscribers = [];
20
20
  this.migrations = [];
21
21
  this.omitables = ['title', 'description', 'enabled'];
@@ -89,6 +89,12 @@ class ConnectionManager {
89
89
  getStoreRepository() {
90
90
  return this.getStore().extend(repository_1.StoreRepository);
91
91
  }
92
+ getSmartCollection() {
93
+ return this.dataSource.getRepository(entity_1.SmartCollection);
94
+ }
95
+ getSmartCollectionRepository() {
96
+ return this.getSmartCollection().extend(repository_1.SmartCollectionRepository);
97
+ }
92
98
  }
93
99
  exports.default = ConnectionManager;
94
100
  //# sourceMappingURL=ConnectionManager.js.map
@@ -1,6 +1,6 @@
1
1
  import { DataSource, DataSourceOptions } from 'typeorm';
2
- import { Collection, Store, Logger } from '../database/entity';
3
- import { CollectionRepository, LoggerRepository, StoreRepository } from '../database/repository';
2
+ import { Collection, Store, Logger, SmartCollection } from '../database/entity';
3
+ import { CollectionRepository, LoggerRepository, SmartCollectionRepository, StoreRepository } from '../database/repository';
4
4
  import ConfigManager from './ConfigManager';
5
5
  import { StorageType } from '../enums/StorageType';
6
6
 
@@ -10,7 +10,7 @@ export default class ConnectionManager {
10
10
 
11
11
  dataSource: DataSource;
12
12
 
13
- schemas: any[] = [Collection, Store, Logger];
13
+ schemas: any[] = [Collection, Store, Logger, SmartCollection];
14
14
  subscribers: any[] = [];
15
15
  migrations: any[] = [];
16
16
 
@@ -104,4 +104,12 @@ export default class ConnectionManager {
104
104
  getStoreRepository() {
105
105
  return this.getStore().extend(StoreRepository);
106
106
  }
107
+
108
+ getSmartCollection() {
109
+ return this.dataSource.getRepository(SmartCollection);
110
+ }
111
+
112
+ getSmartCollectionRepository() {
113
+ return this.getSmartCollection().extend(SmartCollectionRepository);
114
+ }
107
115
  }
@@ -141,6 +141,30 @@ class MessageHandler {
141
141
  yield this.connectionManager.getCollectionRepository().updateCollectionCounts(items);
142
142
  return yield this.fetchCollectionsWithCounts({});
143
143
  }));
144
+ // Smart Collection
145
+ this.handle(ChannelType_1.ChannelType.IPC_SMART_COLLECTION_FIND, (_event) => __awaiter(this, void 0, void 0, function* () {
146
+ return yield this.connectionManager.getSmartCollectionRepository().fetchAll();
147
+ }));
148
+ this.handle(ChannelType_1.ChannelType.IPC_SMART_COLLECTION_CREATE, (_event, args) => __awaiter(this, void 0, void 0, function* () {
149
+ return yield this.connectionManager.getSmartCollectionRepository().createSmartCollection(args);
150
+ }));
151
+ this.handle(ChannelType_1.ChannelType.IPC_SMART_COLLECTION_UPDATE, (_event, args) => __awaiter(this, void 0, void 0, function* () {
152
+ return yield this.connectionManager.getSmartCollectionRepository().updateSmartCollection(args.id, args.data);
153
+ }));
154
+ this.handle(ChannelType_1.ChannelType.IPC_SMART_COLLECTION_DELETE, (_event, args) => __awaiter(this, void 0, void 0, function* () {
155
+ return yield this.connectionManager.getSmartCollectionRepository().deleteSmartCollection(args.id);
156
+ }));
157
+ this.handle(ChannelType_1.ChannelType.IPC_SMART_COLLECTION_EVALUATE, (_event, args) => __awaiter(this, void 0, void 0, function* () {
158
+ const sc = yield this.connectionManager.getSmartCollectionRepository().findOneBy({ id: args.id });
159
+ if (!sc)
160
+ return [[], 0];
161
+ const rules = JSON.parse(sc.rules);
162
+ return yield this.connectionManager.getStoreRepository().evaluateSmartRules(rules, sc.match_type, {
163
+ skip: args.skip,
164
+ take: args.take,
165
+ order: args.order,
166
+ });
167
+ }));
144
168
  // Store
145
169
  this.handle(ChannelType_1.ChannelType.IPC_STORE_FIND, (_event, args) => __awaiter(this, void 0, void 0, function* () {
146
170
  return yield this.connectionManager.getStore().find(args);
@@ -184,6 +184,35 @@ export default class MessageHandler {
184
184
  return await this.fetchCollectionsWithCounts({});
185
185
  });
186
186
 
187
+ // Smart Collection
188
+
189
+ this.handle(ChannelType.IPC_SMART_COLLECTION_FIND, async (_event: IpcMainEvent) => {
190
+ return await this.connectionManager.getSmartCollectionRepository().fetchAll();
191
+ });
192
+
193
+ this.handle(ChannelType.IPC_SMART_COLLECTION_CREATE, async (_event: IpcMainEvent, args: any) => {
194
+ return await this.connectionManager.getSmartCollectionRepository().createSmartCollection(args);
195
+ });
196
+
197
+ this.handle(ChannelType.IPC_SMART_COLLECTION_UPDATE, async (_event: IpcMainEvent, args: any) => {
198
+ return await this.connectionManager.getSmartCollectionRepository().updateSmartCollection(args.id, args.data);
199
+ });
200
+
201
+ this.handle(ChannelType.IPC_SMART_COLLECTION_DELETE, async (_event: IpcMainEvent, args: any) => {
202
+ return await this.connectionManager.getSmartCollectionRepository().deleteSmartCollection(args.id);
203
+ });
204
+
205
+ this.handle(ChannelType.IPC_SMART_COLLECTION_EVALUATE, async (_event: IpcMainEvent, args: any) => {
206
+ const sc = await this.connectionManager.getSmartCollectionRepository().findOneBy({ id: args.id });
207
+ if (!sc) return [[], 0];
208
+ const rules = JSON.parse(sc.rules);
209
+ return await this.connectionManager.getStoreRepository().evaluateSmartRules(rules, sc.match_type, {
210
+ skip: args.skip,
211
+ take: args.take,
212
+ order: args.order,
213
+ });
214
+ });
215
+
187
216
  // Store
188
217
 
189
218
  this.handle(ChannelType.IPC_STORE_FIND, async (_event: IpcMainEvent, args: any) => {
@@ -68,6 +68,21 @@ class DarwinTemplate {
68
68
  this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
69
69
  },
70
70
  },
71
+ { type: 'separator' },
72
+ {
73
+ label: 'Expand Collections',
74
+ accelerator: 'Alt+Command+E',
75
+ click: () => {
76
+ this.mainWindow.webContents.send(enums_1.ChannelType.IPC_TOGGLE_PANEL, 'expand-collections');
77
+ },
78
+ },
79
+ {
80
+ label: 'Collapse Collections',
81
+ accelerator: 'Alt+Command+Shift+E',
82
+ click: () => {
83
+ this.mainWindow.webContents.send(enums_1.ChannelType.IPC_TOGGLE_PANEL, 'collapse-collections');
84
+ },
85
+ },
71
86
  ];
72
87
  if (!this.isProduction) {
73
88
  items.push({ type: 'separator' }, {
@@ -80,6 +80,21 @@ export default class DarwinTemplate {
80
80
  this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
81
81
  },
82
82
  },
83
+ { type: 'separator' },
84
+ {
85
+ label: 'Expand Collections',
86
+ accelerator: 'Alt+Command+E',
87
+ click: () => {
88
+ this.mainWindow.webContents.send(ChannelType.IPC_TOGGLE_PANEL, 'expand-collections');
89
+ },
90
+ },
91
+ {
92
+ label: 'Collapse Collections',
93
+ accelerator: 'Alt+Command+Shift+E',
94
+ click: () => {
95
+ this.mainWindow.webContents.send(ChannelType.IPC_TOGGLE_PANEL, 'collapse-collections');
96
+ },
97
+ },
83
98
  ];
84
99
 
85
100
  if (!this.isProduction) {
@@ -65,6 +65,21 @@ class SystemTemplate {
65
65
  this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
66
66
  },
67
67
  },
68
+ { type: 'separator' },
69
+ {
70
+ label: 'Expand Collections',
71
+ accelerator: 'Alt+Ctrl+E',
72
+ click: () => {
73
+ this.mainWindow.webContents.send(enums_1.ChannelType.IPC_TOGGLE_PANEL, 'expand-collections');
74
+ },
75
+ },
76
+ {
77
+ label: 'Collapse Collections',
78
+ accelerator: 'Alt+Ctrl+Shift+E',
79
+ click: () => {
80
+ this.mainWindow.webContents.send(enums_1.ChannelType.IPC_TOGGLE_PANEL, 'collapse-collections');
81
+ },
82
+ },
68
83
  ];
69
84
  if (!this.isProduction) {
70
85
  items.push({ type: 'separator' }, {
@@ -72,6 +72,21 @@ export default class SystemTemplate {
72
72
  this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
73
73
  },
74
74
  },
75
+ { type: 'separator' },
76
+ {
77
+ label: 'Expand Collections',
78
+ accelerator: 'Alt+Ctrl+E',
79
+ click: () => {
80
+ this.mainWindow.webContents.send(ChannelType.IPC_TOGGLE_PANEL, 'expand-collections');
81
+ },
82
+ },
83
+ {
84
+ label: 'Collapse Collections',
85
+ accelerator: 'Alt+Ctrl+Shift+E',
86
+ click: () => {
87
+ this.mainWindow.webContents.send(ChannelType.IPC_TOGGLE_PANEL, 'collapse-collections');
88
+ },
89
+ },
75
90
  ];
76
91
 
77
92
  if (!this.isProduction) {
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SmartCollection = void 0;
13
+ const typeorm_1 = require("typeorm");
14
+ let SmartCollection = class SmartCollection {
15
+ };
16
+ exports.SmartCollection = SmartCollection;
17
+ __decorate([
18
+ (0, typeorm_1.PrimaryGeneratedColumn)(),
19
+ __metadata("design:type", Number)
20
+ ], SmartCollection.prototype, "id", void 0);
21
+ __decorate([
22
+ (0, typeorm_1.Column)({
23
+ length: 100,
24
+ }),
25
+ __metadata("design:type", String)
26
+ ], SmartCollection.prototype, "title", void 0);
27
+ __decorate([
28
+ (0, typeorm_1.Column)({
29
+ type: 'text',
30
+ }),
31
+ __metadata("design:type", String)
32
+ ], SmartCollection.prototype, "rules", void 0);
33
+ __decorate([
34
+ (0, typeorm_1.Column)({
35
+ type: 'varchar',
36
+ length: 3,
37
+ default: 'AND',
38
+ }),
39
+ __metadata("design:type", String)
40
+ ], SmartCollection.prototype, "match_type", void 0);
41
+ __decorate([
42
+ (0, typeorm_1.Column)({
43
+ type: 'smallint',
44
+ default: 0,
45
+ }),
46
+ __metadata("design:type", Number)
47
+ ], SmartCollection.prototype, "orderby", void 0);
48
+ __decorate([
49
+ (0, typeorm_1.Column)({
50
+ type: 'smallint',
51
+ default: 0,
52
+ }),
53
+ __metadata("design:type", Number)
54
+ ], SmartCollection.prototype, "enabled", void 0);
55
+ __decorate([
56
+ (0, typeorm_1.CreateDateColumn)(),
57
+ __metadata("design:type", Date)
58
+ ], SmartCollection.prototype, "created", void 0);
59
+ __decorate([
60
+ (0, typeorm_1.UpdateDateColumn)(),
61
+ __metadata("design:type", Date)
62
+ ], SmartCollection.prototype, "updated", void 0);
63
+ exports.SmartCollection = SmartCollection = __decorate([
64
+ (0, typeorm_1.Entity)()
65
+ ], SmartCollection);
66
+ //# sourceMappingURL=SmartCollection.schema.js.map
@@ -0,0 +1,39 @@
1
+ import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
2
+
3
+ @Entity()
4
+ export class SmartCollection {
5
+ @PrimaryGeneratedColumn()
6
+ id: number;
7
+
8
+ @Column({
9
+ length: 100,
10
+ })
11
+ title: string;
12
+
13
+ @Column({
14
+ type: 'text',
15
+ })
16
+ rules: string;
17
+
18
+ @Column({
19
+ type: 'varchar',
20
+ length: 3,
21
+ default: 'AND',
22
+ })
23
+ match_type: string;
24
+
25
+ @Column({
26
+ type: 'smallint',
27
+ default: 0,
28
+ })
29
+ orderby: number;
30
+
31
+ @Column({
32
+ type: 'smallint',
33
+ default: 0,
34
+ })
35
+ enabled: number;
36
+
37
+ @CreateDateColumn() public created: Date;
38
+ @UpdateDateColumn() public updated: Date;
39
+ }
@@ -16,5 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./Collection.schema"), exports);
18
18
  __exportStar(require("./Logger.schema"), exports);
19
+ __exportStar(require("./SmartCollection.schema"), exports);
19
20
  __exportStar(require("./Store.schema"), exports);
20
21
  //# sourceMappingURL=index.js.map
@@ -1,3 +1,4 @@
1
1
  export * from './Collection.schema';
2
2
  export * from './Logger.schema';
3
- export * from './Store.schema';
3
+ export * from './SmartCollection.schema';
4
+ export * from './Store.schema';
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SmartCollectionRepository = void 0;
13
+ const entity_1 = require("../entity");
14
+ exports.SmartCollectionRepository = {
15
+ fetchAll() {
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ return this.createQueryBuilder('sc').orderBy('sc.orderby', 'ASC').addOrderBy('LOWER(sc.title)', 'ASC').getMany();
18
+ });
19
+ },
20
+ createSmartCollection(args) {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ yield this.createQueryBuilder()
23
+ .insert()
24
+ .into(entity_1.SmartCollection)
25
+ .values({
26
+ title: args.title,
27
+ rules: args.rules,
28
+ match_type: args.match_type,
29
+ })
30
+ .execute();
31
+ return this.fetchAll();
32
+ });
33
+ },
34
+ updateSmartCollection(id, data) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ yield this.createQueryBuilder().update(entity_1.SmartCollection).set(data).where('id = :id', { id }).execute();
37
+ return this.fetchAll();
38
+ });
39
+ },
40
+ deleteSmartCollection(id) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ yield this.createQueryBuilder().delete().from(entity_1.SmartCollection).where('id = :id', { id }).execute();
43
+ return this.fetchAll();
44
+ });
45
+ },
46
+ };
47
+ //# sourceMappingURL=SmartCollection.repository.js.map
@@ -0,0 +1,30 @@
1
+ import { SmartCollection } from '../entity';
2
+
3
+ export const SmartCollectionRepository = {
4
+ async fetchAll(): Promise<SmartCollection[]> {
5
+ return this.createQueryBuilder('sc').orderBy('sc.orderby', 'ASC').addOrderBy('LOWER(sc.title)', 'ASC').getMany();
6
+ },
7
+
8
+ async createSmartCollection(args: { title: string; rules: string; match_type: string }) {
9
+ await this.createQueryBuilder()
10
+ .insert()
11
+ .into(SmartCollection)
12
+ .values({
13
+ title: args.title,
14
+ rules: args.rules,
15
+ match_type: args.match_type,
16
+ })
17
+ .execute();
18
+ return this.fetchAll();
19
+ },
20
+
21
+ async updateSmartCollection(id: number, data: Partial<SmartCollection>) {
22
+ await this.createQueryBuilder().update(SmartCollection).set(data).where('id = :id', { id }).execute();
23
+ return this.fetchAll();
24
+ },
25
+
26
+ async deleteSmartCollection(id: number) {
27
+ await this.createQueryBuilder().delete().from(SmartCollection).where('id = :id', { id }).execute();
28
+ return this.fetchAll();
29
+ },
30
+ };
@@ -307,6 +307,113 @@ exports.StoreRepository = {
307
307
  .catch((err) => console.log('insert-error', err));
308
308
  });
309
309
  },
310
+ evaluateSmartRules(rules_1, matchType_1) {
311
+ return __awaiter(this, arguments, void 0, function* (rules, matchType, options = {}) {
312
+ const db = this.createQueryBuilder();
313
+ const textFields = [
314
+ 'file_name',
315
+ 'file_path',
316
+ 'compatible_full_name',
317
+ 'copyright',
318
+ 'description',
319
+ 'designer',
320
+ 'designer_url',
321
+ 'font_family',
322
+ 'font_subfamily',
323
+ 'full_name',
324
+ 'license',
325
+ 'license_url',
326
+ 'manufacturer',
327
+ 'manufacturer_url',
328
+ 'post_script_name',
329
+ 'preferred_family',
330
+ 'preferred_sub_family',
331
+ 'sample_text',
332
+ 'trademark',
333
+ 'unique_id',
334
+ 'version',
335
+ ];
336
+ const booleanFields = ['favorite', 'system', 'installable', 'temporary'];
337
+ const numericFields = ['file_size'];
338
+ const dateFields = ['created'];
339
+ if (rules.length > 0) {
340
+ const conditions = rules.map((rule, idx) => {
341
+ const field = `store.${rule.field}`;
342
+ const paramName = `p${idx}`;
343
+ return (qb) => {
344
+ if (textFields.includes(rule.field)) {
345
+ const val = String(rule.value).toLowerCase();
346
+ switch (rule.operator) {
347
+ case 'contains':
348
+ qb.where(`LOWER(${field}) LIKE :${paramName}`, { [paramName]: `%${val}%` });
349
+ break;
350
+ case 'equals':
351
+ qb.where(`LOWER(${field}) = :${paramName}`, { [paramName]: val });
352
+ break;
353
+ case 'starts_with':
354
+ qb.where(`LOWER(${field}) LIKE :${paramName}`, { [paramName]: `${val}%` });
355
+ break;
356
+ case 'ends_with':
357
+ qb.where(`LOWER(${field}) LIKE :${paramName}`, { [paramName]: `%${val}` });
358
+ break;
359
+ default:
360
+ qb.where(`LOWER(${field}) LIKE :${paramName}`, { [paramName]: `%${val}%` });
361
+ }
362
+ }
363
+ else if (rule.field === 'file_type') {
364
+ qb.where(`${field} = :${paramName}`, { [paramName]: rule.value });
365
+ }
366
+ else if (booleanFields.includes(rule.field)) {
367
+ const boolVal = rule.operator === 'is_not' ? 0 : 1;
368
+ qb.where(`${field} = :${paramName}`, { [paramName]: boolVal });
369
+ }
370
+ else if (numericFields.includes(rule.field)) {
371
+ if (rule.operator === 'greater_than') {
372
+ qb.where(`${field} > :${paramName}`, { [paramName]: rule.value });
373
+ }
374
+ else if (rule.operator === 'less_than') {
375
+ qb.where(`${field} < :${paramName}`, { [paramName]: rule.value });
376
+ }
377
+ else {
378
+ qb.where(`${field} = :${paramName}`, { [paramName]: rule.value });
379
+ }
380
+ }
381
+ else if (dateFields.includes(rule.field)) {
382
+ if (rule.operator === 'greater_than') {
383
+ qb.where(`${field} >= :${paramName}`, { [paramName]: rule.value });
384
+ }
385
+ else if (rule.operator === 'less_than') {
386
+ qb.where(`${field} <= :${paramName}`, { [paramName]: rule.value });
387
+ }
388
+ else {
389
+ qb.where(`${field} = :${paramName}`, { [paramName]: rule.value });
390
+ }
391
+ }
392
+ };
393
+ });
394
+ const joiner = matchType === 'OR' ? 'orWhere' : 'andWhere';
395
+ conditions.forEach((condition, i) => {
396
+ if (i === 0) {
397
+ db.where(new typeorm_1.Brackets(condition));
398
+ }
399
+ else {
400
+ db[joiner](new typeorm_1.Brackets(condition));
401
+ }
402
+ });
403
+ }
404
+ if (options.take) {
405
+ db.limit(options.take);
406
+ }
407
+ if (options.skip) {
408
+ db.offset(options.skip);
409
+ }
410
+ if (options.order && options.order.column) {
411
+ const direction = options.order.direction === 'DESC' ? 'DESC' : 'ASC';
412
+ db.orderBy(`store.${options.order.column}`, direction);
413
+ }
414
+ return yield db.getManyAndCount();
415
+ });
416
+ },
310
417
  fetchSystemStats() {
311
418
  return __awaiter(this, void 0, void 0, function* () {
312
419
  const rowCount = yield this.createQueryBuilder().select('COUNT(*)', 'total').getRawOne();