@deallony/shared 1.2.64 → 1.2.69

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 (67) hide show
  1. package/dist/cjs/constants/company/Company.d.ts +12 -0
  2. package/dist/cjs/constants/company/Company.js +13 -1
  3. package/dist/cjs/constants/events/Event.js +3 -0
  4. package/dist/cjs/constants/events/EventPricing.d.ts +44 -0
  5. package/dist/cjs/constants/events/EventPricing.js +18 -0
  6. package/dist/cjs/constants/events/index.d.ts +1 -0
  7. package/dist/cjs/constants/events/index.js +1 -0
  8. package/dist/cjs/constants/routes/app.d.ts +30 -1
  9. package/dist/cjs/constants/routes/app.js +7 -1
  10. package/dist/cjs/dto/ai/gemini-api-key.dto.d.ts +4 -1
  11. package/dist/cjs/dto/analysis/Analysis.dto.d.ts +4 -0
  12. package/dist/cjs/dto/analysis/Analysis.dto.js +2 -0
  13. package/dist/cjs/dto/events/Event.dto.d.ts +3 -1
  14. package/dist/cjs/dto/index.d.ts +1 -0
  15. package/dist/cjs/dto/index.js +2 -1
  16. package/dist/cjs/schema/ad/save-ad.schema.d.ts +35 -0
  17. package/dist/cjs/schema/ad/save-ad.schema.js +23 -1
  18. package/dist/cjs/schema/ai/gemini-api-key.schema.d.ts +46 -3
  19. package/dist/cjs/schema/ai/gemini-api-key.schema.js +36 -9
  20. package/dist/cjs/schema/analysis/analysis-report.schema.d.ts +17 -0
  21. package/dist/cjs/schema/analysis/analysis-report.schema.js +29 -0
  22. package/dist/cjs/schema/analysis/index.d.ts +1 -0
  23. package/dist/cjs/schema/analysis/index.js +17 -0
  24. package/dist/cjs/schema/communication/index.d.ts +1 -0
  25. package/dist/cjs/schema/communication/index.js +1 -0
  26. package/dist/cjs/schema/communication/offer.schema.d.ts +49 -0
  27. package/dist/cjs/schema/communication/offer.schema.js +31 -0
  28. package/dist/cjs/schema/communication/support-ticket.schema.d.ts +3 -3
  29. package/dist/cjs/schema/events/Event.schema.d.ts +20 -0
  30. package/dist/cjs/schema/events/Event.schema.js +11 -1
  31. package/dist/cjs/schema/index.d.ts +1 -0
  32. package/dist/cjs/schema/index.js +2 -1
  33. package/dist/cjs/types/ad/Ad.d.ts +27 -1
  34. package/dist/cjs/types/ad-common/PriceType.d.ts +2 -0
  35. package/dist/cjs/types/analysis/AnalysisDailySnapshot.d.ts +98 -0
  36. package/dist/cjs/types/common/MetaData.d.ts +3 -0
  37. package/dist/cjs/types/communication/Chat.d.ts +30 -1
  38. package/dist/cjs/types/communication/Chat.js +10 -1
  39. package/dist/cjs/types/company/Company.d.ts +26 -0
  40. package/dist/cjs/types/events/Event.d.ts +8 -0
  41. package/dist/cjs/types/integrations/GeminiApiKey.d.ts +11 -2
  42. package/dist/cjs/utils/analysis.d.ts +7 -0
  43. package/dist/cjs/utils/analysis.js +14 -0
  44. package/dist/cjs/utils/index.d.ts +2 -0
  45. package/dist/cjs/utils/index.js +2 -0
  46. package/dist/cjs/utils/rentPrices.d.ts +8 -0
  47. package/dist/cjs/utils/rentPrices.js +33 -0
  48. package/dist/esm/constants/company/Company.js +12 -0
  49. package/dist/esm/constants/events/Event.js +3 -0
  50. package/dist/esm/constants/events/EventPricing.js +15 -0
  51. package/dist/esm/constants/events/index.js +1 -0
  52. package/dist/esm/constants/routes/app.js +7 -1
  53. package/dist/esm/dto/analysis/Analysis.dto.js +1 -0
  54. package/dist/esm/dto/index.js +1 -0
  55. package/dist/esm/schema/ad/save-ad.schema.js +22 -0
  56. package/dist/esm/schema/ai/gemini-api-key.schema.js +35 -8
  57. package/dist/esm/schema/analysis/analysis-report.schema.js +26 -0
  58. package/dist/esm/schema/analysis/index.js +1 -0
  59. package/dist/esm/schema/communication/index.js +1 -0
  60. package/dist/esm/schema/communication/offer.schema.js +28 -0
  61. package/dist/esm/schema/events/Event.schema.js +11 -1
  62. package/dist/esm/schema/index.js +1 -0
  63. package/dist/esm/types/communication/Chat.js +9 -0
  64. package/dist/esm/utils/analysis.js +10 -0
  65. package/dist/esm/utils/index.js +2 -0
  66. package/dist/esm/utils/rentPrices.js +26 -0
  67. package/package.json +58 -58
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rentOfferTotal = exports.sortRentPrices = exports.getDisplayRentPrice = exports.getMainRentPrice = void 0;
4
+ /** The rent price shown on cards: the one marked main, else the first listed. */
5
+ const getMainRentPrice = (ad) => {
6
+ var _a, _b, _c;
7
+ const prices = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) !== null && _a !== void 0 ? _a : [];
8
+ return (_c = (_b = prices.find((item) => item.is_main)) !== null && _b !== void 0 ? _b : prices[0]) !== null && _c !== void 0 ? _c : null;
9
+ };
10
+ exports.getMainRentPrice = getMainRentPrice;
11
+ /** The price to show for an ad in a list: the filtered period, else the main rent price. */
12
+ const getDisplayRentPrice = (ad, priceTypeId) => {
13
+ var _a;
14
+ if (ad === null || ad === void 0 ? void 0 : ad.matched_rent_price)
15
+ return ad.matched_rent_price;
16
+ if (priceTypeId) {
17
+ const match = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) === null || _a === void 0 ? void 0 : _a.find((item) => item.price_type_id === Number(priceTypeId));
18
+ if (match)
19
+ return match;
20
+ }
21
+ return (0, exports.getMainRentPrice)(ad);
22
+ };
23
+ exports.getDisplayRentPrice = getDisplayRentPrice;
24
+ /** Rent prices ordered from the shortest period to the longest. */
25
+ const sortRentPrices = (prices) => [...(prices !== null && prices !== void 0 ? prices : [])].sort((a, b) => {
26
+ var _a, _b, _c, _d, _e, _f, _g, _h;
27
+ return ((_b = (_a = a.price_type) === null || _a === void 0 ? void 0 : _a.period_days) !== null && _b !== void 0 ? _b : Number.MAX_SAFE_INTEGER) -
28
+ ((_d = (_c = b.price_type) === null || _c === void 0 ? void 0 : _c.period_days) !== null && _d !== void 0 ? _d : Number.MAX_SAFE_INTEGER) ||
29
+ ((_f = (_e = a.price_type) === null || _e === void 0 ? void 0 : _e.display_order) !== null && _f !== void 0 ? _f : 0) - ((_h = (_g = b.price_type) === null || _g === void 0 ? void 0 : _g.display_order) !== null && _h !== void 0 ? _h : 0);
30
+ });
31
+ exports.sortRentPrices = sortRentPrices;
32
+ const rentOfferTotal = (pricePerPeriod, duration) => Math.max(0, Number(pricePerPeriod) || 0) * Math.max(0, Math.floor(Number(duration) || 0));
33
+ exports.rentOfferTotal = rentOfferTotal;
@@ -7,3 +7,15 @@ export const CompanyStatusOptions = Object.values(COMPANY_STATUS).map((status) =
7
7
  value: status,
8
8
  label: status.charAt(0).toUpperCase() + status.slice(1),
9
9
  }));
