@promakeai/cli 0.4.4 → 0.4.6

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.
@@ -1,236 +1,345 @@
1
- import axios, { type AxiosInstance, type AxiosResponse } from "axios";
2
- import constants from "@/constants/constants.json";
3
-
4
- // API service class
5
- class ApiService {
6
- private axiosInstance: AxiosInstance;
7
- private settings: any = null;
8
-
9
- constructor() {
10
- // Default configuration - will be updated when settings load
11
- this.axiosInstance = axios.create({
12
- baseURL: "https://backend.promake.ai/api",
13
- timeout: 30000,
14
- headers: {
15
- "Content-Type": "application/json",
16
- },
17
- });
18
-
19
- // Request interceptor to add site slug
20
- this.axiosInstance.interceptors.request.use(
21
- (config) => {
22
- if (this.settings?.site?.slug) {
23
- config.headers["X-Site-Slug"] = this.settings.site.slug;
24
- }
25
- return config;
26
- },
27
- (error) => {
28
- return Promise.reject(error);
29
- },
30
- );
31
-
32
- // Response interceptor for error handling
33
- this.axiosInstance.interceptors.response.use(
34
- (response: AxiosResponse) => response,
35
- (error) => {
36
- console.error("API Error:", {
37
- status: error.response?.status,
38
- statusText: error.response?.statusText,
39
- data: error.response?.data,
40
- url: error.config?.url,
41
- });
42
- return Promise.reject(error);
43
- },
44
- );
45
- }
46
-
47
- // Update settings for API configuration
48
- updateSettings(settings: any) {
49
- this.settings = settings;
50
- if (settings?.api?.baseUrl) {
51
- this.axiosInstance.defaults.baseURL = settings.api.baseUrl;
52
- }
53
- if (settings?.api?.timeout) {
54
- this.axiosInstance.defaults.timeout = settings.api.timeout;
55
- }
56
- }
57
-
58
- // Get endpoint from settings
59
- private getEndpoint(key: string): string {
60
- return this.settings?.api?.endpoints?.[key] || `/${key}`;
61
- }
62
-
63
- // Order API methods
64
- async createOrder(orderData: {
65
- user_id: string;
66
- total_price: number;
67
- status: string;
68
- payment_method: string;
69
- shipping_address: any;
70
- notes?: string | null;
71
- items: Array<{
72
- product_id: number;
73
- quantity: number;
74
- price: number;
75
- }>;
76
- }): Promise<{ orderId: number; success: boolean }> {
77
- try {
78
- const response = await this.axiosInstance.post(
79
- this.getEndpoint("orders"),
80
- {
81
- site_slug: this.settings?.site?.slug || "promake",
82
- order: {
83
- user_id: orderData.user_id,
84
- total_price: orderData.total_price,
85
- status: orderData.status,
86
- payment_method: orderData.payment_method,
87
- shipping_address: orderData.shipping_address,
88
- notes: orderData.notes,
89
- },
90
- items: orderData.items,
91
- },
92
- );
93
-
94
- return {
95
- orderId: response.data.order_id,
96
- success: true,
97
- };
98
- } catch (error: any) {
99
- console.error("Order creation failed:", error);
100
- throw new Error(error.response?.data?.message || "Order creation failed");
101
- }
102
- }
103
-
104
- // Generic form submission to mail service
105
- async submitForm(
106
- formData: Record<string, any>,
107
- formConfig: {
108
- email_subject1: string;
109
- email_subject2: string;
110
- fields: Array<{ name: string; required: boolean }>;
111
- },
112
- currentLanguage: string = "en",
113
- ): Promise<{ success: boolean; message: string }> {
114
- try {
115
- // Extract customer email from form data
116
- const customerEmail = formData["email"];
117
- if (!customerEmail) {
118
- throw new Error("Email field is required in form data");
119
- }
120
-
121
- // Get site owner email from ENV
122
- const tenantEmail = constants.email || import.meta.env.VITE_TENANT_MAIL;
123
- if (!tenantEmail) {
124
- throw new Error("VITE_TENANT_MAIL environment variable is not set");
125
- }
126
-
127
- // Convert formData to backend expected format with required info from config
128
- const data: Record<string, { required: boolean; value: any }> = {};
129
-
130
- formConfig.fields.forEach((field) => {
131
- data[field.name] = {
132
- required: field.required,
133
- value: formData[field.name] || null,
134
- };
135
- });
136
-
137
- // Get mail service URL from ENV
138
- const mailServiceUrl =
139
- import.meta.env.VITE_MAIL_SERVICE_URL ||
140
- "https://mail.promake.ai/api/v1/send-mail";
141
-
142
- // Make request to mail service
143
- const response = await axios.post(
144
- mailServiceUrl,
145
- {
146
- target_email1: customerEmail,
147
- target_email2: tenantEmail,
148
- email_subject1: formConfig.email_subject1,
149
- email_subject2: formConfig.email_subject2,
150
- data: data,
151
- },
152
- {
153
- headers: {
154
- "Accept-Language": currentLanguage,
155
- "Content-Type": "application/json",
156
- },
157
- },
158
- );
159
-
160
- return {
161
- success: true,
162
- message: response.data.message || "Form submitted successfully",
163
- };
164
- } catch (error: any) {
165
- console.error("Form submission failed:", error);
166
- throw new Error(error.response?.data?.message || "Failed to submit form");
167
- }
168
- }
169
-
170
- // DEPRECATED: Use submitForm instead
171
- // Keeping for backward compatibility
172
- async submitContactForm(contactData: {
173
- name: string;
174
- email: string;
175
- subject?: string;
176
- message: string;
177
- }): Promise<{ success: boolean; message: string }> {
178
- console.warn("submitContactForm is deprecated. Use submitForm instead.");
179
- return this.submitForm(
180
- contactData,
181
- {
182
- email_subject1: "Contact Form Submission",
183
- email_subject2: "New Contact Form",
184
- fields: [
185
- { name: "name", required: true },
186
- { name: "email", required: true },
187
- { name: "subject", required: false },
188
- { name: "message", required: true },
189
- ],
190
- },
191
- "en",
192
- );
193
- }
194
-
195
- // Get order by ID (for order confirmation page)
196
- async getOrderById(orderId: number): Promise<any | null> {
197
- try {
198
- const response = await this.axiosInstance.get(
199
- `${this.getEndpoint("orders")}/${orderId}?site_slug=${this.settings?.site?.slug
200
- }`,
201
- );
202
- return response.data.order || null;
203
- } catch (error: any) {
204
- console.error("Failed to fetch order:", error);
205
- return null;
206
- }
207
- }
208
- }
209
-
210
- // Create singleton instance
211
- const apiService = new ApiService();
212
-
213
- // Hook to use API service with settings
214
- export const useApiService = () => {
215
- // Reconstruct settings object for ApiService.updateSettings
216
- const settings = {
217
- site: {
218
- slug: constants.site.slug,
219
- },
220
- api: {
221
- baseUrl: constants.api.baseUrl,
222
- timeout: constants.api.timeout,
223
- endpoints: constants.api.endpoints,
224
- },
225
- };
226
-
227
- // Update API service settings when they change
228
- if (settings && apiService) {
229
- apiService.updateSettings(settings);
230
- }
231
-
232
- return apiService;
233
- };
234
-
235
- // Export the service for direct use
236
- export default apiService;
1
+ import axios, { type AxiosInstance, type AxiosResponse } from "axios";
2
+ import constants from "@/constants/constants.json";
3
+
4
+ // API service class
5
+ class ApiService {
6
+ private axiosInstance: AxiosInstance;
7
+ private settings: any = null;
8
+
9
+ constructor() {
10
+ // Default configuration - will be updated when settings load
11
+ this.axiosInstance = axios.create({
12
+ baseURL: "https://backend.promake.ai/api",
13
+ timeout: 30000,
14
+ headers: {
15
+ "Content-Type": "application/json",
16
+ },
17
+ });
18
+
19
+ // Request interceptor to add site slug
20
+ this.axiosInstance.interceptors.request.use(
21
+ (config) => {
22
+ if (this.settings?.site?.slug) {
23
+ config.headers["X-Site-Slug"] = this.settings.site.slug;
24
+ }
25
+ return config;
26
+ },
27
+ (error) => {
28
+ return Promise.reject(error);
29
+ },
30
+ );
31
+
32
+ // Response interceptor for error handling
33
+ this.axiosInstance.interceptors.response.use(
34
+ (response: AxiosResponse) => response,
35
+ (error) => {
36
+ console.error("API Error:", {
37
+ status: error.response?.status,
38
+ statusText: error.response?.statusText,
39
+ data: error.response?.data,
40
+ url: error.config?.url,
41
+ });
42
+ return Promise.reject(error);
43
+ },
44
+ );
45
+ }
46
+
47
+ // Update settings for API configuration
48
+ updateSettings(settings: any) {
49
+ this.settings = settings;
50
+ if (settings?.api?.baseUrl) {
51
+ this.axiosInstance.defaults.baseURL = settings.api.baseUrl;
52
+ }
53
+ if (settings?.api?.timeout) {
54
+ this.axiosInstance.defaults.timeout = settings.api.timeout;
55
+ }
56
+ }
57
+
58
+ // Get endpoint from settings
59
+ private getEndpoint(key: string): string {
60
+ return this.settings?.api?.endpoints?.[key] || `/${key}`;
61
+ }
62
+
63
+ // Order API methods
64
+ async createOrder(orderData: {
65
+ user_id: string;
66
+ total_price: number;
67
+ status: string;
68
+ payment_method: string;
69
+ shipping_address: any;
70
+ notes?: string | null;
71
+ items: Array<{
72
+ product_id: number;
73
+ quantity: number;
74
+ price: number;
75
+ }>;
76
+ }): Promise<{ orderId: number; success: boolean }> {
77
+ try {
78
+ const response = await this.axiosInstance.post(
79
+ this.getEndpoint("orders"),
80
+ {
81
+ site_slug: this.settings?.site?.slug || "promake",
82
+ order: {
83
+ user_id: orderData.user_id,
84
+ total_price: orderData.total_price,
85
+ status: orderData.status,
86
+ payment_method: orderData.payment_method,
87
+ shipping_address: orderData.shipping_address,
88
+ notes: orderData.notes,
89
+ },
90
+ items: orderData.items,
91
+ },
92
+ );
93
+
94
+ return {
95
+ orderId: response.data.order_id,
96
+ success: true,
97
+ };
98
+ } catch (error: any) {
99
+ console.error("Order creation failed:", error);
100
+ throw new Error(error.response?.data?.message || "Order creation failed");
101
+ }
102
+ }
103
+
104
+
105
+ // Generic form submission with file upload to mail service
106
+ async submitFormWithFile(
107
+ formData: Record<string, any>,
108
+ formConfig: {
109
+ email_subject1: string;
110
+ email_subject2: string;
111
+ fields: Array<{ name: string; required: boolean }>;
112
+ },
113
+ currentLanguage: string = "en",
114
+ ): Promise<{ success: boolean; message: string }> {
115
+ try {
116
+ const customerEmail = formData["email"];
117
+ if (!customerEmail) {
118
+ throw new Error("Email field is required in form data");
119
+ }
120
+
121
+ const tenantEmail = constants.email || import.meta.env.VITE_TENANT_MAIL;
122
+ if (!tenantEmail) {
123
+ throw new Error("VITE_TENANT_MAIL environment variable is not set");
124
+ }
125
+
126
+ const mailServiceUrl =
127
+ import.meta.env.VITE_MAIL_SERVICE_URL ||
128
+ "https://mail.promake.ai/api/v1/send-mail";
129
+
130
+ // Check if there are attachments to determine request type
131
+ const hasAttachments = formData.attachments &&
132
+ Array.isArray(formData.attachments) &&
133
+ formData.attachments.length > 0;
134
+
135
+ if (hasAttachments) {
136
+ // Use FormData for file uploads
137
+ const formDataObj = new FormData();
138
+
139
+ formDataObj.append('target_email1', customerEmail);
140
+ formDataObj.append('target_email2', tenantEmail);
141
+ formDataObj.append('email_subject1', formConfig.email_subject1);
142
+ formDataObj.append('email_subject2', formConfig.email_subject2);
143
+
144
+ // Add form fields
145
+ formConfig.fields.forEach((field) => {
146
+ if (field.name !== 'attachments') {
147
+ formDataObj.append(`data[${field.name}][required]`, field.required.toString());
148
+ formDataObj.append(`data[${field.name}][value]`, formData[field.name] || '');
149
+ }
150
+ });
151
+
152
+ // Add attachments
153
+ formData.attachments.forEach((file: File) => {
154
+ formDataObj.append('attachments[]', file);
155
+ });
156
+
157
+ const response = await axios.post(mailServiceUrl, formDataObj, {
158
+ headers: {
159
+ 'Accept-Language': currentLanguage,
160
+ 'Content-Type': 'multipart/form-data',
161
+ },
162
+ });
163
+
164
+ return {
165
+ success: true,
166
+ message: response.data.message || "Form submitted successfully",
167
+ };
168
+ } else {
169
+ // Use JSON for simple forms (backward compatible)
170
+ const data: Record<string, { required: boolean; value: any }> = {};
171
+
172
+
173
+ formConfig.fields.forEach((field) => {
174
+ if (field.name !== 'attachments') {
175
+ data[field.name] = {
176
+ required: field.required,
177
+ value: formData[field.name] || null,
178
+ };
179
+ }
180
+ });
181
+
182
+ const response = await axios.post(
183
+ mailServiceUrl,
184
+ {
185
+ target_email1: customerEmail,
186
+ target_email2: tenantEmail,
187
+ email_subject1: formConfig.email_subject1,
188
+ email_subject2: formConfig.email_subject2,
189
+ data: data,
190
+ },
191
+ {
192
+ headers: {
193
+ "Accept-Language": currentLanguage,
194
+ "Content-Type": "application/json",
195
+ },
196
+ },
197
+ );
198
+
199
+ return {
200
+ success: true,
201
+ message: response.data.message || "Form submitted successfully",
202
+ };
203
+ }
204
+ } catch (error: any) {
205
+ console.error("Form submission failed:", error);
206
+ throw new Error(error.response?.data?.message || "Failed to submit form");
207
+ }
208
+ }
209
+
210
+
211
+ // DEPRECATED: Use submitFormWithFile instead
212
+ // Keeping for backward compatibility
213
+ // Generic form submission to mail service
214
+ async submitForm(
215
+ formData: Record<string, any>,
216
+ formConfig: {
217
+ email_subject1: string;
218
+ email_subject2: string;
219
+ fields: Array<{ name: string; required: boolean }>;
220
+ },
221
+ currentLanguage: string = "en",
222
+ ): Promise<{ success: boolean; message: string }> {
223
+ try {
224
+ // Extract customer email from form data
225
+ const customerEmail = formData["email"];
226
+ if (!customerEmail) {
227
+ throw new Error("Email field is required in form data");
228
+ }
229
+
230
+ // Get site owner email from ENV
231
+ const tenantEmail = constants.email || import.meta.env.VITE_TENANT_MAIL;
232
+ if (!tenantEmail) {
233
+ throw new Error("VITE_TENANT_MAIL environment variable is not set");
234
+ }
235
+
236
+ // Convert formData to backend expected format with required info from config
237
+ const data: Record<string, { required: boolean; value: any }> = {};
238
+
239
+ formConfig.fields.forEach((field) => {
240
+ data[field.name] = {
241
+ required: field.required,
242
+ value: formData[field.name] || null,
243
+ };
244
+ });
245
+
246
+ // Get mail service URL from ENV
247
+ const mailServiceUrl =
248
+ import.meta.env.VITE_MAIL_SERVICE_URL ||
249
+ "https://mail.promake.ai/api/v1/send-mail";
250
+
251
+ // Make request to mail service
252
+ const response = await axios.post(
253
+ mailServiceUrl,
254
+ {
255
+ target_email1: customerEmail,
256
+ target_email2: tenantEmail,
257
+ email_subject1: formConfig.email_subject1,
258
+ email_subject2: formConfig.email_subject2,
259
+ data: data,
260
+ },
261
+ {
262
+ headers: {
263
+ "Accept-Language": currentLanguage,
264
+ "Content-Type": "application/json",
265
+ },
266
+ },
267
+ );
268
+
269
+ return {
270
+ success: true,
271
+ message: response.data.message || "Form submitted successfully",
272
+ };
273
+ } catch (error: any) {
274
+ console.error("Form submission failed:", error);
275
+ throw new Error(error.response?.data?.message || "Failed to submit form");
276
+ }
277
+ }
278
+
279
+ // DEPRECATED: Use submitForm instead
280
+ // Keeping for backward compatibility
281
+ async submitContactForm(contactData: {
282
+ name: string;
283
+ email: string;
284
+ subject?: string;
285
+ message: string;
286
+ }): Promise<{ success: boolean; message: string }> {
287
+ console.warn("submitContactForm is deprecated. Use submitForm instead.");
288
+ return this.submitForm(
289
+ contactData,
290
+ {
291
+ email_subject1: "Contact Form Submission",
292
+ email_subject2: "New Contact Form",
293
+ fields: [
294
+ { name: "name", required: true },
295
+ { name: "email", required: true },
296
+ { name: "subject", required: false },
297
+ { name: "message", required: true },
298
+ ],
299
+ },
300
+ "en",
301
+ );
302
+ }
303
+
304
+ // Get order by ID (for order confirmation page)
305
+ async getOrderById(orderId: number): Promise<any | null> {
306
+ try {
307
+ const response = await this.axiosInstance.get(
308
+ `${this.getEndpoint("orders")}/${orderId}?site_slug=${this.settings?.site?.slug
309
+ }`,
310
+ );
311
+ return response.data.order || null;
312
+ } catch (error: any) {
313
+ console.error("Failed to fetch order:", error);
314
+ return null;
315
+ }
316
+ }
317
+ }
318
+
319
+ // Create singleton instance
320
+ const apiService = new ApiService();
321
+
322
+ // Hook to use API service with settings
323
+ export const useApiService = () => {
324
+ // Reconstruct settings object for ApiService.updateSettings
325
+ const settings = {
326
+ site: {
327
+ slug: constants.site.slug,
328
+ },
329
+ api: {
330
+ baseUrl: constants.api.baseUrl,
331
+ timeout: constants.api.timeout,
332
+ endpoints: constants.api.endpoints,
333
+ },
334
+ };
335
+
336
+ // Update API service settings when they change
337
+ if (settings && apiService) {
338
+ apiService.updateSettings(settings);
339
+ }
340
+
341
+ return apiService;
342
+ };
343
+
344
+ // Export the service for direct use
345
+ export default apiService;