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