10
+ /** A user's role inside a company; a company has exactly one owner */
11
+ export const COMPANY_MEMBER_ROLE = {
12
+ OWNER: 'owner',
13
+ ADMIN: 'admin',
14
+ MEMBER: 'member',
15
+ };
16
+ /** What the account represents; all three behave the same */
17
+ export const COMPANY_TYPE = {
18
+ COMPANY: 'company',
19
+ TEAM: 'team',
20
+ OTHER: 'other',
21
+ };
@@ -11,6 +11,9 @@ export const emptyEvent = {
11
11
  event_template: null,
12
12
  slug: "",
13
13
  invitation_scan_type: "none",
14
+ venue_id: null,
15
+ venue_name: null,
16
+ location_label: null,
14
17
  wedding: {
15
18
  ...emptyEventWedding,
16
19
  id: undefined,
@@ -0,0 +1,15 @@
1
+ export const EVENT_CREATION_PRICE_SETTING = "event_creation_price";
2
+ export const EventOwnerTypes = {
3
+ COMPANY: { value: "company", name: { en: "Company", ar: "شركة" } },
4
+ USER: { value: "user", name: { en: "Personal", ar: "شخصي" } },
5
+ };
6
+ export const EventOwnerTypeValues = ["company", "user"];
7
+ export const EventOwnerTypeOptions = Object.values(EventOwnerTypes).map((t) => ({
8
+ value: t.value,
9
+ name: t.name,
10
+ }));
11
+ export const EventPublishPaymentMethodValues = [
12
+ "balance",
13
+ "loyalty",
14
+ "combined",
15
+ ];
@@ -4,3 +4,4 @@ export * from "./EventWedding.js";
4
4
  export * from "./EventTemplate.js";
5
5
  export * from "./EventTimeline.js";
6
6
  export * from "./EventInvitationScanType.js";
7
+ export * from "./EventPricing.js";
@@ -135,6 +135,7 @@ export const APP_ROUTES = {
135
135
  by_whatsapp: props.by_whatsapp,
136
136
  phone: props.phone,
137
137
  ad_price: props.ad_price,
138
+ rent_prices: props.rent_prices,
138
139
  },
139
140
  }),
