@codemind.ec/medusa-plugin-invoice 1.0.6 → 1.2.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 +737 -306
- package/.medusa/server/src/admin/index.mjs +740 -309
- 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/.medusa/server/src/modules/invoice-generator/templates/order-invoice.js +3 -10
- package/.medusa/server/src/modules/invoice-generator/templates/quote-proforma.js +3 -2
- package/.medusa/server/src/modules/invoice-generator/templates/strategy.js +19 -1
- package/README.md +91 -25
- 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,13 +303,356 @@ 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
|
});
|
|
313
|
+
const EditCompanyPage = () => {
|
|
314
|
+
const { id } = useParams();
|
|
315
|
+
const navigate = useNavigate();
|
|
316
|
+
const queryClient = useQueryClient();
|
|
317
|
+
const { data, isLoading } = useQuery({
|
|
318
|
+
queryFn: () => sdk.client.fetch(`/admin/invoice-config/${id}`),
|
|
319
|
+
queryKey: ["invoice-config", id],
|
|
320
|
+
enabled: !!id
|
|
321
|
+
});
|
|
322
|
+
const config2 = data == null ? void 0 : data.invoice_config;
|
|
323
|
+
const getDefaults = useCallback(() => ({
|
|
324
|
+
company_name: (config2 == null ? void 0 : config2.company_name) || "",
|
|
325
|
+
company_ruc: (config2 == null ? void 0 : config2.company_ruc) || "",
|
|
326
|
+
company_address: (config2 == null ? void 0 : config2.company_address) || "",
|
|
327
|
+
company_phone: (config2 == null ? void 0 : config2.company_phone) || "",
|
|
328
|
+
company_email: (config2 == null ? void 0 : config2.company_email) || "",
|
|
329
|
+
company_logo: (config2 == null ? void 0 : config2.company_logo) || "",
|
|
330
|
+
notes: (config2 == null ? void 0 : config2.notes) || "",
|
|
331
|
+
admin_notification_email: (config2 == null ? void 0 : config2.admin_notification_email) || "",
|
|
332
|
+
is_default: (config2 == null ? void 0 : config2.is_default) || false
|
|
333
|
+
}), [config2]);
|
|
334
|
+
const form = useForm({ defaultValues: getDefaults() });
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
form.reset(getDefaults());
|
|
337
|
+
}, [getDefaults]);
|
|
338
|
+
const saveMutation = useMutation({
|
|
339
|
+
mutationFn: (payload) => sdk.client.fetch(`/admin/invoice-config/${id}`, {
|
|
340
|
+
method: "POST",
|
|
341
|
+
body: payload
|
|
342
|
+
}),
|
|
343
|
+
onSuccess: () => {
|
|
344
|
+
queryClient.invalidateQueries({ queryKey: ["invoice-config", id] });
|
|
345
|
+
queryClient.invalidateQueries({ queryKey: ["invoice-configs"] });
|
|
346
|
+
toast.success("Empresa actualizada");
|
|
347
|
+
},
|
|
348
|
+
onError: () => toast.error("Error al guardar")
|
|
349
|
+
});
|
|
350
|
+
const uploadLogo = async (event) => {
|
|
351
|
+
var _a;
|
|
352
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
353
|
+
if (!file) return;
|
|
354
|
+
try {
|
|
355
|
+
const { files } = await sdk.admin.upload.create({ files: [file] });
|
|
356
|
+
form.setValue("company_logo", files[0].url);
|
|
357
|
+
} catch {
|
|
358
|
+
toast.error("Error al subir el logo.");
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
const handleSubmit = form.handleSubmit((data2) => saveMutation.mutate(data2));
|
|
362
|
+
if (isLoading) {
|
|
363
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Cargando..." }) });
|
|
364
|
+
}
|
|
365
|
+
if (!config2) {
|
|
366
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Empresa no encontrada" }) });
|
|
367
|
+
}
|
|
368
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
369
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
370
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Editar Empresa" }),
|
|
371
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config/companies"), children: "← Volver" })
|
|
372
|
+
] }),
|
|
373
|
+
/* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4 p-6 max-w-2xl", children: [
|
|
374
|
+
/* @__PURE__ */ jsx("div", { className: "bg-ui-bg-subtle border border-ui-border-base rounded-lg p-4 mb-2", children: /* @__PURE__ */ jsx(
|
|
375
|
+
Controller,
|
|
376
|
+
{
|
|
377
|
+
control: form.control,
|
|
378
|
+
name: "admin_notification_email",
|
|
379
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
380
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "📧 Email de Notificaciones (Admin)" }),
|
|
381
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "admin@empresa.com" }),
|
|
382
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-xs", children: "Notificaciones de nuevos pedidos a este email." })
|
|
383
|
+
] })
|
|
384
|
+
}
|
|
385
|
+
) }),
|
|
386
|
+
/* @__PURE__ */ jsx(
|
|
387
|
+
Controller,
|
|
388
|
+
{
|
|
389
|
+
control: form.control,
|
|
390
|
+
name: "company_name",
|
|
391
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
392
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Nombre de la Empresa *" }),
|
|
393
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
394
|
+
] })
|
|
395
|
+
}
|
|
396
|
+
),
|
|
397
|
+
/* @__PURE__ */ jsx(
|
|
398
|
+
Controller,
|
|
399
|
+
{
|
|
400
|
+
control: form.control,
|
|
401
|
+
name: "company_ruc",
|
|
402
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
403
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "RUC" }),
|
|
404
|
+
/* @__PURE__ */ jsx(Input, { ...field, placeholder: "1234567890001" })
|
|
405
|
+
] })
|
|
406
|
+
}
|
|
407
|
+
),
|
|
408
|
+
/* @__PURE__ */ jsx(
|
|
409
|
+
Controller,
|
|
410
|
+
{
|
|
411
|
+
control: form.control,
|
|
412
|
+
name: "company_address",
|
|
413
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
414
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Dirección" }),
|
|
415
|
+
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
416
|
+
] })
|
|
417
|
+
}
|
|
418
|
+
),
|
|
419
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
420
|
+
/* @__PURE__ */ jsx(
|
|
421
|
+
Controller,
|
|
422
|
+
{
|
|
423
|
+
control: form.control,
|
|
424
|
+
name: "company_phone",
|
|
425
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
426
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Teléfono" }),
|
|
427
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
428
|
+
] })
|
|
429
|
+
}
|
|
430
|
+
),
|
|
431
|
+
/* @__PURE__ */ jsx(
|
|
432
|
+
Controller,
|
|
433
|
+
{
|
|
434
|
+
control: form.control,
|
|
435
|
+
name: "company_email",
|
|
436
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
437
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Email de la Empresa" }),
|
|
438
|
+
/* @__PURE__ */ jsx(Input, { ...field })
|
|
439
|
+
] })
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
] }),
|
|
443
|
+
/* @__PURE__ */ jsx(
|
|
444
|
+
Controller,
|
|
445
|
+
{
|
|
446
|
+
control: form.control,
|
|
447
|
+
name: "notes",
|
|
448
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
449
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Notas" }),
|
|
450
|
+
/* @__PURE__ */ jsx(Textarea, { ...field })
|
|
451
|
+
] })
|
|
452
|
+
}
|
|
453
|
+
),
|
|
454
|
+
/* @__PURE__ */ jsx(
|
|
455
|
+
Controller,
|
|
456
|
+
{
|
|
457
|
+
control: form.control,
|
|
458
|
+
name: "company_logo",
|
|
459
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
460
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Logo de la Empresa" }),
|
|
461
|
+
/* @__PURE__ */ jsx(Input, { type: "file", onChange: uploadLogo, className: "py-1" }),
|
|
462
|
+
field.value && /* @__PURE__ */ jsx("img", { src: field.value, alt: "Logo", className: "mt-2 h-24 w-24 object-contain" })
|
|
463
|
+
] })
|
|
464
|
+
}
|
|
465
|
+
),
|
|
466
|
+
/* @__PURE__ */ jsx(
|
|
467
|
+
Controller,
|
|
468
|
+
{
|
|
469
|
+
control: form.control,
|
|
470
|
+
name: "is_default",
|
|
471
|
+
render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-ui-bg-subtle rounded-lg p-3", children: [
|
|
472
|
+
/* @__PURE__ */ jsx(
|
|
473
|
+
Switch,
|
|
474
|
+
{
|
|
475
|
+
checked: field.value,
|
|
476
|
+
onCheckedChange: field.onChange
|
|
477
|
+
}
|
|
478
|
+
),
|
|
479
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
480
|
+
/* @__PURE__ */ jsx(Label, { size: "small", weight: "plus", children: "Empresa por defecto" }),
|
|
481
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle text-xs", children: "Se usará en plantillas que no tengan empresa asignada." })
|
|
482
|
+
] })
|
|
483
|
+
] })
|
|
484
|
+
}
|
|
485
|
+
),
|
|
486
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 pt-2", children: [
|
|
487
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => navigate("/invoice-config/companies"), children: "Cancelar" }),
|
|
488
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", isLoading: saveMutation.isPending, children: "Guardar Cambios" })
|
|
489
|
+
] })
|
|
490
|
+
] }) })
|
|
491
|
+
] });
|
|
492
|
+
};
|
|
493
|
+
const NewCompanyPage = () => {
|
|
494
|
+
const navigate = useNavigate();
|
|
495
|
+
const form = useForm({
|
|
496
|
+
defaultValues: {
|
|
497
|
+
company_name: "",
|
|
498
|
+
company_ruc: "",
|
|
499
|
+
company_address: "",
|
|
500
|
+
company_phone: "",
|
|
501
|
+
company_email: "",
|
|
502
|
+
company_logo: "",
|
|
503
|
+
notes: "",
|
|
504
|
+
admin_notification_email: "",
|
|
505
|
+
is_default: false
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
const createMutation = useMutation({
|
|
509
|
+
mutationFn: (payload) => sdk.client.fetch("/admin/invoice-config", {
|
|
510
|
+
method: "POST",
|
|
511
|
+
body: payload
|
|
512
|
+
}),
|
|
513
|
+
onSuccess: () => {
|
|
514
|
+
toast.success("Empresa creada exitosamente");
|
|
515
|
+
navigate("/invoice-config/companies");
|
|
516
|
+
},
|
|
517
|
+
onError: () => toast.error("Error al crear la empresa")
|
|
518
|
+
});
|
|
519
|
+
const uploadLogo = async (event) => {
|
|
520
|
+
var _a;
|
|
521
|
+
const file = (_a = event.target.files) == null ? void 0 : _a[0];
|
|
522
|
+
if (!file) return;
|
|
523
|
+
try {
|
|
524
|
+
const { files } = await sdk.admin.upload.create({ files: [file] });
|
|
525
|
+
form.setValue("company_logo", files[0].url);
|
|
526
|
+
} catch {
|
|
527
|
+
toast.error("Error al subir el logo.");
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
const handleSubmit = form.handleSubmit((data) => createMutation.mutate(data));
|
|
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: "Nueva 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, placeholder: "Mi Empresa S.A.S." })
|
|
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, placeholder: "Av. Principal 123, Quito" })
|
|
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, placeholder: "+593 99 123 4567" })
|
|
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, placeholder: "info@empresa.com" })
|
|
602
|
+
] })
|
|
603
|
+
}
|
|
604
|
+
)
|
|
605
|
+
] }),
|
|
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, placeholder: "Notas internas o pie de página del documento" })
|
|
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: createMutation.isPending, children: "Crear Empresa" })
|
|
652
|
+
] })
|
|
653
|
+
] }) })
|
|
654
|
+
] });
|
|
655
|
+
};
|
|
342
656
|
const NewTemplatePage = () => {
|
|
343
657
|
const navigate = useNavigate();
|
|
344
658
|
const [name, setName] = useState("");
|
|
@@ -429,66 +743,139 @@ function getDefaultHtml(type) {
|
|
|
429
743
|
</body>
|
|
430
744
|
</html>`;
|
|
431
745
|
}
|
|
432
|
-
const
|
|
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
|
-
|
|
746
|
+
const COMPANY_VARIABLES = [
|
|
747
|
+
{ name: "company_name" },
|
|
748
|
+
{ name: "company_ruc" },
|
|
749
|
+
{ name: "company_address" },
|
|
750
|
+
{ name: "company_phone" },
|
|
751
|
+
{ name: "company_email" },
|
|
752
|
+
{ name: "company_logo_base64" }
|
|
753
|
+
];
|
|
754
|
+
const ORDER_INVOICE_CATEGORIES = [
|
|
755
|
+
{
|
|
756
|
+
label: "Empresa",
|
|
757
|
+
icon: "🏢",
|
|
758
|
+
variables: COMPANY_VARIABLES
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
label: "Documento",
|
|
762
|
+
icon: "📄",
|
|
763
|
+
variables: [
|
|
764
|
+
{ name: "invoice_id" },
|
|
765
|
+
{ name: "invoice_date" },
|
|
766
|
+
{ name: "order_display_id" },
|
|
767
|
+
{ name: "order_date" }
|
|
768
|
+
]
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
label: "Cliente",
|
|
772
|
+
icon: "👤",
|
|
773
|
+
variables: [
|
|
774
|
+
{ name: "billing_address" },
|
|
775
|
+
{ name: "shipping_address" },
|
|
776
|
+
{ name: "cedula" }
|
|
777
|
+
]
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
label: "Productos",
|
|
781
|
+
icon: "📦",
|
|
782
|
+
variables: [
|
|
783
|
+
{ name: "{{#each items}}", isBlock: true },
|
|
784
|
+
{ name: "this.title" },
|
|
785
|
+
{ name: "this.variant_title" },
|
|
786
|
+
{ name: "this.quantity" },
|
|
787
|
+
{ name: "this.unit_price" },
|
|
788
|
+
{ name: "this.total" },
|
|
789
|
+
{ name: "{{/each}}", isBlock: true }
|
|
790
|
+
]
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
label: "Totales",
|
|
794
|
+
icon: "💰",
|
|
795
|
+
variables: [
|
|
796
|
+
{ name: "subtotal" },
|
|
797
|
+
{ name: "tax_total" },
|
|
798
|
+
{ name: "shipping_total" },
|
|
799
|
+
{ name: "discount_total" },
|
|
800
|
+
{ name: "total" }
|
|
801
|
+
]
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
label: "Otros",
|
|
805
|
+
icon: "📝",
|
|
806
|
+
variables: [
|
|
807
|
+
{ name: "is_home_delivery" },
|
|
808
|
+
{ name: "notes" }
|
|
809
|
+
]
|
|
810
|
+
}
|
|
811
|
+
];
|
|
812
|
+
const QUOTE_PROFORMA_CATEGORIES = [
|
|
813
|
+
{
|
|
814
|
+
label: "Empresa",
|
|
815
|
+
icon: "🏢",
|
|
816
|
+
variables: [...COMPANY_VARIABLES, { name: "company_website" }]
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
label: "Documento",
|
|
820
|
+
icon: "📄",
|
|
821
|
+
variables: [
|
|
822
|
+
{ name: "quote_number" },
|
|
823
|
+
{ name: "date_str" },
|
|
824
|
+
{ name: "service_name" },
|
|
825
|
+
{ name: "is_home_delivery" }
|
|
826
|
+
]
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
label: "Configuración",
|
|
830
|
+
icon: "⚙️",
|
|
831
|
+
variables: [
|
|
832
|
+
{ name: "{{#each config_fields}}", isBlock: true },
|
|
833
|
+
{ name: "this.label" },
|
|
834
|
+
{ name: "this.value" },
|
|
835
|
+
{ name: "{{/each}}", isBlock: true }
|
|
836
|
+
]
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
label: "Desglose",
|
|
840
|
+
icon: "📊",
|
|
841
|
+
variables: [
|
|
842
|
+
{ name: "{{#each breakdown}}", isBlock: true },
|
|
843
|
+
{ name: "this.label" },
|
|
844
|
+
{ name: "this.total_formatted" },
|
|
845
|
+
{ name: "{{/each}}", isBlock: true }
|
|
846
|
+
]
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
label: "Totales",
|
|
850
|
+
icon: "💰",
|
|
851
|
+
variables: [
|
|
852
|
+
{ name: "totals.subtotal_formatted" },
|
|
853
|
+
{ name: "totals.extras_formatted" },
|
|
854
|
+
{ name: "totals.discount_formatted" },
|
|
855
|
+
{ name: "totals.shipping_formatted" },
|
|
856
|
+
{ name: "totals.taxes_provided" },
|
|
857
|
+
{ name: "totals.taxes_formatted" },
|
|
858
|
+
{ name: "totals.total_formatted" }
|
|
859
|
+
]
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
label: "Incluye y Contacto",
|
|
863
|
+
icon: "📋",
|
|
864
|
+
variables: [
|
|
865
|
+
{ name: "{{#each includes_left}}", isBlock: true },
|
|
866
|
+
{ name: "{{#each includes_right}}", isBlock: true },
|
|
867
|
+
{ name: "{{#each contact_rows}}", isBlock: true },
|
|
868
|
+
{ name: "this.label" },
|
|
869
|
+
{ name: "this.value" },
|
|
870
|
+
{ name: "{{/each}}", isBlock: true }
|
|
871
|
+
]
|
|
872
|
+
}
|
|
873
|
+
];
|
|
874
|
+
const CATEGORIES = {
|
|
875
|
+
order_invoice: ORDER_INVOICE_CATEGORIES,
|
|
876
|
+
quote_proforma: QUOTE_PROFORMA_CATEGORIES
|
|
491
877
|
};
|
|
878
|
+
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
879
|
const SAMPLE_DATA = {
|
|
493
880
|
order_invoice: {
|
|
494
881
|
company_name: "Mi Empresa",
|
|
@@ -496,7 +883,7 @@ const SAMPLE_DATA = {
|
|
|
496
883
|
company_address: "Av. Principal 123, Quito",
|
|
497
884
|
company_phone: "+593 99 123 4567",
|
|
498
885
|
company_email: "info@miempresa.com",
|
|
499
|
-
company_logo_base64:
|
|
886
|
+
company_logo_base64: PLACEHOLDER_LOGO_BASE64,
|
|
500
887
|
invoice_id: "INV-000001",
|
|
501
888
|
invoice_date: "19/06/2025",
|
|
502
889
|
order_display_id: "000042",
|
|
@@ -518,6 +905,11 @@ const SAMPLE_DATA = {
|
|
|
518
905
|
},
|
|
519
906
|
quote_proforma: {
|
|
520
907
|
company_name: "Mi Empresa",
|
|
908
|
+
company_ruc: "1234567890001",
|
|
909
|
+
company_address: "Av. Principal 123, Quito",
|
|
910
|
+
company_phone: "+593 99 123 4567",
|
|
911
|
+
company_email: "info@miempresa.com",
|
|
912
|
+
company_logo_base64: PLACEHOLDER_LOGO_BASE64,
|
|
521
913
|
company_website: "www.miempresa.com",
|
|
522
914
|
quote_number: "QP-2025-0001",
|
|
523
915
|
date_str: "19/06/2025",
|
|
@@ -561,14 +953,22 @@ const TemplateEditorPage = () => {
|
|
|
561
953
|
queryKey: ["invoice-template", id],
|
|
562
954
|
enabled: !!id
|
|
563
955
|
});
|
|
956
|
+
const { data: companiesData } = useQuery({
|
|
957
|
+
queryFn: () => sdk.client.fetch("/admin/invoice-config"),
|
|
958
|
+
queryKey: ["invoice-configs"]
|
|
959
|
+
});
|
|
564
960
|
const template = data == null ? void 0 : data.invoice_template;
|
|
961
|
+
const companies = (companiesData == null ? void 0 : companiesData.invoice_configs) ?? [];
|
|
565
962
|
const [name, setName] = useState("");
|
|
566
963
|
const [htmlContent, setHtmlContent] = useState("");
|
|
964
|
+
const [companyId, setCompanyId] = useState(null);
|
|
567
965
|
const [showVariables, setShowVariables] = useState(false);
|
|
966
|
+
const [collapsedSections, setCollapsedSections] = useState({});
|
|
568
967
|
useEffect(() => {
|
|
569
968
|
if (template) {
|
|
570
969
|
setName(template.name);
|
|
571
970
|
setHtmlContent(template.html_content);
|
|
971
|
+
setCompanyId(template.company_id);
|
|
572
972
|
}
|
|
573
973
|
}, [template]);
|
|
574
974
|
const saveMutation = useMutation({
|
|
@@ -585,7 +985,7 @@ const TemplateEditorPage = () => {
|
|
|
585
985
|
}
|
|
586
986
|
});
|
|
587
987
|
const handleSave = () => {
|
|
588
|
-
saveMutation.mutate({ name, html_content: htmlContent });
|
|
988
|
+
saveMutation.mutate({ name, html_content: htmlContent, company_id: companyId });
|
|
589
989
|
};
|
|
590
990
|
const handlePreviewPdf = async () => {
|
|
591
991
|
try {
|
|
@@ -606,7 +1006,7 @@ const TemplateEditorPage = () => {
|
|
|
606
1006
|
if (!(template == null ? void 0 : template.is_default)) return;
|
|
607
1007
|
if (!confirm("¿Restaurar la plantilla a su contenido original?")) return;
|
|
608
1008
|
try {
|
|
609
|
-
|
|
1009
|
+
await sdk.client.fetch(
|
|
610
1010
|
`/admin/invoice-templates/${id}/restore`,
|
|
611
1011
|
{ method: "POST" }
|
|
612
1012
|
);
|
|
@@ -625,77 +1025,102 @@ const TemplateEditorPage = () => {
|
|
|
625
1025
|
return `<div style="color:red;padding:20px;">Error en la plantilla Handlebars</div>`;
|
|
626
1026
|
}
|
|
627
1027
|
}, [htmlContent, template]);
|
|
628
|
-
const insertVariable = useCallback((variable) => {
|
|
629
|
-
const isBlock = variable.startsWith("{{#") || variable.startsWith("{{/");
|
|
1028
|
+
const insertVariable = useCallback((variable, isBlock) => {
|
|
630
1029
|
const insertion = isBlock ? variable : `{{${variable}}}`;
|
|
631
1030
|
setHtmlContent((prev) => prev + insertion);
|
|
632
1031
|
}, []);
|
|
633
|
-
const
|
|
1032
|
+
const toggleSection = useCallback((label) => {
|
|
1033
|
+
setCollapsedSections((prev) => ({ ...prev, [label]: !prev[label] }));
|
|
1034
|
+
}, []);
|
|
1035
|
+
const categories = template ? CATEGORIES[template.type] ?? [] : [];
|
|
634
1036
|
if (isLoading) {
|
|
635
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", {
|
|
1037
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Cargando..." }) });
|
|
636
1038
|
}
|
|
637
1039
|
if (!template) {
|
|
638
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", {
|
|
1040
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: "Plantilla no encontrada" }) });
|
|
639
1041
|
}
|
|
640
|
-
return /* @__PURE__ */ jsxs(Container, { children: [
|
|
641
|
-
/* @__PURE__ */ jsxs("div", {
|
|
642
|
-
/* @__PURE__ */ jsxs("div", {
|
|
1042
|
+
return /* @__PURE__ */ jsxs(Container, { className: "p-0", children: [
|
|
1043
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-ui-border-base", children: [
|
|
1044
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
643
1045
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => navigate("/invoice-config/invoice-templates"), children: "← Volver" }),
|
|
644
|
-
/* @__PURE__ */ jsx(
|
|
1046
|
+
/* @__PURE__ */ jsx(
|
|
645
1047
|
Input,
|
|
646
1048
|
{
|
|
647
1049
|
value: name,
|
|
648
1050
|
onChange: (e) => setName(e.target.value),
|
|
649
|
-
|
|
1051
|
+
className: "font-semibold text-base w-64"
|
|
650
1052
|
}
|
|
651
|
-
)
|
|
652
|
-
/* @__PURE__ */ jsxs(
|
|
1053
|
+
),
|
|
1054
|
+
/* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-muted text-xs", children: [
|
|
653
1055
|
template.slug,
|
|
654
1056
|
" (",
|
|
655
1057
|
template.type,
|
|
656
1058
|
")"
|
|
657
1059
|
] })
|
|
658
1060
|
] }),
|
|
659
|
-
/* @__PURE__ */ jsxs("div", {
|
|
660
|
-
|
|
1061
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1062
|
+
/* @__PURE__ */ jsxs(
|
|
1063
|
+
Select,
|
|
1064
|
+
{
|
|
1065
|
+
value: companyId ?? "__none__",
|
|
1066
|
+
onValueChange: (v) => setCompanyId(v === "__none__" ? null : v),
|
|
1067
|
+
children: [
|
|
1068
|
+
/* @__PURE__ */ jsx(Select.Trigger, { className: "w-48", children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Empresa" }) }),
|
|
1069
|
+
/* @__PURE__ */ jsxs(Select.Content, { children: [
|
|
1070
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "__none__", children: "Sin empresa (usa default)" }),
|
|
1071
|
+
companies.map((c) => /* @__PURE__ */ jsxs(Select.Item, { value: c.id, children: [
|
|
1072
|
+
c.company_name,
|
|
1073
|
+
c.is_default ? " ★" : ""
|
|
1074
|
+
] }, c.id))
|
|
1075
|
+
] })
|
|
1076
|
+
]
|
|
1077
|
+
}
|
|
1078
|
+
),
|
|
1079
|
+
template.is_default && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: handleRestoreDefault, children: "Restaurar" }),
|
|
661
1080
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => setShowVariables(!showVariables), children: showVariables ? "Ocultar Variables" : "Variables" }),
|
|
662
1081
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: handlePreviewPdf, children: "Preview PDF" }),
|
|
663
1082
|
/* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: saveMutation.isPending, children: "Guardar" })
|
|
664
1083
|
] })
|
|
665
1084
|
] }),
|
|
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
|
-
|
|
1085
|
+
showVariables && /* @__PURE__ */ jsxs("div", { className: "border-b border-ui-border-base bg-ui-bg-subtle px-4 py-3", children: [
|
|
1086
|
+
/* @__PURE__ */ jsx(Label, { className: "block mb-2 text-sm", children: "Variables Handlebars — click para insertar" }),
|
|
1087
|
+
/* @__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: [
|
|
1088
|
+
/* @__PURE__ */ jsxs(
|
|
1089
|
+
"button",
|
|
1090
|
+
{
|
|
1091
|
+
type: "button",
|
|
1092
|
+
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",
|
|
1093
|
+
onClick: () => toggleSection(cat.label),
|
|
1094
|
+
children: [
|
|
1095
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1096
|
+
cat.icon,
|
|
1097
|
+
" ",
|
|
1098
|
+
cat.label
|
|
1099
|
+
] }),
|
|
1100
|
+
/* @__PURE__ */ jsxs("span", { className: "text-ui-fg-muted text-xs", children: [
|
|
1101
|
+
collapsedSections[cat.label] ? "▸" : "▾",
|
|
1102
|
+
" ",
|
|
1103
|
+
cat.variables.length
|
|
1104
|
+
] })
|
|
1105
|
+
]
|
|
1106
|
+
}
|
|
1107
|
+
),
|
|
1108
|
+
!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(
|
|
1109
|
+
Badge,
|
|
1110
|
+
{
|
|
1111
|
+
color: v.isBlock ? "orange" : "blue",
|
|
1112
|
+
className: "cursor-pointer hover:opacity-80 transition-opacity text-xs",
|
|
1113
|
+
onClick: () => insertVariable(v.name, v.isBlock),
|
|
1114
|
+
children: v.isBlock ? v.name : `{{${v.name}}}`
|
|
689
1115
|
},
|
|
690
|
-
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
))
|
|
1116
|
+
`${cat.label}-${i}`
|
|
1117
|
+
)) })
|
|
1118
|
+
] }, cat.label)) })
|
|
694
1119
|
] }),
|
|
695
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 16, height: "calc(100vh - 260px)" }, children: [
|
|
1120
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 16, height: "calc(100vh - 260px)", padding: 16 }, children: [
|
|
696
1121
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
697
|
-
/* @__PURE__ */ jsx(Label, {
|
|
698
|
-
/* @__PURE__ */ jsx("div", {
|
|
1122
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-2", children: "Editor HTML + Handlebars" }),
|
|
1123
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border border-ui-border-base rounded-lg overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
699
1124
|
CodeMirror,
|
|
700
1125
|
{
|
|
701
1126
|
ref: editorRef,
|
|
@@ -709,14 +1134,8 @@ const TemplateEditorPage = () => {
|
|
|
709
1134
|
) })
|
|
710
1135
|
] }),
|
|
711
1136
|
/* @__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(
|
|
1137
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-2", children: "Preview en vivo (datos de ejemplo)" }),
|
|
1138
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border border-ui-border-base rounded-lg overflow-auto bg-white", children: /* @__PURE__ */ jsx(
|
|
720
1139
|
"iframe",
|
|
721
1140
|
{
|
|
722
1141
|
srcDoc: previewHtml,
|
|
@@ -742,10 +1161,22 @@ const routeModule = {
|
|
|
742
1161
|
Component: InvoiceConfigPage,
|
|
743
1162
|
path: "/invoice-config"
|
|
744
1163
|
},
|
|
1164
|
+
{
|
|
1165
|
+
Component: CompaniesPage,
|
|
1166
|
+
path: "/invoice-config/companies"
|
|
1167
|
+
},
|
|
745
1168
|
{
|
|
746
1169
|
Component: InvoiceTemplatesPage,
|
|
747
1170
|
path: "/invoice-config/invoice-templates"
|
|
748
1171
|
},
|
|
1172
|
+
{
|
|
1173
|
+
Component: EditCompanyPage,
|
|
1174
|
+
path: "/invoice-config/companies/:id"
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
Component: NewCompanyPage,
|
|
1178
|
+
path: "/invoice-config/companies/new"
|
|
1179
|
+
},
|
|
749
1180
|
{
|
|
750
1181
|
Component: NewTemplatePage,
|
|
751
1182
|
path: "/invoice-config/invoice-templates/new"
|