@codemind.ec/medusa-plugin-invoice 1.0.6 → 1.1.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/.medusa/server/src/admin/index.js +806 -378
- package/.medusa/server/src/admin/index.mjs +809 -381
- package/.medusa/server/src/api/admin/invoice-config/[id]/route.js +31 -0
- package/.medusa/server/src/api/admin/invoice-config/[id]/set-default/route.js +19 -0
- package/.medusa/server/src/api/admin/invoice-config/route.js +6 -8
- package/.medusa/server/src/api/admin/invoice-templates/route.js +2 -2
- package/.medusa/server/src/api/admin/invoice-templates/validators.js +3 -1
- package/.medusa/server/src/modules/invoice-generator/migrations/Migration20260321120000.js +29 -0
- package/.medusa/server/src/modules/invoice-generator/models/invoice-config.js +2 -1
- package/.medusa/server/src/modules/invoice-generator/models/invoice-template.js +2 -1
- package/.medusa/server/src/modules/invoice-generator/service.js +14 -2
- package/package.json +86 -87
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
-
import { Container, Heading, Text, Button, toast, Label, Input, Textarea,
|
|
3
|
+
import { Container, Heading, Text, Button, toast, Badge, Table, Label, Input, Textarea, Switch, Select } from "@medusajs/ui";
|
|
4
4
|
import Medusa from "@medusajs/js-sdk";
|
|
5
5
|
import { useState, useCallback, useEffect, useRef, useMemo } from "react";
|
|
6
|
-
import { useQuery,
|
|
7
|
-
import { useForm, FormProvider, Controller } from "react-hook-form";
|
|
8
|
-
import { z } from "@medusajs/framework/zod";
|
|
6
|
+
import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
|
|
9
7
|
import { useNavigate, useParams } from "react-router-dom";
|
|
10
8
|
import { DocumentText } from "@medusajs/icons";
|
|
9
|
+
import { useForm, FormProvider, Controller } from "react-hook-form";
|
|
11
10
|
import CodeMirror from "@uiw/react-codemirror";
|
|
12
11
|
import { html } from "@codemirror/lang-html";
|
|
13
12
|
import { oneDark } from "@codemirror/theme-one-dark";
|
|
@@ -67,202 +66,167 @@ const OrderInvoiceWidget = ({ data: order }) => {
|
|
|
67
66
|
defineWidgetConfig({
|
|
68
67
|
zone: "order.details.side.before"
|
|
69
68
|
});
|
|
70
|
-
z.object({
|
|
71
|
-
company_name: z.string().optional(),
|
|
72
|
-
company_ruc: z.string().optional(),
|
|
73
|
-
company_address: z.string().optional(),
|
|
74
|
-
company_phone: z.string().optional(),
|
|
75
|
-
company_email: z.string().email().optional().or(z.literal("")),
|
|
76
|
-
company_logo: z.string().optional(),
|
|
77
|
-
notes: z.string().optional(),
|
|
78
|
-
admin_notification_email: z.string().email().optional().or(z.literal(""))
|
|
79
|
-
});
|
|
80
69
|
const InvoiceConfigPage = () => {
|
|
81
70
|
const navigate = useNavigate();
|
|
82
|
-
const { data, isLoading
|
|
71
|
+
const { data: configData, isLoading: loadingConfigs } = useQuery({
|
|
83
72
|
queryFn: () => sdk.client.fetch("/admin/invoice-config"),
|
|
84
|
-
queryKey: ["invoice-
|
|
73
|
+
queryKey: ["invoice-configs"]
|
|
85
74
|
});
|
|
86
|
-
const {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
75
|
+
const { data: templateData, isLoading: loadingTemplates } = useQuery({
|
|
76
|
+
queryFn: () => sdk.client.fetch("/admin/invoice-templates"),
|
|
77
|
+
queryKey: ["invoice-templates"]
|
|
78
|
+
});
|
|
79
|
+
const configs = (configData == null ? void 0 : configData.invoice_configs) ?? [];
|
|
80
|
+
const templates = (templateData == null ? void 0 : templateData.invoice_templates) ?? [];
|
|
81
|
+
const defaultCompany = configs.find((c) => c.is_default);
|
|
82
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4", children: [
|
|
84
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Comprobantes y Cotizaciones" }),
|
|
85
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle mt-1", children: "Gestiona empresas emisoras y plantillas de documentos." })
|
|
86
|
+
] }),
|
|
87
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 p-6", children: [
|
|
88
|
+
/* @__PURE__ */ jsxs("div", { className: "border border-ui-border-base rounded-lg p-5 flex flex-col gap-3", children: [
|
|
89
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
90
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Empresas" }),
|
|
91
|
+
/* @__PURE__ */ jsx(Badge, { color: configs.length > 0 ? "blue" : "grey", children: configs.length })
|
|
92
|
+
] }),
|
|
93
|
+
loadingConfigs ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-sm", children: "Cargando..." }) : defaultCompany ? /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle rounded-md p-3", children: [
|
|
94
|
+
/* @__PURE__ */ jsx(Text, { className: "text-sm font-medium", children: defaultCompany.company_name }),
|
|
95
|
+
defaultCompany.company_ruc && /* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-subtle text-xs", children: [
|
|
96
|
+
"RUC: ",
|
|
97
|
+
defaultCompany.company_ruc
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ jsx(Badge, { color: "green", className: "mt-1", children: "Default" })
|
|
100
|
+
] }) : /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-sm", children: "No hay empresas configuradas." }),
|
|
101
|
+
/* @__PURE__ */ jsx(
|
|
102
|
+
Button,
|
|
103
|
+
{
|
|
104
|
+
variant: "secondary",
|
|
105
|
+
className: "mt-auto",
|
|
106
|
+
onClick: () => navigate("/invoice-config/companies"),
|
|
107
|
+
children: "Gestionar Empresas"
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
] }),
|
|
111
|
+
/* @__PURE__ */ jsxs("div", { className: "border border-ui-border-base rounded-lg p-5 flex flex-col gap-3", children: [
|
|
112
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
113
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Plantillas" }),
|
|
114
|
+
/* @__PURE__ */ jsx(Badge, { color: templates.length > 0 ? "blue" : "grey", children: templates.length })
|
|
115
|
+
] }),
|
|
116
|
+
loadingTemplates ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-sm", children: "Cargando..." }) : templates.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "bg-ui-bg-subtle rounded-md p-3 flex flex-col gap-1", children: [
|
|
117
|
+
templates.slice(0, 3).map((t) => /* @__PURE__ */ jsx(Text, { className: "text-sm", children: t.name }, t.id)),
|
|
118
|
+
templates.length > 3 && /* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-subtle text-xs", children: [
|
|
119
|
+
"+",
|
|
120
|
+
templates.length - 3,
|
|
121
|
+
" más..."
|
|
122
|
+
] })
|
|
123
|
+
] }) : /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-sm", children: "No hay plantillas. Se crearán al reiniciar el servidor." }),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
Button,
|
|
126
|
+
{
|
|
127
|
+
variant: "secondary",
|
|
128
|
+
className: "mt-auto",
|
|
129
|
+
onClick: () => navigate("/invoice-config/invoice-templates"),
|
|
130
|
+
children: "Gestionar Plantillas"
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
] })
|
|
134
|
+
] })
|
|
135
|
+
] });
|
|
136
|
+
};
|
|
137
|
+
const config$1 = defineRouteConfig({
|
|
138
|
+
label: "Comprobantes y Cotizaciones"
|
|
139
|
+
});
|
|
140
|
+
const CompaniesPage = () => {
|
|
141
|
+
const navigate = useNavigate();
|
|
142
|
+
const queryClient = useQueryClient();
|
|
143
|
+
const { data, isLoading } = useQuery({
|
|
144
|
+
queryFn: () => sdk.client.fetch("/admin/invoice-config"),
|
|
145
|
+
queryKey: ["invoice-configs"]
|
|
146
|
+
});
|
|
147
|
+
const setDefaultMutation = useMutation({
|
|
148
|
+
mutationFn: (id) => sdk.client.fetch(`/admin/invoice-config/${id}/set-default`, { method: "POST" }),
|
|
91
149
|
onSuccess: () => {
|
|
92
|
-
|
|
93
|
-
toast.success("
|
|
150
|
+
queryClient.invalidateQueries({ queryKey: ["invoice-configs"] });
|
|
151
|
+
toast.success("Empresa marcada como default");
|
|
94
152
|
},
|
|
95
|
-
onError: () =>
|
|
96
|
-
toast.error("Error al guardar la configuración");
|
|
97
|
-
}
|
|
153
|
+
onError: () => toast.error("Error al cambiar empresa default")
|
|
98
154
|
});
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
company_logo: (data == null ? void 0 : data.invoice_config.company_logo) || "",
|
|
107
|
-
notes: (data == null ? void 0 : data.invoice_config.notes) || "",
|
|
108
|
-
admin_notification_email: (data == null ? void 0 : data.invoice_config.admin_notification_email) || ""
|
|
109
|
-
};
|
|
110
|
-
}, [data]);
|
|
111
|
-
const form = useForm({
|
|
112
|
-
defaultValues: getFormDefaultValues()
|
|
155
|
+
const deleteMutation = useMutation({
|
|
156
|
+
mutationFn: (id) => sdk.client.fetch(`/admin/invoice-config/${id}`, { method: "DELETE" }),
|
|
157
|
+
onSuccess: () => {
|
|
158
|
+
queryClient.invalidateQueries({ queryKey: ["invoice-configs"] });
|
|
159
|
+
toast.success("Empresa eliminada");
|
|
160
|
+
},
|
|
161
|
+
onError: () => toast.error("No se pudo eliminar la empresa")
|
|
113
162
|
});
|
|
114
|
-
const
|
|
115
|
-
const uploadLogo = async (event) => {
|
|
116
|
-
var _a;
|
|
117
|
-
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
118
|
-
if (!file) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
try {
|
|
122
|
-
const { files } = await sdk.admin.upload.create({
|
|
123
|
-
files: [file]
|
|
124
|
-
});
|
|
125
|
-
form.setValue("company_logo", files[0].url);
|
|
126
|
-
} catch (error) {
|
|
127
|
-
toast.error("Error al subir el logo. Verifica que el archivo sea una imagen válida.");
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
form.reset(getFormDefaultValues());
|
|
132
|
-
}, [getFormDefaultValues]);
|
|
163
|
+
const configs = (data == null ? void 0 : data.invoice_configs) ?? [];
|
|
133
164
|
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
134
165
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
135
|
-
/* @__PURE__ */
|
|
136
|
-
|
|
166
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
167
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Empresas" }),
|
|
168
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-sm mt-1", children: "Empresas emisoras de comprobantes y cotizaciones." })
|
|
169
|
+
] }),
|
|
170
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
171
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config"), children: "← Panel" }),
|
|
172
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => navigate("/invoice-config/companies/new"), children: "+ Nueva Empresa" })
|
|
173
|
+
] })
|
|
137
174
|
] }),
|
|
138
|
-
/* @__PURE__ */ jsx(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
children:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
] });
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
),
|
|
184
|
-
/* @__PURE__ */ jsx(
|
|
185
|
-
Controller,
|
|
186
|
-
{
|
|
187
|
-
control: form.control,
|
|
188
|
-
name: "company_address",
|
|
189
|
-
render: ({ field }) => {
|
|
190
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
191
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Dirección de la Empresa" }) }),
|
|
192
|
-
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
193
|
-
] });
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
),
|
|
197
|
-
/* @__PURE__ */ jsx(
|
|
198
|
-
Controller,
|
|
199
|
-
{
|
|
200
|
-
control: form.control,
|
|
201
|
-
name: "company_phone",
|
|
202
|
-
render: ({ field }) => {
|
|
203
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
204
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Teléfono de la Empresa" }) }),
|
|
205
|
-
/* @__PURE__ */ jsx(Input, { ...field })
|
|
206
|
-
] });
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
),
|
|
210
|
-
/* @__PURE__ */ jsx(
|
|
211
|
-
Controller,
|
|
212
|
-
{
|
|
213
|
-
control: form.control,
|
|
214
|
-
name: "company_email",
|
|
215
|
-
render: ({ field }) => {
|
|
216
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
217
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Email de la Empresa" }) }),
|
|
218
|
-
/* @__PURE__ */ jsx(Input, { ...field })
|
|
219
|
-
] });
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
),
|
|
223
|
-
/* @__PURE__ */ jsx(
|
|
224
|
-
Controller,
|
|
225
|
-
{
|
|
226
|
-
control: form.control,
|
|
227
|
-
name: "notes",
|
|
228
|
-
render: ({ field }) => {
|
|
229
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
230
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Notas" }) }),
|
|
231
|
-
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
232
|
-
] });
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
),
|
|
236
|
-
/* @__PURE__ */ jsx(
|
|
237
|
-
Controller,
|
|
238
|
-
{
|
|
239
|
-
control: form.control,
|
|
240
|
-
name: "company_logo",
|
|
241
|
-
render: ({ field }) => {
|
|
242
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-2", children: [
|
|
243
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-x-1", children: /* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Logo de la Empresa" }) }),
|
|
244
|
-
/* @__PURE__ */ jsx(Input, { type: "file", onChange: uploadLogo, className: "py-1" }),
|
|
245
|
-
field.value && /* @__PURE__ */ jsx(
|
|
246
|
-
"img",
|
|
247
|
-
{
|
|
248
|
-
src: field.value,
|
|
249
|
-
alt: "Logo de la Empresa",
|
|
250
|
-
className: "mt-2 h-24 w-24"
|
|
175
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 pb-4", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "text-center py-10 text-ui-fg-subtle", children: "Cargando..." }) : configs.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-10 text-ui-fg-subtle", children: "No hay empresas configuradas." }) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
176
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
177
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Nombre" }),
|
|
178
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "RUC" }),
|
|
179
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Email" }),
|
|
180
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Teléfono" }),
|
|
181
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Estado" }),
|
|
182
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { style: { textAlign: "right" }, children: "Acciones" })
|
|
183
|
+
] }) }),
|
|
184
|
+
/* @__PURE__ */ jsx(Table.Body, { children: configs.map((cfg) => /* @__PURE__ */ jsxs(
|
|
185
|
+
Table.Row,
|
|
186
|
+
{
|
|
187
|
+
onClick: () => navigate(`/invoice-config/companies/${cfg.id}`),
|
|
188
|
+
style: { cursor: "pointer" },
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "font-medium", children: cfg.company_name }),
|
|
191
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx("code", { className: "text-xs", children: cfg.company_ruc || "—" }) }),
|
|
192
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: cfg.company_email }),
|
|
193
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: cfg.company_phone }),
|
|
194
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: cfg.is_default ? /* @__PURE__ */ jsx(Badge, { color: "green", children: "Default" }) : /* @__PURE__ */ jsx(Badge, { color: "grey", children: "—" }) }),
|
|
195
|
+
/* @__PURE__ */ jsx(Table.Cell, { style: { textAlign: "right" }, children: /* @__PURE__ */ jsx("div", { className: "flex gap-2 justify-end", children: !cfg.is_default && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
196
|
+
/* @__PURE__ */ jsx(
|
|
197
|
+
Button,
|
|
198
|
+
{
|
|
199
|
+
variant: "secondary",
|
|
200
|
+
size: "small",
|
|
201
|
+
onClick: (e) => {
|
|
202
|
+
e.stopPropagation();
|
|
203
|
+
setDefaultMutation.mutate(cfg.id);
|
|
204
|
+
},
|
|
205
|
+
children: "Default"
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
/* @__PURE__ */ jsx(
|
|
209
|
+
Button,
|
|
210
|
+
{
|
|
211
|
+
variant: "danger",
|
|
212
|
+
size: "small",
|
|
213
|
+
onClick: (e) => {
|
|
214
|
+
e.stopPropagation();
|
|
215
|
+
if (confirm("¿Eliminar esta empresa?")) {
|
|
216
|
+
deleteMutation.mutate(cfg.id);
|
|
251
217
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
218
|
+
},
|
|
219
|
+
children: "Eliminar"
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
] }) }) })
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
cfg.id
|
|
226
|
+
)) })
|
|
227
|
+
] }) })
|
|
261
228
|
] });
|
|
262
229
|
};
|
|
263
|
-
const config$1 = defineRouteConfig({
|
|
264
|
-
label: "Comprobante de Pedido"
|
|
265
|
-
});
|
|
266
230
|
const TYPE_LABELS = {
|
|
267
231
|
order_invoice: "Comprobante de Pedido",
|
|
268
232
|
quote_proforma: "Cotización Proforma"
|
|
@@ -274,6 +238,10 @@ const InvoiceTemplatesPage = () => {
|
|
|
274
238
|
queryFn: () => sdk.client.fetch("/admin/invoice-templates"),
|
|
275
239
|
queryKey: ["invoice-templates"]
|
|
276
240
|
});
|
|
241
|
+
const { data: companiesData } = useQuery({
|
|
242
|
+
queryFn: () => sdk.client.fetch("/admin/invoice-config"),
|
|
243
|
+
queryKey: ["invoice-configs"]
|
|
244
|
+
});
|
|
277
245
|
const deleteMutation = useMutation({
|
|
278
246
|
mutationFn: (id) => sdk.client.fetch(`/admin/invoice-templates/${id}`, { method: "DELETE" }),
|
|
279
247
|
onSuccess: () => {
|
|
@@ -285,19 +253,21 @@ const InvoiceTemplatesPage = () => {
|
|
|
285
253
|
}
|
|
286
254
|
});
|
|
287
255
|
const templates = (data == null ? void 0 : data.invoice_templates) ?? [];
|
|
288
|
-
|
|
289
|
-
|
|
256
|
+
const companiesMap = new Map(((companiesData == null ? void 0 : companiesData.invoice_configs) ?? []).map((c) => [c.id, c.company_name]));
|
|
257
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
258
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
290
259
|
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Plantillas de Documentos" }),
|
|
291
|
-
/* @__PURE__ */ jsxs("div", {
|
|
292
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config"), children: "
|
|
260
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
261
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config"), children: "← Panel" }),
|
|
293
262
|
/* @__PURE__ */ jsx(Button, { onClick: () => navigate("/invoice-config/invoice-templates/new"), children: "+ Nueva Plantilla" })
|
|
294
263
|
] })
|
|
295
264
|
] }),
|
|
296
|
-
|
|
265
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 pb-4", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "text-center py-10 text-ui-fg-subtle", children: "Cargando..." }) : templates.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-10 text-ui-fg-subtle", children: "No hay plantillas. Se crearán automáticamente al reiniciar el servidor." }) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
297
266
|
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
298
267
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Nombre" }),
|
|
299
268
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Slug" }),
|
|
300
269
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Tipo" }),
|
|
270
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Empresa" }),
|
|
301
271
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Estado" }),
|
|
302
272
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Actualizado" }),
|
|
303
273
|
/* @__PURE__ */ jsx(Table.HeaderCell, { style: { textAlign: "right" }, children: "Acciones" })
|
|
@@ -308,9 +278,10 @@ const InvoiceTemplatesPage = () => {
|
|
|
308
278
|
onClick: () => navigate(`/invoice-config/invoice-templates/${tpl.id}`),
|
|
309
279
|
style: { cursor: "pointer" },
|
|
310
280
|
children: [
|
|
311
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: tpl.name }),
|
|
312
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx("code", {
|
|
281
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "font-medium", children: tpl.name }),
|
|
282
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx("code", { className: "text-xs", children: tpl.slug }) }),
|
|
313
283
|
/* @__PURE__ */ jsx(Table.Cell, { children: TYPE_LABELS[tpl.type] ?? tpl.type }),
|
|
284
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Text, { className: "text-sm", children: tpl.company_id ? companiesMap.get(tpl.company_id) ?? "—" : "—" }) }),
|
|
314
285
|
/* @__PURE__ */ jsx(Table.Cell, { children: tpl.is_default ? /* @__PURE__ */ jsx(Badge, { color: "blue", children: "Por defecto" }) : /* @__PURE__ */ jsx(Badge, { color: "grey", children: "Personalizada" }) }),
|
|
315
286
|
/* @__PURE__ */ jsx(Table.Cell, { children: new Date(tpl.updated_at).toLocaleDateString("es-ES") }),
|
|
316
287
|
/* @__PURE__ */ jsx(Table.Cell, { style: { textAlign: "right" }, children: /* @__PURE__ */ jsx(
|
|
@@ -332,163 +303,491 @@ const InvoiceTemplatesPage = () => {
|
|
|
332
303
|
},
|
|
333
304
|
tpl.id
|
|
334
305
|
)) })
|
|
335
|
-
] })
|
|
306
|
+
] }) })
|
|
336
307
|
] });
|
|
337
308
|
};
|
|
338
309
|
const config = defineRouteConfig({
|
|
339
310
|
label: "Plantillas PDF",
|
|
340
311
|
icon: DocumentText
|
|
341
312
|
});
|
|
342
|
-
const
|
|
313
|
+
const NewCompanyPage = () => {
|
|
343
314
|
const navigate = useNavigate();
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
315
|
+
const form = useForm({
|
|
316
|
+
defaultValues: {
|
|
317
|
+
company_name: "",
|
|
318
|
+
company_ruc: "",
|
|
319
|
+
company_address: "",
|
|
320
|
+
company_phone: "",
|
|
321
|
+
company_email: "",
|
|
322
|
+
company_logo: "",
|
|
323
|
+
notes: "",
|
|
324
|
+
admin_notification_email: "",
|
|
325
|
+
is_default: false
|
|
326
|
+
}
|
|
327
|
+
});
|
|
347
328
|
const createMutation = useMutation({
|
|
348
|
-
mutationFn: (payload) => sdk.client.fetch("/admin/invoice-
|
|
329
|
+
mutationFn: (payload) => sdk.client.fetch("/admin/invoice-config", {
|
|
349
330
|
method: "POST",
|
|
350
331
|
body: payload
|
|
351
332
|
}),
|
|
352
|
-
onSuccess: (
|
|
353
|
-
toast.success("
|
|
354
|
-
navigate(
|
|
333
|
+
onSuccess: () => {
|
|
334
|
+
toast.success("Empresa creada exitosamente");
|
|
335
|
+
navigate("/invoice-config/companies");
|
|
355
336
|
},
|
|
356
|
-
onError: () =>
|
|
357
|
-
toast.error("Error al crear la plantilla");
|
|
358
|
-
}
|
|
337
|
+
onError: () => toast.error("Error al crear la empresa")
|
|
359
338
|
});
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
339
|
+
const uploadLogo = async (event) => {
|
|
340
|
+
var _a;
|
|
341
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
342
|
+
if (!file) return;
|
|
343
|
+
try {
|
|
344
|
+
const { files } = await sdk.admin.upload.create({ files: [file] });
|
|
345
|
+
form.setValue("company_logo", files[0].url);
|
|
346
|
+
} catch {
|
|
347
|
+
toast.error("Error al subir el logo.");
|
|
364
348
|
}
|
|
365
|
-
createMutation.mutate({
|
|
366
|
-
name,
|
|
367
|
-
slug,
|
|
368
|
-
type,
|
|
369
|
-
html_content: getDefaultHtml(type)
|
|
370
|
-
});
|
|
371
349
|
};
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
/* @__PURE__ */ jsxs("div", {
|
|
375
|
-
/* @__PURE__ */
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
/* @__PURE__ */
|
|
380
|
-
|
|
350
|
+
const handleSubmit = form.handleSubmit((data) => createMutation.mutate(data));
|
|
351
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
352
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
353
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Nueva Empresa" }),
|
|
354
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config/companies"), children: "← Volver" })
|
|
355
|
+
] }),
|
|
356
|
+
/* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4 p-6 max-w-2xl", children: [
|
|
357
|
+
/* @__PURE__ */ jsx("div", { className: "bg-ui-bg-subtle border border-ui-border-base rounded-lg p-4 mb-2", children: /* @__PURE__ */ jsx(
|
|
358
|
+
Controller,
|
|
359
|
+
{
|
|
360
|
+
control: form.control,
|
|
361
|
+
name: "admin_notification_email",
|
|
362
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
363
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "📧 Email de Notificaciones (Admin)" }),
|
|
364
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "admin@empresa.com" }),
|
|
365
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-xs", children: "Notificaciones de nuevos pedidos a este email." })
|
|
366
|
+
] })
|
|
367
|
+
}
|
|
368
|
+
) }),
|
|
369
|
+
/* @__PURE__ */ jsx(
|
|
370
|
+
Controller,
|
|
371
|
+
{
|
|
372
|
+
control: form.control,
|
|
373
|
+
name: "company_name",
|
|
374
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
375
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Nombre de la Empresa *" }),
|
|
376
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "Mi Empresa S.A.S." })
|
|
377
|
+
] })
|
|
378
|
+
}
|
|
379
|
+
),
|
|
380
|
+
/* @__PURE__ */ jsx(
|
|
381
|
+
Controller,
|
|
382
|
+
{
|
|
383
|
+
control: form.control,
|
|
384
|
+
name: "company_ruc",
|
|
385
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
386
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "RUC" }),
|
|
387
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "1234567890001" })
|
|
388
|
+
] })
|
|
389
|
+
}
|
|
390
|
+
),
|
|
391
|
+
/* @__PURE__ */ jsx(
|
|
392
|
+
Controller,
|
|
393
|
+
{
|
|
394
|
+
control: form.control,
|
|
395
|
+
name: "company_address",
|
|
396
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
397
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Dirección" }),
|
|
398
|
+
/* @__PURE__ */ jsx(Textarea, { ...field, placeholder: "Av. Principal 123, Quito" })
|
|
399
|
+
] })
|
|
400
|
+
}
|
|
401
|
+
),
|
|
402
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
381
403
|
/* @__PURE__ */ jsx(
|
|
382
|
-
|
|
404
|
+
Controller,
|
|
383
405
|
{
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
406
|
+
control: form.control,
|
|
407
|
+
name: "company_phone",
|
|
408
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
409
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Teléfono" }),
|
|
410
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "+593 99 123 4567" })
|
|
411
|
+
] })
|
|
412
|
+
}
|
|
413
|
+
),
|
|
414
|
+
/* @__PURE__ */ jsx(
|
|
415
|
+
Controller,
|
|
416
|
+
{
|
|
417
|
+
control: form.control,
|
|
418
|
+
name: "company_email",
|
|
419
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
420
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Email de la Empresa" }),
|
|
421
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "info@empresa.com" })
|
|
422
|
+
] })
|
|
388
423
|
}
|
|
389
424
|
)
|
|
390
425
|
] }),
|
|
391
|
-
/* @__PURE__ */
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
/* @__PURE__ */ jsx(
|
|
426
|
+
/* @__PURE__ */ jsx(
|
|
427
|
+
Controller,
|
|
428
|
+
{
|
|
429
|
+
control: form.control,
|
|
430
|
+
name: "notes",
|
|
431
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
432
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Notas" }),
|
|
433
|
+
/* @__PURE__ */ jsx(Textarea, { ...field, placeholder: "Notas internas o pie de página del documento" })
|
|
398
434
|
] })
|
|
399
|
-
|
|
435
|
+
}
|
|
436
|
+
),
|
|
437
|
+
/* @__PURE__ */ jsx(
|
|
438
|
+
Controller,
|
|
439
|
+
{
|
|
440
|
+
control: form.control,
|
|
441
|
+
name: "company_logo",
|
|
442
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
443
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Logo de la Empresa" }),
|
|
444
|
+
/* @__PURE__ */ jsx(Input, { type: "file", onChange: uploadLogo, className: "py-1" }),
|
|
445
|
+
field.value && /* @__PURE__ */ jsx("img", { src: field.value, alt: "Logo", className: "mt-2 h-24 w-24 object-contain" })
|
|
446
|
+
] })
|
|
447
|
+
}
|
|
448
|
+
),
|
|
449
|
+
/* @__PURE__ */ jsx(
|
|
450
|
+
Controller,
|
|
451
|
+
{
|
|
452
|
+
control: form.control,
|
|
453
|
+
name: "is_default",
|
|
454
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-ui-bg-subtle rounded-lg p-3", children: [
|
|
455
|
+
/* @__PURE__ */ jsx(
|
|
456
|
+
Switch,
|
|
457
|
+
{
|
|
458
|
+
checked: field.value,
|
|
459
|
+
onCheckedChange: field.onChange
|
|
460
|
+
}
|
|
461
|
+
),
|
|
462
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
463
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Empresa por defecto" }),
|
|
464
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-xs", children: "Se usará en plantillas que no tengan empresa asignada." })
|
|
465
|
+
] })
|
|
466
|
+
] })
|
|
467
|
+
}
|
|
468
|
+
),
|
|
469
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 pt-2", children: [
|
|
470
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config/companies"), children: "Cancelar" }),
|
|
471
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", isLoading: createMutation.isPending, children: "Crear Empresa" })
|
|
472
|
+
] })
|
|
473
|
+
] }) })
|
|
474
|
+
] });
|
|
475
|
+
};
|
|
476
|
+
const EditCompanyPage = () => {
|
|
477
|
+
const { id } = useParams();
|
|
478
|
+
const navigate = useNavigate();
|
|
479
|
+
const queryClient = useQueryClient();
|
|
480
|
+
const { data, isLoading } = useQuery({
|
|
481
|
+
queryFn: () => sdk.client.fetch(`/admin/invoice-config/${id}`),
|
|
482
|
+
queryKey: ["invoice-config", id],
|
|
483
|
+
enabled: !!id
|
|
484
|
+
});
|
|
485
|
+
const config2 = data == null ? void 0 : data.invoice_config;
|
|
486
|
+
const getDefaults = useCallback(() => ({
|
|
487
|
+
company_name: (config2 == null ? void 0 : config2.company_name) || "",
|
|
488
|
+
company_ruc: (config2 == null ? void 0 : config2.company_ruc) || "",
|
|
489
|
+
company_address: (config2 == null ? void 0 : config2.company_address) || "",
|
|
490
|
+
company_phone: (config2 == null ? void 0 : config2.company_phone) || "",
|
|
491
|
+
company_email: (config2 == null ? void 0 : config2.company_email) || "",
|
|
492
|
+
company_logo: (config2 == null ? void 0 : config2.company_logo) || "",
|
|
493
|
+
notes: (config2 == null ? void 0 : config2.notes) || "",
|
|
494
|
+
admin_notification_email: (config2 == null ? void 0 : config2.admin_notification_email) || "",
|
|
495
|
+
is_default: (config2 == null ? void 0 : config2.is_default) || false
|
|
496
|
+
}), [config2]);
|
|
497
|
+
const form = useForm({ defaultValues: getDefaults() });
|
|
498
|
+
useEffect(() => {
|
|
499
|
+
form.reset(getDefaults());
|
|
500
|
+
}, [getDefaults]);
|
|
501
|
+
const saveMutation = useMutation({
|
|
502
|
+
mutationFn: (payload) => sdk.client.fetch(`/admin/invoice-config/${id}`, {
|
|
503
|
+
method: "POST",
|
|
504
|
+
body: payload
|
|
505
|
+
}),
|
|
506
|
+
onSuccess: () => {
|
|
507
|
+
queryClient.invalidateQueries({ queryKey: ["invoice-config", id] });
|
|
508
|
+
queryClient.invalidateQueries({ queryKey: ["invoice-configs"] });
|
|
509
|
+
toast.success("Empresa actualizada");
|
|
510
|
+
},
|
|
511
|
+
onError: () => toast.error("Error al guardar")
|
|
512
|
+
});
|
|
513
|
+
const uploadLogo = async (event) => {
|
|
514
|
+
var _a;
|
|
515
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
516
|
+
if (!file) return;
|
|
517
|
+
try {
|
|
518
|
+
const { files } = await sdk.admin.upload.create({ files: [file] });
|
|
519
|
+
form.setValue("company_logo", files[0].url);
|
|
520
|
+
} catch {
|
|
521
|
+
toast.error("Error al subir el logo.");
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
const handleSubmit = form.handleSubmit((data2) => saveMutation.mutate(data2));
|
|
525
|
+
if (isLoading) {
|
|
526
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Cargando..." }) });
|
|
527
|
+
}
|
|
528
|
+
if (!config2) {
|
|
529
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Empresa no encontrada" }) });
|
|
530
|
+
}
|
|
531
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
532
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
533
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Editar Empresa" }),
|
|
534
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config/companies"), children: "← Volver" })
|
|
535
|
+
] }),
|
|
536
|
+
/* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4 p-6 max-w-2xl", children: [
|
|
537
|
+
/* @__PURE__ */ jsx("div", { className: "bg-ui-bg-subtle border border-ui-border-base rounded-lg p-4 mb-2", children: /* @__PURE__ */ jsx(
|
|
538
|
+
Controller,
|
|
539
|
+
{
|
|
540
|
+
control: form.control,
|
|
541
|
+
name: "admin_notification_email",
|
|
542
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
543
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "📧 Email de Notificaciones (Admin)" }),
|
|
544
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "admin@empresa.com" }),
|
|
545
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-xs", children: "Notificaciones de nuevos pedidos a este email." })
|
|
546
|
+
] })
|
|
547
|
+
}
|
|
548
|
+
) }),
|
|
549
|
+
/* @__PURE__ */ jsx(
|
|
550
|
+
Controller,
|
|
551
|
+
{
|
|
552
|
+
control: form.control,
|
|
553
|
+
name: "company_name",
|
|
554
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
555
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Nombre de la Empresa *" }),
|
|
556
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
557
|
+
] })
|
|
558
|
+
}
|
|
559
|
+
),
|
|
560
|
+
/* @__PURE__ */ jsx(
|
|
561
|
+
Controller,
|
|
562
|
+
{
|
|
563
|
+
control: form.control,
|
|
564
|
+
name: "company_ruc",
|
|
565
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
566
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "RUC" }),
|
|
567
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "1234567890001" })
|
|
568
|
+
] })
|
|
569
|
+
}
|
|
570
|
+
),
|
|
571
|
+
/* @__PURE__ */ jsx(
|
|
572
|
+
Controller,
|
|
573
|
+
{
|
|
574
|
+
control: form.control,
|
|
575
|
+
name: "company_address",
|
|
576
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
577
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Dirección" }),
|
|
578
|
+
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
579
|
+
] })
|
|
580
|
+
}
|
|
581
|
+
),
|
|
582
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
583
|
+
/* @__PURE__ */ jsx(
|
|
584
|
+
Controller,
|
|
585
|
+
{
|
|
586
|
+
control: form.control,
|
|
587
|
+
name: "company_phone",
|
|
588
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
589
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Teléfono" }),
|
|
590
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
591
|
+
] })
|
|
592
|
+
}
|
|
593
|
+
),
|
|
594
|
+
/* @__PURE__ */ jsx(
|
|
595
|
+
Controller,
|
|
596
|
+
{
|
|
597
|
+
control: form.control,
|
|
598
|
+
name: "company_email",
|
|
599
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
600
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Email de la Empresa" }),
|
|
601
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
602
|
+
] })
|
|
603
|
+
}
|
|
604
|
+
)
|
|
400
605
|
] }),
|
|
401
|
-
/* @__PURE__ */
|
|
402
|
-
|
|
403
|
-
|
|
606
|
+
/* @__PURE__ */ jsx(
|
|
607
|
+
Controller,
|
|
608
|
+
{
|
|
609
|
+
control: form.control,
|
|
610
|
+
name: "notes",
|
|
611
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
612
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Notas" }),
|
|
613
|
+
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
614
|
+
] })
|
|
615
|
+
}
|
|
616
|
+
),
|
|
617
|
+
/* @__PURE__ */ jsx(
|
|
618
|
+
Controller,
|
|
619
|
+
{
|
|
620
|
+
control: form.control,
|
|
621
|
+
name: "company_logo",
|
|
622
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
623
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Logo de la Empresa" }),
|
|
624
|
+
/* @__PURE__ */ jsx(Input, { type: "file", onChange: uploadLogo, className: "py-1" }),
|
|
625
|
+
field.value && /* @__PURE__ */ jsx("img", { src: field.value, alt: "Logo", className: "mt-2 h-24 w-24 object-contain" })
|
|
626
|
+
] })
|
|
627
|
+
}
|
|
628
|
+
),
|
|
629
|
+
/* @__PURE__ */ jsx(
|
|
630
|
+
Controller,
|
|
631
|
+
{
|
|
632
|
+
control: form.control,
|
|
633
|
+
name: "is_default",
|
|
634
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-ui-bg-subtle rounded-lg p-3", children: [
|
|
635
|
+
/* @__PURE__ */ jsx(
|
|
636
|
+
Switch,
|
|
637
|
+
{
|
|
638
|
+
checked: field.value,
|
|
639
|
+
onCheckedChange: field.onChange
|
|
640
|
+
}
|
|
641
|
+
),
|
|
642
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
643
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Empresa por defecto" }),
|
|
644
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-xs", children: "Se usará en plantillas que no tengan empresa asignada." })
|
|
645
|
+
] })
|
|
646
|
+
] })
|
|
647
|
+
}
|
|
648
|
+
),
|
|
649
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 pt-2", children: [
|
|
650
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config/companies"), children: "Cancelar" }),
|
|
651
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", isLoading: saveMutation.isPending, children: "Guardar Cambios" })
|
|
404
652
|
] })
|
|
405
|
-
] })
|
|
653
|
+
] }) })
|
|
406
654
|
] });
|
|
407
655
|
};
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
656
|
+
const ORDER_INVOICE_CATEGORIES = [
|
|
657
|
+
{
|
|
658
|
+
label: "Empresa",
|
|
659
|
+
icon: "🏢",
|
|
660
|
+
variables: [
|
|
661
|
+
{ name: "company_name" },
|
|
662
|
+
{ name: "company_ruc" },
|
|
663
|
+
{ name: "company_address" },
|
|
664
|
+
{ name: "company_phone" },
|
|
665
|
+
{ name: "company_email" },
|
|
666
|
+
{ name: "company_logo_base64" }
|
|
667
|
+
]
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
label: "Documento",
|
|
671
|
+
icon: "📄",
|
|
672
|
+
variables: [
|
|
673
|
+
{ name: "invoice_id" },
|
|
674
|
+
{ name: "invoice_date" },
|
|
675
|
+
{ name: "order_display_id" },
|
|
676
|
+
{ name: "order_date" }
|
|
677
|
+
]
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
label: "Cliente",
|
|
681
|
+
icon: "👤",
|
|
682
|
+
variables: [
|
|
683
|
+
{ name: "billing_address" },
|
|
684
|
+
{ name: "shipping_address" },
|
|
685
|
+
{ name: "cedula" }
|
|
686
|
+
]
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
label: "Productos",
|
|
690
|
+
icon: "📦",
|
|
691
|
+
variables: [
|
|
692
|
+
{ name: "{{#each items}}", isBlock: true },
|
|
693
|
+
{ name: "this.title" },
|
|
694
|
+
{ name: "this.variant_title" },
|
|
695
|
+
{ name: "this.quantity" },
|
|
696
|
+
{ name: "this.unit_price" },
|
|
697
|
+
{ name: "this.total" },
|
|
698
|
+
{ name: "{{/each}}", isBlock: true }
|
|
699
|
+
]
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
label: "Totales",
|
|
703
|
+
icon: "💰",
|
|
704
|
+
variables: [
|
|
705
|
+
{ name: "subtotal" },
|
|
706
|
+
{ name: "tax_total" },
|
|
707
|
+
{ name: "shipping_total" },
|
|
708
|
+
{ name: "discount_total" },
|
|
709
|
+
{ name: "total" }
|
|
710
|
+
]
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
label: "Otros",
|
|
714
|
+
icon: "📝",
|
|
715
|
+
variables: [
|
|
716
|
+
{ name: "is_home_delivery" },
|
|
717
|
+
{ name: "notes" }
|
|
718
|
+
]
|
|
420
719
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
"
|
|
443
|
-
"
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
"
|
|
453
|
-
"
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
"
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
"
|
|
476
|
-
"
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
]
|
|
720
|
+
];
|
|
721
|
+
const QUOTE_PROFORMA_CATEGORIES = [
|
|
722
|
+
{
|
|
723
|
+
label: "Empresa",
|
|
724
|
+
icon: "🏢",
|
|
725
|
+
variables: [
|
|
726
|
+
{ name: "company_name" },
|
|
727
|
+
{ name: "company_website" }
|
|
728
|
+
]
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
label: "Documento",
|
|
732
|
+
icon: "📄",
|
|
733
|
+
variables: [
|
|
734
|
+
{ name: "quote_number" },
|
|
735
|
+
{ name: "date_str" },
|
|
736
|
+
{ name: "service_name" },
|
|
737
|
+
{ name: "is_home_delivery" }
|
|
738
|
+
]
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
label: "Configuración",
|
|
742
|
+
icon: "⚙️",
|
|
743
|
+
variables: [
|
|
744
|
+
{ name: "{{#each config_fields}}", isBlock: true },
|
|
745
|
+
{ name: "this.label" },
|
|
746
|
+
{ name: "this.value" },
|
|
747
|
+
{ name: "{{/each}}", isBlock: true }
|
|
748
|
+
]
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
label: "Desglose",
|
|
752
|
+
icon: "📊",
|
|
753
|
+
variables: [
|
|
754
|
+
{ name: "{{#each breakdown}}", isBlock: true },
|
|
755
|
+
{ name: "this.label" },
|
|
756
|
+
{ name: "this.total_formatted" },
|
|
757
|
+
{ name: "{{/each}}", isBlock: true }
|
|
758
|
+
]
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
label: "Totales",
|
|
762
|
+
icon: "💰",
|
|
763
|
+
variables: [
|
|
764
|
+
{ name: "totals.subtotal_formatted" },
|
|
765
|
+
{ name: "totals.extras_formatted" },
|
|
766
|
+
{ name: "totals.discount_formatted" },
|
|
767
|
+
{ name: "totals.shipping_formatted" },
|
|
768
|
+
{ name: "totals.taxes_provided" },
|
|
769
|
+
{ name: "totals.taxes_formatted" },
|
|
770
|
+
{ name: "totals.total_formatted" }
|
|
771
|
+
]
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
label: "Incluye y Contacto",
|
|
775
|
+
icon: "📋",
|
|
776
|
+
variables: [
|
|
777
|
+
{ name: "{{#each includes_left}}", isBlock: true },
|
|
778
|
+
{ name: "{{#each includes_right}}", isBlock: true },
|
|
779
|
+
{ name: "{{#each contact_rows}}", isBlock: true },
|
|
780
|
+
{ name: "this.label" },
|
|
781
|
+
{ name: "this.value" },
|
|
782
|
+
{ name: "{{/each}}", isBlock: true }
|
|
783
|
+
]
|
|
784
|
+
}
|
|
785
|
+
];
|
|
786
|
+
const CATEGORIES = {
|
|
787
|
+
order_invoice: ORDER_INVOICE_CATEGORIES,
|
|
788
|
+
quote_proforma: QUOTE_PROFORMA_CATEGORIES
|
|
491
789
|
};
|
|
790
|
+
const PLACEHOLDER_LOGO_BASE64 = "data:image/svg+xml;base64," + btoa(`<svg xmlns="http://www.w3.org/2000/svg" width="120" height="40" viewBox="0 0 120 40"><rect width="120" height="40" rx="4" fill="#e2e8f0"/><text x="60" y="24" text-anchor="middle" fill="#64748b" font-family="sans-serif" font-size="11">LOGO</text></svg>`);
|
|
492
791
|
const SAMPLE_DATA = {
|
|
493
792
|
order_invoice: {
|
|
494
793
|
company_name: "Mi Empresa",
|
|
@@ -496,7 +795,7 @@ const SAMPLE_DATA = {
|
|
|
496
795
|
company_address: "Av. Principal 123, Quito",
|
|
497
796
|
company_phone: "+593 99 123 4567",
|
|
498
797
|
company_email: "info@miempresa.com",
|
|
499
|
-
company_logo_base64:
|
|
798
|
+
company_logo_base64: PLACEHOLDER_LOGO_BASE64,
|
|
500
799
|
invoice_id: "INV-000001",
|
|
501
800
|
invoice_date: "19/06/2025",
|
|
502
801
|
order_display_id: "000042",
|
|
@@ -561,14 +860,22 @@ const TemplateEditorPage = () => {
|
|
|
561
860
|
queryKey: ["invoice-template", id],
|
|
562
861
|
enabled: !!id
|
|
563
862
|
});
|
|
863
|
+
const { data: companiesData } = useQuery({
|
|
864
|
+
queryFn: () => sdk.client.fetch("/admin/invoice-config"),
|
|
865
|
+
queryKey: ["invoice-configs"]
|
|
866
|
+
});
|
|
564
867
|
const template = data == null ? void 0 : data.invoice_template;
|
|
868
|
+
const companies = (companiesData == null ? void 0 : companiesData.invoice_configs) ?? [];
|
|
565
869
|
const [name, setName] = useState("");
|
|
566
870
|
const [htmlContent, setHtmlContent] = useState("");
|
|
871
|
+
const [companyId, setCompanyId] = useState(null);
|
|
567
872
|
const [showVariables, setShowVariables] = useState(false);
|
|
873
|
+
const [collapsedSections, setCollapsedSections] = useState({});
|
|
568
874
|
useEffect(() => {
|
|
569
875
|
if (template) {
|
|
570
876
|
setName(template.name);
|
|
571
877
|
setHtmlContent(template.html_content);
|
|
878
|
+
setCompanyId(template.company_id);
|
|
572
879
|
}
|
|
573
880
|
}, [template]);
|
|
574
881
|
const saveMutation = useMutation({
|
|
@@ -585,7 +892,7 @@ const TemplateEditorPage = () => {
|
|
|
585
892
|
}
|
|
586
893
|
});
|
|
587
894
|
const handleSave = () => {
|
|
588
|
-
saveMutation.mutate({ name, html_content: htmlContent });
|
|
895
|
+
saveMutation.mutate({ name, html_content: htmlContent, company_id: companyId });
|
|
589
896
|
};
|
|
590
897
|
const handlePreviewPdf = async () => {
|
|
591
898
|
try {
|
|
@@ -606,7 +913,7 @@ const TemplateEditorPage = () => {
|
|
|
606
913
|
if (!(template == null ? void 0 : template.is_default)) return;
|
|
607
914
|
if (!confirm("¿Restaurar la plantilla a su contenido original?")) return;
|
|
608
915
|
try {
|
|
609
|
-
|
|
916
|
+
await sdk.client.fetch(
|
|
610
917
|
`/admin/invoice-templates/${id}/restore`,
|
|
611
918
|
{ method: "POST" }
|
|
612
919
|
);
|
|
@@ -625,77 +932,102 @@ const TemplateEditorPage = () => {
|
|
|
625
932
|
return `<div style="color:red;padding:20px;">Error en la plantilla Handlebars</div>`;
|
|
626
933
|
}
|
|
627
934
|
}, [htmlContent, template]);
|
|
628
|
-
const insertVariable = useCallback((variable) => {
|
|
629
|
-
const isBlock = variable.startsWith("{{#") || variable.startsWith("{{/");
|
|
935
|
+
const insertVariable = useCallback((variable, isBlock) => {
|
|
630
936
|
const insertion = isBlock ? variable : `{{${variable}}}`;
|
|
631
937
|
setHtmlContent((prev) => prev + insertion);
|
|
632
938
|
}, []);
|
|
633
|
-
const
|
|
939
|
+
const toggleSection = useCallback((label) => {
|
|
940
|
+
setCollapsedSections((prev) => ({ ...prev, [label]: !prev[label] }));
|
|
941
|
+
}, []);
|
|
942
|
+
const categories = template ? CATEGORIES[template.type] ?? [] : [];
|
|
634
943
|
if (isLoading) {
|
|
635
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", {
|
|
944
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Cargando..." }) });
|
|
636
945
|
}
|
|
637
946
|
if (!template) {
|
|
638
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", {
|
|
947
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Plantilla no encontrada" }) });
|
|
639
948
|
}
|
|
640
|
-
return /* @__PURE__ */ jsxs(Container, { children: [
|
|
641
|
-
/* @__PURE__ */ jsxs("div", {
|
|
642
|
-
/* @__PURE__ */ jsxs("div", {
|
|
949
|
+
return /* @__PURE__ */ jsxs(Container, { className: "p-0", children: [
|
|
950
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-ui-border-base", children: [
|
|
951
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
643
952
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => navigate("/invoice-config/invoice-templates"), children: "← Volver" }),
|
|
644
|
-
/* @__PURE__ */ jsx(
|
|
953
|
+
/* @__PURE__ */ jsx(
|
|
645
954
|
Input,
|
|
646
955
|
{
|
|
647
956
|
value: name,
|
|
648
957
|
onChange: (e) => setName(e.target.value),
|
|
649
|
-
|
|
958
|
+
className: "font-semibold text-base w-64"
|
|
650
959
|
}
|
|
651
|
-
)
|
|
652
|
-
/* @__PURE__ */ jsxs(
|
|
960
|
+
),
|
|
961
|
+
/* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-muted text-xs", children: [
|
|
653
962
|
template.slug,
|
|
654
963
|
" (",
|
|
655
964
|
template.type,
|
|
656
965
|
")"
|
|
657
966
|
] })
|
|
658
967
|
] }),
|
|
659
|
-
/* @__PURE__ */ jsxs("div", {
|
|
660
|
-
|
|
968
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
969
|
+
/* @__PURE__ */ jsxs(
|
|
970
|
+
Select,
|
|
971
|
+
{
|
|
972
|
+
value: companyId ?? "__none__",
|
|
973
|
+
onValueChange: (v) => setCompanyId(v === "__none__" ? null : v),
|
|
974
|
+
children: [
|
|
975
|
+
/* @__PURE__ */ jsx(Select.Trigger, { className: "w-48", children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Empresa" }) }),
|
|
976
|
+
/* @__PURE__ */ jsxs(Select.Content, { children: [
|
|
977
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "__none__", children: "Sin empresa (usa default)" }),
|
|
978
|
+
companies.map((c) => /* @__PURE__ */ jsxs(Select.Item, { value: c.id, children: [
|
|
979
|
+
c.company_name,
|
|
980
|
+
c.is_default ? " ★" : ""
|
|
981
|
+
] }, c.id))
|
|
982
|
+
] })
|
|
983
|
+
]
|
|
984
|
+
}
|
|
985
|
+
),
|
|
986
|
+
template.is_default && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: handleRestoreDefault, children: "Restaurar" }),
|
|
661
987
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => setShowVariables(!showVariables), children: showVariables ? "Ocultar Variables" : "Variables" }),
|
|
662
988
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: handlePreviewPdf, children: "Preview PDF" }),
|
|
663
989
|
/* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: saveMutation.isPending, children: "Guardar" })
|
|
664
990
|
] })
|
|
665
991
|
] }),
|
|
666
|
-
showVariables && /* @__PURE__ */ jsxs("div", {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
992
|
+
showVariables && /* @__PURE__ */ jsxs("div", { className: "border-b border-ui-border-base bg-ui-bg-subtle px-4 py-3", children: [
|
|
993
|
+
/* @__PURE__ */ jsx(Label, { className: "block mb-2 text-sm", children: "Variables Handlebars — click para insertar" }),
|
|
994
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: categories.map((cat) => /* @__PURE__ */ jsxs("div", { className: "border border-ui-border-base rounded-lg bg-ui-bg-base overflow-hidden", children: [
|
|
995
|
+
/* @__PURE__ */ jsxs(
|
|
996
|
+
"button",
|
|
997
|
+
{
|
|
998
|
+
type: "button",
|
|
999
|
+
className: "w-full flex items-center justify-between px-3 py-2 text-left text-sm font-medium hover:bg-ui-bg-subtle-hover transition-colors",
|
|
1000
|
+
onClick: () => toggleSection(cat.label),
|
|
1001
|
+
children: [
|
|
1002
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1003
|
+
cat.icon,
|
|
1004
|
+
" ",
|
|
1005
|
+
cat.label
|
|
1006
|
+
] }),
|
|
1007
|
+
/* @__PURE__ */ jsxs("span", { className: "text-ui-fg-muted text-xs", children: [
|
|
1008
|
+
collapsedSections[cat.label] ? "▸" : "▾",
|
|
1009
|
+
" ",
|
|
1010
|
+
cat.variables.length
|
|
1011
|
+
] })
|
|
1012
|
+
]
|
|
1013
|
+
}
|
|
1014
|
+
),
|
|
1015
|
+
!collapsedSections[cat.label] && /* @__PURE__ */ jsx("div", { className: "px-3 pb-2 flex flex-wrap gap-1.5", children: cat.variables.map((v, i) => /* @__PURE__ */ jsx(
|
|
1016
|
+
Badge,
|
|
1017
|
+
{
|
|
1018
|
+
color: v.isBlock ? "orange" : "blue",
|
|
1019
|
+
className: "cursor-pointer hover:opacity-80 transition-opacity text-xs",
|
|
1020
|
+
onClick: () => insertVariable(v.name, v.isBlock),
|
|
1021
|
+
children: v.isBlock ? v.name : `{{${v.name}}}`
|
|
689
1022
|
},
|
|
690
|
-
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
))
|
|
1023
|
+
`${cat.label}-${i}`
|
|
1024
|
+
)) })
|
|
1025
|
+
] }, cat.label)) })
|
|
694
1026
|
] }),
|
|
695
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 16, height: "calc(100vh - 260px)" }, children: [
|
|
1027
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 16, height: "calc(100vh - 260px)", padding: 16 }, children: [
|
|
696
1028
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
697
|
-
/* @__PURE__ */ jsx(Label, {
|
|
698
|
-
/* @__PURE__ */ jsx("div", {
|
|
1029
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-2", children: "Editor HTML + Handlebars" }),
|
|
1030
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border border-ui-border-base rounded-lg overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
699
1031
|
CodeMirror,
|
|
700
1032
|
{
|
|
701
1033
|
ref: editorRef,
|
|
@@ -709,14 +1041,8 @@ const TemplateEditorPage = () => {
|
|
|
709
1041
|
) })
|
|
710
1042
|
] }),
|
|
711
1043
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
712
|
-
/* @__PURE__ */ jsx(Label, {
|
|
713
|
-
/* @__PURE__ */ jsx("div", {
|
|
714
|
-
flex: 1,
|
|
715
|
-
border: "1px solid #e2e8f0",
|
|
716
|
-
borderRadius: 8,
|
|
717
|
-
overflow: "auto",
|
|
718
|
-
background: "#fff"
|
|
719
|
-
}, children: /* @__PURE__ */ jsx(
|
|
1044
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-2", children: "Preview en vivo (datos de ejemplo)" }),
|
|
1045
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border border-ui-border-base rounded-lg overflow-auto bg-white", children: /* @__PURE__ */ jsx(
|
|
720
1046
|
"iframe",
|
|
721
1047
|
{
|
|
722
1048
|
srcDoc: previewHtml,
|
|
@@ -729,6 +1055,96 @@ const TemplateEditorPage = () => {
|
|
|
729
1055
|
] })
|
|
730
1056
|
] });
|
|
731
1057
|
};
|
|
1058
|
+
const NewTemplatePage = () => {
|
|
1059
|
+
const navigate = useNavigate();
|
|
1060
|
+
const [name, setName] = useState("");
|
|
1061
|
+
const [slug, setSlug] = useState("");
|
|
1062
|
+
const [type, setType] = useState("order_invoice");
|
|
1063
|
+
const createMutation = useMutation({
|
|
1064
|
+
mutationFn: (payload) => sdk.client.fetch("/admin/invoice-templates", {
|
|
1065
|
+
method: "POST",
|
|
1066
|
+
body: payload
|
|
1067
|
+
}),
|
|
1068
|
+
onSuccess: (data) => {
|
|
1069
|
+
toast.success("Plantilla creada");
|
|
1070
|
+
navigate(`/invoice-config/invoice-templates/${data.invoice_template.id}`);
|
|
1071
|
+
},
|
|
1072
|
+
onError: () => {
|
|
1073
|
+
toast.error("Error al crear la plantilla");
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
const handleCreate = () => {
|
|
1077
|
+
if (!name || !slug) {
|
|
1078
|
+
toast.error("Nombre y slug son requeridos");
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
createMutation.mutate({
|
|
1082
|
+
name,
|
|
1083
|
+
slug,
|
|
1084
|
+
type,
|
|
1085
|
+
html_content: getDefaultHtml(type)
|
|
1086
|
+
});
|
|
1087
|
+
};
|
|
1088
|
+
return /* @__PURE__ */ jsxs(Container, { children: [
|
|
1089
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", style: { marginBottom: 24 }, children: "Nueva Plantilla" }),
|
|
1090
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16, maxWidth: 500 }, children: [
|
|
1091
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1092
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "Nombre" }),
|
|
1093
|
+
/* @__PURE__ */ jsx(Input, { id: "name", value: name, onChange: (e) => setName(e.target.value), placeholder: "Ej: Mi Comprobante" })
|
|
1094
|
+
] }),
|
|
1095
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1096
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "slug", children: "Slug (identificador único)" }),
|
|
1097
|
+
/* @__PURE__ */ jsx(
|
|
1098
|
+
Input,
|
|
1099
|
+
{
|
|
1100
|
+
id: "slug",
|
|
1101
|
+
value: slug,
|
|
1102
|
+
onChange: (e) => setSlug(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_")),
|
|
1103
|
+
placeholder: "Ej: my_custom_invoice"
|
|
1104
|
+
}
|
|
1105
|
+
)
|
|
1106
|
+
] }),
|
|
1107
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1108
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "type", children: "Tipo de documento" }),
|
|
1109
|
+
/* @__PURE__ */ jsxs(Select, { value: type, onValueChange: setType, children: [
|
|
1110
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Seleccionar tipo" }) }),
|
|
1111
|
+
/* @__PURE__ */ jsxs(Select.Content, { children: [
|
|
1112
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "order_invoice", children: "Comprobante de Pedido" }),
|
|
1113
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "quote_proforma", children: "Cotización Proforma" })
|
|
1114
|
+
] })
|
|
1115
|
+
] })
|
|
1116
|
+
] }),
|
|
1117
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, marginTop: 16 }, children: [
|
|
1118
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config/invoice-templates"), children: "Cancelar" }),
|
|
1119
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleCreate, isLoading: createMutation.isPending, children: "Crear Plantilla" })
|
|
1120
|
+
] })
|
|
1121
|
+
] })
|
|
1122
|
+
] });
|
|
1123
|
+
};
|
|
1124
|
+
function getDefaultHtml(type) {
|
|
1125
|
+
if (type === "quote_proforma") {
|
|
1126
|
+
return `<!DOCTYPE html>
|
|
1127
|
+
<html>
|
|
1128
|
+
<head><style>body { font-family: Helvetica, sans-serif; }</style></head>
|
|
1129
|
+
<body>
|
|
1130
|
+
<h1>COTIZACIÓN {{quote_number}}</h1>
|
|
1131
|
+
<p>Fecha: {{date_str}}</p>
|
|
1132
|
+
<p>Servicio: {{service_name}}</p>
|
|
1133
|
+
<!-- Personaliza tu plantilla aquí -->
|
|
1134
|
+
</body>
|
|
1135
|
+
</html>`;
|
|
1136
|
+
}
|
|
1137
|
+
return `<!DOCTYPE html>
|
|
1138
|
+
<html>
|
|
1139
|
+
<head><style>body { font-family: Helvetica, sans-serif; }</style></head>
|
|
1140
|
+
<body>
|
|
1141
|
+
<h1>COMPROBANTE {{invoice_id}}</h1>
|
|
1142
|
+
<p>Fecha: {{invoice_date}}</p>
|
|
1143
|
+
<p>Empresa: {{company_name}}</p>
|
|
1144
|
+
<!-- Personaliza tu plantilla aquí -->
|
|
1145
|
+
</body>
|
|
1146
|
+
</html>`;
|
|
1147
|
+
}
|
|
732
1148
|
const i18nTranslations0 = {};
|
|
733
1149
|
const widgetModule = { widgets: [
|
|
734
1150
|
{
|
|
@@ -742,17 +1158,29 @@ const routeModule = {
|
|
|
742
1158
|
Component: InvoiceConfigPage,
|
|
743
1159
|
path: "/invoice-config"
|
|
744
1160
|
},
|
|
1161
|
+
{
|
|
1162
|
+
Component: CompaniesPage,
|
|
1163
|
+
path: "/invoice-config/companies"
|
|
1164
|
+
},
|
|
745
1165
|
{
|
|
746
1166
|
Component: InvoiceTemplatesPage,
|
|
747
1167
|
path: "/invoice-config/invoice-templates"
|
|
748
1168
|
},
|
|
749
1169
|
{
|
|
750
|
-
Component:
|
|
751
|
-
path: "/invoice-config/
|
|
1170
|
+
Component: NewCompanyPage,
|
|
1171
|
+
path: "/invoice-config/companies/new"
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
Component: EditCompanyPage,
|
|
1175
|
+
path: "/invoice-config/companies/:id"
|
|
752
1176
|
},
|
|
753
1177
|
{
|
|
754
1178
|
Component: TemplateEditorPage,
|
|
755
1179
|
path: "/invoice-config/invoice-templates/:id"
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
Component: NewTemplatePage,
|
|
1183
|
+
path: "/invoice-config/invoice-templates/new"
|
|
756
1184
|
}
|
|
757
1185
|
]
|
|
758
1186
|
};
|