140
141
  },
@@ -165,6 +166,8 @@ export const APP_ROUTES = {
165
166
  new: () => ({ pathname: "/company/new" }),
166
167
  edit: (id) => ({ pathname: "/company/new", params: { id } }),
167
168
  details: (id) => ({ pathname: "/company/details/[id]", params: { id } }),
169
+ /** Public profile with the company's ads */
170
+ profile: (id) => ({ pathname: "/company/profile/[id]", params: { id } }),
168
171
  },
169
172
  venue: {
170
173
  new: (companyId) => ({ pathname: "/venue/new", params: { company_id: companyId } }),
@@ -172,7 +175,10 @@ export const APP_ROUTES = {
172
175
  details: (id) => ({ pathname: "/venue/details/[id]", params: { id } }),
173
176
  },
174
177
  event: {
175
- new: (venueId) => ({ pathname: "/event/new", params: { venue_id: venueId } }),
178
+ new: (venueId) => venueId ? { pathname: "/event/new", params: { venue_id: venueId } } : { pathname: "/event/new", params: { personal: "1" } },
179
+ start: () => ({ pathname: "/event/start" }),
180
+ mine: () => ({ pathname: "/event/mine" }),
181
+ publish: (id) => ({ pathname: "/event/publish/[id]", params: { id } }),
176
182
  edit: (id) => ({ pathname: "/event/new", params: { id } }),
177
183
  details: (id) => ({ pathname: "/event/details/[id]", params: { id } }),
178
184
  },
@@ -0,0 +1 @@
1
+ export {};
@@ -6,3 +6,4 @@ export * as GEMINI_API_KEYS from './ai/gemini-api-key.dto.js';
6
6
  export * as PAYMENTS from './payments/index.js';
7
7
  export * as ADDRESSES from './addresses/addresses.dto.js';
8
8
  export * as POTENTIAL_USERS from './potential-users/potential-user-import.dto.js';
9
+ export * as ANALYSIS from './analysis/Analysis.dto.js';
@@ -163,6 +163,26 @@ export const adMediaSchema = z
163
163
  })
164
164
  .optional()
165
165
  .nullable();
