@codemind.ec/medusa-plugin-invoice 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.
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
3
|
+
const adminSdk = require("@medusajs/admin-sdk");
|
|
4
|
+
const ui = require("@medusajs/ui");
|
|
5
|
+
const Medusa = require("@medusajs/js-sdk");
|
|
6
|
+
const react = require("react");
|
|
7
|
+
const reactQuery = require("@tanstack/react-query");
|
|
8
|
+
const reactHookForm = require("react-hook-form");
|
|
9
|
+
const zod = require("@medusajs/framework/zod");
|
|
10
|
+
require("@medusajs/admin-shared");
|
|
11
|
+
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
12
|
+
const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
|
|
13
|
+
const sdk = new Medusa__default.default({
|
|
14
|
+
baseUrl: "/",
|
|
15
|
+
debug: false,
|
|
16
|
+
auth: {
|
|
17
|
+
type: "session"
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
const OrderInvoiceWidget = ({ data: order }) => {
|
|
21
|
+
const [isDownloading, setIsDownloading] = react.useState(false);
|
|
22
|
+
const downloadInvoice = async () => {
|
|
23
|
+
setIsDownloading(true);
|
|
24
|
+
try {
|
|
25
|
+
const response = await sdk.client.fetch(`/admin/orders/${order.id}/invoices`, {
|
|
26
|
+
method: "GET",
|
|
27
|
+
headers: {
|
|
28
|
+
"accept": "application/pdf"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const blob = await response.blob();
|
|
32
|
+
const url = window.URL.createObjectURL(blob);
|
|
33
|
+
const a = document.createElement("a");
|
|
34
|
+
a.href = url;
|
|
35
|
+
a.download = `comprobante-pedido-${order.id}.pdf`;
|
|
36
|
+
document.body.appendChild(a);
|
|
37
|
+
a.click();
|
|
38
|
+
window.URL.revokeObjectURL(url);
|
|
39
|
+
document.body.removeChild(a);
|
|
40
|
+
setIsDownloading(false);
|
|
41
|
+
ui.toast.success("Comprobante generado y descargado exitosamente");
|
|
42
|
+
} catch (error) {
|
|
43
|
+
ui.toast.error(`Error al generar comprobante: ${error}`);
|
|
44
|
+
setIsDownloading(false);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
48
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
49
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Comprobante" }),
|
|
50
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Generar y descargar comprobante de pedido" })
|
|
51
|
+
] }) }),
|
|
52
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
53
|
+
ui.Button,
|
|
54
|
+
{
|
|
55
|
+
variant: "secondary",
|
|
56
|
+
disabled: isDownloading,
|
|
57
|
+
onClick: downloadInvoice,
|
|
58
|
+
isLoading: isDownloading,
|
|
59
|
+
children: "Descargar Comprobante"
|
|
60
|
+
}
|
|
61
|
+
) })
|
|
62
|
+
] });
|
|
63
|
+
};
|
|
64
|
+
adminSdk.defineWidgetConfig({
|
|
65
|
+
zone: "order.details.side.before"
|
|
66
|
+
});
|
|
67
|
+
zod.z.object({
|
|
68
|
+
company_name: zod.z.string().optional(),
|
|
69
|
+
company_ruc: zod.z.string().optional(),
|
|
70
|
+
company_address: zod.z.string().optional(),
|
|
71
|
+
company_phone: zod.z.string().optional(),
|
|
72
|
+
company_email: zod.z.string().email().optional(),
|
|
73
|
+
company_logo: zod.z.string().url().optional(),
|
|
74
|
+
notes: zod.z.string().optional(),
|
|
75
|
+
admin_notification_email: zod.z.string().email().optional().or(zod.z.literal(""))
|
|
76
|
+
});
|
|
77
|
+
const InvoiceConfigPage = () => {
|
|
78
|
+
const { data, isLoading, refetch } = reactQuery.useQuery({
|
|
79
|
+
queryFn: () => sdk.client.fetch("/admin/invoice-config"),
|
|
80
|
+
queryKey: ["invoice-config"]
|
|
81
|
+
});
|
|
82
|
+
const { mutateAsync, isPending } = reactQuery.useMutation({
|
|
83
|
+
mutationFn: (payload) => sdk.client.fetch("/admin/invoice-config", {
|
|
84
|
+
method: "POST",
|
|
85
|
+
body: payload
|
|
86
|
+
}),
|
|
87
|
+
onSuccess: () => {
|
|
88
|
+
refetch();
|
|
89
|
+
ui.toast.success("Configuración actualizada exitosamente");
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const getFormDefaultValues = react.useCallback(() => {
|
|
93
|
+
return {
|
|
94
|
+
company_name: (data == null ? void 0 : data.invoice_config.company_name) || "",
|
|
95
|
+
company_ruc: (data == null ? void 0 : data.invoice_config.company_ruc) || "",
|
|
96
|
+
company_address: (data == null ? void 0 : data.invoice_config.company_address) || "",
|
|
97
|
+
company_phone: (data == null ? void 0 : data.invoice_config.company_phone) || "",
|
|
98
|
+
company_email: (data == null ? void 0 : data.invoice_config.company_email) || "",
|
|
99
|
+
company_logo: (data == null ? void 0 : data.invoice_config.company_logo) || "",
|
|
100
|
+
notes: (data == null ? void 0 : data.invoice_config.notes) || "",
|
|
101
|
+
admin_notification_email: (data == null ? void 0 : data.invoice_config.admin_notification_email) || ""
|
|
102
|
+
};
|
|
103
|
+
}, [data]);
|
|
104
|
+
const form = reactHookForm.useForm({
|
|
105
|
+
defaultValues: getFormDefaultValues()
|
|
106
|
+
});
|
|
107
|
+
const handleSubmit = form.handleSubmit((formData) => mutateAsync(formData));
|
|
108
|
+
const uploadLogo = async (event) => {
|
|
109
|
+
var _a;
|
|
110
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
111
|
+
if (!file) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const { files } = await sdk.admin.upload.create({
|
|
115
|
+
files: [file]
|
|
116
|
+
});
|
|
117
|
+
form.setValue("company_logo", files[0].url);
|
|
118
|
+
};
|
|
119
|
+
react.useEffect(() => {
|
|
120
|
+
form.reset(getFormDefaultValues());
|
|
121
|
+
}, [getFormDefaultValues]);
|
|
122
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Configuración de Comprobante" }) }),
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactHookForm.FormProvider, { ...form, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
125
|
+
"form",
|
|
126
|
+
{
|
|
127
|
+
onSubmit: handleSubmit,
|
|
128
|
+
className: "flex h-full flex-col overflow-hidden p-2 gap-2",
|
|
129
|
+
children: [
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-ui-bg-subtle border border-ui-border-base rounded-lg p-3 mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
131
|
+
reactHookForm.Controller,
|
|
132
|
+
{
|
|
133
|
+
control: form.control,
|
|
134
|
+
name: "admin_notification_email",
|
|
135
|
+
render: ({ field }) => {
|
|
136
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
137
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "📧 Email de Notificaciones (Admin)" }) }),
|
|
138
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...field, onChange: field.onChange, value: field.value, placeholder: "admin@mariquita.food" }),
|
|
139
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ui-fg-subtle text-xs", children: "Se enviarán notificaciones de nuevos pedidos y comprobantes de pago a este email." })
|
|
140
|
+
] });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
) }),
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
145
|
+
reactHookForm.Controller,
|
|
146
|
+
{
|
|
147
|
+
control: form.control,
|
|
148
|
+
name: "company_name",
|
|
149
|
+
render: ({ field }) => {
|
|
150
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "Nombre de la Empresa" }) }),
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...field, onChange: field.onChange, value: field.value })
|
|
153
|
+
] });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
),
|
|
157
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
158
|
+
reactHookForm.Controller,
|
|
159
|
+
{
|
|
160
|
+
control: form.control,
|
|
161
|
+
name: "company_ruc",
|
|
162
|
+
render: ({ field }) => {
|
|
163
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
164
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "RUC" }) }),
|
|
165
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...field, onChange: field.onChange, value: field.value, placeholder: "1234567890001" })
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
),
|
|
170
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
reactHookForm.Controller,
|
|
172
|
+
{
|
|
173
|
+
control: form.control,
|
|
174
|
+
name: "company_address",
|
|
175
|
+
render: ({ field }) => {
|
|
176
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
177
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "Dirección de la Empresa" }) }),
|
|
178
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Textarea, { ...field })
|
|
179
|
+
] });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
),
|
|
183
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
184
|
+
reactHookForm.Controller,
|
|
185
|
+
{
|
|
186
|
+
control: form.control,
|
|
187
|
+
name: "company_phone",
|
|
188
|
+
render: ({ field }) => {
|
|
189
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
190
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "Teléfono de la Empresa" }) }),
|
|
191
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...field })
|
|
192
|
+
] });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
),
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
197
|
+
reactHookForm.Controller,
|
|
198
|
+
{
|
|
199
|
+
control: form.control,
|
|
200
|
+
name: "company_email",
|
|
201
|
+
render: ({ field }) => {
|
|
202
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "Email de la Empresa" }) }),
|
|
204
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { ...field })
|
|
205
|
+
] });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
),
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
210
|
+
reactHookForm.Controller,
|
|
211
|
+
{
|
|
212
|
+
control: form.control,
|
|
213
|
+
name: "notes",
|
|
214
|
+
render: ({ field }) => {
|
|
215
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
216
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "Notas" }) }),
|
|
217
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Textarea, { ...field })
|
|
218
|
+
] });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
),
|
|
222
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
223
|
+
reactHookForm.Controller,
|
|
224
|
+
{
|
|
225
|
+
control: form.control,
|
|
226
|
+
name: "company_logo",
|
|
227
|
+
render: ({ field }) => {
|
|
228
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
229
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { size: "small", weight: "plus", children: "Logo de la Empresa" }) }),
|
|
230
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { type: "file", onChange: uploadLogo, className: "py-1" }),
|
|
231
|
+
field.value && /* @__PURE__ */ jsxRuntime.jsx(
|
|
232
|
+
"img",
|
|
233
|
+
{
|
|
234
|
+
src: field.value,
|
|
235
|
+
alt: "Logo de la Empresa",
|
|
236
|
+
className: "mt-2 h-24 w-24"
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
] });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
),
|
|
243
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isLoading || isPending, children: "Guardar" })
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
) })
|
|
247
|
+
] });
|
|
248
|
+
};
|
|
249
|
+
const config = adminSdk.defineRouteConfig({
|
|
250
|
+
label: "Comprobante de Pedido"
|
|
251
|
+
});
|
|
252
|
+
const i18nTranslations0 = {};
|
|
253
|
+
const widgetModule = { widgets: [
|
|
254
|
+
{
|
|
255
|
+
Component: OrderInvoiceWidget,
|
|
256
|
+
zone: ["order.details.side.before"]
|
|
257
|
+
}
|
|
258
|
+
] };
|
|
259
|
+
const routeModule = {
|
|
260
|
+
routes: [
|
|
261
|
+
{
|
|
262
|
+
Component: InvoiceConfigPage,
|
|
263
|
+
path: "/invoice-config"
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
};
|
|
267
|
+
const menuItemModule = {
|
|
268
|
+
menuItems: [
|
|
269
|
+
{
|
|
270
|
+
label: config.label,
|
|
271
|
+
icon: void 0,
|
|
272
|
+
path: "/invoice-config",
|
|
273
|
+
nested: void 0,
|
|
274
|
+
rank: void 0,
|
|
275
|
+
translationNs: void 0
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
};
|
|
279
|
+
const formModule = { customFields: {} };
|
|
280
|
+
const displayModule = {
|
|
281
|
+
displays: {}
|
|
282
|
+
};
|
|
283
|
+
const i18nModule = { resources: i18nTranslations0 };
|
|
284
|
+
const plugin = {
|
|
285
|
+
widgetModule,
|
|
286
|
+
routeModule,
|
|
287
|
+
menuItemModule,
|
|
288
|
+
formModule,
|
|
289
|
+
displayModule,
|
|
290
|
+
i18nModule
|
|
291
|
+
};
|
|
292
|
+
module.exports = plugin;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
+
import { Container, Heading, Text, Button, toast, Label, Input, Textarea } from "@medusajs/ui";
|
|
4
|
+
import Medusa from "@medusajs/js-sdk";
|
|
5
|
+
import { useState, useCallback, useEffect } from "react";
|
|
6
|
+
import { useQuery, useMutation } from "@tanstack/react-query";
|
|
7
|
+
import { useForm, FormProvider, Controller } from "react-hook-form";
|
|
8
|
+
import { z } from "@medusajs/framework/zod";
|
|
9
|
+
import "@medusajs/admin-shared";
|
|
10
|
+
const sdk = new Medusa({
|
|
11
|
+
baseUrl: "/",
|
|
12
|
+
debug: false,
|
|
13
|
+
auth: {
|
|
14
|
+
type: "session"
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const OrderInvoiceWidget = ({ data: order }) => {
|
|
18
|
+
const [isDownloading, setIsDownloading] = useState(false);
|
|
19
|
+
const downloadInvoice = async () => {
|
|
20
|
+
setIsDownloading(true);
|
|
21
|
+
try {
|
|
22
|
+
const response = await sdk.client.fetch(`/admin/orders/${order.id}/invoices`, {
|
|
23
|
+
method: "GET",
|
|
24
|
+
headers: {
|
|
25
|
+
"accept": "application/pdf"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const blob = await response.blob();
|
|
29
|
+
const url = window.URL.createObjectURL(blob);
|
|
30
|
+
const a = document.createElement("a");
|
|
31
|
+
a.href = url;
|
|
32
|
+
a.download = `comprobante-pedido-${order.id}.pdf`;
|
|
33
|
+
document.body.appendChild(a);
|
|
34
|
+
a.click();
|
|
35
|
+
window.URL.revokeObjectURL(url);
|
|
36
|
+
document.body.removeChild(a);
|
|
37
|
+
setIsDownloading(false);
|
|
38
|
+
toast.success("Comprobante generado y descargado exitosamente");
|
|
39
|
+
} catch (error) {
|
|
40
|
+
toast.error(`Error al generar comprobante: ${error}`);
|
|
41
|
+
setIsDownloading(false);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
45
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
46
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Comprobante" }),
|
|
47
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Generar y descargar comprobante de pedido" })
|
|
48
|
+
] }) }),
|
|
49
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-end px-6 py-4", children: /* @__PURE__ */ jsx(
|
|
50
|
+
Button,
|
|
51
|
+
{
|
|
52
|
+
variant: "secondary",
|
|
53
|
+
disabled: isDownloading,
|
|
54
|
+
onClick: downloadInvoice,
|
|
55
|
+
isLoading: isDownloading,
|
|
56
|
+
children: "Descargar Comprobante"
|
|
57
|
+
}
|
|
58
|
+
) })
|
|
59
|
+
] });
|
|
60
|
+
};
|
|
61
|
+
defineWidgetConfig({
|
|
62
|
+
zone: "order.details.side.before"
|
|
63
|
+
});
|
|
64
|
+
z.object({
|
|
65
|
+
company_name: z.string().optional(),
|
|
66
|
+
company_ruc: z.string().optional(),
|
|
67
|
+
company_address: z.string().optional(),
|
|
68
|
+
company_phone: z.string().optional(),
|
|
69
|
+
company_email: z.string().email().optional(),
|
|
70
|
+
company_logo: z.string().url().optional(),
|
|
71
|
+
notes: z.string().optional(),
|
|
72
|
+
admin_notification_email: z.string().email().optional().or(z.literal(""))
|
|
73
|
+
});
|
|
74
|
+
const InvoiceConfigPage = () => {
|
|
75
|
+
const { data, isLoading, refetch } = useQuery({
|
|
76
|
+
queryFn: () => sdk.client.fetch("/admin/invoice-config"),
|
|
77
|
+
queryKey: ["invoice-config"]
|
|
78
|
+
});
|
|
79
|
+
const { mutateAsync, isPending } = useMutation({
|
|
80
|
+
mutationFn: (payload) => sdk.client.fetch("/admin/invoice-config", {
|
|
81
|
+
method: "POST",
|
|
82
|
+
body: payload
|
|
83
|
+
}),
|
|
84
|
+
onSuccess: () => {
|
|
85
|
+
refetch();
|
|
86
|
+
toast.success("Configuración actualizada exitosamente");
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const getFormDefaultValues = useCallback(() => {
|
|
90
|
+
return {
|
|
91
|
+
company_name: (data == null ? void 0 : data.invoice_config.company_name) || "",
|
|
92
|
+
company_ruc: (data == null ? void 0 : data.invoice_config.company_ruc) || "",
|
|
93
|
+
company_address: (data == null ? void 0 : data.invoice_config.company_address) || "",
|
|
94
|
+
company_phone: (data == null ? void 0 : data.invoice_config.company_phone) || "",
|
|
95
|
+
company_email: (data == null ? void 0 : data.invoice_config.company_email) || "",
|
|
96
|
+
company_logo: (data == null ? void 0 : data.invoice_config.company_logo) || "",
|
|
97
|
+
notes: (data == null ? void 0 : data.invoice_config.notes) || "",
|
|
98
|
+
admin_notification_email: (data == null ? void 0 : data.invoice_config.admin_notification_email) || ""
|
|
99
|
+
};
|
|
100
|
+
}, [data]);
|
|
101
|
+
const form = useForm({
|
|
102
|
+
defaultValues: getFormDefaultValues()
|
|
103
|
+
});
|
|
104
|
+
const handleSubmit = form.handleSubmit((formData) => mutateAsync(formData));
|
|
105
|
+
const uploadLogo = async (event) => {
|
|
106
|
+
var _a;
|
|
107
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
108
|
+
if (!file) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const { files } = await sdk.admin.upload.create({
|
|
112
|
+
files: [file]
|
|
113
|
+
});
|
|
114
|
+
form.setValue("company_logo", files[0].url);
|
|
115
|
+
};
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
form.reset(getFormDefaultValues());
|
|
118
|
+
}, [getFormDefaultValues]);
|
|
119
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
120
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Configuración de Comprobante" }) }),
|
|
121
|
+
/* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs(
|
|
122
|
+
"form",
|
|
123
|
+
{
|
|
124
|
+
onSubmit: handleSubmit,
|
|
125
|
+
className: "flex h-full flex-col overflow-hidden p-2 gap-2",
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ jsx("div", { className: "bg-ui-bg-subtle border border-ui-border-base rounded-lg p-3 mb-2", children: /* @__PURE__ */ jsx(
|
|
128
|
+
Controller,
|
|
129
|
+
{
|
|
130
|
+
control: form.control,
|
|
131
|
+
name: "admin_notification_email",
|
|
132
|
+
render: ({ field }) => {
|
|
133
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
134
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "📧 Email de Notificaciones (Admin)" }) }),
|
|
135
|
+
/* @__PURE__ */ jsx(Input, { ...field, onChange: field.onChange, value: field.value, placeholder: "admin@mariquita.food" }),
|
|
136
|
+
/* @__PURE__ */ jsx("span", { className: "text-ui-fg-subtle text-xs", children: "Se enviarán notificaciones de nuevos pedidos y comprobantes de pago a este email." })
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
) }),
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
Controller,
|
|
143
|
+
{
|
|
144
|
+
control: form.control,
|
|
145
|
+
name: "company_name",
|
|
146
|
+
render: ({ field }) => {
|
|
147
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
148
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Nombre de la Empresa" }) }),
|
|
149
|
+
/* @__PURE__ */ jsx(Input, { ...field, onChange: field.onChange, value: field.value })
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
/* @__PURE__ */ jsx(
|
|
155
|
+
Controller,
|
|
156
|
+
{
|
|
157
|
+
control: form.control,
|
|
158
|
+
name: "company_ruc",
|
|
159
|
+
render: ({ field }) => {
|
|
160
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
161
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "RUC" }) }),
|
|
162
|
+
/* @__PURE__ */ jsx(Input, { ...field, onChange: field.onChange, value: field.value, placeholder: "1234567890001" })
|
|
163
|
+
] });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
/* @__PURE__ */ jsx(
|
|
168
|
+
Controller,
|
|
169
|
+
{
|
|
170
|
+
control: form.control,
|
|
171
|
+
name: "company_address",
|
|
172
|
+
render: ({ field }) => {
|
|
173
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
174
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Dirección de la Empresa" }) }),
|
|
175
|
+
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
176
|
+
] });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
Controller,
|
|
182
|
+
{
|
|
183
|
+
control: form.control,
|
|
184
|
+
name: "company_phone",
|
|
185
|
+
render: ({ field }) => {
|
|
186
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
187
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Teléfono de la Empresa" }) }),
|
|
188
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
189
|
+
] });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
),
|
|
193
|
+
/* @__PURE__ */ jsx(
|
|
194
|
+
Controller,
|
|
195
|
+
{
|
|
196
|
+
control: form.control,
|
|
197
|
+
name: "company_email",
|
|
198
|
+
render: ({ field }) => {
|
|
199
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
200
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Email de la Empresa" }) }),
|
|
201
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
202
|
+
] });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
/* @__PURE__ */ jsx(
|
|
207
|
+
Controller,
|
|
208
|
+
{
|
|
209
|
+
control: form.control,
|
|
210
|
+
name: "notes",
|
|
211
|
+
render: ({ field }) => {
|
|
212
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
213
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Notas" }) }),
|
|
214
|
+
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
215
|
+
] });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
/* @__PURE__ */ jsx(
|
|
220
|
+
Controller,
|
|
221
|
+
{
|
|
222
|
+
control: form.control,
|
|
223
|
+
name: "company_logo",
|
|
224
|
+
render: ({ field }) => {
|
|
225
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
226
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Logo de la Empresa" }) }),
|
|
227
|
+
/* @__PURE__ */ jsx(Input, { type: "file", onChange: uploadLogo, className: "py-1" }),
|
|
228
|
+
field.value && /* @__PURE__ */ jsx(
|
|
229
|
+
"img",
|
|
230
|
+
{
|
|
231
|
+
src: field.value,
|
|
232
|
+
alt: "Logo de la Empresa",
|
|
233
|
+
className: "mt-2 h-24 w-24"
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
] });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
),
|
|
240
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isLoading || isPending, children: "Guardar" })
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
) })
|
|
244
|
+
] });
|
|
245
|
+
};
|
|
246
|
+
const config = defineRouteConfig({
|
|
247
|
+
label: "Comprobante de Pedido"
|
|
248
|
+
});
|
|
249
|
+
const i18nTranslations0 = {};
|
|
250
|
+
const widgetModule = { widgets: [
|
|
251
|
+
{
|
|
252
|
+
Component: OrderInvoiceWidget,
|
|
253
|
+
zone: ["order.details.side.before"]
|
|
254
|
+
}
|
|
255
|
+
] };
|
|
256
|
+
const routeModule = {
|
|
257
|
+
routes: [
|
|
258
|
+
{
|
|
259
|
+
Component: InvoiceConfigPage,
|
|
260
|
+
path: "/invoice-config"
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
};
|
|
264
|
+
const menuItemModule = {
|
|
265
|
+
menuItems: [
|
|
266
|
+
{
|
|
267
|
+
label: config.label,
|
|
268
|
+
icon: void 0,
|
|
269
|
+
path: "/invoice-config",
|
|
270
|
+
nested: void 0,
|
|
271
|
+
rank: void 0,
|
|
272
|
+
translationNs: void 0
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
};
|
|
276
|
+
const formModule = { customFields: {} };
|
|
277
|
+
const displayModule = {
|
|
278
|
+
displays: {}
|
|
279
|
+
};
|
|
280
|
+
const i18nModule = { resources: i18nTranslations0 };
|
|
281
|
+
const plugin = {
|
|
282
|
+
widgetModule,
|
|
283
|
+
routeModule,
|
|
284
|
+
menuItemModule,
|
|
285
|
+
formModule,
|
|
286
|
+
displayModule,
|
|
287
|
+
i18nModule
|
|
288
|
+
};
|
|
289
|
+
export {
|
|
290
|
+
plugin as default
|
|
291
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codemind.ec/medusa-plugin-invoice",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Invoice & quotation PDF widget for Medusa v2 admin — download order receipts and manage company billing config.",
|
|
5
|
+
"author": "Kollor <dev@codemind.ec> (https://codemind.ec)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
".medusa/server"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
"./package.json": "./package.json",
|
|
12
|
+
"./admin": {
|
|
13
|
+
"import": "./.medusa/server/src/admin/index.mjs",
|
|
14
|
+
"require": "./.medusa/server/src/admin/index.js",
|
|
15
|
+
"default": "./.medusa/server/src/admin/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"medusa",
|
|
20
|
+
"medusa-plugin",
|
|
21
|
+
"medusa-v2",
|
|
22
|
+
"plugin",
|
|
23
|
+
"invoice",
|
|
24
|
+
"pdf",
|
|
25
|
+
"comprobante",
|
|
26
|
+
"codemind",
|
|
27
|
+
"kollor"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "medusa plugin:build",
|
|
31
|
+
"dev": "medusa plugin:develop",
|
|
32
|
+
"prepublishOnly": "medusa plugin:build"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@medusajs/admin-sdk": "2.13.3",
|
|
36
|
+
"@medusajs/admin-shared": "2.13.3",
|
|
37
|
+
"@medusajs/cli": "2.13.3",
|
|
38
|
+
"@medusajs/framework": "2.13.3",
|
|
39
|
+
"@medusajs/icons": "^2.13.3",
|
|
40
|
+
"@medusajs/js-sdk": "^2.13.3",
|
|
41
|
+
"@medusajs/medusa": "2.13.3",
|
|
42
|
+
"@medusajs/test-utils": "2.13.3",
|
|
43
|
+
"@medusajs/ui": "^4.1.3",
|
|
44
|
+
"@swc/core": "^1.7.28",
|
|
45
|
+
"@tanstack/react-query": "^5.64.2",
|
|
46
|
+
"@types/node": "^20",
|
|
47
|
+
"@types/react": "^18.3.3",
|
|
48
|
+
"@types/react-dom": "^18.3.0",
|
|
49
|
+
"prop-types": "^15.8.1",
|
|
50
|
+
"react": "^18.3.1",
|
|
51
|
+
"react-dom": "^18.3.1",
|
|
52
|
+
"react-hook-form": "^7.54.2",
|
|
53
|
+
"ts-node": "^10.9.2",
|
|
54
|
+
"typescript": "^5",
|
|
55
|
+
"vite": "^5.4.14"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@medusajs/admin-sdk": "2.13.3",
|
|
59
|
+
"@medusajs/cli": "2.13.3",
|
|
60
|
+
"@medusajs/framework": "2.13.3",
|
|
61
|
+
"@medusajs/icons": "^2.13.3",
|
|
62
|
+
"@medusajs/medusa": "2.13.3",
|
|
63
|
+
"@medusajs/test-utils": "2.13.3",
|
|
64
|
+
"@medusajs/ui": "^4.1.3"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=20"
|
|
68
|
+
}
|
|
69
|
+
}
|