medusa-shiprocket-fulfillment-plugin 0.3.0 → 0.3.2
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/.medusa/server/src/api/middlewares.js +15 -0
- package/.medusa/server/src/api/store/shiprocket/delivery-estimate/cache.js +54 -0
- package/.medusa/server/src/api/store/shiprocket/delivery-estimate/rate-limiter.js +92 -0
- package/.medusa/server/src/api/store/shiprocket/delivery-estimate/route.js +150 -0
- package/.medusa/server/src/providers/shiprocket/client/handle-error.js +54 -22
- package/.medusa/server/src/providers/shiprocket/client/index.js +200 -74
- package/.medusa/server/src/providers/shiprocket/client/methods/authenticate.js +15 -16
- package/.medusa/server/src/providers/shiprocket/service.js +75 -87
- package/README.md +51 -0
- package/package.json +6 -8
- package/.medusa/server/src/admin/vite-env.d.js +0 -1
- package/.medusa/server/src/admin/widgets/printables.js +0 -82
|
@@ -6,10 +6,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const utils_1 = require("@medusajs/framework/utils");
|
|
7
7
|
const client_1 = __importDefault(require("./client"));
|
|
8
8
|
class ShipRocketFulfillmentProviderService extends utils_1.AbstractFulfillmentProviderService {
|
|
9
|
+
/**
|
|
10
|
+
* Validates the plugin options at startup.
|
|
11
|
+
* @param options - The plugin configuration options
|
|
12
|
+
* @throws Error if required options are missing
|
|
13
|
+
*/
|
|
14
|
+
static validateOptions(options) {
|
|
15
|
+
if (!options.email || typeof options.email !== "string") {
|
|
16
|
+
throw new Error("Shiprocket plugin requires 'email' option (API user email)");
|
|
17
|
+
}
|
|
18
|
+
if (!options.password || typeof options.password !== "string") {
|
|
19
|
+
throw new Error("Shiprocket plugin requires 'password' option (API user password)");
|
|
20
|
+
}
|
|
21
|
+
// Validate pickup_location if provided
|
|
22
|
+
if (options.pickup_location && typeof options.pickup_location !== "string") {
|
|
23
|
+
throw new Error("Shiprocket 'pickup_location' option must be a string");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
9
26
|
/**
|
|
10
27
|
* Constructs a new instance of the ShipRocketFulfillmentProviderService.
|
|
11
|
-
* @param {Logger} logger - The logger instance.
|
|
12
|
-
* @param {Options} options - The options for the Shiprocket client.
|
|
13
28
|
*/
|
|
14
29
|
constructor({ logger }, options) {
|
|
15
30
|
super();
|
|
@@ -19,21 +34,22 @@ class ShipRocketFulfillmentProviderService extends utils_1.AbstractFulfillmentPr
|
|
|
19
34
|
email: options.email,
|
|
20
35
|
password: options.password,
|
|
21
36
|
pickup_location: options.pickup_location,
|
|
37
|
+
timeout: options.timeout,
|
|
22
38
|
});
|
|
39
|
+
this.logger_.info("Shiprocket fulfillment provider initialized");
|
|
23
40
|
}
|
|
24
41
|
/**
|
|
25
42
|
* Returns the fulfillment options for Shiprocket.
|
|
26
|
-
* @returns An array of fulfillment options.
|
|
27
43
|
*/
|
|
28
44
|
async getFulfillmentOptions() {
|
|
29
45
|
return [
|
|
30
46
|
{
|
|
31
|
-
id: "
|
|
47
|
+
id: "shiprocket-standard",
|
|
32
48
|
name: "Standard Shipping",
|
|
33
49
|
is_return: false,
|
|
34
50
|
},
|
|
35
51
|
{
|
|
36
|
-
id: "
|
|
52
|
+
id: "shiprocket-return",
|
|
37
53
|
name: "Return Shipping",
|
|
38
54
|
is_return: true,
|
|
39
55
|
},
|
|
@@ -41,50 +57,41 @@ class ShipRocketFulfillmentProviderService extends utils_1.AbstractFulfillmentPr
|
|
|
41
57
|
}
|
|
42
58
|
/**
|
|
43
59
|
* Determines whether the fulfillment option can calculate the shipping rate.
|
|
44
|
-
* @param data - The fulfillment option data.
|
|
45
|
-
* @returns A promise that resolves to a boolean indicating whether the option can calculate the rate.
|
|
46
60
|
*/
|
|
47
|
-
async canCalculate(
|
|
61
|
+
async canCalculate(_data) {
|
|
48
62
|
return true;
|
|
49
63
|
}
|
|
50
64
|
/**
|
|
51
65
|
* Calculates the shipping rate for a given order.
|
|
52
|
-
* @param optionData - The fulfillment option data.
|
|
53
|
-
* @param data - The fulfillment data.
|
|
54
|
-
* @param context - The fulfillment context.
|
|
55
|
-
* @returns The calculated shipping rate.
|
|
56
|
-
* @throws {Error} If either pickup or delivery postcodes are missing.
|
|
57
|
-
* @throws {Error} If weight is missing.
|
|
58
66
|
*/
|
|
59
|
-
async calculatePrice(
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
async calculatePrice(_optionData, _data, context) {
|
|
68
|
+
const pickupPostcode = context["from_location"]?.address?.postal_code;
|
|
69
|
+
const deliveryPostcode = context["shipping_address"]?.postal_code;
|
|
70
|
+
if (!pickupPostcode) {
|
|
71
|
+
this.logger_.warn("Shiprocket: Missing pickup postcode. Ensure a Stock Location with an address is linked to the Sales Channel.");
|
|
72
|
+
}
|
|
73
|
+
if (!pickupPostcode || !deliveryPostcode) {
|
|
74
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Both pickup and delivery postcodes are required for rate calculation");
|
|
75
|
+
}
|
|
76
|
+
// Calculate total weight from items
|
|
67
77
|
const items = (context["items"] || []);
|
|
68
78
|
let totalWeightGrams = 0;
|
|
69
79
|
for (const item of items) {
|
|
70
80
|
const quantity = item.quantity || 1;
|
|
71
|
-
|
|
72
|
-
// Try variant weight first, then metadata, then default to 0
|
|
73
|
-
const itemWeight = (item.variant?.weight ?? item.metadata?.weight ?? 0);
|
|
81
|
+
const itemWeight = item.variant?.weight ?? item.metadata?.weight ?? 0;
|
|
74
82
|
totalWeightGrams += itemWeight * quantity;
|
|
75
83
|
}
|
|
76
|
-
// Convert to kg
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
throw new Error("Both pickup and delivery postcodes are required for rate calculation.");
|
|
86
|
-
}
|
|
84
|
+
// Convert to kg, default to 0.5kg if no weight set
|
|
85
|
+
const weightKg = totalWeightGrams > 0 ? totalWeightGrams / 1000 : 0.5;
|
|
86
|
+
const params = {
|
|
87
|
+
pickup_postcode: pickupPostcode,
|
|
88
|
+
delivery_postcode: deliveryPostcode,
|
|
89
|
+
weight: weightKg,
|
|
90
|
+
cod: (this.options_.cod === "true" || this.options_.cod === 1) ? 1 : 0,
|
|
91
|
+
};
|
|
92
|
+
this.logger_.debug(`Shiprocket: Calculating rate for ${pickupPostcode} -> ${deliveryPostcode}, weight: ${weightKg}kg`);
|
|
87
93
|
const price = await this.client.calculate(params);
|
|
94
|
+
this.logger_.debug(`Shiprocket: Calculated rate: ${price}`);
|
|
88
95
|
return {
|
|
89
96
|
calculated_amount: price,
|
|
90
97
|
is_calculated_price_tax_inclusive: true,
|
|
@@ -92,15 +99,14 @@ class ShipRocketFulfillmentProviderService extends utils_1.AbstractFulfillmentPr
|
|
|
92
99
|
}
|
|
93
100
|
/**
|
|
94
101
|
* Creates a fulfillment in Shiprocket.
|
|
95
|
-
* @param data - The fulfillment data.
|
|
96
|
-
* @param items - The items in the fulfillment.
|
|
97
|
-
* @param order - The order associated with the fulfillment.
|
|
98
|
-
* @param fulfillment - The fulfillment data.
|
|
99
|
-
* @returns The created fulfillment data.
|
|
100
102
|
*/
|
|
101
103
|
async createFulfillment(data, items, order, fulfillment) {
|
|
104
|
+
const orderId = order?.id || "unknown";
|
|
105
|
+
this.logger_.info(`Shiprocket: Creating fulfillment for order ${orderId}`);
|
|
102
106
|
try {
|
|
103
107
|
const externalData = await this.client.create(fulfillment, items, order);
|
|
108
|
+
this.logger_.info(`Shiprocket: Fulfillment created - Order ID: ${externalData.order_id}, ` +
|
|
109
|
+
`Shipment ID: ${externalData.shipment_id}, AWB: ${externalData.awb}`);
|
|
104
110
|
const { label, manifest, invoice } = await this.client.createDocuments(externalData);
|
|
105
111
|
return {
|
|
106
112
|
data: {
|
|
@@ -112,34 +118,34 @@ class ShipRocketFulfillmentProviderService extends utils_1.AbstractFulfillmentPr
|
|
|
112
118
|
tracking_number: externalData.tracking_number || "",
|
|
113
119
|
tracking_url: externalData.tracking_url || "",
|
|
114
120
|
label_url: label || "",
|
|
115
|
-
// invoice_url: invoice || "", // types might not support this in label object, but okay to omit if not needed
|
|
116
121
|
},
|
|
117
122
|
],
|
|
118
123
|
};
|
|
119
124
|
}
|
|
120
125
|
catch (err) {
|
|
121
|
-
|
|
126
|
+
this.logger_.error(`Shiprocket: Failed to create fulfillment for order ${orderId}: ${err.message}`);
|
|
127
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, err?.message || "Failed to create fulfillment");
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
/**
|
|
125
131
|
* Cancels a fulfillment in Shiprocket.
|
|
126
|
-
* @param data - The fulfillment data.
|
|
127
|
-
* @throws {MedusaError} If the order ID is not provided.
|
|
128
132
|
*/
|
|
129
133
|
async cancelFulfillment(data) {
|
|
130
|
-
const
|
|
131
|
-
if (!
|
|
132
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "
|
|
134
|
+
const orderId = data.order_id;
|
|
135
|
+
if (!orderId) {
|
|
136
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Shiprocket order_id is required to cancel fulfillment");
|
|
133
137
|
}
|
|
134
|
-
|
|
138
|
+
this.logger_.info(`Shiprocket: Cancelling fulfillment for order ${orderId}`);
|
|
139
|
+
await this.client.cancel(orderId);
|
|
140
|
+
this.logger_.info(`Shiprocket: Fulfillment cancelled for order ${orderId}`);
|
|
135
141
|
}
|
|
136
142
|
/**
|
|
137
143
|
* Creates a return fulfillment in Shiprocket.
|
|
138
|
-
* @param fulfillment - The fulfillment data.
|
|
139
|
-
* @returns The created return fulfillment data.
|
|
140
144
|
*/
|
|
141
145
|
async createReturnFulfillment(fulfillment) {
|
|
146
|
+
this.logger_.info(`Shiprocket: Creating return fulfillment`);
|
|
142
147
|
const externalData = await this.client.createReturn(fulfillment);
|
|
148
|
+
this.logger_.info(`Shiprocket: Return fulfillment created - AWB: ${externalData.awb || externalData.tracking_number}`);
|
|
143
149
|
return {
|
|
144
150
|
data: {
|
|
145
151
|
...(fulfillment || {}),
|
|
@@ -155,65 +161,47 @@ class ShipRocketFulfillmentProviderService extends utils_1.AbstractFulfillmentPr
|
|
|
155
161
|
};
|
|
156
162
|
}
|
|
157
163
|
/**
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const invoice = await this.client.generateInvoice(data);
|
|
164
|
-
return invoice || [];
|
|
164
|
+
* Retrieves the documents associated with a fulfillment.
|
|
165
|
+
*/
|
|
166
|
+
async getFulfillmentDocuments(_data) {
|
|
167
|
+
// Shiprocket documents are fetched during fulfillment creation
|
|
168
|
+
return [];
|
|
165
169
|
}
|
|
166
170
|
/**
|
|
167
171
|
* Retrieves the documents associated with a shipment.
|
|
168
|
-
* @param data - The shipment data.
|
|
169
|
-
* @returns An array of documents associated with the shipment.
|
|
170
172
|
*/
|
|
171
|
-
async getShipmentDocuments(
|
|
172
|
-
|
|
173
|
-
return
|
|
173
|
+
async getShipmentDocuments(_data) {
|
|
174
|
+
// Shiprocket documents are fetched during fulfillment creation
|
|
175
|
+
return [];
|
|
174
176
|
}
|
|
175
177
|
/**
|
|
176
178
|
* Retrieves the documents associated with a return fulfillment.
|
|
177
|
-
* @param data - The return fulfillment data.
|
|
178
|
-
* @returns An empty array, as document retrieval is not supported for returns.
|
|
179
179
|
*/
|
|
180
|
-
async getReturnDocuments(
|
|
180
|
+
async getReturnDocuments(_data) {
|
|
181
181
|
return [];
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
184
|
-
* Retrieves the documents associated with a fulfillment
|
|
185
|
-
* @param fulfillmentData - The fulfillment data.
|
|
186
|
-
* @param documentType - The type of documents to retrieve.
|
|
187
|
-
* @returns A promise that resolves once the documents have been retrieved.
|
|
188
|
-
* @remarks Document retrieval is not supported by this provider.
|
|
184
|
+
* Retrieves the documents associated with a fulfillment by type.
|
|
189
185
|
*/
|
|
190
|
-
async retrieveDocuments(
|
|
191
|
-
this.logger_.debug("Document retrieval not supported");
|
|
186
|
+
async retrieveDocuments(_fulfillmentData, _documentType) {
|
|
187
|
+
this.logger_.debug("Shiprocket: Document retrieval by type not supported");
|
|
192
188
|
}
|
|
193
189
|
/**
|
|
194
|
-
* Validates the fulfillment data
|
|
195
|
-
* If the external ID is not present, it will be generated automatically.
|
|
196
|
-
* @param optionData - The data provided by the user when creating a fulfillment option.
|
|
197
|
-
* @param data - The data provided by the user when creating a fulfillment.
|
|
198
|
-
* @param context - The context of the fulfillment.
|
|
199
|
-
* @returns A promise that resolves with the validated fulfillment data.
|
|
190
|
+
* Validates the fulfillment data.
|
|
200
191
|
*/
|
|
201
|
-
async validateFulfillmentData(
|
|
192
|
+
async validateFulfillmentData(_optionData, data, _context) {
|
|
202
193
|
return {
|
|
203
194
|
...data,
|
|
204
|
-
external_id: `
|
|
195
|
+
external_id: `shiprocket_${Date.now()}`,
|
|
205
196
|
};
|
|
206
197
|
}
|
|
207
198
|
/**
|
|
208
|
-
* Validates a fulfillment option
|
|
209
|
-
* @param data - The data provided by the user when creating a fulfillment option.
|
|
210
|
-
* @returns A promise that resolves with a boolean indicating whether the option is valid.
|
|
211
|
-
* @remarks A fulfillment option is valid if it has an external ID.
|
|
199
|
+
* Validates a fulfillment option.
|
|
212
200
|
*/
|
|
213
201
|
async validateOption(data) {
|
|
214
|
-
return data.external_id !== undefined;
|
|
202
|
+
return data.id === "shiprocket-standard" || data.id === "shiprocket-return" || data.external_id !== undefined;
|
|
215
203
|
}
|
|
216
204
|
}
|
|
217
205
|
ShipRocketFulfillmentProviderService.identifier = "shiprocket";
|
|
218
206
|
exports.default = ShipRocketFulfillmentProviderService;
|
|
219
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
207
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9wcm92aWRlcnMvc2hpcHJvY2tldC9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEscURBQTRGO0FBYTVGLHNEQUF3QztBQWN4QyxNQUFNLG9DQUFxQyxTQUFRLDBDQUFrQztJQU9qRjs7OztPQUlHO0lBQ0gsTUFBTSxDQUFDLGVBQWUsQ0FBQyxPQUFnQztRQUNuRCxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssSUFBSSxPQUFPLE9BQU8sQ0FBQyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDdEQsTUFBTSxJQUFJLEtBQUssQ0FBQyw0REFBNEQsQ0FBQyxDQUFDO1FBQ2xGLENBQUM7UUFDRCxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsSUFBSSxPQUFPLE9BQU8sQ0FBQyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDNUQsTUFBTSxJQUFJLEtBQUssQ0FBQyxrRUFBa0UsQ0FBQyxDQUFDO1FBQ3hGLENBQUM7UUFDRCx1Q0FBdUM7UUFDdkMsSUFBSSxPQUFPLENBQUMsZUFBZSxJQUFJLE9BQU8sT0FBTyxDQUFDLGVBQWUsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUN6RSxNQUFNLElBQUksS0FBSyxDQUFDLHNEQUFzRCxDQUFDLENBQUM7UUFDNUUsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNILFlBQVksRUFBRSxNQUFNLEVBQXdCLEVBQUUsT0FBZ0I7UUFDMUQsS0FBSyxFQUFFLENBQUM7UUFDUixJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztRQUN0QixJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQztRQUN4QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksZ0JBQWdCLENBQUM7WUFDL0IsS0FBSyxFQUFFLE9BQU8sQ0FBQyxLQUFLO1lBQ3BCLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUTtZQUMxQixl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
|
package/README.md
CHANGED
|
@@ -74,6 +74,7 @@ Add your Shiprocket credentials to your `.env` file.
|
|
|
74
74
|
SHIPROCKET_EMAIL="your_email@example.com"
|
|
75
75
|
SHIPROCKET_PASSWORD="your_shiprocket_password"
|
|
76
76
|
# Must match the 'Nickname' of a pickup location in your Shiprocket settings
|
|
77
|
+
# This is also used to auto-fetch the pickup pincode for the delivery estimate API
|
|
77
78
|
SHIPROCKET_PICKUP_LOCATION="Primary"
|
|
78
79
|
```
|
|
79
80
|
|
|
@@ -110,6 +111,56 @@ module.exports = defineConfig({
|
|
|
110
111
|
});
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
## 🌐 API Endpoints
|
|
115
|
+
|
|
116
|
+
### Delivery Estimate API
|
|
117
|
+
|
|
118
|
+
Check serviceability and get estimated delivery dates for any pincode without creating an order.
|
|
119
|
+
|
|
120
|
+
**Endpoint:** `GET /store/shiprocket/delivery-estimate`
|
|
121
|
+
|
|
122
|
+
> [!NOTE]
|
|
123
|
+
> This endpoint is public — no API key required. Rate limited to **30 requests/min per IP**. Results are cached for **10 minutes**.
|
|
124
|
+
|
|
125
|
+
**Query Parameters:**
|
|
126
|
+
| Parameter | Required | Description |
|
|
127
|
+
| :--- | :--- | :--- |
|
|
128
|
+
| `delivery_pincode` | ✅ Yes | The destination pincode (6 digits) |
|
|
129
|
+
| `pickup_pincode` | ❌ No | Pickup location pincode. If not provided, auto-fetched from `SHIPROCKET_PICKUP_LOCATION` |
|
|
130
|
+
| `weight` | ❌ No | Package weight in kg (defaults to 0.5) |
|
|
131
|
+
| `cod` | ❌ No | Cash on delivery: 0 or 1 (defaults to 0) |
|
|
132
|
+
|
|
133
|
+
**Example Request:**
|
|
134
|
+
```bash
|
|
135
|
+
curl "https://your-store.com/store/shiprocket/delivery-estimate?delivery_pincode=110001"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Example Response:**
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"serviceable": true,
|
|
142
|
+
"fastest_delivery": {
|
|
143
|
+
"courier_name": "Delhivery Surface",
|
|
144
|
+
"courier_company_id": 21,
|
|
145
|
+
"estimated_days": 2,
|
|
146
|
+
"estimated_delivery_date": "2025-01-06",
|
|
147
|
+
"rate": 45,
|
|
148
|
+
"is_surface": true
|
|
149
|
+
},
|
|
150
|
+
"all_options": [...]
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Response Headers:**
|
|
155
|
+
| Header | Description |
|
|
156
|
+
| :--- | :--- |
|
|
157
|
+
| `X-RateLimit-Limit` | Max requests per minute (30) |
|
|
158
|
+
| `X-RateLimit-Remaining` | Requests remaining |
|
|
159
|
+
| `X-Cache` | `HIT` if cached, `MISS` if fresh |
|
|
160
|
+
|
|
161
|
+
> [!TIP]
|
|
162
|
+
> Set `SHIPROCKET_PICKUP_LOCATION` in your `.env` to auto-fetch the pickup pincode from your Shiprocket account. No need to pass `pickup_pincode` in every request!
|
|
163
|
+
|
|
113
164
|
## 💻 Usage Guide
|
|
114
165
|
|
|
115
166
|
### Enabling the Provider
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "medusa-shiprocket-fulfillment-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Shiprocket Fulfillment Provider Plugin for MedusaJS 2",
|
|
5
5
|
"author": "SAM-AEL",
|
|
6
6
|
"homepage": "https://github.com/SAM-AEL",
|
|
@@ -11,18 +11,13 @@
|
|
|
11
11
|
],
|
|
12
12
|
"main": ".medusa/server/src/providers/shiprocket/index.js",
|
|
13
13
|
"exports": {
|
|
14
|
-
".": "./.medusa/server/src/providers/
|
|
14
|
+
".": "./.medusa/server/src/providers/shiprocket/index.js",
|
|
15
15
|
"./package.json": "./package.json",
|
|
16
16
|
"./workflows": "./.medusa/server/src/workflows/index.js",
|
|
17
17
|
"./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
18
18
|
"./modules/*": "./.medusa/server/src/modules/*/index.js",
|
|
19
19
|
"./providers/*": "./.medusa/server/src/providers/*/index.js",
|
|
20
|
-
"./*": "./.medusa/server/src/*.js"
|
|
21
|
-
"./admin": {
|
|
22
|
-
"import": "./.medusa/server/src/admin/index.mjs",
|
|
23
|
-
"require": "./.medusa/server/src/admin/index.js",
|
|
24
|
-
"default": "./.medusa/server/src/admin/index.js"
|
|
25
|
-
}
|
|
20
|
+
"./*": "./.medusa/server/src/*.js"
|
|
26
21
|
},
|
|
27
22
|
"keywords": [
|
|
28
23
|
"medusa",
|
|
@@ -36,6 +31,9 @@
|
|
|
36
31
|
"dev": "medusa plugin:develop",
|
|
37
32
|
"prepublishOnly": "medusa plugin:build"
|
|
38
33
|
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"axios": "^1.7.0"
|
|
36
|
+
},
|
|
39
37
|
"devDependencies": {
|
|
40
38
|
"@medusajs/admin-sdk": "2.4.0",
|
|
41
39
|
"@medusajs/cli": "2.4.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { defineWidgetConfig } from "@medusajs/admin-sdk";
|
|
3
|
-
import { Container, Heading, Text } from "@medusajs/ui";
|
|
4
|
-
const ShiprocketTrackingWidget = ({ data: order }) => {
|
|
5
|
-
var _a;
|
|
6
|
-
if (!(order == null ? void 0 : order.id) || !(order == null ? void 0 : order.fulfillments)) return null;
|
|
7
|
-
const activeShipments = (_a = order.fulfillments) == null ? void 0 : _a.filter(
|
|
8
|
-
(f) => {
|
|
9
|
-
var _a2;
|
|
10
|
-
return !f.canceled_at && f.data.awb && f.data.shipment_id && ((_a2 = f.provider) == null ? void 0 : _a2.id) === "shiprocket_shiprocket";
|
|
11
|
-
}
|
|
12
|
-
);
|
|
13
|
-
return /* @__PURE__ */ jsxs(Container, { className: "p-6 rounded-lg shadow-lg border border-neutral-700", children: [
|
|
14
|
-
/* @__PURE__ */ jsx("header", { className: "mb-6", children: /* @__PURE__ */ jsx(Heading, { level: "h2", className: "font-semibold text-lg text-white", children: "Shiprocket Printables" }) }),
|
|
15
|
-
(activeShipments == null ? void 0 : activeShipments.length) === 0 ? /* @__PURE__ */ jsx(Text, { className: "text-sm italic text-gray-400", children: "No active shipments" }) : /* @__PURE__ */ jsx("ul", { className: "space-y-6", children: activeShipments.map((fulfillment) => {
|
|
16
|
-
var _a2, _b, _c, _d, _e, _f, _g;
|
|
17
|
-
return /* @__PURE__ */ jsxs(
|
|
18
|
-
"li",
|
|
19
|
-
{
|
|
20
|
-
className: "bg-neutral-900/10 rounded-xl shadow-none p-5 flex flex-col gap-4 border border-neutral-900/10",
|
|
21
|
-
children: [
|
|
22
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
23
|
-
/* @__PURE__ */ jsxs(Text, { className: "text-sm text-gray-300 font-normal", children: [
|
|
24
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-white", children: "Shipment ID:" }),
|
|
25
|
-
" ",
|
|
26
|
-
((_a2 = fulfillment.data) == null ? void 0 : _a2.shipment_id) || "No AWB found"
|
|
27
|
-
] }),
|
|
28
|
-
/* @__PURE__ */ jsx("span", { className: `
|
|
29
|
-
px-2 py-1 rounded-lg shadow-none text-[9px] font-semibold uppercase select-none
|
|
30
|
-
${fulfillment.status === "delivered" ? "bg-green-500 text-black" : fulfillment.status === "pending" ? "bg-yellow-400 text-black" : "bg-red-700 text-white"}
|
|
31
|
-
min-w-[4.5rem] text-center`, children: fulfillment.status || "Cancelled" })
|
|
32
|
-
] }),
|
|
33
|
-
JSON.stringify(fulfillment, null, 2),
|
|
34
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-center items-center gap-3 mt-4", children: [
|
|
35
|
-
((_b = fulfillment.labels) == null ? void 0 : _b.label_url) && /* @__PURE__ */ jsx(
|
|
36
|
-
"a",
|
|
37
|
-
{
|
|
38
|
-
href: `${(_c = fulfillment.labels) == null ? void 0 : _c.label_url}`,
|
|
39
|
-
target: "_blank",
|
|
40
|
-
rel: "noopener noreferrer",
|
|
41
|
-
className: "flex-1 px-4 py-1 text-xs bg-neutral-900/60 font-medium hover:bg-neutral-900/90 rounded-lg transition-colors text-center",
|
|
42
|
-
"aria-disabled": "false",
|
|
43
|
-
children: "Label"
|
|
44
|
-
}
|
|
45
|
-
),
|
|
46
|
-
((_d = fulfillment.labels) == null ? void 0 : _d.manifest_url) && /* @__PURE__ */ jsx(
|
|
47
|
-
"a",
|
|
48
|
-
{
|
|
49
|
-
href: `${(_e = fulfillment.labels) == null ? void 0 : _e.manifest_url}`,
|
|
50
|
-
target: "_blank",
|
|
51
|
-
rel: "noopener noreferrer",
|
|
52
|
-
className: "flex-1 px-4 py-1 text-xs bg-neutral-900/60 font-medium hover:bg-neutral-900/90 rounded-lg transition-colors text-center",
|
|
53
|
-
"aria-disabled": "false",
|
|
54
|
-
children: "Manifest"
|
|
55
|
-
}
|
|
56
|
-
),
|
|
57
|
-
((_f = fulfillment.labels) == null ? void 0 : _f.invoice_url) && /* @__PURE__ */ jsx(
|
|
58
|
-
"a",
|
|
59
|
-
{
|
|
60
|
-
href: `${(_g = fulfillment.labels) == null ? void 0 : _g.invoice_url}`,
|
|
61
|
-
target: "_blank",
|
|
62
|
-
rel: "noopener noreferrer",
|
|
63
|
-
className: "flex-1 px-4 py-1 text-xs bg-neutral-900/60 font-medium hover:bg-neutral-900/90 rounded-lg transition-colors text-center",
|
|
64
|
-
"aria-disabled": "false",
|
|
65
|
-
children: "Invoice"
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
] })
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
fulfillment.id
|
|
72
|
-
);
|
|
73
|
-
}) })
|
|
74
|
-
] });
|
|
75
|
-
};
|
|
76
|
-
const config = defineWidgetConfig({
|
|
77
|
-
zone: "order.details.side.after"
|
|
78
|
-
});
|
|
79
|
-
export {
|
|
80
|
-
config,
|
|
81
|
-
ShiprocketTrackingWidget as default
|
|
82
|
-
};
|