@land-catalyst/batch-data-sdk 1.3.1 → 1.3.3
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/dist/client/client.d.ts +4 -29
- package/dist/client/client.js +7 -95
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/property-field/display.d.ts +100 -0
- package/dist/property-field/display.js +338 -0
- package/package.json +1 -1
package/dist/client/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AxiosInstance, AxiosResponse, AxiosError, InternalAxiosRequestConfig } from "axios";
|
|
2
2
|
import { IBatchDataClient } from "./client.interface";
|
|
3
3
|
import { ILogger } from "../core/logger.interface";
|
|
4
|
-
import { PropertySubscriptionRequest, PropertySubscriptionResponse, PropertyCountRequest, PropertySearchRequest, PropertySearchResponse, PropertyLookupOptions, AddressVerifyRequest, AddressVerifyResponse, AddressAutocompleteRequest, AddressAutocompleteResponse, AddressGeocodeRequest, AddressGeocodeResponse, AddressReverseGeocodeRequest, AddressReverseGeocodeResponse, PropertyLookupRequest, PropertyLookupResponse, PropertyLookupAsyncRequest, PropertyLookupAsyncResponse, PropertySearchAsyncRequest, PropertySearchAsyncResponse, PropertySkipTraceRequest, PropertySkipTraceResponse, PropertySkipTraceAsyncRequest, PropertySkipTraceAsyncResponse, PhoneVerificationRequest, PhoneVerificationResponse, PhoneVerificationAsyncRequest, PhoneVerificationAsyncResponse, PhoneDNCRequest, PhoneDNCResponse, PhoneDNCAsyncRequest, PhoneDNCAsyncResponse, PhoneTCPARequest, PhoneTCPAResponse, PhoneTCPAAsyncRequest, PhoneTCPAAsyncResponse, GetPropertySubscriptionsResponse, GetPropertySubscriptionDetailResponse, DeletePropertySubscriptionResponse, PropertyPermitRequest, PropertyPermitResponse, PropertySkipTraceV3Request, PropertySkipTraceV3Response, PropertySkipTraceV3AsyncRequest, PropertySkipTraceV3AsyncResponse,
|
|
4
|
+
import { PropertySubscriptionRequest, PropertySubscriptionResponse, PropertyCountRequest, PropertySearchRequest, PropertySearchResponse, PropertyLookupOptions, AddressVerifyRequest, AddressVerifyResponse, AddressAutocompleteRequest, AddressAutocompleteResponse, AddressGeocodeRequest, AddressGeocodeResponse, AddressReverseGeocodeRequest, AddressReverseGeocodeResponse, PropertyLookupRequest, PropertyLookupResponse, PropertyLookupAsyncRequest, PropertyLookupAsyncResponse, PropertySearchAsyncRequest, PropertySearchAsyncResponse, PropertySkipTraceRequest, PropertySkipTraceResponse, PropertySkipTraceAsyncRequest, PropertySkipTraceAsyncResponse, PhoneVerificationRequest, PhoneVerificationResponse, PhoneVerificationAsyncRequest, PhoneVerificationAsyncResponse, PhoneDNCRequest, PhoneDNCResponse, PhoneDNCAsyncRequest, PhoneDNCAsyncResponse, PhoneTCPARequest, PhoneTCPAResponse, PhoneTCPAAsyncRequest, PhoneTCPAAsyncResponse, GetPropertySubscriptionsResponse, GetPropertySubscriptionDetailResponse, DeletePropertySubscriptionResponse, PropertyPermitRequest, PropertyPermitResponse, PropertySkipTraceV3Request, PropertySkipTraceV3Response, PropertySkipTraceV3AsyncRequest, PropertySkipTraceV3AsyncResponse, DeliveryConfig } from "../core/types";
|
|
5
5
|
/**
|
|
6
6
|
* Middleware function that executes before an HTTP request is sent.
|
|
7
7
|
* Can modify the request config or perform side effects (logging, metrics, etc.).
|
|
@@ -102,16 +102,6 @@ export interface AsyncWebhookConfiguration {
|
|
|
102
102
|
* Event Hub configuration for async API requests
|
|
103
103
|
* Allows specifying Event Hub config per endpoint type, with fallback to defaults
|
|
104
104
|
*/
|
|
105
|
-
export interface AsyncEventHubConfiguration {
|
|
106
|
-
/**
|
|
107
|
-
* Default Event Hub config used as fallback for all async endpoints
|
|
108
|
-
*/
|
|
109
|
-
default?: EventHubConfig;
|
|
110
|
-
/**
|
|
111
|
-
* Event Hub config for property subscription requests
|
|
112
|
-
*/
|
|
113
|
-
propertySubscription?: EventHubConfig;
|
|
114
|
-
}
|
|
115
105
|
/**
|
|
116
106
|
* BatchData API Client Options
|
|
117
107
|
*/
|
|
@@ -143,15 +133,9 @@ export interface BatchDataClientOptions {
|
|
|
143
133
|
* Allows specifying webhook URLs per endpoint type, with fallback to defaults
|
|
144
134
|
*/
|
|
145
135
|
webhooks?: AsyncWebhookConfiguration;
|
|
146
|
-
/**
|
|
147
|
-
* Optional Event Hub configuration for async requests
|
|
148
|
-
* Allows specifying Event Hub config per endpoint type, with fallback to defaults
|
|
149
|
-
*/
|
|
150
|
-
eventHubs?: AsyncEventHubConfiguration;
|
|
151
136
|
/**
|
|
152
137
|
* Optional default delivery configuration for property subscriptions
|
|
153
138
|
* If provided, this will be used when a subscription request doesn't specify deliveryConfig
|
|
154
|
-
* Takes precedence over webhooks and eventHubs configurations
|
|
155
139
|
*
|
|
156
140
|
* @example
|
|
157
141
|
* ```typescript
|
|
@@ -214,7 +198,6 @@ export declare class BatchDataClient implements IBatchDataClient {
|
|
|
214
198
|
protected readonly axiosInstance: AxiosInstance;
|
|
215
199
|
protected readonly logger: ILogger;
|
|
216
200
|
private readonly webhooks?;
|
|
217
|
-
private readonly eventHubs?;
|
|
218
201
|
private readonly defaultDeliveryConfig?;
|
|
219
202
|
private readonly defaultOptions?;
|
|
220
203
|
private readonly maxTake?;
|
|
@@ -269,20 +252,12 @@ export declare class BatchDataClient implements IBatchDataClient {
|
|
|
269
252
|
*/
|
|
270
253
|
private applyDefaultOptions;
|
|
271
254
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* @returns Delivery config from fallbacks, or undefined if none available
|
|
276
|
-
*/
|
|
277
|
-
private createDeliveryConfigFromFallbacks;
|
|
278
|
-
/**
|
|
279
|
-
* Apply default delivery configuration (Event Hub or webhook) to a property subscription request.
|
|
280
|
-
* Event Hub takes precedence over webhooks if both are configured.
|
|
281
|
-
* If deliveryConfig is not set, it will be created based on available configs (Event Hub preferred).
|
|
255
|
+
* Apply default delivery configuration to a property subscription request.
|
|
256
|
+
* If deliveryConfig is not set in the request, use defaultDeliveryConfig if available.
|
|
282
257
|
*
|
|
283
258
|
* @param request The property subscription request to modify
|
|
284
259
|
* @returns The request with delivery config applied (if needed)
|
|
285
|
-
* @throws Error if deliveryConfig
|
|
260
|
+
* @throws Error if deliveryConfig is not set and no default is available
|
|
286
261
|
*/
|
|
287
262
|
private applyDeliveryConfigDefaults;
|
|
288
263
|
/**
|
package/dist/client/client.js
CHANGED
|
@@ -24,7 +24,6 @@ class BatchDataClient {
|
|
|
24
24
|
this.v3BaseUrl = options.v3BaseUrl || "https://api.batchdata.com/api/v3";
|
|
25
25
|
this.logger = options.logger || new logger_interface_1.ConsoleLogger();
|
|
26
26
|
this.webhooks = options.webhooks;
|
|
27
|
-
this.eventHubs = options.eventHubs;
|
|
28
27
|
this.defaultDeliveryConfig = options.defaultDeliveryConfig;
|
|
29
28
|
this.defaultOptions = options.defaultOptions;
|
|
30
29
|
this.maxTake = options.maxTake;
|
|
@@ -290,108 +289,21 @@ class BatchDataClient {
|
|
|
290
289
|
};
|
|
291
290
|
}
|
|
292
291
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* @returns Delivery config from fallbacks, or undefined if none available
|
|
297
|
-
*/
|
|
298
|
-
createDeliveryConfigFromFallbacks() {
|
|
299
|
-
// Explicit defaultDeliveryConfig takes highest precedence
|
|
300
|
-
if (this.defaultDeliveryConfig) {
|
|
301
|
-
return this.defaultDeliveryConfig;
|
|
302
|
-
}
|
|
303
|
-
// Determine which Event Hub config to use (property subscription-specific first, then default)
|
|
304
|
-
const eventHubConfig = this.eventHubs?.propertySubscription ?? this.eventHubs?.default;
|
|
305
|
-
// Determine which webhook URLs to use
|
|
306
|
-
let webhookUrl;
|
|
307
|
-
let errorWebhookUrl;
|
|
308
|
-
// First, try property subscription-specific webhook config
|
|
309
|
-
if (this.webhooks?.propertySubscription) {
|
|
310
|
-
webhookUrl = this.webhooks.propertySubscription.url;
|
|
311
|
-
errorWebhookUrl = this.webhooks.propertySubscription.errorUrl;
|
|
312
|
-
}
|
|
313
|
-
// Fall back to default webhook config
|
|
314
|
-
if (!webhookUrl && this.webhooks?.default) {
|
|
315
|
-
webhookUrl = this.webhooks.default.url;
|
|
316
|
-
errorWebhookUrl = errorWebhookUrl || this.webhooks.default.errorUrl;
|
|
317
|
-
}
|
|
318
|
-
// Event Hub takes precedence over webhooks
|
|
319
|
-
if (eventHubConfig) {
|
|
320
|
-
return {
|
|
321
|
-
type: "event-hub",
|
|
322
|
-
eventHub: eventHubConfig,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
if (webhookUrl || errorWebhookUrl) {
|
|
326
|
-
return {
|
|
327
|
-
type: "webhook",
|
|
328
|
-
...(webhookUrl && { url: webhookUrl }),
|
|
329
|
-
...(errorWebhookUrl && { errorUrl: errorWebhookUrl }),
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
return undefined;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Apply default delivery configuration (Event Hub or webhook) to a property subscription request.
|
|
336
|
-
* Event Hub takes precedence over webhooks if both are configured.
|
|
337
|
-
* If deliveryConfig is not set, it will be created based on available configs (Event Hub preferred).
|
|
292
|
+
* Apply default delivery configuration to a property subscription request.
|
|
293
|
+
* If deliveryConfig is not set in the request, use defaultDeliveryConfig if available.
|
|
338
294
|
*
|
|
339
295
|
* @param request The property subscription request to modify
|
|
340
296
|
* @returns The request with delivery config applied (if needed)
|
|
341
|
-
* @throws Error if deliveryConfig
|
|
297
|
+
* @throws Error if deliveryConfig is not set and no default is available
|
|
342
298
|
*/
|
|
343
299
|
applyDeliveryConfigDefaults(request) {
|
|
344
|
-
|
|
345
|
-
// If deliveryConfig is not set, create it from fallbacks
|
|
300
|
+
// If deliveryConfig is not set, use defaultDeliveryConfig
|
|
346
301
|
if (!request.deliveryConfig) {
|
|
347
|
-
if (
|
|
348
|
-
return { ...request, deliveryConfig:
|
|
302
|
+
if (this.defaultDeliveryConfig) {
|
|
303
|
+
return { ...request, deliveryConfig: this.defaultDeliveryConfig };
|
|
349
304
|
}
|
|
350
|
-
// If no fallback config is available, throw an error
|
|
351
305
|
throw new Error("Property subscription requires delivery configuration. " +
|
|
352
|
-
"Please provide deliveryConfig in the request, or configure
|
|
353
|
-
}
|
|
354
|
-
const deliveryConfig = request.deliveryConfig;
|
|
355
|
-
// If type is event-hub, use request's eventHub or fallback's eventHub
|
|
356
|
-
if (deliveryConfig.type === "event-hub") {
|
|
357
|
-
const eventHubConfig = deliveryConfig.eventHub ??
|
|
358
|
-
(fallbackConfig?.type === "event-hub"
|
|
359
|
-
? fallbackConfig.eventHub
|
|
360
|
-
: undefined);
|
|
361
|
-
if (!eventHubConfig) {
|
|
362
|
-
throw new Error("Property subscription requires Event Hub configuration, but none is available. " +
|
|
363
|
-
"Please provide eventHub in deliveryConfig, or configure eventHubs in BatchDataClient options.");
|
|
364
|
-
}
|
|
365
|
-
if (!deliveryConfig.eventHub) {
|
|
366
|
-
return {
|
|
367
|
-
...request,
|
|
368
|
-
deliveryConfig: {
|
|
369
|
-
...deliveryConfig,
|
|
370
|
-
eventHub: eventHubConfig,
|
|
371
|
-
},
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// If type is webhook, use request's URLs or fallback's URLs
|
|
376
|
-
if (deliveryConfig.type === "webhook") {
|
|
377
|
-
const webhookUrl = deliveryConfig.url ??
|
|
378
|
-
(fallbackConfig?.type === "webhook" ? fallbackConfig.url : undefined);
|
|
379
|
-
const errorWebhookUrl = deliveryConfig.errorUrl ??
|
|
380
|
-
(fallbackConfig?.type === "webhook"
|
|
381
|
-
? fallbackConfig.errorUrl
|
|
382
|
-
: undefined);
|
|
383
|
-
if ((!deliveryConfig.url && webhookUrl) ||
|
|
384
|
-
(!deliveryConfig.errorUrl && errorWebhookUrl)) {
|
|
385
|
-
return {
|
|
386
|
-
...request,
|
|
387
|
-
deliveryConfig: {
|
|
388
|
-
...deliveryConfig,
|
|
389
|
-
...(webhookUrl && !deliveryConfig.url && { url: webhookUrl }),
|
|
390
|
-
...(errorWebhookUrl &&
|
|
391
|
-
!deliveryConfig.errorUrl && { errorUrl: errorWebhookUrl }),
|
|
392
|
-
},
|
|
393
|
-
};
|
|
394
|
-
}
|
|
306
|
+
"Please provide deliveryConfig in the request, or configure defaultDeliveryConfig in BatchDataClient options.");
|
|
395
307
|
}
|
|
396
308
|
return request;
|
|
397
309
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./client/client.interface";
|
|
|
12
12
|
export * from "./core/logger.interface";
|
|
13
13
|
export * from "./property-field/metadata";
|
|
14
14
|
export * from "./property-field/utils";
|
|
15
|
+
export * from "./property-field/display";
|
|
15
16
|
export * from "./property-field/search-criteria-filter-context";
|
|
16
17
|
export type { SearchCriteriaFilterContext, SearchCriteriaFilterType, } from "./property-field/search-criteria-filter-context";
|
|
17
18
|
export * from "./property-field/search-criteria-ai-context";
|
package/dist/index.js
CHANGED
|
@@ -28,5 +28,6 @@ __exportStar(require("./client/client.interface"), exports);
|
|
|
28
28
|
__exportStar(require("./core/logger.interface"), exports);
|
|
29
29
|
__exportStar(require("./property-field/metadata"), exports);
|
|
30
30
|
__exportStar(require("./property-field/utils"), exports);
|
|
31
|
+
__exportStar(require("./property-field/display"), exports);
|
|
31
32
|
__exportStar(require("./property-field/search-criteria-filter-context"), exports);
|
|
32
33
|
__exportStar(require("./property-field/search-criteria-ai-context"), exports);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property Field Display Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utilities for formatting property field values for display in the UI.
|
|
5
|
+
* These helpers take raw property data and field metadata and produce
|
|
6
|
+
* human-readable formatted strings.
|
|
7
|
+
*/
|
|
8
|
+
import type { Property } from "../core/types";
|
|
9
|
+
import { type PropertyFieldMetadata, type PropertyFieldType } from "./metadata";
|
|
10
|
+
/**
|
|
11
|
+
* Display field entry for rendering property data
|
|
12
|
+
*/
|
|
13
|
+
export interface PropertyDisplayField {
|
|
14
|
+
/** The field path (e.g., "building.yearBuilt") */
|
|
15
|
+
fieldPath: string;
|
|
16
|
+
/** Human-readable label */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Formatted display value */
|
|
19
|
+
value: string;
|
|
20
|
+
/** The field's data type */
|
|
21
|
+
dataType: PropertyFieldType;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Format a property field value based on its metadata
|
|
25
|
+
*
|
|
26
|
+
* @param value The raw field value
|
|
27
|
+
* @param metadata The field's metadata
|
|
28
|
+
* @returns Formatted display string, or empty string for null/undefined
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const meta = getPropertyFieldMetadata("valuation.estimatedValue");
|
|
33
|
+
* formatPropertyFieldValue(450000, meta); // "$450,000"
|
|
34
|
+
*
|
|
35
|
+
* const yearMeta = getPropertyFieldMetadata("building.yearBuilt");
|
|
36
|
+
* formatPropertyFieldValue(1985, yearMeta); // "1985"
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function formatPropertyFieldValue(value: unknown, metadata: PropertyFieldMetadata): string;
|
|
40
|
+
/**
|
|
41
|
+
* Get the display label for a property field path
|
|
42
|
+
*
|
|
43
|
+
* @param fieldPath The field path (e.g., "building.yearBuilt")
|
|
44
|
+
* @returns Human-readable label (e.g., "Year Built")
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* getPropertyFieldDisplayLabel("building.yearBuilt"); // "Year Built"
|
|
49
|
+
* getPropertyFieldDisplayLabel("owner.mailingAddress.city"); // "City"
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function getPropertyFieldDisplayLabel(fieldPath: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Get the display name for a property response group
|
|
55
|
+
*
|
|
56
|
+
* @param groupName The group name (e.g., "building", "propertyOwnerProfile")
|
|
57
|
+
* @returns Human-readable display name (e.g., "Building", "Owner Portfolio")
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* getPropertyGroupDisplayName("building"); // "Building"
|
|
62
|
+
* getPropertyGroupDisplayName("propertyOwnerProfile"); // "Owner Portfolio"
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function getPropertyGroupDisplayName(groupName: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Get all displayable fields for a property within a specific group
|
|
68
|
+
*
|
|
69
|
+
* Returns formatted field data for rendering in the UI. Fields with
|
|
70
|
+
* null/undefined values are excluded.
|
|
71
|
+
*
|
|
72
|
+
* @param property The property object (can be raw BatchData JSON)
|
|
73
|
+
* @param groupName The response group name (e.g., "building", "valuation")
|
|
74
|
+
* @returns Array of display field objects with label, formatted value, etc.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const buildingFields = getPropertyFieldsForDisplay(property, "building");
|
|
79
|
+
* // Returns:
|
|
80
|
+
* // [
|
|
81
|
+
* // { fieldPath: "building.yearBuilt", label: "Year Built", value: "1985", dataType: "number" },
|
|
82
|
+
* // { fieldPath: "building.bedrooms", label: "Bedrooms", value: "3", dataType: "number" },
|
|
83
|
+
* // ...
|
|
84
|
+
* // ]
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function getPropertyFieldsForDisplay(property: Property | Record<string, unknown>, groupName: string): PropertyDisplayField[];
|
|
88
|
+
/**
|
|
89
|
+
* Get all response groups that have displayable data in a property
|
|
90
|
+
*
|
|
91
|
+
* @param property The property object
|
|
92
|
+
* @returns Array of group names that have at least one displayable field
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const groups = getPropertyGroupsWithData(property);
|
|
97
|
+
* // Returns: ["address", "building", "valuation", "owner", ...]
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export declare function getPropertyGroupsWithData(property: Property | Record<string, unknown>): string[];
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Property Field Display Utilities
|
|
4
|
+
*
|
|
5
|
+
* Utilities for formatting property field values for display in the UI.
|
|
6
|
+
* These helpers take raw property data and field metadata and produce
|
|
7
|
+
* human-readable formatted strings.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.formatPropertyFieldValue = formatPropertyFieldValue;
|
|
11
|
+
exports.getPropertyFieldDisplayLabel = getPropertyFieldDisplayLabel;
|
|
12
|
+
exports.getPropertyGroupDisplayName = getPropertyGroupDisplayName;
|
|
13
|
+
exports.getPropertyFieldsForDisplay = getPropertyFieldsForDisplay;
|
|
14
|
+
exports.getPropertyGroupsWithData = getPropertyGroupsWithData;
|
|
15
|
+
const metadata_1 = require("./metadata");
|
|
16
|
+
const utils_1 = require("./utils");
|
|
17
|
+
/**
|
|
18
|
+
* Group display name mappings
|
|
19
|
+
*/
|
|
20
|
+
const GROUP_DISPLAY_NAMES = {
|
|
21
|
+
address: "Address",
|
|
22
|
+
assessment: "Assessment",
|
|
23
|
+
building: "Building",
|
|
24
|
+
deedHistory: "Deed History",
|
|
25
|
+
demographics: "Demographics",
|
|
26
|
+
foreclosure: "Foreclosure",
|
|
27
|
+
general: "General",
|
|
28
|
+
ids: "Identifiers",
|
|
29
|
+
intel: "Intel",
|
|
30
|
+
involuntaryLien: "Involuntary Liens",
|
|
31
|
+
legal: "Legal",
|
|
32
|
+
listing: "Listing",
|
|
33
|
+
lot: "Lot",
|
|
34
|
+
meta: "Meta",
|
|
35
|
+
mortgageHistory: "Mortgage History",
|
|
36
|
+
openLien: "Open Liens",
|
|
37
|
+
owner: "Owner",
|
|
38
|
+
permit: "Permits",
|
|
39
|
+
propertyOwnerProfile: "Owner Portfolio",
|
|
40
|
+
quickLists: "Quick Lists",
|
|
41
|
+
sale: "Sale History",
|
|
42
|
+
tax: "Tax",
|
|
43
|
+
valuation: "Valuation",
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Convert a field name to a display label
|
|
47
|
+
* Handles camelCase and common abbreviations
|
|
48
|
+
*/
|
|
49
|
+
function fieldNameToLabel(name) {
|
|
50
|
+
// Handle common abbreviations
|
|
51
|
+
const abbrevs = {
|
|
52
|
+
sqft: "Sq Ft",
|
|
53
|
+
apn: "APN",
|
|
54
|
+
adu: "ADU",
|
|
55
|
+
avm: "AVM",
|
|
56
|
+
hoa: "HOA",
|
|
57
|
+
id: "ID",
|
|
58
|
+
mls: "MLS",
|
|
59
|
+
url: "URL",
|
|
60
|
+
};
|
|
61
|
+
const lower = name.toLowerCase();
|
|
62
|
+
if (abbrevs[lower]) {
|
|
63
|
+
return abbrevs[lower];
|
|
64
|
+
}
|
|
65
|
+
// Split camelCase and capitalize
|
|
66
|
+
return name
|
|
67
|
+
.replace(/([A-Z])/g, " $1")
|
|
68
|
+
.replace(/^./, (c) => c.toUpperCase())
|
|
69
|
+
.trim();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Format a number as currency (USD)
|
|
73
|
+
*/
|
|
74
|
+
function formatCurrency(value) {
|
|
75
|
+
return new Intl.NumberFormat("en-US", {
|
|
76
|
+
style: "currency",
|
|
77
|
+
currency: "USD",
|
|
78
|
+
minimumFractionDigits: 0,
|
|
79
|
+
maximumFractionDigits: 0,
|
|
80
|
+
}).format(value);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Format a number with commas
|
|
84
|
+
*/
|
|
85
|
+
function formatNumber(value) {
|
|
86
|
+
return new Intl.NumberFormat("en-US").format(value);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Format a date string
|
|
90
|
+
*/
|
|
91
|
+
function formatDate(value) {
|
|
92
|
+
try {
|
|
93
|
+
const date = typeof value === "string" ? new Date(value) : value;
|
|
94
|
+
if (isNaN(date.getTime()))
|
|
95
|
+
return String(value);
|
|
96
|
+
return date.toLocaleDateString("en-US", {
|
|
97
|
+
year: "numeric",
|
|
98
|
+
month: "short",
|
|
99
|
+
day: "numeric",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return String(value);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if a field name suggests currency formatting
|
|
108
|
+
*/
|
|
109
|
+
function isCurrencyField(fieldPath, name) {
|
|
110
|
+
const lower = (fieldPath + name).toLowerCase();
|
|
111
|
+
return (lower.includes("value") ||
|
|
112
|
+
lower.includes("price") ||
|
|
113
|
+
lower.includes("assessment") ||
|
|
114
|
+
lower.includes("amount") ||
|
|
115
|
+
lower.includes("equity") ||
|
|
116
|
+
lower.includes("balance") ||
|
|
117
|
+
lower.includes("cost"));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if a field name suggests year formatting
|
|
121
|
+
*/
|
|
122
|
+
function isYearField(name) {
|
|
123
|
+
const lower = name.toLowerCase();
|
|
124
|
+
return lower.includes("year") && !lower.includes("years");
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if a field name suggests square footage formatting
|
|
128
|
+
*/
|
|
129
|
+
function isSqftField(name) {
|
|
130
|
+
const lower = name.toLowerCase();
|
|
131
|
+
return (lower.includes("sqft") ||
|
|
132
|
+
lower.includes("area") ||
|
|
133
|
+
lower.includes("squarefeet"));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a field name suggests acreage formatting
|
|
137
|
+
*/
|
|
138
|
+
function isAcresField(name) {
|
|
139
|
+
const lower = name.toLowerCase();
|
|
140
|
+
return lower.includes("acre") || lower.includes("acreage");
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Format a property field value based on its metadata
|
|
144
|
+
*
|
|
145
|
+
* @param value The raw field value
|
|
146
|
+
* @param metadata The field's metadata
|
|
147
|
+
* @returns Formatted display string, or empty string for null/undefined
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const meta = getPropertyFieldMetadata("valuation.estimatedValue");
|
|
152
|
+
* formatPropertyFieldValue(450000, meta); // "$450,000"
|
|
153
|
+
*
|
|
154
|
+
* const yearMeta = getPropertyFieldMetadata("building.yearBuilt");
|
|
155
|
+
* formatPropertyFieldValue(1985, yearMeta); // "1985"
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
function formatPropertyFieldValue(value, metadata) {
|
|
159
|
+
// Handle null/undefined
|
|
160
|
+
if (value == null) {
|
|
161
|
+
return "";
|
|
162
|
+
}
|
|
163
|
+
const { dataType, name, fieldPath, possibleValuesRef } = metadata;
|
|
164
|
+
// Handle boolean
|
|
165
|
+
if (dataType === "boolean") {
|
|
166
|
+
return value ? "Yes" : "No";
|
|
167
|
+
}
|
|
168
|
+
// Handle date
|
|
169
|
+
if (dataType === "date") {
|
|
170
|
+
return formatDate(value);
|
|
171
|
+
}
|
|
172
|
+
// Handle array[string]
|
|
173
|
+
if (dataType === "array[string]") {
|
|
174
|
+
if (Array.isArray(value)) {
|
|
175
|
+
return value.filter((v) => v != null).join(", ");
|
|
176
|
+
}
|
|
177
|
+
return String(value);
|
|
178
|
+
}
|
|
179
|
+
// Handle array[number]
|
|
180
|
+
if (dataType === "array[number]") {
|
|
181
|
+
if (Array.isArray(value)) {
|
|
182
|
+
return value
|
|
183
|
+
.filter((v) => v != null)
|
|
184
|
+
.map(formatNumber)
|
|
185
|
+
.join(", ");
|
|
186
|
+
}
|
|
187
|
+
return String(value);
|
|
188
|
+
}
|
|
189
|
+
// Handle number with formatting rules
|
|
190
|
+
if (dataType === "number" && typeof value === "number") {
|
|
191
|
+
// Year - plain integer
|
|
192
|
+
if (isYearField(name)) {
|
|
193
|
+
return String(Math.round(value));
|
|
194
|
+
}
|
|
195
|
+
// Currency fields
|
|
196
|
+
if (isCurrencyField(fieldPath, name)) {
|
|
197
|
+
return formatCurrency(value);
|
|
198
|
+
}
|
|
199
|
+
// Square footage
|
|
200
|
+
if (isSqftField(name)) {
|
|
201
|
+
return `${formatNumber(Math.round(value))} sq ft`;
|
|
202
|
+
}
|
|
203
|
+
// Acreage
|
|
204
|
+
if (isAcresField(name)) {
|
|
205
|
+
return `${value.toFixed(2)} acres`;
|
|
206
|
+
}
|
|
207
|
+
// Default number formatting
|
|
208
|
+
if (Number.isInteger(value)) {
|
|
209
|
+
return formatNumber(value);
|
|
210
|
+
}
|
|
211
|
+
return value.toFixed(2);
|
|
212
|
+
}
|
|
213
|
+
// Handle string with possibleValues lookup
|
|
214
|
+
if (dataType === "string" && typeof value === "string") {
|
|
215
|
+
// If there's a possibleValuesRef, the value itself is usually the display value
|
|
216
|
+
// in POSSIBLE_VALUES_CONSTANTS (they're already human-readable)
|
|
217
|
+
if (possibleValuesRef) {
|
|
218
|
+
const possibleValues = metadata_1.POSSIBLE_VALUES_CONSTANTS[possibleValuesRef];
|
|
219
|
+
if (possibleValues?.includes(value)) {
|
|
220
|
+
return value; // Already display-friendly
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
// Handle object - just return stringified for now
|
|
226
|
+
if (dataType === "object" && typeof value === "object") {
|
|
227
|
+
return JSON.stringify(value);
|
|
228
|
+
}
|
|
229
|
+
// Default: convert to string
|
|
230
|
+
return String(value);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get the display label for a property field path
|
|
234
|
+
*
|
|
235
|
+
* @param fieldPath The field path (e.g., "building.yearBuilt")
|
|
236
|
+
* @returns Human-readable label (e.g., "Year Built")
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```typescript
|
|
240
|
+
* getPropertyFieldDisplayLabel("building.yearBuilt"); // "Year Built"
|
|
241
|
+
* getPropertyFieldDisplayLabel("owner.mailingAddress.city"); // "City"
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
function getPropertyFieldDisplayLabel(fieldPath) {
|
|
245
|
+
const metadata = metadata_1.PROPERTY_FIELD_METADATA[fieldPath];
|
|
246
|
+
if (metadata) {
|
|
247
|
+
return fieldNameToLabel(metadata.name);
|
|
248
|
+
}
|
|
249
|
+
// Fallback: use the last part of the path
|
|
250
|
+
const parts = fieldPath.split(".");
|
|
251
|
+
const last = parts[parts.length - 1] || fieldPath;
|
|
252
|
+
return fieldNameToLabel(last);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get the display name for a property response group
|
|
256
|
+
*
|
|
257
|
+
* @param groupName The group name (e.g., "building", "propertyOwnerProfile")
|
|
258
|
+
* @returns Human-readable display name (e.g., "Building", "Owner Portfolio")
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```typescript
|
|
262
|
+
* getPropertyGroupDisplayName("building"); // "Building"
|
|
263
|
+
* getPropertyGroupDisplayName("propertyOwnerProfile"); // "Owner Portfolio"
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
function getPropertyGroupDisplayName(groupName) {
|
|
267
|
+
return GROUP_DISPLAY_NAMES[groupName] || fieldNameToLabel(groupName);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get all displayable fields for a property within a specific group
|
|
271
|
+
*
|
|
272
|
+
* Returns formatted field data for rendering in the UI. Fields with
|
|
273
|
+
* null/undefined values are excluded.
|
|
274
|
+
*
|
|
275
|
+
* @param property The property object (can be raw BatchData JSON)
|
|
276
|
+
* @param groupName The response group name (e.g., "building", "valuation")
|
|
277
|
+
* @returns Array of display field objects with label, formatted value, etc.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```typescript
|
|
281
|
+
* const buildingFields = getPropertyFieldsForDisplay(property, "building");
|
|
282
|
+
* // Returns:
|
|
283
|
+
* // [
|
|
284
|
+
* // { fieldPath: "building.yearBuilt", label: "Year Built", value: "1985", dataType: "number" },
|
|
285
|
+
* // { fieldPath: "building.bedrooms", label: "Bedrooms", value: "3", dataType: "number" },
|
|
286
|
+
* // ...
|
|
287
|
+
* // ]
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
function getPropertyFieldsForDisplay(property, groupName) {
|
|
291
|
+
const groupFields = (0, utils_1.getPropertyGroupFields)(groupName);
|
|
292
|
+
const results = [];
|
|
293
|
+
for (const metadata of groupFields) {
|
|
294
|
+
// Skip array element placeholders ([n] patterns)
|
|
295
|
+
if (metadata.isArrayElement)
|
|
296
|
+
continue;
|
|
297
|
+
const value = (0, utils_1.getPropertyFieldValue)(property, metadata.fieldPath);
|
|
298
|
+
// Skip null/undefined values
|
|
299
|
+
if (value == null)
|
|
300
|
+
continue;
|
|
301
|
+
// Skip empty strings
|
|
302
|
+
if (typeof value === "string" && value.trim() === "")
|
|
303
|
+
continue;
|
|
304
|
+
// Skip empty arrays
|
|
305
|
+
if (Array.isArray(value) && value.length === 0)
|
|
306
|
+
continue;
|
|
307
|
+
const formatted = formatPropertyFieldValue(value, metadata);
|
|
308
|
+
// Skip if formatting returned empty
|
|
309
|
+
if (!formatted)
|
|
310
|
+
continue;
|
|
311
|
+
results.push({
|
|
312
|
+
fieldPath: metadata.fieldPath,
|
|
313
|
+
label: getPropertyFieldDisplayLabel(metadata.fieldPath),
|
|
314
|
+
value: formatted,
|
|
315
|
+
dataType: metadata.dataType,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return results;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get all response groups that have displayable data in a property
|
|
322
|
+
*
|
|
323
|
+
* @param property The property object
|
|
324
|
+
* @returns Array of group names that have at least one displayable field
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* const groups = getPropertyGroupsWithData(property);
|
|
329
|
+
* // Returns: ["address", "building", "valuation", "owner", ...]
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
function getPropertyGroupsWithData(property) {
|
|
333
|
+
const allGroups = Object.keys(GROUP_DISPLAY_NAMES);
|
|
334
|
+
return allGroups.filter((group) => {
|
|
335
|
+
const fields = getPropertyFieldsForDisplay(property, group);
|
|
336
|
+
return fields.length > 0;
|
|
337
|
+
});
|
|
338
|
+
}
|
package/package.json
CHANGED