166
+ /** One rent price per period. Backend checks the period belongs to "For Rent". */
167
+ export const adRentPriceSchema = z.object({
168
+ price_type_id: z.coerce.number().int().positive(),
169
+ price: z.coerce.number().positive(),
170
+ is_main: z.coerce.boolean().optional(),
171
+ });
172
+ export const adRentPricesSchema = z
173
+ .array(adRentPriceSchema)
174
+ .max(10)
175
+ .superRefine((prices, ctx) => {
176
+ const ids = prices.map((item) => item.price_type_id);
177
+ if (new Set(ids).size !== ids.length) {
178
+ ctx.addIssue({ code: "custom", message: "Each rent period can be listed once" });
179
+ }
180
+ if (prices.filter((item) => item.is_main).length > 1) {
181
+ ctx.addIssue({ code: "custom", message: "Only one rent price can be the main price" });
182
+ }
183
+ })
184
+ .optional()
185
+ .nullable();
166
186
  const baseAdCommon = z.object({
167
187
  title: z.string().trim().optional(),
168
188
  status_id: z.coerce.number().int().positive().optional(),
@@ -191,8 +211,10 @@ const baseAdCommon = z.object({
191
211
  .optional()
192
212
  .nullable(),
193
213
  selected_phone: z.string().trim().optional().nullable(),
214
+ company_id: z.coerce.number().int().positive().optional().nullable(),
194
215
  expires_at: z.iso.datetime().optional().nullable(),
195
216
  adLocations: z.array(adLocationSchema).optional().nullable(),
217
+ rent_prices: adRentPricesSchema,
196
218
  });
197
219
  export const createAdSchema = z.object({
198
220
  user_id: z.coerce.number().int().positive(),
@@ -1,15 +1,30 @@
1
1
  import { z } from 'zod';
2
2
  /**
3
- * Gemini API keys are stored in backend-core (one table) and shared by every Deallony service.
4
- * The dashboard manages them through the admin schemas; other services lease a key and report
5
- * the tokens they used through the internal schemas.
3
+ * AI API keys (Gemini and Claude) are stored in backend-core (one table) and shared by every
4
+ * Deallony service. The dashboard manages them through the admin schemas; other services lease a
5
+ * key and report the tokens they used through the internal schemas.
6
6
  */
7
7
  const geminiApiKeyIdSchema = z.coerce.number().int().positive();
8
- /** Gemini model ID, e.g. gemini-3.1-flash-lite */
8
+ /** Which company's API a key belongs to: Google Gemini or Anthropic Claude */
9
+ export const AI_PROVIDERS = ['gemini', 'anthropic'];
10
+ export const aiProviderSchema = z.enum(AI_PROVIDERS);
11
+ /** Models offered in the dashboard for each provider; any other model ID can still be typed */
12
+ export const AI_PROVIDER_MODELS = {
13
+ gemini: ['gemini-3.5-flash-lite', 'gemini-3.1-flash-lite'],
14
+ anthropic: ['claude-haiku-5-5', 'claude-sonnet-5-5', 'claude-opus-5-5'],
15
+ };
16
+ /**
17
+ * How backend-core picks a key: `priority` always takes the first available key in the dashboard
18
+ * order, `balanced` takes the key with the fewest tokens used today.
19
+ */
20
+ export const AI_KEY_SELECTION_MODES = ['priority', 'balanced'];
21
+ export const aiKeySelectionModeSchema = z.enum(AI_KEY_SELECTION_MODES);
22
+ /** Default model ID of a key, e.g. gemini-3.1-flash-lite or claude-haiku-5-5 */
9
23
  export const geminiModelNameSchema = z.string().trim().min(1).max(100);
10
24
  // ---------- Admin (dashboard) ----------
11
25
  export const createGeminiApiKeySchema = z.object({
12
26
  name: z.string().trim().min(1).max(255),
27
+ provider: aiProviderSchema.default('gemini'),
13
28
  api_key: z.string().trim().min(20).max(512),
14
29
  model_name: geminiModelNameSchema,
15
30
  is_active: z.boolean().default(true),
@@ -18,6 +33,7 @@ export const createGeminiApiKeySchema = z.object({
18
33
  export const updateGeminiApiKeySchema = z.object({
19
34
  id: geminiApiKeyIdSchema,
20
35
  name: z.string().trim().min(1).max(255).optional(),
36
+ provider: aiProviderSchema.optional(),
21
37
  api_key: z.string().trim().min(20).max(512).optional(),
22
38
  model_name: geminiModelNameSchema.optional(),
23
39
  is_active: z.boolean().optional(),
@@ -29,23 +45,34 @@ export const geminiApiKeyIdBodySchema = z.object({
29
45
  export const reorderGeminiApiKeysSchema = z.object({
30
46
  ids: z.array(geminiApiKeyIdSchema).min(1),
31
47
  });
48
+ export const aiKeySettingsSchema = z.object({
49
+ selection_mode: aiKeySelectionModeSchema,
50
+ });
32
51
  // ---------- Internal (other services) ----------
33
- /** Ask backend-core for the least-used available key, optionally only keys for one model. */
52
+ /**
53
+ * Ask backend-core for the next available key. `exclude_ids` are keys that already failed for
54
+ * this call, so a retry moves on to the next key in the order.
55
+ */
34
56
  export const leaseGeminiApiKeySchema = z.object({
57
+ /** Only keys whose default model is this one */
35
58
  model_name: geminiModelNameSchema.optional(),
59
+ /** Only keys of these providers; all providers when left out */
60
+ providers: z.array(aiProviderSchema).min(1).optional(),
61
+ exclude_ids: z.array(geminiApiKeyIdSchema).optional(),
36
62
  });
37
63
  export const leasedGeminiApiKeySchema = z.object({
38
64
  id: z.number().int().positive(),
65
+ provider: aiProviderSchema,
39
66
  api_key: z.string(),
40
67
  model_name: z.string(),
41
68
  });
42
- /** Sent after every Gemini call made with a leased key. */
69
+ /** Sent after every AI call made with a leased key. */
43
70
  export const reportGeminiApiKeyUsageSchema = z.object({
44
71
  id: geminiApiKeyIdSchema,
45
- /** usageMetadata.totalTokenCount of the Gemini response; 0 when the call failed */
72
+ /** Total tokens of the response (input + output); 0 when the call failed */
46
73
  total_tokens: z.coerce.number().int().min(0).default(0),
47
74
  success: z.boolean().default(true),
48
- /** HTTP status Gemini answered with, e.g. 429 */
75
+ /** HTTP status the provider answered with, e.g. 429 */
49
76
  error_code: z.coerce.number().int().optional(),
50
77
  error_message: z.string().trim().max(2000).optional(),
51
78
  });
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod';
2
+ /** `metrics_version` of snapshots holding daily views (older snapshots held all-time totals) */
3
+ export const ANALYSIS_METRICS_VERSION = 2;
4
+ /** Longest period one analysis report covers */
5
+ export const ANALYSIS_REPORT_MAX_DAYS = 366;
6
+ /** Longest period rebuilt in one request; daily view records older than 180 days are deleted anyway */
7
+ export const ANALYSIS_REBUILD_MAX_DAYS = 180;
8
+ const DAY_MS = 24 * 60 * 60 * 1000;
9
+ const day = z
10
+ .string()
11
+ .trim()
12
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Use YYYY-MM-DD')
13
+ .refine((value) => !Number.isNaN(Date.parse(`${value}T00:00:00Z`)), 'Invalid date');
14
+ /** Days in `from`..`to`, both included */
15
+ const spanDays = ({ from, to }) => Math.round((Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / DAY_MS) + 1;
16
+ const period = (maxDays) => z
17
+ .object({ from: day, to: day })
18
+ .refine((value) => value.from <= value.to, { message: '`from` must not be after `to`', path: ['to'] })
19
+ .refine((value) => spanDays(value) <= maxDays, {
20
+ message: `The period can be at most ${maxDays} days`,
21
+ path: ['from'],
22
+ });
23
+ /** Body of `POST admin/analysis/snapshots/report`: one day when `from` equals `to` */
24
+ export const analysisReportSchema = period(ANALYSIS_REPORT_MAX_DAYS);
25
+ /** Body of `POST admin/analysis/snapshots/rebuild`: recalculates the snapshots of these days */
26
+ export const analysisRebuildSchema = period(ANALYSIS_REBUILD_MAX_DAYS);
@@ -0,0 +1 @@
1
+ export * from './analysis-report.schema.js';
@@ -1,2 +1,3 @@
1
1
  export * from './ai-conversation.schema.js';
2
2
  export * from './support-ticket.schema.js';
3
+ export * from './offer.schema.js';
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ export const offerStatusSchema = z.enum(["pending", "accepted", "countered", "declined"]);
3
+ // The seller's side can be 0 when the ad has no price; the backend requires a positive buyer price
4
+ const offerSideSchema = z.object({
5
+ offered_price: z.coerce.number().min(0),
6
+ currency_id: z.coerce.number().int().positive().optional(),
7
+ });
8
+ /** PRICE_OFFER payload. Replies (accept, counter, decline) point at the offer they answer. */
9
+ export const priceOfferPayloadSchema = z.object({
10
+ provider: offerSideSchema,
11
+ requester: offerSideSchema,
12
+ status: offerStatusSchema.optional(),
13
+ reply_to_message_id: z.coerce.number().int().positive().optional().nullable(),
14
+ /** Free text the sender added; the message text itself is a readable summary for older apps */
15
+ note: z.string().trim().max(1000).optional().nullable(),
16
+ });
17
+ /** RENT_OFFER payload: prices are per period, duration counts periods. */
18
+ export const rentOfferPayloadSchema = priceOfferPayloadSchema.extend({
19
+ price_type_id: z.coerce.number().int().positive(),
20
+ currency_id: z.coerce.number().int().positive(),
21
+ duration: z.coerce.number().int().min(1).max(1000),
22
+ start_date: z
23
+ .string()
24
+ .trim()
25
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
26
+ .optional()
27
+ .nullable(),
28
+ });
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { saveEventTimelineSchema } from "./EventTimeline.schema.js";
3
3
  import { saveEventWeddingSchema } from "./EventWedding.schema.js";
4
- import { EventInvitationScanTypeValues } from "../../constants/events/index.js";
4
+ import { EventInvitationScanTypeValues, EventPublishPaymentMethodValues, } from "../../constants/events/index.js";
5
5
  export const eventBaseSchema = z.object({
6
6
  lat: z.coerce.number().optional().nullable(),
7
7
  long: z.coerce.number().optional().nullable(),
@@ -14,6 +14,8 @@ export const eventBaseSchema = z.object({
14
14
  event_type: z.string().trim().max(255).nullable().optional(),
15
15
  event_template: z.string().trim().max(255).nullable().optional(),
16
16
  venue_id: z.coerce.number().int().positive().nullable().optional(),
17
+ venue_name: z.string().trim().max(255).nullable().optional(),
18
+ location_label: z.string().trim().nullable().optional(),
17
19
  invitation_scan_type: z.enum(EventInvitationScanTypeValues).optional(),
18
20
  });
19
21
  export const createEventSchema = eventBaseSchema;
@@ -23,6 +25,14 @@ export const updateEventSchema = eventBaseSchema.extend({
23
25
  export const saveEventSchema = eventBaseSchema.extend({
24
26
  id: z.coerce.number().int().positive().optional(),
25
27
  });
28
+ export const publishEventSchema = z.object({
29
+ id: z.coerce.number().int().positive(),
30
+ method: z.enum(EventPublishPaymentMethodValues),
31
+ points_used: z.coerce.number().int().nonnegative().optional(),
32
+ });
33
+ export const eventIdSchema = z.object({
34
+ id: z.coerce.number().int().positive(),
35
+ });
26
36
  export const getEventsFilterSchema = z.object({
27
37
  is_published: z.boolean().optional(),
28
38
  search: z.string().trim().optional(),
@@ -6,3 +6,4 @@ export * as PAYMENTS from './payments/index.js';
6
6
  export * as COMMUNICATION from './communication/index.js';
7
7
  export * as ADDRESSES from './addresses/addresses.schema.js';
8
8
  export * as POTENTIAL_USERS from './potential-users/index.js';
9
+ export * as ANALYSIS from './analysis/index.js';
@@ -3,5 +3,14 @@ export var MessageTypeEnum;
3
3
  MessageTypeEnum[MessageTypeEnum["TEXT"] = 1] = "TEXT";
4
4
  MessageTypeEnum[MessageTypeEnum["PRICE_OFFER"] = 2] = "PRICE_OFFER";
5
5
  MessageTypeEnum[MessageTypeEnum["CONTACT_INVITATION"] = 3] = "CONTACT_INVITATION";
6
+ MessageTypeEnum[MessageTypeEnum["RENT_OFFER"] = 4] = "RENT_OFFER";
6
7
  })(MessageTypeEnum || (MessageTypeEnum = {}));
8
+ /** Lifecycle of a price or rent offer. A reply is a new message pointing at the offer it answers. */
9
+ export var OfferStatusEnum;
10
+ (function (OfferStatusEnum) {
11
+ OfferStatusEnum["PENDING"] = "pending";
12
+ OfferStatusEnum["ACCEPTED"] = "accepted";
13
+ OfferStatusEnum["COUNTERED"] = "countered";
14
+ OfferStatusEnum["DECLINED"] = "declined";
15
+ })(OfferStatusEnum || (OfferStatusEnum = {}));
7
16
  export { MessageTypeEnum as MessageTypeType };
@@ -0,0 +1,10 @@
1
+ /** Engagement of a day: WhatsApp, call and chat clicks, shares, chat messages and new chat rooms */
2
+ export const analysisEngagementTotal = ({ ad_interactions: a, app_metrics: m }) => {
3
+ var _a, _b, _c, _d, _e, _f;
4
+ return ((_a = a === null || a === void 0 ? void 0 : a.whatsapp_clicks) !== null && _a !== void 0 ? _a : 0) +
5
+ ((_b = a === null || a === void 0 ? void 0 : a.call_clicks) !== null && _b !== void 0 ? _b : 0) +
6
+ ((_c = a === null || a === void 0 ? void 0 : a.chat_clicks) !== null && _c !== void 0 ? _c : 0) +
7
+ ((_d = a === null || a === void 0 ? void 0 : a.shares) !== null && _d !== void 0 ? _d : 0) +
8
+ ((_e = m === null || m === void 0 ? void 0 : m.chat_messages_count) !== null && _e !== void 0 ? _e : 0) +
9
+ ((_f = m === null || m === void 0 ? void 0 : m.chat_rooms_count) !== null && _f !== void 0 ? _f : 0);
10
+ };
@@ -1,3 +1,5 @@
1
1
  export * from './converter.js';
2
2
  export * from './routes.js';
3
3
  export * from './adMedia.js';
4
+ export * from './analysis.js';
5
+ export * from './rentPrices.js';
@@ -0,0 +1,26 @@
1
+ /** The rent price shown on cards: the one marked main, else the first listed. */
2
+ export const getMainRentPrice = (ad) => {
3
+ var _a, _b, _c;
4
+ const prices = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) !== null && _a !== void 0 ? _a : [];
5
+ return (_c = (_b = prices.find((item) => item.is_main)) !== null && _b !== void 0 ? _b : prices[0]) !== null && _c !== void 0 ? _c : null;
6
+ };
7
+ /** The price to show for an ad in a list: the filtered period, else the main rent price. */
8
+ export const getDisplayRentPrice = (ad, priceTypeId) => {
9
+ var _a;
10
+ if (ad === null || ad === void 0 ? void 0 : ad.matched_rent_price)
11
+ return ad.matched_rent_price;
12
+ if (priceTypeId) {
13
+ const match = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) === null || _a === void 0 ? void 0 : _a.find((item) => item.price_type_id === Number(priceTypeId));
14
+ if (match)
15
+ return match;
16
+ }
17
+ return getMainRentPrice(ad);
18
+ };
19
+ /** Rent prices ordered from the shortest period to the longest. */
20
+ export const sortRentPrices = (prices) => [...(prices !== null && prices !== void 0 ? prices : [])].sort((a, b) => {
21
+ var _a, _b, _c, _d, _e, _f, _g, _h;
22
+ return ((_b = (_a = a.price_type) === null || _a === void 0 ? void 0 : _a.period_days) !== null && _b !== void 0 ? _b : Number.MAX_SAFE_INTEGER) -
23
+ ((_d = (_c = b.price_type) === null || _c === void 0 ? void 0 : _c.period_days) !== null && _d !== void 0 ? _d : Number.MAX_SAFE_INTEGER) ||
24
+ ((_f = (_e = a.price_type) === null || _e === void 0 ? void 0 : _e.display_order) !== null && _f !== void 0 ? _f : 0) - ((_h = (_g = b.price_type) === null || _g === void 0 ? void 0 : _g.display_order) !== null && _h !== void 0 ? _h : 0);
25
+ });
26
+ export const rentOfferTotal = (pricePerPeriod, duration) => Math.max(0, Number(pricePerPeriod) || 0) * Math.max(0, Math.floor(Number(duration) || 0));
package/package.json CHANGED
@@ -1,58 +1,58 @@
1
- {
2
- "name": "@deallony/shared",
3
- "version": "1.2.64",
4
- "private": false,
5
- "main": "dist/cjs/index.js",
6
- "module": "dist/esm/index.js",
7
- "react-native": "dist/esm/index.js",
8
- "types": "dist/cjs/index.d.ts",
9
- "sideEffects": false,
10
- "exports": {
11
- ".": {
12
- "require": "./dist/cjs/index.js",
13
- "import": "./dist/esm/index.js",
14
- "types": "./dist/cjs/index.d.ts"
15
- }
16
- },
17
- "files": [
18
- "dist"
19
- ],
20
- "scripts": {
21
- "deploy": "node ./scripts/publish.js npm patch",
22
- "=========": "============= UPDATE APPS LIBRARY =============",
23
- "all:update": "npm run mobile:update && npm run backend:update && npm run dashboard:update && npm run whatsapp:update && npm run collect:update",
24
- "mobile:update": "cd ../app && npm run upgrade:deallony:shared",
25
- "backend:update": "cd ../backend-service-marketplace && pnpm run upgrade:deallony:shared",
26
- "dashboard:update": "cd ../frontend-react-aloo && pnpm run upgrade:deallony:shared",
27
- "whatsapp:update": "cd ../backend-nest-whatsapp-service-aloo && pnpm run upgrade:deallony:shared",
28
- "collect:update": "cd ../qatayef-collect/backend && npm run upgrade:deallony:shared",
29
- "current": "node -p \"require('./package.json').version\"",
30
- "============================= Links with project =============================": "",
31
- "link:backend": "cd ../backend-service-marketplace && pnpm add @deallony/shared@file:../shared-deallony",
32
- "link:dashboard": "cd ../frontend-react-aloo && pnpm add @deallony/shared@file:../shared-deallony",
33
- "link:mobile": "cd ../app && npm install ../shared-deallony",
34
- "========": "============ DEFAULT ============",
35
- "build": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node ./scripts/fix-esm-imports.mjs",
36
- "watch": "tsc -p tsconfig.cjs.json --watch",
37
- "deploy:patch": "node ./scripts/publish.js npm patch",
38
- "deploy:minor": "node ./scripts/publish.js npm minor",
39
- "deploy:major": "node ./scripts/publish.js npm major",
40
- "deploy:dry-run": "node ./scripts/publish.js npm patch --dry-run",
41
- "deploy:git": "node ./scripts/publish.js git patch",
42
- "deploy:git:minor": "node ./scripts/publish.js git minor",
43
- "deploy:git:major": "node ./scripts/publish.js git major",
44
- "deploy:git:dry-run": "node ./scripts/publish.js git patch --dry-run",
45
- "prepublishOnly": "npm run build"
46
- },
47
- "dependencies": {
48
- "zod": "^4.2.1"
49
- },
50
- "devDependencies": {
51
- "@types/node": "^25.0.3",
52
- "typescript": "^5.9.3"
53
- },
54
- "publishConfig": {
55
- "registry": "https://registry.npmjs.org/",
56
- "access": "public"
57
- }
58
- }
1
+ {
2
+ "name": "@deallony/shared",
3
+ "version": "1.2.69",
4
+ "private": false,
5
+ "main": "dist/cjs/index.js",
6
+ "module": "dist/esm/index.js",
7
+ "react-native": "dist/esm/index.js",
8
+ "types": "dist/cjs/index.d.ts",
9
+ "sideEffects": false,
10
+ "exports": {
11
+ ".": {
12
+ "require": "./dist/cjs/index.js",
13
+ "import": "./dist/esm/index.js",
14
+ "types": "./dist/cjs/index.d.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "deploy": "node ./scripts/publish.js npm patch",
22
+ "=========": "============= UPDATE APPS LIBRARY =============",
23
+ "all:update": "npm run mobile:update && npm run backend:update && npm run dashboard:update && npm run whatsapp:update && npm run collect:update",
24
+ "mobile:update": "cd ../app && npm run upgrade:deallony:shared",
25
+ "backend:update": "cd ../backend-service-marketplace && pnpm run upgrade:deallony:shared",
26
+ "dashboard:update": "cd ../frontend-react-aloo && pnpm run upgrade:deallony:shared",
27
+ "whatsapp:update": "cd ../backend-nest-whatsapp-service-aloo && pnpm run upgrade:deallony:shared",
28
+ "collect:update": "cd ../qatayef-collect/backend && npm run upgrade:deallony:shared",
29
+ "current": "node -p \"require('./package.json').version\"",
30
+ "============================= Links with project =============================": "",
31
+ "link:backend": "cd ../backend-service-marketplace && pnpm add @deallony/shared@file:../shared-deallony",
32
+ "link:dashboard": "cd ../frontend-react-aloo && pnpm add @deallony/shared@file:../shared-deallony",
33
+ "link:mobile": "cd ../app && npm install ../shared-deallony",
34
+ "========": "============ DEFAULT ============",
35
+ "build": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node ./scripts/fix-esm-imports.mjs",
36
+ "watch": "tsc -p tsconfig.cjs.json --watch",
37
+ "deploy:patch": "node ./scripts/publish.js npm patch",
38
+ "deploy:minor": "node ./scripts/publish.js npm minor",
39
+ "deploy:major": "node ./scripts/publish.js npm major",
40
+ "deploy:dry-run": "node ./scripts/publish.js npm patch --dry-run",
41
+ "deploy:git": "node ./scripts/publish.js git patch",
42
+ "deploy:git:minor": "node ./scripts/publish.js git minor",
43
+ "deploy:git:major": "node ./scripts/publish.js git major",
44
+ "deploy:git:dry-run": "node ./scripts/publish.js git patch --dry-run",
45
+ "prepublishOnly": "npm run build"
46
+ },
47
+ "dependencies": {
48
+ "zod": "^4.2.1"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^25.0.3",
52
+ "typescript": "^5.9.3"
53
+ },
54
+ "publishConfig": {
55
+ "registry": "https://registry.npmjs.org/",
56
+ "access": "public"
57
+ }
58
+ }