@mantaray-digital/plugin-automotive 1.0.0
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/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +221 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +190 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { MantarayStore, MantarayPlugin } from '@mantaray-digital/store-sdk';
|
|
2
|
+
|
|
3
|
+
interface VehicleBrand {
|
|
4
|
+
_id: string;
|
|
5
|
+
nameEn: string;
|
|
6
|
+
nameAr: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
logo?: string;
|
|
9
|
+
active: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface VehicleModel {
|
|
12
|
+
_id: string;
|
|
13
|
+
brandId: string;
|
|
14
|
+
nameEn: string;
|
|
15
|
+
nameAr: string;
|
|
16
|
+
chassis: string;
|
|
17
|
+
bodyType: string;
|
|
18
|
+
yearFrom: number;
|
|
19
|
+
yearTo: number;
|
|
20
|
+
image?: string;
|
|
21
|
+
active: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface ExchangeRate {
|
|
24
|
+
rate: number;
|
|
25
|
+
minRate: number;
|
|
26
|
+
effectiveRate: number;
|
|
27
|
+
baseCurrency: string;
|
|
28
|
+
displayCurrency: string;
|
|
29
|
+
}
|
|
30
|
+
interface OemSearchResult {
|
|
31
|
+
_id: string;
|
|
32
|
+
oemNumber?: string;
|
|
33
|
+
alternativeOems?: string[];
|
|
34
|
+
name: string;
|
|
35
|
+
nameAr?: string;
|
|
36
|
+
basePrice: number;
|
|
37
|
+
images: string[];
|
|
38
|
+
categoryId?: string;
|
|
39
|
+
}
|
|
40
|
+
interface FitmentFilter {
|
|
41
|
+
modelId: string;
|
|
42
|
+
year?: number;
|
|
43
|
+
categoryId?: string;
|
|
44
|
+
group?: 'body' | 'lighting' | 'cooling' | 'brakes';
|
|
45
|
+
}
|
|
46
|
+
interface SavedCar {
|
|
47
|
+
brandId: string;
|
|
48
|
+
modelId: string;
|
|
49
|
+
year: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Egyptian-specific shipping address format.
|
|
53
|
+
* Replaces the SDK's generic ShippingAddress for this storefront.
|
|
54
|
+
* Note: no postalCode — Egypt doesn't use postal codes in practice.
|
|
55
|
+
*/
|
|
56
|
+
interface EgyptianShippingAddress {
|
|
57
|
+
fullName: string;
|
|
58
|
+
phone: string;
|
|
59
|
+
city: string;
|
|
60
|
+
area: string;
|
|
61
|
+
address: string;
|
|
62
|
+
notes?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare class FitmentModule {
|
|
66
|
+
private store;
|
|
67
|
+
constructor(store: MantarayStore);
|
|
68
|
+
getBrands(): Promise<VehicleBrand[]>;
|
|
69
|
+
getModels(brandId: string): Promise<VehicleModel[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Get unique years available for a model.
|
|
72
|
+
* Derived from model.yearFrom and model.yearTo — no separate query needed.
|
|
73
|
+
*/
|
|
74
|
+
getYears(model: VehicleModel): number[];
|
|
75
|
+
getProducts(filter: FitmentFilter): Promise<any>;
|
|
76
|
+
/**
|
|
77
|
+
* Get all vehicles compatible with a specific product.
|
|
78
|
+
* Used on the part detail page to render the compatibility table.
|
|
79
|
+
* Returns brand + model + chassis + year range for each fitment record.
|
|
80
|
+
*/
|
|
81
|
+
getCompatibleVehicles(productId: string): Promise<any>;
|
|
82
|
+
/**
|
|
83
|
+
* Saved cars — customers save their car (brand + model + year) for quick lookup.
|
|
84
|
+
* Stored on the customer profile. Requires customer to be logged in.
|
|
85
|
+
*/
|
|
86
|
+
getSavedCars(): Promise<SavedCar[]>;
|
|
87
|
+
saveCar(car: SavedCar): Promise<void>;
|
|
88
|
+
removeSavedCar(carIndex: number): Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare class PricingModule {
|
|
92
|
+
private store;
|
|
93
|
+
private _rate;
|
|
94
|
+
constructor(store: MantarayStore);
|
|
95
|
+
getRate(): Promise<ExchangeRate>;
|
|
96
|
+
/**
|
|
97
|
+
* Check whether a price is available (non-null, positive).
|
|
98
|
+
* Some scraped parts have null prices — show "اتصل للسعر" instead.
|
|
99
|
+
*/
|
|
100
|
+
isPriceAvailable(usdAmount: number | null | undefined): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Convert USD price to display currency (EGP).
|
|
103
|
+
* Uses effectiveRate = max(adminRate, minRate).
|
|
104
|
+
* Call getRate() at app initialization before using this.
|
|
105
|
+
*/
|
|
106
|
+
toDisplayPrice(usdAmount: number): number;
|
|
107
|
+
/**
|
|
108
|
+
* Format price for display to Egyptian customers.
|
|
109
|
+
* Returns e.g. "٢٥٠٠ ج.م" or "2500 EGP".
|
|
110
|
+
* Returns null if price is unavailable — show "اتصل للسعر" in that case.
|
|
111
|
+
*/
|
|
112
|
+
formatPrice(usdAmount: number | null | undefined, locale?: 'ar' | 'en'): string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare class OemModule {
|
|
116
|
+
private store;
|
|
117
|
+
constructor(store: MantarayStore);
|
|
118
|
+
/**
|
|
119
|
+
* Search by OEM number or part name (Arabic or English).
|
|
120
|
+
*
|
|
121
|
+
* Search priority (handled by the Convex backend):
|
|
122
|
+
* 1. Exact OEM number match (primary oemNumber field)
|
|
123
|
+
* 2. Alternative OEM match (alternativeOemsText denormalized field)
|
|
124
|
+
* 3. Egyptian market name match (categories.marketNameAr → all parts in that category)
|
|
125
|
+
* e.g. "إكصدام" → finds bumper category → returns ALL bumper parts
|
|
126
|
+
* 4. Arabic name full-text search (nameAr)
|
|
127
|
+
* 5. English name full-text search (name)
|
|
128
|
+
*/
|
|
129
|
+
search(query: string): Promise<OemSearchResult[]>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* TypeScript module augmentation.
|
|
134
|
+
* Imported automatically when the plugin is imported — no manual setup needed.
|
|
135
|
+
* Gives full IntelliSense for store.fitment, store.pricing, store.oem.
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
declare module '@mantaray-digital/store-sdk' {
|
|
139
|
+
interface MantarayStore {
|
|
140
|
+
fitment: FitmentModule;
|
|
141
|
+
pricing: PricingModule;
|
|
142
|
+
oem: OemModule;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* All 26 Egyptian governorates used in the checkout city dropdown.
|
|
148
|
+
* Source: cart.html mockup.
|
|
149
|
+
*/
|
|
150
|
+
declare const EGYPTIAN_GOVERNORATES: readonly ["القاهرة", "الجيزة", "الاسكندرية", "الشرقية", "الدقهلية", "المنوفية", "الغربية", "البحيرة", "كفر الشيخ", "الفيوم", "بني سويف", "المنيا", "اسيوط", "سوهاج", "قنا", "الاقصر", "اسوان", "البحر الاحمر", "الوادي الجديد", "مطروح", "شمال سيناء", "جنوب سيناء", "السويس", "الاسماعيلية", "بورسعيد", "دمياط"];
|
|
151
|
+
type EgyptianGovernorate = typeof EGYPTIAN_GOVERNORATES[number];
|
|
152
|
+
|
|
153
|
+
declare const AutomotivePlugin: MantarayPlugin;
|
|
154
|
+
|
|
155
|
+
export { AutomotivePlugin, EGYPTIAN_GOVERNORATES, type EgyptianGovernorate, type EgyptianShippingAddress, type ExchangeRate, type FitmentFilter, FitmentModule, OemModule, type OemSearchResult, PricingModule, type SavedCar, type VehicleBrand, type VehicleModel };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { MantarayStore, MantarayPlugin } from '@mantaray-digital/store-sdk';
|
|
2
|
+
|
|
3
|
+
interface VehicleBrand {
|
|
4
|
+
_id: string;
|
|
5
|
+
nameEn: string;
|
|
6
|
+
nameAr: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
logo?: string;
|
|
9
|
+
active: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface VehicleModel {
|
|
12
|
+
_id: string;
|
|
13
|
+
brandId: string;
|
|
14
|
+
nameEn: string;
|
|
15
|
+
nameAr: string;
|
|
16
|
+
chassis: string;
|
|
17
|
+
bodyType: string;
|
|
18
|
+
yearFrom: number;
|
|
19
|
+
yearTo: number;
|
|
20
|
+
image?: string;
|
|
21
|
+
active: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface ExchangeRate {
|
|
24
|
+
rate: number;
|
|
25
|
+
minRate: number;
|
|
26
|
+
effectiveRate: number;
|
|
27
|
+
baseCurrency: string;
|
|
28
|
+
displayCurrency: string;
|
|
29
|
+
}
|
|
30
|
+
interface OemSearchResult {
|
|
31
|
+
_id: string;
|
|
32
|
+
oemNumber?: string;
|
|
33
|
+
alternativeOems?: string[];
|
|
34
|
+
name: string;
|
|
35
|
+
nameAr?: string;
|
|
36
|
+
basePrice: number;
|
|
37
|
+
images: string[];
|
|
38
|
+
categoryId?: string;
|
|
39
|
+
}
|
|
40
|
+
interface FitmentFilter {
|
|
41
|
+
modelId: string;
|
|
42
|
+
year?: number;
|
|
43
|
+
categoryId?: string;
|
|
44
|
+
group?: 'body' | 'lighting' | 'cooling' | 'brakes';
|
|
45
|
+
}
|
|
46
|
+
interface SavedCar {
|
|
47
|
+
brandId: string;
|
|
48
|
+
modelId: string;
|
|
49
|
+
year: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Egyptian-specific shipping address format.
|
|
53
|
+
* Replaces the SDK's generic ShippingAddress for this storefront.
|
|
54
|
+
* Note: no postalCode — Egypt doesn't use postal codes in practice.
|
|
55
|
+
*/
|
|
56
|
+
interface EgyptianShippingAddress {
|
|
57
|
+
fullName: string;
|
|
58
|
+
phone: string;
|
|
59
|
+
city: string;
|
|
60
|
+
area: string;
|
|
61
|
+
address: string;
|
|
62
|
+
notes?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare class FitmentModule {
|
|
66
|
+
private store;
|
|
67
|
+
constructor(store: MantarayStore);
|
|
68
|
+
getBrands(): Promise<VehicleBrand[]>;
|
|
69
|
+
getModels(brandId: string): Promise<VehicleModel[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Get unique years available for a model.
|
|
72
|
+
* Derived from model.yearFrom and model.yearTo — no separate query needed.
|
|
73
|
+
*/
|
|
74
|
+
getYears(model: VehicleModel): number[];
|
|
75
|
+
getProducts(filter: FitmentFilter): Promise<any>;
|
|
76
|
+
/**
|
|
77
|
+
* Get all vehicles compatible with a specific product.
|
|
78
|
+
* Used on the part detail page to render the compatibility table.
|
|
79
|
+
* Returns brand + model + chassis + year range for each fitment record.
|
|
80
|
+
*/
|
|
81
|
+
getCompatibleVehicles(productId: string): Promise<any>;
|
|
82
|
+
/**
|
|
83
|
+
* Saved cars — customers save their car (brand + model + year) for quick lookup.
|
|
84
|
+
* Stored on the customer profile. Requires customer to be logged in.
|
|
85
|
+
*/
|
|
86
|
+
getSavedCars(): Promise<SavedCar[]>;
|
|
87
|
+
saveCar(car: SavedCar): Promise<void>;
|
|
88
|
+
removeSavedCar(carIndex: number): Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare class PricingModule {
|
|
92
|
+
private store;
|
|
93
|
+
private _rate;
|
|
94
|
+
constructor(store: MantarayStore);
|
|
95
|
+
getRate(): Promise<ExchangeRate>;
|
|
96
|
+
/**
|
|
97
|
+
* Check whether a price is available (non-null, positive).
|
|
98
|
+
* Some scraped parts have null prices — show "اتصل للسعر" instead.
|
|
99
|
+
*/
|
|
100
|
+
isPriceAvailable(usdAmount: number | null | undefined): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Convert USD price to display currency (EGP).
|
|
103
|
+
* Uses effectiveRate = max(adminRate, minRate).
|
|
104
|
+
* Call getRate() at app initialization before using this.
|
|
105
|
+
*/
|
|
106
|
+
toDisplayPrice(usdAmount: number): number;
|
|
107
|
+
/**
|
|
108
|
+
* Format price for display to Egyptian customers.
|
|
109
|
+
* Returns e.g. "٢٥٠٠ ج.م" or "2500 EGP".
|
|
110
|
+
* Returns null if price is unavailable — show "اتصل للسعر" in that case.
|
|
111
|
+
*/
|
|
112
|
+
formatPrice(usdAmount: number | null | undefined, locale?: 'ar' | 'en'): string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare class OemModule {
|
|
116
|
+
private store;
|
|
117
|
+
constructor(store: MantarayStore);
|
|
118
|
+
/**
|
|
119
|
+
* Search by OEM number or part name (Arabic or English).
|
|
120
|
+
*
|
|
121
|
+
* Search priority (handled by the Convex backend):
|
|
122
|
+
* 1. Exact OEM number match (primary oemNumber field)
|
|
123
|
+
* 2. Alternative OEM match (alternativeOemsText denormalized field)
|
|
124
|
+
* 3. Egyptian market name match (categories.marketNameAr → all parts in that category)
|
|
125
|
+
* e.g. "إكصدام" → finds bumper category → returns ALL bumper parts
|
|
126
|
+
* 4. Arabic name full-text search (nameAr)
|
|
127
|
+
* 5. English name full-text search (name)
|
|
128
|
+
*/
|
|
129
|
+
search(query: string): Promise<OemSearchResult[]>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* TypeScript module augmentation.
|
|
134
|
+
* Imported automatically when the plugin is imported — no manual setup needed.
|
|
135
|
+
* Gives full IntelliSense for store.fitment, store.pricing, store.oem.
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
declare module '@mantaray-digital/store-sdk' {
|
|
139
|
+
interface MantarayStore {
|
|
140
|
+
fitment: FitmentModule;
|
|
141
|
+
pricing: PricingModule;
|
|
142
|
+
oem: OemModule;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* All 26 Egyptian governorates used in the checkout city dropdown.
|
|
148
|
+
* Source: cart.html mockup.
|
|
149
|
+
*/
|
|
150
|
+
declare const EGYPTIAN_GOVERNORATES: readonly ["القاهرة", "الجيزة", "الاسكندرية", "الشرقية", "الدقهلية", "المنوفية", "الغربية", "البحيرة", "كفر الشيخ", "الفيوم", "بني سويف", "المنيا", "اسيوط", "سوهاج", "قنا", "الاقصر", "اسوان", "البحر الاحمر", "الوادي الجديد", "مطروح", "شمال سيناء", "جنوب سيناء", "السويس", "الاسماعيلية", "بورسعيد", "دمياط"];
|
|
151
|
+
type EgyptianGovernorate = typeof EGYPTIAN_GOVERNORATES[number];
|
|
152
|
+
|
|
153
|
+
declare const AutomotivePlugin: MantarayPlugin;
|
|
154
|
+
|
|
155
|
+
export { AutomotivePlugin, EGYPTIAN_GOVERNORATES, type EgyptianGovernorate, type EgyptianShippingAddress, type ExchangeRate, type FitmentFilter, FitmentModule, OemModule, type OemSearchResult, PricingModule, type SavedCar, type VehicleBrand, type VehicleModel };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AutomotivePlugin: () => AutomotivePlugin,
|
|
24
|
+
EGYPTIAN_GOVERNORATES: () => EGYPTIAN_GOVERNORATES,
|
|
25
|
+
FitmentModule: () => FitmentModule,
|
|
26
|
+
OemModule: () => OemModule,
|
|
27
|
+
PricingModule: () => PricingModule
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/modules/fitment.ts
|
|
32
|
+
var FitmentModule = class {
|
|
33
|
+
constructor(store) {
|
|
34
|
+
this.store = store;
|
|
35
|
+
}
|
|
36
|
+
async getBrands() {
|
|
37
|
+
return this.store["_client"].query("fitment:getBrands", {
|
|
38
|
+
apiKey: this.store["_apiKey"]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async getModels(brandId) {
|
|
42
|
+
return this.store["_client"].query("fitment:getModels", {
|
|
43
|
+
apiKey: this.store["_apiKey"],
|
|
44
|
+
brandId
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get unique years available for a model.
|
|
49
|
+
* Derived from model.yearFrom and model.yearTo — no separate query needed.
|
|
50
|
+
*/
|
|
51
|
+
getYears(model) {
|
|
52
|
+
const years = [];
|
|
53
|
+
for (let y = model.yearFrom; y <= model.yearTo; y++) {
|
|
54
|
+
years.push(y);
|
|
55
|
+
}
|
|
56
|
+
return years;
|
|
57
|
+
}
|
|
58
|
+
async getProducts(filter) {
|
|
59
|
+
return this.store["_client"].query("fitment:getProductsByVehicle", {
|
|
60
|
+
apiKey: this.store["_apiKey"],
|
|
61
|
+
modelId: filter.modelId,
|
|
62
|
+
year: filter.year,
|
|
63
|
+
categoryId: filter.categoryId
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get all vehicles compatible with a specific product.
|
|
68
|
+
* Used on the part detail page to render the compatibility table.
|
|
69
|
+
* Returns brand + model + chassis + year range for each fitment record.
|
|
70
|
+
*/
|
|
71
|
+
async getCompatibleVehicles(productId) {
|
|
72
|
+
return this.store["_client"].query("fitment:getCompatibleVehicles", {
|
|
73
|
+
apiKey: this.store["_apiKey"],
|
|
74
|
+
productId
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Saved cars — customers save their car (brand + model + year) for quick lookup.
|
|
79
|
+
* Stored on the customer profile. Requires customer to be logged in.
|
|
80
|
+
*/
|
|
81
|
+
async getSavedCars() {
|
|
82
|
+
return this.store["_client"].query("fitment:getSavedCars", {
|
|
83
|
+
apiKey: this.store["_apiKey"]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async saveCar(car) {
|
|
87
|
+
return this.store["_client"].mutation("fitment:saveCar", {
|
|
88
|
+
apiKey: this.store["_apiKey"],
|
|
89
|
+
brandId: car.brandId,
|
|
90
|
+
modelId: car.modelId,
|
|
91
|
+
year: car.year
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async removeSavedCar(carIndex) {
|
|
95
|
+
return this.store["_client"].mutation("fitment:removeSavedCar", {
|
|
96
|
+
apiKey: this.store["_apiKey"],
|
|
97
|
+
carIndex
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// src/modules/pricing.ts
|
|
103
|
+
var PricingModule = class {
|
|
104
|
+
constructor(store) {
|
|
105
|
+
this.store = store;
|
|
106
|
+
this._rate = null;
|
|
107
|
+
}
|
|
108
|
+
async getRate() {
|
|
109
|
+
const rate = await this.store["_client"].query("automotive:getExchangeRate", {
|
|
110
|
+
apiKey: this.store["_apiKey"]
|
|
111
|
+
});
|
|
112
|
+
this._rate = rate;
|
|
113
|
+
return rate;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check whether a price is available (non-null, positive).
|
|
117
|
+
* Some scraped parts have null prices — show "اتصل للسعر" instead.
|
|
118
|
+
*/
|
|
119
|
+
isPriceAvailable(usdAmount) {
|
|
120
|
+
return usdAmount != null && usdAmount > 0;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Convert USD price to display currency (EGP).
|
|
124
|
+
* Uses effectiveRate = max(adminRate, minRate).
|
|
125
|
+
* Call getRate() at app initialization before using this.
|
|
126
|
+
*/
|
|
127
|
+
toDisplayPrice(usdAmount) {
|
|
128
|
+
if (!this._rate) {
|
|
129
|
+
throw new Error("[AutomotivePlugin] Call store.pricing.getRate() before toDisplayPrice()");
|
|
130
|
+
}
|
|
131
|
+
return Math.round(usdAmount * this._rate.effectiveRate);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Format price for display to Egyptian customers.
|
|
135
|
+
* Returns e.g. "٢٥٠٠ ج.م" or "2500 EGP".
|
|
136
|
+
* Returns null if price is unavailable — show "اتصل للسعر" in that case.
|
|
137
|
+
*/
|
|
138
|
+
formatPrice(usdAmount, locale = "ar") {
|
|
139
|
+
if (!this.isPriceAvailable(usdAmount)) {
|
|
140
|
+
return locale === "ar" ? "\u0627\u062A\u0635\u0644 \u0644\u0644\u0633\u0639\u0631" : "Contact for price";
|
|
141
|
+
}
|
|
142
|
+
const egp = this.toDisplayPrice(usdAmount);
|
|
143
|
+
if (locale === "ar") {
|
|
144
|
+
return `${egp.toLocaleString("ar-EG")} \u062C.\u0645`;
|
|
145
|
+
}
|
|
146
|
+
return `${egp.toLocaleString("en-EG")} EGP`;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/modules/oem.ts
|
|
151
|
+
var OemModule = class {
|
|
152
|
+
constructor(store) {
|
|
153
|
+
this.store = store;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Search by OEM number or part name (Arabic or English).
|
|
157
|
+
*
|
|
158
|
+
* Search priority (handled by the Convex backend):
|
|
159
|
+
* 1. Exact OEM number match (primary oemNumber field)
|
|
160
|
+
* 2. Alternative OEM match (alternativeOemsText denormalized field)
|
|
161
|
+
* 3. Egyptian market name match (categories.marketNameAr → all parts in that category)
|
|
162
|
+
* e.g. "إكصدام" → finds bumper category → returns ALL bumper parts
|
|
163
|
+
* 4. Arabic name full-text search (nameAr)
|
|
164
|
+
* 5. English name full-text search (name)
|
|
165
|
+
*/
|
|
166
|
+
async search(query) {
|
|
167
|
+
return this.store["_client"].query("automotive:searchByOem", {
|
|
168
|
+
apiKey: this.store["_apiKey"],
|
|
169
|
+
query
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// src/constants.ts
|
|
175
|
+
var EGYPTIAN_GOVERNORATES = [
|
|
176
|
+
"\u0627\u0644\u0642\u0627\u0647\u0631\u0629",
|
|
177
|
+
"\u0627\u0644\u062C\u064A\u0632\u0629",
|
|
178
|
+
"\u0627\u0644\u0627\u0633\u0643\u0646\u062F\u0631\u064A\u0629",
|
|
179
|
+
"\u0627\u0644\u0634\u0631\u0642\u064A\u0629",
|
|
180
|
+
"\u0627\u0644\u062F\u0642\u0647\u0644\u064A\u0629",
|
|
181
|
+
"\u0627\u0644\u0645\u0646\u0648\u0641\u064A\u0629",
|
|
182
|
+
"\u0627\u0644\u063A\u0631\u0628\u064A\u0629",
|
|
183
|
+
"\u0627\u0644\u0628\u062D\u064A\u0631\u0629",
|
|
184
|
+
"\u0643\u0641\u0631 \u0627\u0644\u0634\u064A\u062E",
|
|
185
|
+
"\u0627\u0644\u0641\u064A\u0648\u0645",
|
|
186
|
+
"\u0628\u0646\u064A \u0633\u0648\u064A\u0641",
|
|
187
|
+
"\u0627\u0644\u0645\u0646\u064A\u0627",
|
|
188
|
+
"\u0627\u0633\u064A\u0648\u0637",
|
|
189
|
+
"\u0633\u0648\u0647\u0627\u062C",
|
|
190
|
+
"\u0642\u0646\u0627",
|
|
191
|
+
"\u0627\u0644\u0627\u0642\u0635\u0631",
|
|
192
|
+
"\u0627\u0633\u0648\u0627\u0646",
|
|
193
|
+
"\u0627\u0644\u0628\u062D\u0631 \u0627\u0644\u0627\u062D\u0645\u0631",
|
|
194
|
+
"\u0627\u0644\u0648\u0627\u062F\u064A \u0627\u0644\u062C\u062F\u064A\u062F",
|
|
195
|
+
"\u0645\u0637\u0631\u0648\u062D",
|
|
196
|
+
"\u0634\u0645\u0627\u0644 \u0633\u064A\u0646\u0627\u0621",
|
|
197
|
+
"\u062C\u0646\u0648\u0628 \u0633\u064A\u0646\u0627\u0621",
|
|
198
|
+
"\u0627\u0644\u0633\u0648\u064A\u0633",
|
|
199
|
+
"\u0627\u0644\u0627\u0633\u0645\u0627\u0639\u064A\u0644\u064A\u0629",
|
|
200
|
+
"\u0628\u0648\u0631\u0633\u0639\u064A\u062F",
|
|
201
|
+
"\u062F\u0645\u064A\u0627\u0637"
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
// src/index.ts
|
|
205
|
+
var AutomotivePlugin = {
|
|
206
|
+
name: "automotive",
|
|
207
|
+
install(store) {
|
|
208
|
+
store.extend("fitment", new FitmentModule(store));
|
|
209
|
+
store.extend("pricing", new PricingModule(store));
|
|
210
|
+
store.extend("oem", new OemModule(store));
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
214
|
+
0 && (module.exports = {
|
|
215
|
+
AutomotivePlugin,
|
|
216
|
+
EGYPTIAN_GOVERNORATES,
|
|
217
|
+
FitmentModule,
|
|
218
|
+
OemModule,
|
|
219
|
+
PricingModule
|
|
220
|
+
});
|
|
221
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/modules/fitment.ts","../src/modules/pricing.ts","../src/modules/oem.ts","../src/constants.ts"],"sourcesContent":["import type { MantarayPlugin, MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport { FitmentModule } from './modules/fitment'\r\nimport { PricingModule } from './modules/pricing'\r\nimport { OemModule } from './modules/oem'\r\n\r\n// Apply TypeScript augmentation automatically on import\r\nimport './augment'\r\n\r\nexport const AutomotivePlugin: MantarayPlugin = {\r\n name: 'automotive',\r\n\r\n install(store: MantarayStore) {\r\n store.extend('fitment', new FitmentModule(store))\r\n store.extend('pricing', new PricingModule(store))\r\n store.extend('oem', new OemModule(store))\r\n },\r\n}\r\n\r\n// Re-export all types so storefront can import from one place\r\nexport type {\r\n VehicleBrand,\r\n VehicleModel,\r\n ExchangeRate,\r\n OemSearchResult,\r\n FitmentFilter,\r\n SavedCar,\r\n EgyptianShippingAddress,\r\n} from './types'\r\n\r\n// Re-export constants\r\nexport { EGYPTIAN_GOVERNORATES } from './constants'\r\nexport type { EgyptianGovernorate } from './constants'\r\n\r\n// Re-export modules for advanced use (e.g. instanceof checks)\r\nexport { FitmentModule } from './modules/fitment'\r\nexport { PricingModule } from './modules/pricing'\r\nexport { OemModule } from './modules/oem'\r\n","import type { MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport type { VehicleBrand, VehicleModel, FitmentFilter, SavedCar } from '../types'\r\n\r\nexport class FitmentModule {\r\n constructor(private store: MantarayStore) {}\r\n\r\n async getBrands(): Promise<VehicleBrand[]> {\r\n return this.store['_client'].query('fitment:getBrands', {\r\n apiKey: this.store['_apiKey'],\r\n })\r\n }\r\n\r\n async getModels(brandId: string): Promise<VehicleModel[]> {\r\n return this.store['_client'].query('fitment:getModels', {\r\n apiKey: this.store['_apiKey'],\r\n brandId,\r\n })\r\n }\r\n\r\n /**\r\n * Get unique years available for a model.\r\n * Derived from model.yearFrom and model.yearTo — no separate query needed.\r\n */\r\n getYears(model: VehicleModel): number[] {\r\n const years: number[] = []\r\n for (let y = model.yearFrom; y <= model.yearTo; y++) {\r\n years.push(y)\r\n }\r\n return years\r\n }\r\n\r\n async getProducts(filter: FitmentFilter) {\r\n return this.store['_client'].query('fitment:getProductsByVehicle', {\r\n apiKey: this.store['_apiKey'],\r\n modelId: filter.modelId,\r\n year: filter.year,\r\n categoryId: filter.categoryId,\r\n })\r\n }\r\n\r\n /**\r\n * Get all vehicles compatible with a specific product.\r\n * Used on the part detail page to render the compatibility table.\r\n * Returns brand + model + chassis + year range for each fitment record.\r\n */\r\n async getCompatibleVehicles(productId: string) {\r\n return this.store['_client'].query('fitment:getCompatibleVehicles', {\r\n apiKey: this.store['_apiKey'],\r\n productId,\r\n })\r\n }\r\n\r\n /**\r\n * Saved cars — customers save their car (brand + model + year) for quick lookup.\r\n * Stored on the customer profile. Requires customer to be logged in.\r\n */\r\n async getSavedCars(): Promise<SavedCar[]> {\r\n return this.store['_client'].query('fitment:getSavedCars', {\r\n apiKey: this.store['_apiKey'],\r\n })\r\n }\r\n\r\n async saveCar(car: SavedCar): Promise<void> {\r\n return this.store['_client'].mutation('fitment:saveCar', {\r\n apiKey: this.store['_apiKey'],\r\n brandId: car.brandId,\r\n modelId: car.modelId,\r\n year: car.year,\r\n })\r\n }\r\n\r\n async removeSavedCar(carIndex: number): Promise<void> {\r\n return this.store['_client'].mutation('fitment:removeSavedCar', {\r\n apiKey: this.store['_apiKey'],\r\n carIndex,\r\n })\r\n }\r\n}\r\n","import type { MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport type { ExchangeRate } from '../types'\r\n\r\nexport class PricingModule {\r\n private _rate: ExchangeRate | null = null\r\n\r\n constructor(private store: MantarayStore) {}\r\n\r\n async getRate(): Promise<ExchangeRate> {\r\n const rate = await this.store['_client'].query('automotive:getExchangeRate', {\r\n apiKey: this.store['_apiKey'],\r\n })\r\n this._rate = rate\r\n return rate\r\n }\r\n\r\n /**\r\n * Check whether a price is available (non-null, positive).\r\n * Some scraped parts have null prices — show \"اتصل للسعر\" instead.\r\n */\r\n isPriceAvailable(usdAmount: number | null | undefined): boolean {\r\n return usdAmount != null && usdAmount > 0\r\n }\r\n\r\n /**\r\n * Convert USD price to display currency (EGP).\r\n * Uses effectiveRate = max(adminRate, minRate).\r\n * Call getRate() at app initialization before using this.\r\n */\r\n toDisplayPrice(usdAmount: number): number {\r\n if (!this._rate) {\r\n throw new Error('[AutomotivePlugin] Call store.pricing.getRate() before toDisplayPrice()')\r\n }\r\n return Math.round(usdAmount * this._rate.effectiveRate)\r\n }\r\n\r\n /**\r\n * Format price for display to Egyptian customers.\r\n * Returns e.g. \"٢٥٠٠ ج.م\" or \"2500 EGP\".\r\n * Returns null if price is unavailable — show \"اتصل للسعر\" in that case.\r\n */\r\n formatPrice(usdAmount: number | null | undefined, locale: 'ar' | 'en' = 'ar'): string {\r\n if (!this.isPriceAvailable(usdAmount)) {\r\n return locale === 'ar' ? 'اتصل للسعر' : 'Contact for price'\r\n }\r\n const egp = this.toDisplayPrice(usdAmount as number)\r\n if (locale === 'ar') {\r\n return `${egp.toLocaleString('ar-EG')} ج.م`\r\n }\r\n return `${egp.toLocaleString('en-EG')} EGP`\r\n }\r\n}\r\n","import type { MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport type { OemSearchResult } from '../types'\r\n\r\nexport class OemModule {\r\n constructor(private store: MantarayStore) {}\r\n\r\n /**\r\n * Search by OEM number or part name (Arabic or English).\r\n *\r\n * Search priority (handled by the Convex backend):\r\n * 1. Exact OEM number match (primary oemNumber field)\r\n * 2. Alternative OEM match (alternativeOemsText denormalized field)\r\n * 3. Egyptian market name match (categories.marketNameAr → all parts in that category)\r\n * e.g. \"إكصدام\" → finds bumper category → returns ALL bumper parts\r\n * 4. Arabic name full-text search (nameAr)\r\n * 5. English name full-text search (name)\r\n */\r\n async search(query: string): Promise<OemSearchResult[]> {\r\n return this.store['_client'].query('automotive:searchByOem', {\r\n apiKey: this.store['_apiKey'],\r\n query,\r\n })\r\n }\r\n}\r\n","/**\r\n * All 26 Egyptian governorates used in the checkout city dropdown.\r\n * Source: cart.html mockup.\r\n */\r\nexport const EGYPTIAN_GOVERNORATES = [\r\n 'القاهرة', 'الجيزة', 'الاسكندرية', 'الشرقية', 'الدقهلية',\r\n 'المنوفية', 'الغربية', 'البحيرة', 'كفر الشيخ', 'الفيوم',\r\n 'بني سويف', 'المنيا', 'اسيوط', 'سوهاج', 'قنا',\r\n 'الاقصر', 'اسوان', 'البحر الاحمر', 'الوادي الجديد', 'مطروح',\r\n 'شمال سيناء', 'جنوب سيناء', 'السويس', 'الاسماعيلية',\r\n 'بورسعيد', 'دمياط',\r\n] as const\r\n\r\nexport type EgyptianGovernorate = typeof EGYPTIAN_GOVERNORATES[number]\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YAAoB,OAAsB;AAAtB;AAAA,EAAuB;AAAA,EAE3C,MAAM,YAAqC;AACzC,WAAO,KAAK,MAAM,SAAS,EAAE,MAAM,qBAAqB;AAAA,MACtD,QAAQ,KAAK,MAAM,SAAS;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,SAA0C;AACxD,WAAO,KAAK,MAAM,SAAS,EAAE,MAAM,qBAAqB;AAAA,MACtD,QAAQ,KAAK,MAAM,SAAS;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAA+B;AACtC,UAAM,QAAkB,CAAC;AACzB,aAAS,IAAI,MAAM,UAAU,KAAK,MAAM,QAAQ,KAAK;AACnD,YAAM,KAAK,CAAC;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,QAAuB;AACvC,WAAO,KAAK,MAAM,SAAS,EAAE,MAAM,gCAAgC;AAAA,MACjE,QAAQ,KAAK,MAAM,SAAS;AAAA,MAC5B,SAAS,OAAO;AAAA,MAChB,MAAM,OAAO;AAAA,MACb,YAAY,OAAO;AAAA,IACrB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,sBAAsB,WAAmB;AAC7C,WAAO,KAAK,MAAM,SAAS,EAAE,MAAM,iCAAiC;AAAA,MAClE,QAAQ,KAAK,MAAM,SAAS;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAoC;AACxC,WAAO,KAAK,MAAM,SAAS,EAAE,MAAM,wBAAwB;AAAA,MACzD,QAAQ,KAAK,MAAM,SAAS;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ,KAA8B;AAC1C,WAAO,KAAK,MAAM,SAAS,EAAE,SAAS,mBAAmB;AAAA,MACvD,QAAQ,KAAK,MAAM,SAAS;AAAA,MAC5B,SAAS,IAAI;AAAA,MACb,SAAS,IAAI;AAAA,MACb,MAAM,IAAI;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,eAAe,UAAiC;AACpD,WAAO,KAAK,MAAM,SAAS,EAAE,SAAS,0BAA0B;AAAA,MAC9D,QAAQ,KAAK,MAAM,SAAS;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AC1EO,IAAM,gBAAN,MAAoB;AAAA,EAGzB,YAAoB,OAAsB;AAAtB;AAFpB,SAAQ,QAA6B;AAAA,EAEM;AAAA,EAE3C,MAAM,UAAiC;AACrC,UAAM,OAAO,MAAM,KAAK,MAAM,SAAS,EAAE,MAAM,8BAA8B;AAAA,MAC3E,QAAQ,KAAK,MAAM,SAAS;AAAA,IAC9B,CAAC;AACD,SAAK,QAAQ;AACb,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,WAA+C;AAC9D,WAAO,aAAa,QAAQ,YAAY;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,WAA2B;AACxC,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,yEAAyE;AAAA,IAC3F;AACA,WAAO,KAAK,MAAM,YAAY,KAAK,MAAM,aAAa;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,WAAsC,SAAsB,MAAc;AACpF,QAAI,CAAC,KAAK,iBAAiB,SAAS,GAAG;AACrC,aAAO,WAAW,OAAO,4DAAe;AAAA,IAC1C;AACA,UAAM,MAAM,KAAK,eAAe,SAAmB;AACnD,QAAI,WAAW,MAAM;AACnB,aAAO,GAAG,IAAI,eAAe,OAAO,CAAC;AAAA,IACvC;AACA,WAAO,GAAG,IAAI,eAAe,OAAO,CAAC;AAAA,EACvC;AACF;;;AChDO,IAAM,YAAN,MAAgB;AAAA,EACrB,YAAoB,OAAsB;AAAtB;AAAA,EAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAa3C,MAAM,OAAO,OAA2C;AACtD,WAAO,KAAK,MAAM,SAAS,EAAE,MAAM,0BAA0B;AAAA,MAC3D,QAAQ,KAAK,MAAM,SAAS;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACnBO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EAAW;AAAA,EAAU;AAAA,EAAc;AAAA,EAAW;AAAA,EAC9C;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAa;AAAA,EAC/C;AAAA,EAAY;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EACxC;AAAA,EAAU;AAAA,EAAS;AAAA,EAAgB;AAAA,EAAiB;AAAA,EACpD;AAAA,EAAc;AAAA,EAAc;AAAA,EAAU;AAAA,EACtC;AAAA,EAAW;AACb;;;AJHO,IAAM,mBAAmC;AAAA,EAC9C,MAAM;AAAA,EAEN,QAAQ,OAAsB;AAC5B,UAAM,OAAO,WAAW,IAAI,cAAc,KAAK,CAAC;AAChD,UAAM,OAAO,WAAW,IAAI,cAAc,KAAK,CAAC;AAChD,UAAM,OAAO,OAAO,IAAI,UAAU,KAAK,CAAC;AAAA,EAC1C;AACF;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// src/modules/fitment.ts
|
|
2
|
+
var FitmentModule = class {
|
|
3
|
+
constructor(store) {
|
|
4
|
+
this.store = store;
|
|
5
|
+
}
|
|
6
|
+
async getBrands() {
|
|
7
|
+
return this.store["_client"].query("fitment:getBrands", {
|
|
8
|
+
apiKey: this.store["_apiKey"]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async getModels(brandId) {
|
|
12
|
+
return this.store["_client"].query("fitment:getModels", {
|
|
13
|
+
apiKey: this.store["_apiKey"],
|
|
14
|
+
brandId
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get unique years available for a model.
|
|
19
|
+
* Derived from model.yearFrom and model.yearTo — no separate query needed.
|
|
20
|
+
*/
|
|
21
|
+
getYears(model) {
|
|
22
|
+
const years = [];
|
|
23
|
+
for (let y = model.yearFrom; y <= model.yearTo; y++) {
|
|
24
|
+
years.push(y);
|
|
25
|
+
}
|
|
26
|
+
return years;
|
|
27
|
+
}
|
|
28
|
+
async getProducts(filter) {
|
|
29
|
+
return this.store["_client"].query("fitment:getProductsByVehicle", {
|
|
30
|
+
apiKey: this.store["_apiKey"],
|
|
31
|
+
modelId: filter.modelId,
|
|
32
|
+
year: filter.year,
|
|
33
|
+
categoryId: filter.categoryId
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get all vehicles compatible with a specific product.
|
|
38
|
+
* Used on the part detail page to render the compatibility table.
|
|
39
|
+
* Returns brand + model + chassis + year range for each fitment record.
|
|
40
|
+
*/
|
|
41
|
+
async getCompatibleVehicles(productId) {
|
|
42
|
+
return this.store["_client"].query("fitment:getCompatibleVehicles", {
|
|
43
|
+
apiKey: this.store["_apiKey"],
|
|
44
|
+
productId
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Saved cars — customers save their car (brand + model + year) for quick lookup.
|
|
49
|
+
* Stored on the customer profile. Requires customer to be logged in.
|
|
50
|
+
*/
|
|
51
|
+
async getSavedCars() {
|
|
52
|
+
return this.store["_client"].query("fitment:getSavedCars", {
|
|
53
|
+
apiKey: this.store["_apiKey"]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async saveCar(car) {
|
|
57
|
+
return this.store["_client"].mutation("fitment:saveCar", {
|
|
58
|
+
apiKey: this.store["_apiKey"],
|
|
59
|
+
brandId: car.brandId,
|
|
60
|
+
modelId: car.modelId,
|
|
61
|
+
year: car.year
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async removeSavedCar(carIndex) {
|
|
65
|
+
return this.store["_client"].mutation("fitment:removeSavedCar", {
|
|
66
|
+
apiKey: this.store["_apiKey"],
|
|
67
|
+
carIndex
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/modules/pricing.ts
|
|
73
|
+
var PricingModule = class {
|
|
74
|
+
constructor(store) {
|
|
75
|
+
this.store = store;
|
|
76
|
+
this._rate = null;
|
|
77
|
+
}
|
|
78
|
+
async getRate() {
|
|
79
|
+
const rate = await this.store["_client"].query("automotive:getExchangeRate", {
|
|
80
|
+
apiKey: this.store["_apiKey"]
|
|
81
|
+
});
|
|
82
|
+
this._rate = rate;
|
|
83
|
+
return rate;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check whether a price is available (non-null, positive).
|
|
87
|
+
* Some scraped parts have null prices — show "اتصل للسعر" instead.
|
|
88
|
+
*/
|
|
89
|
+
isPriceAvailable(usdAmount) {
|
|
90
|
+
return usdAmount != null && usdAmount > 0;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Convert USD price to display currency (EGP).
|
|
94
|
+
* Uses effectiveRate = max(adminRate, minRate).
|
|
95
|
+
* Call getRate() at app initialization before using this.
|
|
96
|
+
*/
|
|
97
|
+
toDisplayPrice(usdAmount) {
|
|
98
|
+
if (!this._rate) {
|
|
99
|
+
throw new Error("[AutomotivePlugin] Call store.pricing.getRate() before toDisplayPrice()");
|
|
100
|
+
}
|
|
101
|
+
return Math.round(usdAmount * this._rate.effectiveRate);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Format price for display to Egyptian customers.
|
|
105
|
+
* Returns e.g. "٢٥٠٠ ج.م" or "2500 EGP".
|
|
106
|
+
* Returns null if price is unavailable — show "اتصل للسعر" in that case.
|
|
107
|
+
*/
|
|
108
|
+
formatPrice(usdAmount, locale = "ar") {
|
|
109
|
+
if (!this.isPriceAvailable(usdAmount)) {
|
|
110
|
+
return locale === "ar" ? "\u0627\u062A\u0635\u0644 \u0644\u0644\u0633\u0639\u0631" : "Contact for price";
|
|
111
|
+
}
|
|
112
|
+
const egp = this.toDisplayPrice(usdAmount);
|
|
113
|
+
if (locale === "ar") {
|
|
114
|
+
return `${egp.toLocaleString("ar-EG")} \u062C.\u0645`;
|
|
115
|
+
}
|
|
116
|
+
return `${egp.toLocaleString("en-EG")} EGP`;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/modules/oem.ts
|
|
121
|
+
var OemModule = class {
|
|
122
|
+
constructor(store) {
|
|
123
|
+
this.store = store;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Search by OEM number or part name (Arabic or English).
|
|
127
|
+
*
|
|
128
|
+
* Search priority (handled by the Convex backend):
|
|
129
|
+
* 1. Exact OEM number match (primary oemNumber field)
|
|
130
|
+
* 2. Alternative OEM match (alternativeOemsText denormalized field)
|
|
131
|
+
* 3. Egyptian market name match (categories.marketNameAr → all parts in that category)
|
|
132
|
+
* e.g. "إكصدام" → finds bumper category → returns ALL bumper parts
|
|
133
|
+
* 4. Arabic name full-text search (nameAr)
|
|
134
|
+
* 5. English name full-text search (name)
|
|
135
|
+
*/
|
|
136
|
+
async search(query) {
|
|
137
|
+
return this.store["_client"].query("automotive:searchByOem", {
|
|
138
|
+
apiKey: this.store["_apiKey"],
|
|
139
|
+
query
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/constants.ts
|
|
145
|
+
var EGYPTIAN_GOVERNORATES = [
|
|
146
|
+
"\u0627\u0644\u0642\u0627\u0647\u0631\u0629",
|
|
147
|
+
"\u0627\u0644\u062C\u064A\u0632\u0629",
|
|
148
|
+
"\u0627\u0644\u0627\u0633\u0643\u0646\u062F\u0631\u064A\u0629",
|
|
149
|
+
"\u0627\u0644\u0634\u0631\u0642\u064A\u0629",
|
|
150
|
+
"\u0627\u0644\u062F\u0642\u0647\u0644\u064A\u0629",
|
|
151
|
+
"\u0627\u0644\u0645\u0646\u0648\u0641\u064A\u0629",
|
|
152
|
+
"\u0627\u0644\u063A\u0631\u0628\u064A\u0629",
|
|
153
|
+
"\u0627\u0644\u0628\u062D\u064A\u0631\u0629",
|
|
154
|
+
"\u0643\u0641\u0631 \u0627\u0644\u0634\u064A\u062E",
|
|
155
|
+
"\u0627\u0644\u0641\u064A\u0648\u0645",
|
|
156
|
+
"\u0628\u0646\u064A \u0633\u0648\u064A\u0641",
|
|
157
|
+
"\u0627\u0644\u0645\u0646\u064A\u0627",
|
|
158
|
+
"\u0627\u0633\u064A\u0648\u0637",
|
|
159
|
+
"\u0633\u0648\u0647\u0627\u062C",
|
|
160
|
+
"\u0642\u0646\u0627",
|
|
161
|
+
"\u0627\u0644\u0627\u0642\u0635\u0631",
|
|
162
|
+
"\u0627\u0633\u0648\u0627\u0646",
|
|
163
|
+
"\u0627\u0644\u0628\u062D\u0631 \u0627\u0644\u0627\u062D\u0645\u0631",
|
|
164
|
+
"\u0627\u0644\u0648\u0627\u062F\u064A \u0627\u0644\u062C\u062F\u064A\u062F",
|
|
165
|
+
"\u0645\u0637\u0631\u0648\u062D",
|
|
166
|
+
"\u0634\u0645\u0627\u0644 \u0633\u064A\u0646\u0627\u0621",
|
|
167
|
+
"\u062C\u0646\u0648\u0628 \u0633\u064A\u0646\u0627\u0621",
|
|
168
|
+
"\u0627\u0644\u0633\u0648\u064A\u0633",
|
|
169
|
+
"\u0627\u0644\u0627\u0633\u0645\u0627\u0639\u064A\u0644\u064A\u0629",
|
|
170
|
+
"\u0628\u0648\u0631\u0633\u0639\u064A\u062F",
|
|
171
|
+
"\u062F\u0645\u064A\u0627\u0637"
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
// src/index.ts
|
|
175
|
+
var AutomotivePlugin = {
|
|
176
|
+
name: "automotive",
|
|
177
|
+
install(store) {
|
|
178
|
+
store.extend("fitment", new FitmentModule(store));
|
|
179
|
+
store.extend("pricing", new PricingModule(store));
|
|
180
|
+
store.extend("oem", new OemModule(store));
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
export {
|
|
184
|
+
AutomotivePlugin,
|
|
185
|
+
EGYPTIAN_GOVERNORATES,
|
|
186
|
+
FitmentModule,
|
|
187
|
+
OemModule,
|
|
188
|
+
PricingModule
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/modules/fitment.ts","../src/modules/pricing.ts","../src/modules/oem.ts","../src/constants.ts","../src/index.ts"],"sourcesContent":["import type { MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport type { VehicleBrand, VehicleModel, FitmentFilter, SavedCar } from '../types'\r\n\r\nexport class FitmentModule {\r\n constructor(private store: MantarayStore) {}\r\n\r\n async getBrands(): Promise<VehicleBrand[]> {\r\n return this.store['_client'].query('fitment:getBrands', {\r\n apiKey: this.store['_apiKey'],\r\n })\r\n }\r\n\r\n async getModels(brandId: string): Promise<VehicleModel[]> {\r\n return this.store['_client'].query('fitment:getModels', {\r\n apiKey: this.store['_apiKey'],\r\n brandId,\r\n })\r\n }\r\n\r\n /**\r\n * Get unique years available for a model.\r\n * Derived from model.yearFrom and model.yearTo — no separate query needed.\r\n */\r\n getYears(model: VehicleModel): number[] {\r\n const years: number[] = []\r\n for (let y = model.yearFrom; y <= model.yearTo; y++) {\r\n years.push(y)\r\n }\r\n return years\r\n }\r\n\r\n async getProducts(filter: FitmentFilter) {\r\n return this.store['_client'].query('fitment:getProductsByVehicle', {\r\n apiKey: this.store['_apiKey'],\r\n modelId: filter.modelId,\r\n year: filter.year,\r\n categoryId: filter.categoryId,\r\n })\r\n }\r\n\r\n /**\r\n * Get all vehicles compatible with a specific product.\r\n * Used on the part detail page to render the compatibility table.\r\n * Returns brand + model + chassis + year range for each fitment record.\r\n */\r\n async getCompatibleVehicles(productId: string) {\r\n return this.store['_client'].query('fitment:getCompatibleVehicles', {\r\n apiKey: this.store['_apiKey'],\r\n productId,\r\n })\r\n }\r\n\r\n /**\r\n * Saved cars — customers save their car (brand + model + year) for quick lookup.\r\n * Stored on the customer profile. Requires customer to be logged in.\r\n */\r\n async getSavedCars(): Promise<SavedCar[]> {\r\n return this.store['_client'].query('fitment:getSavedCars', {\r\n apiKey: this.store['_apiKey'],\r\n })\r\n }\r\n\r\n async saveCar(car: SavedCar): Promise<void> {\r\n return this.store['_client'].mutation('fitment:saveCar', {\r\n apiKey: this.store['_apiKey'],\r\n brandId: car.brandId,\r\n modelId: car.modelId,\r\n year: car.year,\r\n })\r\n }\r\n\r\n async removeSavedCar(carIndex: number): Promise<void> {\r\n return this.store['_client'].mutation('fitment:removeSavedCar', {\r\n apiKey: this.store['_apiKey'],\r\n carIndex,\r\n })\r\n }\r\n}\r\n","import type { MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport type { ExchangeRate } from '../types'\r\n\r\nexport class PricingModule {\r\n private _rate: ExchangeRate | null = null\r\n\r\n constructor(private store: MantarayStore) {}\r\n\r\n async getRate(): Promise<ExchangeRate> {\r\n const rate = await this.store['_client'].query('automotive:getExchangeRate', {\r\n apiKey: this.store['_apiKey'],\r\n })\r\n this._rate = rate\r\n return rate\r\n }\r\n\r\n /**\r\n * Check whether a price is available (non-null, positive).\r\n * Some scraped parts have null prices — show \"اتصل للسعر\" instead.\r\n */\r\n isPriceAvailable(usdAmount: number | null | undefined): boolean {\r\n return usdAmount != null && usdAmount > 0\r\n }\r\n\r\n /**\r\n * Convert USD price to display currency (EGP).\r\n * Uses effectiveRate = max(adminRate, minRate).\r\n * Call getRate() at app initialization before using this.\r\n */\r\n toDisplayPrice(usdAmount: number): number {\r\n if (!this._rate) {\r\n throw new Error('[AutomotivePlugin] Call store.pricing.getRate() before toDisplayPrice()')\r\n }\r\n return Math.round(usdAmount * this._rate.effectiveRate)\r\n }\r\n\r\n /**\r\n * Format price for display to Egyptian customers.\r\n * Returns e.g. \"٢٥٠٠ ج.م\" or \"2500 EGP\".\r\n * Returns null if price is unavailable — show \"اتصل للسعر\" in that case.\r\n */\r\n formatPrice(usdAmount: number | null | undefined, locale: 'ar' | 'en' = 'ar'): string {\r\n if (!this.isPriceAvailable(usdAmount)) {\r\n return locale === 'ar' ? 'اتصل للسعر' : 'Contact for price'\r\n }\r\n const egp = this.toDisplayPrice(usdAmount as number)\r\n if (locale === 'ar') {\r\n return `${egp.toLocaleString('ar-EG')} ج.م`\r\n }\r\n return `${egp.toLocaleString('en-EG')} EGP`\r\n }\r\n}\r\n","import type { MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport type { OemSearchResult } from '../types'\r\n\r\nexport class OemModule {\r\n constructor(private store: MantarayStore) {}\r\n\r\n /**\r\n * Search by OEM number or part name (Arabic or English).\r\n *\r\n * Search priority (handled by the Convex backend):\r\n * 1. Exact OEM number match (primary oemNumber field)\r\n * 2. Alternative OEM match (alternativeOemsText denormalized field)\r\n * 3. Egyptian market name match (categories.marketNameAr → all parts in that category)\r\n * e.g. \"إكصدام\" → finds bumper category → returns ALL bumper parts\r\n * 4. Arabic name full-text search (nameAr)\r\n * 5. English name full-text search (name)\r\n */\r\n async search(query: string): Promise<OemSearchResult[]> {\r\n return this.store['_client'].query('automotive:searchByOem', {\r\n apiKey: this.store['_apiKey'],\r\n query,\r\n })\r\n }\r\n}\r\n","/**\r\n * All 26 Egyptian governorates used in the checkout city dropdown.\r\n * Source: cart.html mockup.\r\n */\r\nexport const EGYPTIAN_GOVERNORATES = [\r\n 'القاهرة', 'الجيزة', 'الاسكندرية', 'الشرقية', 'الدقهلية',\r\n 'المنوفية', 'الغربية', 'البحيرة', 'كفر الشيخ', 'الفيوم',\r\n 'بني سويف', 'المنيا', 'اسيوط', 'سوهاج', 'قنا',\r\n 'الاقصر', 'اسوان', 'البحر الاحمر', 'الوادي الجديد', 'مطروح',\r\n 'شمال سيناء', 'جنوب سيناء', 'السويس', 'الاسماعيلية',\r\n 'بورسعيد', 'دمياط',\r\n] as const\r\n\r\nexport type EgyptianGovernorate = typeof EGYPTIAN_GOVERNORATES[number]\r\n","import type { MantarayPlugin, MantarayStore } from '@mantaray-digital/store-sdk'\r\nimport { FitmentModule } from './modules/fitment'\r\nimport { PricingModule } from './modules/pricing'\r\nimport { OemModule } from './modules/oem'\r\n\r\n// Apply TypeScript augmentation automatically on import\r\nimport './augment'\r\n\r\nexport const AutomotivePlugin: MantarayPlugin = {\r\n name: 'automotive',\r\n\r\n install(store: MantarayStore) {\r\n store.extend('fitment', new FitmentModule(store))\r\n store.extend('pricing', new PricingModule(store))\r\n store.extend('oem', new OemModule(store))\r\n },\r\n}\r\n\r\n// Re-export all types so storefront can import from one place\r\nexport type {\r\n VehicleBrand,\r\n VehicleModel,\r\n ExchangeRate,\r\n OemSearchResult,\r\n FitmentFilter,\r\n SavedCar,\r\n EgyptianShippingAddress,\r\n} from './types'\r\n\r\n// Re-export constants\r\nexport { EGYPTIAN_GOVERNORATES } from './constants'\r\nexport type { EgyptianGovernorate } from './constants'\r\n\r\n// Re-export modules for advanced use (e.g. instanceof checks)\r\nexport { FitmentModule } from './modules/fitment'\r\nexport { PricingModule } from './modules/pricing'\r\nexport { OemModule } from './modules/oem'\r\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mantaray-digital/plugin-automotive",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Automotive plugin for Mantaray Store SDK — vehicle fitment, OEM search, exchange rate pricing",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@mantaray-digital/store-sdk": ">=1.1.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@mantaray-digital/store-sdk": "^1.1.1",
|
|
29
|
+
"tsup": "^8.0.0",
|
|
30
|
+
"typescript": "^5.0.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"mantaray",
|
|
34
|
+
"plugin",
|
|
35
|
+
"automotive",
|
|
36
|
+
"fitment",
|
|
37
|
+
"oem",
|
|
38
|
+
"egypt"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT"
|
|
41
|
+
}
|