@limetech/lime-crm-building-blocks 1.103.5 → 1.103.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/CHANGELOG.md +14 -0
- package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +2 -4
- package/dist/cjs/limebb-lime-query-filter-group_3.cjs.entry.js +123 -12
- package/dist/cjs/limebb-lime-query-response-format-editor_2.cjs.entry.js +48 -34
- package/dist/collection/components/lime-query-builder/expressions/lime-query-value-input.js +15 -12
- package/dist/collection/components/lime-query-builder/lime-query-builder.js +2 -4
- package/dist/collection/components/lime-query-builder/response-format/response-format-editor.js +48 -34
- package/dist/collection/components/lime-query-builder/type-resolution.js +108 -0
- package/dist/components/lime-query-value-input.js +123 -12
- package/dist/components/limebb-lime-query-builder.js +2 -4
- package/dist/components/response-format-editor.js +48 -34
- package/dist/esm/limebb-lime-query-builder.entry.js +2 -4
- package/dist/esm/limebb-lime-query-filter-group_3.entry.js +123 -12
- package/dist/esm/limebb-lime-query-response-format-editor_2.entry.js +48 -34
- package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
- package/dist/lime-crm-building-blocks/p-908dd7d5.entry.js +1 -0
- package/dist/lime-crm-building-blocks/p-adfb9e90.entry.js +1 -0
- package/dist/lime-crm-building-blocks/p-fe6a94a1.entry.js +1 -0
- package/dist/types/components/lime-query-builder/expressions/lime-query-value-input.d.ts +1 -2
- package/dist/types/components/lime-query-builder/response-format/response-format-editor.d.ts +16 -0
- package/dist/types/components/lime-query-builder/type-resolution.d.ts +73 -0
- package/package.json +1 -1
- package/dist/lime-crm-building-blocks/p-47f4f505.entry.js +0 -1
- package/dist/lime-crm-building-blocks/p-4f605428.entry.js +0 -1
- package/dist/lime-crm-building-blocks/p-d635e6fc.entry.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.103.7](https://github.com/Lundalogik/lime-crm-building-blocks/compare/v1.103.6...v1.103.7) (2025-11-17)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
* **lime-query-builder:** fix problem where visual mode would not be in sync with underlying value ([5333966](https://github.com/Lundalogik/lime-crm-building-blocks/commit/5333966b845ad7d980a0280d3e9d52331b37523b)), closes [Lundalogik/crm-insights-and-intelligence#149](https://github.com/Lundalogik/crm-insights-and-intelligence/issues/149)
|
|
7
|
+
|
|
8
|
+
## [1.103.6](https://github.com/Lundalogik/lime-crm-building-blocks/compare/v1.103.5...v1.103.6) (2025-11-14)
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
* **lime-query-builder:** render correct input types for date/time properties ([9c8fa6a](https://github.com/Lundalogik/lime-crm-building-blocks/commit/9c8fa6a659ba71dc4b9675f1ce60b4fd7eb30d69)), closes [Lundalogik/crm-insights-and-intelligence#143](https://github.com/Lundalogik/crm-insights-and-intelligence/issues/143)
|
|
14
|
+
|
|
1
15
|
## [1.103.5](https://github.com/Lundalogik/lime-crm-building-blocks/compare/v1.103.4...v1.103.5) (2025-11-13)
|
|
2
16
|
|
|
3
17
|
### Bug Fixes
|
|
@@ -32,11 +32,9 @@ const LimeQueryBuilder = class {
|
|
|
32
32
|
this.limetype = event.detail;
|
|
33
33
|
// Reset filter when limetype changes
|
|
34
34
|
this.filter = undefined;
|
|
35
|
-
// Reset response format when limetype changes
|
|
35
|
+
// Reset response format when limetype changes - empty state
|
|
36
36
|
this.internalResponseFormat = {
|
|
37
|
-
object: {
|
|
38
|
-
_id: null,
|
|
39
|
-
},
|
|
37
|
+
object: {},
|
|
40
38
|
};
|
|
41
39
|
this.emitChange();
|
|
42
40
|
};
|
|
@@ -246,6 +246,115 @@ const LimeQueryFilterNotComponent = class {
|
|
|
246
246
|
};
|
|
247
247
|
LimeQueryFilterNotComponent.style = LimebbLimeQueryFilterNotStyle0;
|
|
248
248
|
|
|
249
|
+
/**
|
|
250
|
+
* System property name to actual type mapping.
|
|
251
|
+
*
|
|
252
|
+
* Maps system property names (with or without underscore prefix) to their actual data types.
|
|
253
|
+
* This mapping is necessary because system properties have `type='system'` in the metadata
|
|
254
|
+
* (for historical reasons - database schema fieldtype 255), but we need to know their
|
|
255
|
+
* actual data types to render appropriate inputs.
|
|
256
|
+
*/
|
|
257
|
+
const SYSTEM_TYPE_MAP = {
|
|
258
|
+
timestamp: 'time',
|
|
259
|
+
createdtime: 'time',
|
|
260
|
+
updatedtime: 'time',
|
|
261
|
+
id: 'integer',
|
|
262
|
+
createduser: 'integer',
|
|
263
|
+
updateduser: 'integer',
|
|
264
|
+
descriptive: 'string',
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Lime CRM date/time property type to Lime Elements date picker type mapping.
|
|
268
|
+
*
|
|
269
|
+
* Maps Lime CRM property types to the corresponding Lime Elements date picker types,
|
|
270
|
+
* as they use different naming conventions.
|
|
271
|
+
*/
|
|
272
|
+
const DATE_TIME_TYPE_MAP = {
|
|
273
|
+
date: 'date',
|
|
274
|
+
time: 'datetime',
|
|
275
|
+
timeofday: 'time',
|
|
276
|
+
month: 'month',
|
|
277
|
+
quarter: 'quarter',
|
|
278
|
+
year: 'year',
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Get the actual type of a property, resolving 'system' type to the underlying type.
|
|
282
|
+
*
|
|
283
|
+
* System properties in Lime CRM have `type: 'system'` in the metadata (a categorical marker
|
|
284
|
+
* from the database schema fieldtype 255), but we need to know their actual data types
|
|
285
|
+
* to render appropriate input controls.
|
|
286
|
+
*
|
|
287
|
+
* @param property - The property to get the type for
|
|
288
|
+
* @param property.type
|
|
289
|
+
* @param property.name
|
|
290
|
+
* @returns The actual property type (resolves 'system' to the underlying type)
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* const property = { name: '_timestamp', type: 'system' };
|
|
295
|
+
* getActualPropertyType(property); // Returns 'time'
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
function getActualPropertyType(property) {
|
|
299
|
+
if (property.type === 'system') {
|
|
300
|
+
return resolveSystemPropertyType(property.name);
|
|
301
|
+
}
|
|
302
|
+
return property.type;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Map system property names to their actual data types.
|
|
306
|
+
*
|
|
307
|
+
* This mapping is necessary because system properties have `type='system'` in the metadata
|
|
308
|
+
* (for historical reasons - database schema fieldtype 255), but we need to know their
|
|
309
|
+
* actual data types to render appropriate inputs.
|
|
310
|
+
*
|
|
311
|
+
* The mapping is based on the Lime CRM database schema:
|
|
312
|
+
* - System datetime fields: `timestamp`, `createdtime`, `updatedtime`
|
|
313
|
+
* - System integer fields: `id`, `createduser`, `updateduser`
|
|
314
|
+
* - System string fields: `descriptive`
|
|
315
|
+
*
|
|
316
|
+
* @param propertyName - The system property name (with or without underscore prefix)
|
|
317
|
+
* @returns The actual property type
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* resolveSystemPropertyType('_timestamp'); // Returns 'time'
|
|
322
|
+
* resolveSystemPropertyType('timestamp'); // Returns 'time'
|
|
323
|
+
* resolveSystemPropertyType('_id'); // Returns 'integer'
|
|
324
|
+
* resolveSystemPropertyType('unknown'); // Returns 'string' (default)
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
function resolveSystemPropertyType(propertyName) {
|
|
328
|
+
const name = propertyName.replace(/^_/, '');
|
|
329
|
+
return SYSTEM_TYPE_MAP[name] || 'string';
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Map Lime CRM property types to Lime Elements date picker types.
|
|
333
|
+
*
|
|
334
|
+
* Lime CRM and Lime Elements use different naming conventions for date/time types,
|
|
335
|
+
* so we need to translate between them:
|
|
336
|
+
*
|
|
337
|
+
* - `'date'` → `'date'` - Date only
|
|
338
|
+
* - `'time'` → `'datetime'` - Full datetime (date + time) in Lime CRM
|
|
339
|
+
* - `'timeofday'` → `'time'` - Time only (hh:mm)
|
|
340
|
+
* - `'month'` → `'month'` - Month picker (YYYY-MM)
|
|
341
|
+
* - `'quarter'` → `'quarter'` - Quarter picker
|
|
342
|
+
* - `'year'` → `'year'` - Year picker
|
|
343
|
+
*
|
|
344
|
+
* @param propertyType - The Lime CRM date/time property type
|
|
345
|
+
* @returns The corresponding Lime Elements date picker type
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```typescript
|
|
349
|
+
* mapPropertyTypeToPickerType('time'); // Returns 'datetime'
|
|
350
|
+
* mapPropertyTypeToPickerType('timeofday'); // Returns 'time'
|
|
351
|
+
* mapPropertyTypeToPickerType('date'); // Returns 'date'
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
function mapPropertyTypeToPickerType(propertyType) {
|
|
355
|
+
return DATE_TIME_TYPE_MAP[propertyType] || 'date';
|
|
356
|
+
}
|
|
357
|
+
|
|
249
358
|
const limeQueryValueInputCss = ":host{display:flex;gap:0.5rem}:host>*{flex-grow:1}*{box-sizing:border-box}.value-input-container{display:flex;flex-direction:row;align-items:flex-start;gap:0.5rem;width:100%}.mode-toggle{flex-shrink:0;flex-grow:0}.placeholder-input{flex-grow:1;display:flex;flex-direction:column;gap:0.5rem}.placeholder-preview{display:flex;align-items:center;gap:0.5rem;padding:0.5rem;background-color:rgba(var(--color-blue-light), 0.1);border-radius:var(--border-radius-small);font-size:0.875rem;color:rgb(var(--color-blue-default));border-left:3px solid rgb(var(--color-blue-default))}.placeholder-preview limel-icon{flex-shrink:0;color:rgb(var(--color-blue-default))}.placeholder-preview span{font-family:var(--font-monospace);word-break:break-all}";
|
|
250
359
|
const LimebbLimeQueryValueInputStyle0 = limeQueryValueInputCss;
|
|
251
360
|
|
|
@@ -408,10 +517,12 @@ const LimeQueryValueInput = class {
|
|
|
408
517
|
if (!property) {
|
|
409
518
|
return this.renderTextInput();
|
|
410
519
|
}
|
|
411
|
-
|
|
520
|
+
// Resolve 'system' type to actual type
|
|
521
|
+
const actualType = getActualPropertyType(property);
|
|
522
|
+
switch (actualType) {
|
|
412
523
|
case 'integer':
|
|
413
524
|
case 'decimal': {
|
|
414
|
-
return this.renderNumberInput(
|
|
525
|
+
return this.renderNumberInput(actualType);
|
|
415
526
|
}
|
|
416
527
|
case 'yesno': {
|
|
417
528
|
return this.renderBooleanInput();
|
|
@@ -419,11 +530,13 @@ const LimeQueryValueInput = class {
|
|
|
419
530
|
case 'option': {
|
|
420
531
|
return this.renderOptionInput(property);
|
|
421
532
|
}
|
|
422
|
-
case 'date':
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
case '
|
|
426
|
-
|
|
533
|
+
case 'date':
|
|
534
|
+
case 'time':
|
|
535
|
+
case 'timeofday':
|
|
536
|
+
case 'year':
|
|
537
|
+
case 'month':
|
|
538
|
+
case 'quarter': {
|
|
539
|
+
return this.renderDateTimeInput(actualType);
|
|
427
540
|
}
|
|
428
541
|
default: {
|
|
429
542
|
return this.renderTextInput();
|
|
@@ -459,13 +572,11 @@ const LimeQueryValueInput = class {
|
|
|
459
572
|
const selectedOption = options.find((o) => o.value === this.value);
|
|
460
573
|
return (index.h("limel-select", { label: this.label, options: options, value: selectedOption, onChange: this.handleSelectChange }));
|
|
461
574
|
}
|
|
462
|
-
|
|
575
|
+
renderDateTimeInput(type) {
|
|
463
576
|
// Convert string to Date if needed
|
|
464
577
|
const dateValue = typeof this.value === 'string' ? new Date(this.value) : this.value;
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
renderTimeInput() {
|
|
468
|
-
return (index.h("limel-input-field", { label: this.label, type: "time", value: this.value || '', onChange: this.handleTextChange }));
|
|
578
|
+
const pickerType = mapPropertyTypeToPickerType(type);
|
|
579
|
+
return (index.h("limel-date-picker", { label: this.label, value: dateValue, type: pickerType, onChange: this.handleDateChange }));
|
|
469
580
|
}
|
|
470
581
|
renderMultiValueInput() {
|
|
471
582
|
// For IN operator, allow comma-separated values
|
|
@@ -196,46 +196,29 @@ const ResponseFormatEditor = class {
|
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
componentWillLoad() {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// Keep items empty for truly empty objects
|
|
199
|
+
if (!this.value) {
|
|
200
|
+
// No value provided at all, use default _id
|
|
201
|
+
this.items = [{ path: '_id' }];
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (this.isEmptyResponseFormat(this.value)) {
|
|
205
|
+
// Show empty state for explicit empty objects
|
|
207
206
|
this.items = [];
|
|
208
207
|
}
|
|
209
|
-
else if (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
this.items = converted;
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
// Empty object property, but not a truly empty value
|
|
216
|
-
// Use default _id for backward compatibility
|
|
217
|
-
this.items = [{ path: '_id' }];
|
|
218
|
-
}
|
|
208
|
+
else if (this.value.object) {
|
|
209
|
+
// Has object property with actual properties
|
|
210
|
+
this.items = propertySelectionToItems(this.value.object);
|
|
219
211
|
}
|
|
220
|
-
else
|
|
221
|
-
//
|
|
222
|
-
this.items = [
|
|
212
|
+
else {
|
|
213
|
+
// If value has aggregates but no object property, initialize items to empty array
|
|
214
|
+
this.items = [];
|
|
223
215
|
}
|
|
224
216
|
}
|
|
225
217
|
componentWillUpdate() {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const isTrulyEmpty = this.value &&
|
|
229
|
-
Object.keys(this.value).length === 0 &&
|
|
230
|
-
!this.value.object &&
|
|
231
|
-
!this.value.aggregates;
|
|
232
|
-
if (isTrulyEmpty) {
|
|
233
|
-
// Keep items empty for truly empty objects
|
|
234
|
-
if (this.items.length > 0) {
|
|
235
|
-
this.items = [];
|
|
236
|
-
}
|
|
218
|
+
if (!this.value) {
|
|
219
|
+
return;
|
|
237
220
|
}
|
|
238
|
-
|
|
221
|
+
if (this.value.object && !this.isEmptyResponseFormat(this.value)) {
|
|
239
222
|
const currentItems = propertySelectionToItems(this.value.object);
|
|
240
223
|
// Check if items have changed
|
|
241
224
|
const itemsChanged = currentItems.length !== this.items.length ||
|
|
@@ -247,10 +230,15 @@ const ResponseFormatEditor = class {
|
|
|
247
230
|
item.description === current.description);
|
|
248
231
|
});
|
|
249
232
|
if (itemsChanged) {
|
|
250
|
-
// Allow empty items array - don't force _id
|
|
251
233
|
this.items = currentItems;
|
|
252
234
|
}
|
|
253
235
|
}
|
|
236
|
+
else {
|
|
237
|
+
// Either empty response format or no object property - clear items
|
|
238
|
+
if (this.items.length > 0) {
|
|
239
|
+
this.items = [];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
254
242
|
}
|
|
255
243
|
render() {
|
|
256
244
|
if (!this.limetype) {
|
|
@@ -281,6 +269,32 @@ const ResponseFormatEditor = class {
|
|
|
281
269
|
};
|
|
282
270
|
this.change.emit(responseFormat);
|
|
283
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Check if the response format is empty
|
|
274
|
+
*
|
|
275
|
+
* A response format is considered empty in the following cases:
|
|
276
|
+
* - Empty object: `{}`
|
|
277
|
+
* - Object with empty object property and no aggregates: `{ object: {} }`
|
|
278
|
+
*
|
|
279
|
+
* Returns false for:
|
|
280
|
+
* - `null` or `undefined`
|
|
281
|
+
* - Objects with properties: `{ object: { _id: null } }`
|
|
282
|
+
* - Objects with aggregates: `{ aggregates: {...} }`
|
|
283
|
+
*
|
|
284
|
+
* @param value - The ResponseFormat to check
|
|
285
|
+
* @returns True if the response format is empty, false otherwise
|
|
286
|
+
*/
|
|
287
|
+
isEmptyResponseFormat(value) {
|
|
288
|
+
if (!value) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
if (Object.keys(value).length === 0) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
return !!(value.object &&
|
|
295
|
+
Object.keys(value.object).length === 0 &&
|
|
296
|
+
!value.aggregates);
|
|
297
|
+
}
|
|
284
298
|
};
|
|
285
299
|
ResponseFormatEditor.style = LimebbLimeQueryResponseFormatEditorStyle0;
|
|
286
300
|
|
|
@@ -11,6 +11,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
11
11
|
import { h, Host, } from "@stencil/core";
|
|
12
12
|
import { Operator, SelectLimeTypes as Limetypes, } from "@limetech/lime-web-components";
|
|
13
13
|
import { getPropertyFromPath } from "../property-resolution";
|
|
14
|
+
import { getActualPropertyType, mapPropertyTypeToPickerType, } from "../type-resolution";
|
|
14
15
|
/**
|
|
15
16
|
* Query Value Input Component
|
|
16
17
|
*
|
|
@@ -182,10 +183,12 @@ export class LimeQueryValueInput {
|
|
|
182
183
|
if (!property) {
|
|
183
184
|
return this.renderTextInput();
|
|
184
185
|
}
|
|
185
|
-
|
|
186
|
+
// Resolve 'system' type to actual type
|
|
187
|
+
const actualType = getActualPropertyType(property);
|
|
188
|
+
switch (actualType) {
|
|
186
189
|
case 'integer':
|
|
187
190
|
case 'decimal': {
|
|
188
|
-
return this.renderNumberInput(
|
|
191
|
+
return this.renderNumberInput(actualType);
|
|
189
192
|
}
|
|
190
193
|
case 'yesno': {
|
|
191
194
|
return this.renderBooleanInput();
|
|
@@ -193,11 +196,13 @@ export class LimeQueryValueInput {
|
|
|
193
196
|
case 'option': {
|
|
194
197
|
return this.renderOptionInput(property);
|
|
195
198
|
}
|
|
196
|
-
case 'date':
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
case '
|
|
200
|
-
|
|
199
|
+
case 'date':
|
|
200
|
+
case 'time':
|
|
201
|
+
case 'timeofday':
|
|
202
|
+
case 'year':
|
|
203
|
+
case 'month':
|
|
204
|
+
case 'quarter': {
|
|
205
|
+
return this.renderDateTimeInput(actualType);
|
|
201
206
|
}
|
|
202
207
|
default: {
|
|
203
208
|
return this.renderTextInput();
|
|
@@ -233,13 +238,11 @@ export class LimeQueryValueInput {
|
|
|
233
238
|
const selectedOption = options.find((o) => o.value === this.value);
|
|
234
239
|
return (h("limel-select", { label: this.label, options: options, value: selectedOption, onChange: this.handleSelectChange }));
|
|
235
240
|
}
|
|
236
|
-
|
|
241
|
+
renderDateTimeInput(type) {
|
|
237
242
|
// Convert string to Date if needed
|
|
238
243
|
const dateValue = typeof this.value === 'string' ? new Date(this.value) : this.value;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
renderTimeInput() {
|
|
242
|
-
return (h("limel-input-field", { label: this.label, type: "time", value: this.value || '', onChange: this.handleTextChange }));
|
|
244
|
+
const pickerType = mapPropertyTypeToPickerType(type);
|
|
245
|
+
return (h("limel-date-picker", { label: this.label, value: dateValue, type: pickerType, onChange: this.handleDateChange }));
|
|
243
246
|
}
|
|
244
247
|
renderMultiValueInput() {
|
|
245
248
|
// For IN operator, allow comma-separated values
|
|
@@ -50,11 +50,9 @@ export class LimeQueryBuilder {
|
|
|
50
50
|
this.limetype = event.detail;
|
|
51
51
|
// Reset filter when limetype changes
|
|
52
52
|
this.filter = undefined;
|
|
53
|
-
// Reset response format when limetype changes
|
|
53
|
+
// Reset response format when limetype changes - empty state
|
|
54
54
|
this.internalResponseFormat = {
|
|
55
|
-
object: {
|
|
56
|
-
_id: null,
|
|
57
|
-
},
|
|
55
|
+
object: {},
|
|
58
56
|
};
|
|
59
57
|
this.emitChange();
|
|
60
58
|
};
|
package/dist/collection/components/lime-query-builder/response-format/response-format-editor.js
CHANGED
|
@@ -47,46 +47,29 @@ export class ResponseFormatEditor {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
componentWillLoad() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Keep items empty for truly empty objects
|
|
50
|
+
if (!this.value) {
|
|
51
|
+
// No value provided at all, use default _id
|
|
52
|
+
this.items = [{ path: '_id' }];
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (this.isEmptyResponseFormat(this.value)) {
|
|
56
|
+
// Show empty state for explicit empty objects
|
|
58
57
|
this.items = [];
|
|
59
58
|
}
|
|
60
|
-
else if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.items = converted;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
// Empty object property, but not a truly empty value
|
|
67
|
-
// Use default _id for backward compatibility
|
|
68
|
-
this.items = [{ path: '_id' }];
|
|
69
|
-
}
|
|
59
|
+
else if (this.value.object) {
|
|
60
|
+
// Has object property with actual properties
|
|
61
|
+
this.items = propertySelectionToItems(this.value.object);
|
|
70
62
|
}
|
|
71
|
-
else
|
|
72
|
-
//
|
|
73
|
-
this.items = [
|
|
63
|
+
else {
|
|
64
|
+
// If value has aggregates but no object property, initialize items to empty array
|
|
65
|
+
this.items = [];
|
|
74
66
|
}
|
|
75
67
|
}
|
|
76
68
|
componentWillUpdate() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const isTrulyEmpty = this.value &&
|
|
80
|
-
Object.keys(this.value).length === 0 &&
|
|
81
|
-
!this.value.object &&
|
|
82
|
-
!this.value.aggregates;
|
|
83
|
-
if (isTrulyEmpty) {
|
|
84
|
-
// Keep items empty for truly empty objects
|
|
85
|
-
if (this.items.length > 0) {
|
|
86
|
-
this.items = [];
|
|
87
|
-
}
|
|
69
|
+
if (!this.value) {
|
|
70
|
+
return;
|
|
88
71
|
}
|
|
89
|
-
|
|
72
|
+
if (this.value.object && !this.isEmptyResponseFormat(this.value)) {
|
|
90
73
|
const currentItems = propertySelectionToItems(this.value.object);
|
|
91
74
|
// Check if items have changed
|
|
92
75
|
const itemsChanged = currentItems.length !== this.items.length ||
|
|
@@ -98,10 +81,15 @@ export class ResponseFormatEditor {
|
|
|
98
81
|
item.description === current.description);
|
|
99
82
|
});
|
|
100
83
|
if (itemsChanged) {
|
|
101
|
-
// Allow empty items array - don't force _id
|
|
102
84
|
this.items = currentItems;
|
|
103
85
|
}
|
|
104
86
|
}
|
|
87
|
+
else {
|
|
88
|
+
// Either empty response format or no object property - clear items
|
|
89
|
+
if (this.items.length > 0) {
|
|
90
|
+
this.items = [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
105
93
|
}
|
|
106
94
|
render() {
|
|
107
95
|
if (!this.limetype) {
|
|
@@ -132,6 +120,32 @@ export class ResponseFormatEditor {
|
|
|
132
120
|
};
|
|
133
121
|
this.change.emit(responseFormat);
|
|
134
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if the response format is empty
|
|
125
|
+
*
|
|
126
|
+
* A response format is considered empty in the following cases:
|
|
127
|
+
* - Empty object: `{}`
|
|
128
|
+
* - Object with empty object property and no aggregates: `{ object: {} }`
|
|
129
|
+
*
|
|
130
|
+
* Returns false for:
|
|
131
|
+
* - `null` or `undefined`
|
|
132
|
+
* - Objects with properties: `{ object: { _id: null } }`
|
|
133
|
+
* - Objects with aggregates: `{ aggregates: {...} }`
|
|
134
|
+
*
|
|
135
|
+
* @param value - The ResponseFormat to check
|
|
136
|
+
* @returns True if the response format is empty, false otherwise
|
|
137
|
+
*/
|
|
138
|
+
isEmptyResponseFormat(value) {
|
|
139
|
+
if (!value) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (Object.keys(value).length === 0) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
return !!(value.object &&
|
|
146
|
+
Object.keys(value.object).length === 0 &&
|
|
147
|
+
!value.aggregates);
|
|
148
|
+
}
|
|
135
149
|
static get is() { return "limebb-lime-query-response-format-editor"; }
|
|
136
150
|
static get encapsulation() { return "shadow"; }
|
|
137
151
|
static get originalStyleUrls() {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System property name to actual type mapping.
|
|
3
|
+
*
|
|
4
|
+
* Maps system property names (with or without underscore prefix) to their actual data types.
|
|
5
|
+
* This mapping is necessary because system properties have `type='system'` in the metadata
|
|
6
|
+
* (for historical reasons - database schema fieldtype 255), but we need to know their
|
|
7
|
+
* actual data types to render appropriate inputs.
|
|
8
|
+
*/
|
|
9
|
+
const SYSTEM_TYPE_MAP = {
|
|
10
|
+
timestamp: 'time',
|
|
11
|
+
createdtime: 'time',
|
|
12
|
+
updatedtime: 'time',
|
|
13
|
+
id: 'integer',
|
|
14
|
+
createduser: 'integer',
|
|
15
|
+
updateduser: 'integer',
|
|
16
|
+
descriptive: 'string',
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Lime CRM date/time property type to Lime Elements date picker type mapping.
|
|
20
|
+
*
|
|
21
|
+
* Maps Lime CRM property types to the corresponding Lime Elements date picker types,
|
|
22
|
+
* as they use different naming conventions.
|
|
23
|
+
*/
|
|
24
|
+
const DATE_TIME_TYPE_MAP = {
|
|
25
|
+
date: 'date',
|
|
26
|
+
time: 'datetime',
|
|
27
|
+
timeofday: 'time',
|
|
28
|
+
month: 'month',
|
|
29
|
+
quarter: 'quarter',
|
|
30
|
+
year: 'year',
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Get the actual type of a property, resolving 'system' type to the underlying type.
|
|
34
|
+
*
|
|
35
|
+
* System properties in Lime CRM have `type: 'system'` in the metadata (a categorical marker
|
|
36
|
+
* from the database schema fieldtype 255), but we need to know their actual data types
|
|
37
|
+
* to render appropriate input controls.
|
|
38
|
+
*
|
|
39
|
+
* @param property - The property to get the type for
|
|
40
|
+
* @param property.type
|
|
41
|
+
* @param property.name
|
|
42
|
+
* @returns The actual property type (resolves 'system' to the underlying type)
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const property = { name: '_timestamp', type: 'system' };
|
|
47
|
+
* getActualPropertyType(property); // Returns 'time'
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function getActualPropertyType(property) {
|
|
51
|
+
if (property.type === 'system') {
|
|
52
|
+
return resolveSystemPropertyType(property.name);
|
|
53
|
+
}
|
|
54
|
+
return property.type;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Map system property names to their actual data types.
|
|
58
|
+
*
|
|
59
|
+
* This mapping is necessary because system properties have `type='system'` in the metadata
|
|
60
|
+
* (for historical reasons - database schema fieldtype 255), but we need to know their
|
|
61
|
+
* actual data types to render appropriate inputs.
|
|
62
|
+
*
|
|
63
|
+
* The mapping is based on the Lime CRM database schema:
|
|
64
|
+
* - System datetime fields: `timestamp`, `createdtime`, `updatedtime`
|
|
65
|
+
* - System integer fields: `id`, `createduser`, `updateduser`
|
|
66
|
+
* - System string fields: `descriptive`
|
|
67
|
+
*
|
|
68
|
+
* @param propertyName - The system property name (with or without underscore prefix)
|
|
69
|
+
* @returns The actual property type
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* resolveSystemPropertyType('_timestamp'); // Returns 'time'
|
|
74
|
+
* resolveSystemPropertyType('timestamp'); // Returns 'time'
|
|
75
|
+
* resolveSystemPropertyType('_id'); // Returns 'integer'
|
|
76
|
+
* resolveSystemPropertyType('unknown'); // Returns 'string' (default)
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function resolveSystemPropertyType(propertyName) {
|
|
80
|
+
const name = propertyName.replace(/^_/, '');
|
|
81
|
+
return SYSTEM_TYPE_MAP[name] || 'string';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Map Lime CRM property types to Lime Elements date picker types.
|
|
85
|
+
*
|
|
86
|
+
* Lime CRM and Lime Elements use different naming conventions for date/time types,
|
|
87
|
+
* so we need to translate between them:
|
|
88
|
+
*
|
|
89
|
+
* - `'date'` → `'date'` - Date only
|
|
90
|
+
* - `'time'` → `'datetime'` - Full datetime (date + time) in Lime CRM
|
|
91
|
+
* - `'timeofday'` → `'time'` - Time only (hh:mm)
|
|
92
|
+
* - `'month'` → `'month'` - Month picker (YYYY-MM)
|
|
93
|
+
* - `'quarter'` → `'quarter'` - Quarter picker
|
|
94
|
+
* - `'year'` → `'year'` - Year picker
|
|
95
|
+
*
|
|
96
|
+
* @param propertyType - The Lime CRM date/time property type
|
|
97
|
+
* @returns The corresponding Lime Elements date picker type
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* mapPropertyTypeToPickerType('time'); // Returns 'datetime'
|
|
102
|
+
* mapPropertyTypeToPickerType('timeofday'); // Returns 'time'
|
|
103
|
+
* mapPropertyTypeToPickerType('date'); // Returns 'date'
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export function mapPropertyTypeToPickerType(propertyType) {
|
|
107
|
+
return DATE_TIME_TYPE_MAP[propertyType] || 'date';
|
|
108
|
+
}
|