@interware/content-generator 0.6.1 → 0.7.1

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.
Files changed (58) hide show
  1. package/dist/_chunks/App-Cwr5ra5m.js +1380 -0
  2. package/dist/_chunks/App-h4_txTsI.mjs +1362 -0
  3. package/dist/_chunks/{ImageStudioTabs-BLAH3KYv.mjs → ImageStudioTabs-B-mOwDfY.mjs} +687 -682
  4. package/dist/_chunks/{ImageStudioTabs-DtusqpQO.js → ImageStudioTabs-CG4KTn1S.js} +683 -678
  5. package/dist/_chunks/{Settings-o2GngLaW.mjs → Settings-BT-EqvXs.mjs} +2 -2
  6. package/dist/_chunks/{Settings-BQXxV3LO.js → Settings-D4NGBTk1.js} +4 -4
  7. package/dist/_chunks/{StudioApp-BaySMwrk.js → StudioApp-_ViSAwCm.js} +2 -2
  8. package/dist/_chunks/{StudioApp-C_znlQRo.mjs → StudioApp-xaEy-uIk.mjs} +2 -2
  9. package/dist/_chunks/{index-BKigRXm3.mjs → index-D67xWUPz.mjs} +3 -28
  10. package/dist/_chunks/{index-BKhe-Jyx.js → index-NA9OuMGs.js} +3 -28
  11. package/dist/admin/index.js +1 -1
  12. package/dist/admin/index.mjs +1 -1
  13. package/dist/admin/src/features/articles/ArticlePage.d.ts +5 -0
  14. package/dist/admin/src/features/articles/ArticlesPage.d.ts +7 -0
  15. package/dist/admin/src/features/articles/NewArticlePage.d.ts +6 -0
  16. package/dist/admin/src/features/articles/SharePreview.d.ts +19 -0
  17. package/dist/admin/src/features/articles/articlesApi.d.ts +94 -0
  18. package/dist/admin/src/features/articles/model.d.ts +24 -0
  19. package/dist/admin/src/features/articles/useArticlesApi.d.ts +19 -0
  20. package/dist/admin/src/features/library/LibraryImagePicker.d.ts +6 -1
  21. package/dist/admin/src/lib/postText.d.ts +25 -0
  22. package/dist/server/index.js +534 -327
  23. package/dist/server/index.mjs +535 -328
  24. package/dist/server/src/articles/generation.d.ts +135 -0
  25. package/dist/server/src/articles/schedule.d.ts +12 -0
  26. package/dist/server/src/articles/sources.d.ts +12 -0
  27. package/dist/server/src/bootstrap.d.ts +2 -0
  28. package/dist/server/src/controllers/articles.d.ts +30 -0
  29. package/dist/server/src/controllers/controller.d.ts +0 -4
  30. package/dist/server/src/controllers/index.d.ts +92 -4
  31. package/dist/server/src/index.d.ts +122 -67
  32. package/dist/server/src/services/articles.d.ts +126 -0
  33. package/dist/server/src/services/image-studio.d.ts +6 -11
  34. package/dist/server/src/services/index.d.ts +30 -63
  35. package/dist/server/src/services/service.d.ts +1 -62
  36. package/package.json +1 -1
  37. package/dist/_chunks/App-CJxSC8yt.js +0 -1105
  38. package/dist/_chunks/App-qbgUXlOZ.mjs +0 -1087
  39. package/dist/_chunks/getTranslation-B6Bsu-uq.mjs +0 -5
  40. package/dist/_chunks/getTranslation-BRVMWSf8.js +0 -4
  41. package/dist/admin/src/components/commons/dialog/index.d.ts +0 -12
  42. package/dist/admin/src/components/commons/generate-image-dialog/index.d.ts +0 -5
  43. package/dist/admin/src/components/generate-content/Form.d.ts +0 -15
  44. package/dist/admin/src/components/home/table.d.ts +0 -13
  45. package/dist/admin/src/components/index.d.ts +0 -12
  46. package/dist/admin/src/containers/generate-content/index.d.ts +0 -2
  47. package/dist/admin/src/containers/home/index.d.ts +0 -2
  48. package/dist/admin/src/hooks/useDialog.d.ts +0 -11
  49. package/dist/admin/src/lib/use-cases/articles/index.d.ts +0 -16
  50. package/dist/admin/src/lib/use-cases/categories/index.d.ts +0 -16
  51. package/dist/admin/src/lib/use-cases/facebook/index.d.ts +0 -5
  52. package/dist/admin/src/lib/use-cases/generate-content/index.d.ts +0 -6
  53. package/dist/admin/src/lib/use-cases/linkedin/index.d.ts +0 -5
  54. package/dist/admin/src/lib/use-cases/users/index.d.ts +0 -16
  55. package/dist/admin/src/pages/GenerateContent.d.ts +0 -2
  56. package/dist/admin/src/pages/HomePage.d.ts +0 -2
  57. package/dist/admin/src/reducers/dialog/index.d.ts +0 -6
  58. package/dist/admin/src/types/reducers.d.ts +0 -4
@@ -0,0 +1,1380 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const admin = require("@strapi/strapi/admin");
5
+ const reactRouterDom = require("react-router-dom");
6
+ const PluginProviders = require("./PluginProviders-D79TeGSE.js");
7
+ const react = require("react");
8
+ const designSystem = require("@strapi/design-system");
9
+ const icons = require("@strapi/icons");
10
+ const index = require("./index-NA9OuMGs.js");
11
+ const ImageStudioTabs = require("./ImageStudioTabs-CG4KTn1S.js");
12
+ const reactIntl = require("react-intl");
13
+ const formik = require("formik");
14
+ const Yup = require("yup");
15
+ function _interopNamespace(e) {
16
+ if (e && e.__esModule) return e;
17
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
18
+ if (e) {
19
+ for (const k in e) {
20
+ if (k !== "default") {
21
+ const d = Object.getOwnPropertyDescriptor(e, k);
22
+ Object.defineProperty(n, k, d.get ? d : {
23
+ enumerable: true,
24
+ get: () => e[k]
25
+ });
26
+ }
27
+ }
28
+ }
29
+ n.default = e;
30
+ return Object.freeze(n);
31
+ }
32
+ const Yup__namespace = /* @__PURE__ */ _interopNamespace(Yup);
33
+ const OFFSET = "-06:00";
34
+ const ZONA = "America/Mexico_City";
35
+ const nextAction = (r) => {
36
+ if (r.estado === "programado") return { label: "Ver programación", variant: "tertiary" };
37
+ if (r.estado === "publicado") return r.linkedInPostId && r.facebookPostId ? { label: "Ver", variant: "tertiary" } : { label: "Compartir", variant: "tertiary" };
38
+ return r.imagen ? { label: "Publicar", variant: "secondary" } : { label: "Elegir imagen", variant: "secondary" };
39
+ };
40
+ const FILTROS = [
41
+ { id: "todos", label: "Todos", test: () => true },
42
+ { id: "borradores", label: "Borradores", test: (r) => r.estado === "borrador" },
43
+ { id: "programados", label: "Programados", test: (r) => r.estado === "programado" },
44
+ { id: "sin-imagen", label: "Sin imagen", test: (r) => !r.imagen },
45
+ { id: "publicados", label: "Publicados", test: (r) => r.estado === "publicado" }
46
+ ];
47
+ const MESES = ["ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"];
48
+ const formatFecha = (fecha) => {
49
+ if (!fecha) return "Sin fecha";
50
+ const [y, m, d] = fecha.split("-").map(Number);
51
+ return `${d} ${MESES[m - 1]} ${y}`;
52
+ };
53
+ const partes = (iso) => {
54
+ const p = new Intl.DateTimeFormat("en-CA", { timeZone: ZONA, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hourCycle: "h23" }).formatToParts(new Date(iso)).reduce((acc, x) => ({ ...acc, [x.type]: x.value }), {});
55
+ return { fecha: `${p.year}-${p.month}-${p.day}`, hora: `${p.hour}:${p.minute}` };
56
+ };
57
+ const formatFechaHora = (iso) => {
58
+ const { fecha, hora } = partes(iso);
59
+ return `${formatFecha(fecha)} · ${hora.replace(/^0/, "")}`;
60
+ };
61
+ const fromMexicoLocal = (fecha, hora) => (/* @__PURE__ */ new Date(`${fecha}T${hora}:00${OFFSET}`)).toISOString();
62
+ const toMexicoLocal = (iso) => partes(iso);
63
+ const BASE = "/strapi-content-generator-gemini";
64
+ const GENERATE_TIMEOUT_MS = 24e4;
65
+ const errorMessage = (err, fallback) => {
66
+ const message = err?.response?.data?.error?.message;
67
+ return typeof message === "string" && message ? message : fallback;
68
+ };
69
+ const createArticlesApi = (client) => ({
70
+ async list() {
71
+ const { data } = await client.get(`${BASE}/articles`);
72
+ return data.articles;
73
+ },
74
+ async detail(documentId) {
75
+ const { data } = await client.get(`${BASE}/articles/${documentId}`);
76
+ return data;
77
+ },
78
+ async generate(body) {
79
+ const signal = AbortSignal.timeout(GENERATE_TIMEOUT_MS);
80
+ const { data } = await client.post(`${BASE}/articles/generate`, body, { signal });
81
+ return data.documentId;
82
+ },
83
+ async enrich(texto) {
84
+ const { data } = await client.post(`${BASE}/articles/enrich`, { texto });
85
+ return data.texto;
86
+ },
87
+ async basePrompt() {
88
+ const { data } = await client.get(`${BASE}/articles/base-prompt`);
89
+ return data;
90
+ },
91
+ async saveBasePrompt(prompt) {
92
+ const { data } = await client.put(`${BASE}/articles/base-prompt`, { prompt });
93
+ return data;
94
+ },
95
+ async resetBasePrompt() {
96
+ const { data } = await client.del(`${BASE}/articles/base-prompt`);
97
+ return data;
98
+ },
99
+ async setImage(documentId, fileId) {
100
+ const { data } = await client.put(`${BASE}/articles/${documentId}/image`, { fileId });
101
+ return data;
102
+ },
103
+ async publish(documentId) {
104
+ const { data } = await client.post(`${BASE}/articles/${documentId}/publish`, {});
105
+ return data;
106
+ },
107
+ async schedule(documentId, at) {
108
+ const { data } = await client.post(`${BASE}/articles/${documentId}/schedule`, { at });
109
+ return data;
110
+ },
111
+ async unschedule(documentId) {
112
+ const { data } = await client.del(`${BASE}/articles/${documentId}/schedule`);
113
+ return data;
114
+ },
115
+ async categories() {
116
+ const { data } = await client.get(`${BASE}/categories`);
117
+ return (data ?? []).map((c) => ({ id: c.documentId, nombre: c.categoria }));
118
+ },
119
+ /** Compartir en LinkedIn o Facebook con el texto escrito en el panel (vacío → el predeterminado). */
120
+ async share(documentId, red, mensaje) {
121
+ await client.post(`${BASE}/publish-${red}`, { articleId: documentId, mensaje });
122
+ }
123
+ });
124
+ const useArticlesApi = () => {
125
+ const { get, post, put, del } = admin.useFetchClient();
126
+ const { toggleNotification } = admin.useNotification();
127
+ return react.useMemo(() => {
128
+ const api = createArticlesApi({ get, post, put, del });
129
+ const notify = (type, message) => toggleNotification({ type, message });
130
+ return { api, notify };
131
+ }, [get, post, put, del, toggleNotification]);
132
+ };
133
+ const ESTADO$1 = {
134
+ borrador: { label: "Borrador", variant: "warning" },
135
+ programado: { label: "Programado", variant: "secondary" },
136
+ publicado: { label: "Publicado", variant: "success" }
137
+ };
138
+ const articlePath = (documentId) => `/plugins/${index.PLUGIN_ID}/articles/${documentId}`;
139
+ const NEW_ARTICLE_PATH = `/plugins/${index.PLUGIN_ID}/generate-content`;
140
+ const contentManagerUrl$1 = (documentId) => `/admin/content-manager/collection-types/api::articulo.articulo/${documentId}`;
141
+ const fechaDe = (r) => r.programadaPara && r.estado === "programado" ? formatFechaHora(r.programadaPara) : formatFecha(r.fechaPublicacion);
142
+ const Redes = ({ r }) => {
143
+ if (!r.linkedInPostId && !r.facebookPostId) return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "—" });
144
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, wrap: "wrap", children: [
145
+ r.linkedInPostId && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: `https://www.linkedin.com/feed/update/${r.linkedInPostId}`, isExternal: true, endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}), children: "LinkedIn" }),
146
+ r.facebookPostId && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: `https://www.facebook.com/${r.facebookPostId}`, isExternal: true, endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}), children: "Facebook" })
147
+ ] });
148
+ };
149
+ const ArticlesPage = () => {
150
+ const { api, notify } = useArticlesApi();
151
+ const navigate = reactRouterDom.useNavigate();
152
+ const [rows, setRows] = react.useState(null);
153
+ const [filtro, setFiltro] = react.useState("todos");
154
+ const [busqueda, setBusqueda] = react.useState("");
155
+ react.useEffect(() => {
156
+ let alive = true;
157
+ api.list().then(
158
+ (list) => alive && setRows(list),
159
+ () => {
160
+ if (!alive) return;
161
+ setRows([]);
162
+ notify("danger", "No se pudieron cargar los artículos.");
163
+ }
164
+ );
165
+ return () => {
166
+ alive = false;
167
+ };
168
+ }, [api, notify]);
169
+ const visibles = react.useMemo(() => {
170
+ const test = FILTROS.find((f) => f.id === filtro)?.test ?? (() => true);
171
+ const q = busqueda.trim().toLowerCase();
172
+ return (rows ?? []).filter((r) => test(r) && (!q || r.titulo.toLowerCase().includes(q)));
173
+ }, [rows, filtro, busqueda]);
174
+ const abrir = (r) => navigate(articlePath(r.documentId));
175
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, paddingTop: 10, children: [
176
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "flex-start", gap: 4, wrap: "wrap", marginBottom: 8, children: [
177
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
178
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: "Generador de contenido" }),
179
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: "Artículos del blog y qué le falta a cada uno para publicarse." })
180
+ ] }),
181
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "L", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: () => navigate(NEW_ARTICLE_PATH), children: "Generar artículo" })
182
+ ] }),
183
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 4, wrap: "wrap", marginBottom: 4, children: [
184
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, wrap: "wrap", role: "group", "aria-label": "Filtrar artículos", children: FILTROS.map((f) => {
185
+ const on = f.id === filtro;
186
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: on ? "secondary" : "tertiary", "aria-pressed": on, onClick: () => setFiltro(f.id), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
187
+ f.label,
188
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { active: on, children: (rows ?? []).filter(f.test).length })
189
+ ] }) }, f.id);
190
+ }) }),
191
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "0 1 320px", minWidth: 220 }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.TextInput, { "aria-label": "Buscar por título", placeholder: "Buscar por título", startAction: /* @__PURE__ */ jsxRuntime.jsx(icons.Search, {}), value: busqueda, onChange: (e) => setBusqueda(e.target.value) }) })
192
+ ] }),
193
+ rows === null ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 10, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Cargando artículos…" }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
194
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 7, rowCount: visibles.length + 1, children: [
195
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
196
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Artículo" }) }),
197
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Estado" }) }),
198
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Imagen" }) }),
199
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Publicación" }) }),
200
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Compartido en" }) }),
201
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: "Acción" }) }),
202
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: "Editar" }) })
203
+ ] }) }),
204
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: visibles.map((r) => {
205
+ const accion = nextAction(r);
206
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
207
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: 420, whiteSpace: "normal" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
208
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TextButton, { onClick: () => abrir(r), style: { textAlign: "left", whiteSpace: "normal", height: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", textColor: "neutral800", style: { whiteSpace: "normal", overflowWrap: "anywhere" }, children: r.titulo }) }),
209
+ r.categoria && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: r.categoria })
210
+ ] }) }),
211
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: ESTADO$1[r.estado].variant, children: ESTADO$1[r.estado].label }) }),
212
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: r.imagen ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: "success", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
213
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Check, { width: 10, height: 10 }),
214
+ "Lista"
215
+ ] }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: "Falta" }) }),
216
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral700", style: { whiteSpace: "nowrap" }, children: fechaDe(r) }) }),
217
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(Redes, { r }) }),
218
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: accion.variant, onClick: () => abrir(r), children: accion.label }) }) }),
219
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: `Editar «${r.titulo}» en el Content Manager`, variant: "ghost", tag: "a", href: contentManagerUrl$1(r.documentId), children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {}) }) })
220
+ ] }, r.documentId);
221
+ }) })
222
+ ] }),
223
+ visibles.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 2, padding: 10, background: "neutral0", children: [
224
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "p", children: "Nada por aquí" }),
225
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: rows.length ? "Ningún artículo cumple este filtro." : "Aún no hay artículos. Genera el primero." })
226
+ ] }),
227
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 3, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: `${visibles.length} de ${rows.length} artículos` }) })
228
+ ] })
229
+ ] });
230
+ };
231
+ const POST_MAX = 3e3;
232
+ const hashtags = (keywords) => keywords.map((k) => k.trim().replace(/\s+/g, "").replace(/[^\p{L}\p{N}_]/gu, "")).filter(Boolean).map((k) => `#${k}`).join(" ");
233
+ const defaultPostText = ({ titulo, descripcion, keywords }, red) => [red === "facebook" ? titulo : "", descripcion, hashtags(keywords)].filter(Boolean).join("\n\n");
234
+ const DOMINIO = "interware.com.mx";
235
+ const Texto = ({ mensaje }) => /* @__PURE__ */ jsxRuntime.jsx(
236
+ designSystem.Typography,
237
+ {
238
+ tag: "p",
239
+ textColor: "neutral800",
240
+ style: { whiteSpace: "pre-wrap", overflowWrap: "anywhere", lineHeight: 1.5 },
241
+ children: mensaje.split(/(#[\p{L}\p{N}_]+)/u).map(
242
+ (parte, i) => parte.startsWith("#") ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", textColor: "primary600", fontWeight: "semiBold", "data-hashtag": "", children: parte }, i) : parte
243
+ )
244
+ }
245
+ );
246
+ const SharePreview = ({ red, mensaje, article }) => {
247
+ const nombre = red === "linkedin" ? "LinkedIn" : "Facebook";
248
+ const vacio = !mensaje.trim();
249
+ return /* @__PURE__ */ jsxRuntime.jsxs(
250
+ designSystem.Box,
251
+ {
252
+ tag: "section",
253
+ "aria-label": `Vista previa en ${nombre}`,
254
+ background: "neutral0",
255
+ hasRadius: true,
256
+ borderColor: "neutral200",
257
+ borderStyle: "solid",
258
+ borderWidth: "1px",
259
+ overflow: "hidden",
260
+ children: [
261
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", padding: 4, paddingBottom: 3, children: [
262
+ /* @__PURE__ */ jsxRuntime.jsx(
263
+ designSystem.Flex,
264
+ {
265
+ justifyContent: "center",
266
+ alignItems: "center",
267
+ background: "neutral800",
268
+ hasRadius: true,
269
+ style: { width: 40, height: 40, flex: "none" },
270
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral0", fontWeight: "bold", children: "IW" })
271
+ }
272
+ ),
273
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", children: [
274
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", children: "InterWare" }),
275
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Ahora · Público" })
276
+ ] })
277
+ ] }),
278
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingLeft: 4, paddingRight: 4, paddingBottom: 3, children: vacio ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger600", children: "Escribe el texto de la publicación." }) : /* @__PURE__ */ jsxRuntime.jsx(Texto, { mensaje }) }),
279
+ /* @__PURE__ */ jsxRuntime.jsxs(
280
+ designSystem.Box,
281
+ {
282
+ background: "neutral100",
283
+ borderColor: "neutral200",
284
+ borderStyle: "solid",
285
+ borderWidth: "1px 0 0",
286
+ children: [
287
+ article.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(
288
+ "img",
289
+ {
290
+ src: article.imageUrl,
291
+ alt: article.imageName ?? "",
292
+ style: { display: "block", width: "100%", aspectRatio: "1.91 / 1", maxHeight: 200, objectFit: "cover" }
293
+ }
294
+ ),
295
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, padding: 3, children: red === "facebook" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
296
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: DOMINIO.toUpperCase() }),
297
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", children: article.titulo }),
298
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", ellipsis: true, style: { maxWidth: "100%" }, children: article.descripcion })
299
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
300
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", children: article.titulo }),
301
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: DOMINIO })
302
+ ] }) })
303
+ ]
304
+ }
305
+ )
306
+ ]
307
+ }
308
+ );
309
+ };
310
+ const ESTADO = {
311
+ borrador: { label: "Borrador", variant: "warning" },
312
+ programado: { label: "Programado", variant: "secondary" },
313
+ publicado: { label: "Publicado", variant: "success" }
314
+ };
315
+ const REDES = [
316
+ {
317
+ id: "linkedin",
318
+ nombre: "LinkedIn",
319
+ campo: "linkedInPostId",
320
+ url: (id) => `https://www.linkedin.com/feed/update/${id}`
321
+ },
322
+ {
323
+ id: "facebook",
324
+ nombre: "Facebook",
325
+ campo: "facebookPostId",
326
+ url: (id) => `https://www.facebook.com/${id}`
327
+ }
328
+ ];
329
+ const SITE_URL = "https://www.interware.com.mx/blog";
330
+ const contentManagerUrl = (documentId) => `/admin/content-manager/collection-types/api::articulo.articulo/${documentId}`;
331
+ const extracto = (html, max = 700) => {
332
+ const text = (new DOMParser().parseFromString(html, "text/html").body.textContent ?? "").replace(/\s+/g, " ").trim();
333
+ return text.length > max ? `${text.slice(0, max)}…` : text;
334
+ };
335
+ const manana = () => toMexicoLocal(new Date(Date.now() + 24 * 60 * 60 * 1e3).toISOString()).fecha;
336
+ const Tarjeta = ({
337
+ titulo,
338
+ extra,
339
+ children
340
+ }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", padding: 6, hasRadius: true, shadow: "tableShadow", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
341
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 2, children: [
342
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: titulo }),
343
+ extra
344
+ ] }),
345
+ children
346
+ ] }) });
347
+ const Requisito = ({
348
+ ok,
349
+ children,
350
+ accion
351
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 3, style: { minHeight: 44 }, children: [
352
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
353
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { color: ok ? "success600" : "warning600", children: ok ? /* @__PURE__ */ jsxRuntime.jsx(icons.Check, { width: 16, height: 16 }) : /* @__PURE__ */ jsxRuntime.jsx(icons.WarningCircle, { width: 16, height: 16 }) }),
354
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children })
355
+ ] }),
356
+ accion
357
+ ] });
358
+ const ArticlePage = () => {
359
+ const { documentId = "" } = reactRouterDom.useParams();
360
+ const navigate = reactRouterDom.useNavigate();
361
+ const { api, notify } = useArticlesApi();
362
+ const [d, setD] = react.useState(null);
363
+ const [picker, setPicker] = react.useState(false);
364
+ const [cuando, setCuando] = react.useState("ahora");
365
+ const [fecha, setFecha] = react.useState(manana);
366
+ const [hora, setHora] = react.useState("09:00");
367
+ const [busy, setBusy] = react.useState(null);
368
+ const [confirmar, setConfirmar] = react.useState(null);
369
+ const [mensaje, setMensaje] = react.useState("");
370
+ react.useEffect(() => {
371
+ let alive = true;
372
+ api.detail(documentId).then(
373
+ (x) => alive && setD(x),
374
+ (err) => notify("danger", errorMessage(err, "No se pudo cargar el artículo."))
375
+ );
376
+ return () => {
377
+ alive = false;
378
+ };
379
+ }, [api, notify, documentId]);
380
+ const texto = react.useMemo(() => d ? extracto(d.content) : "", [d]);
381
+ const run = async (id, work, ok, fallback) => {
382
+ setBusy(id);
383
+ try {
384
+ setD(await work());
385
+ notify("success", ok);
386
+ } catch (err) {
387
+ notify("danger", errorMessage(err, fallback));
388
+ } finally {
389
+ setBusy(null);
390
+ }
391
+ };
392
+ const elegirImagen = (files) => {
393
+ setPicker(false);
394
+ if (files[0])
395
+ void run(
396
+ "imagen",
397
+ () => api.setImage(documentId, files[0].id),
398
+ "Imagen asignada.",
399
+ "No se pudo asignar la imagen."
400
+ );
401
+ };
402
+ const publicar = () => {
403
+ if (cuando === "ahora")
404
+ return run(
405
+ "publicar",
406
+ () => api.publish(documentId),
407
+ "Artículo publicado en el sitio.",
408
+ "No se pudo publicar."
409
+ );
410
+ return run(
411
+ "publicar",
412
+ () => api.schedule(documentId, fromMexicoLocal(fecha, hora)),
413
+ "Publicación programada.",
414
+ "No se pudo programar."
415
+ );
416
+ };
417
+ const abrirCompartir = (red) => {
418
+ if (!d) return;
419
+ setMensaje(
420
+ defaultPostText({ titulo: d.titulo, descripcion: d.descripcion, keywords: d.keywords }, red)
421
+ );
422
+ setConfirmar(red);
423
+ };
424
+ const compartir = async (red) => {
425
+ setConfirmar(null);
426
+ setBusy(red);
427
+ try {
428
+ await api.share(documentId, red, mensaje.trim());
429
+ setD(await api.detail(documentId));
430
+ notify("success", `Compartido en ${red === "linkedin" ? "LinkedIn" : "Facebook"}.`);
431
+ } catch (err) {
432
+ notify("danger", errorMessage(err, "No se pudo compartir."));
433
+ } finally {
434
+ setBusy(null);
435
+ }
436
+ };
437
+ if (!d) {
438
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 10, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Cargando artículo…" }) });
439
+ }
440
+ const referencias = d.fuentes.filter((f) => f.tipo === "referencia");
441
+ const encontradas = d.fuentes.filter((f) => f.tipo === "busqueda");
442
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, paddingTop: 6, children: [
443
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, marginBottom: 6, children: [
444
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
445
+ designSystem.Button,
446
+ {
447
+ variant: "ghost",
448
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}),
449
+ onClick: () => navigate(`/plugins/${index.PLUGIN_ID}`),
450
+ children: "Volver a artículos"
451
+ }
452
+ ) }),
453
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "flex-end", gap: 4, wrap: "wrap", children: [
454
+ /* @__PURE__ */ jsxRuntime.jsxs(
455
+ designSystem.Flex,
456
+ {
457
+ direction: "column",
458
+ alignItems: "flex-start",
459
+ gap: 2,
460
+ style: { flex: "1 1 520px", minWidth: 0 },
461
+ children: [
462
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
463
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: ESTADO[d.estado].variant, children: ESTADO[d.estado].label }),
464
+ d.categoria && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: d.categoria }),
465
+ d.minuteRead && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: `${d.minuteRead} min de lectura` })
466
+ ] }),
467
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: d.titulo })
468
+ ]
469
+ }
470
+ ),
471
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
472
+ /* @__PURE__ */ jsxRuntime.jsx(
473
+ designSystem.LinkButton,
474
+ {
475
+ variant: "tertiary",
476
+ size: "L",
477
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {}),
478
+ href: contentManagerUrl(d.documentId),
479
+ children: "Editar en el Content Manager"
480
+ }
481
+ ),
482
+ d.estado === "publicado" && d.slug && /* @__PURE__ */ jsxRuntime.jsx(
483
+ designSystem.LinkButton,
484
+ {
485
+ variant: "tertiary",
486
+ size: "L",
487
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}),
488
+ href: `${SITE_URL}/${d.slug}`,
489
+ isExternal: true,
490
+ children: "Ver en el sitio"
491
+ }
492
+ )
493
+ ] })
494
+ ] })
495
+ ] }),
496
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 6, alignItems: "flex-start", wrap: "wrap", children: [
497
+ /* @__PURE__ */ jsxRuntime.jsx(
498
+ designSystem.Box,
499
+ {
500
+ background: "neutral0",
501
+ padding: 6,
502
+ hasRadius: true,
503
+ shadow: "tableShadow",
504
+ style: { flex: "999 1 560px", minWidth: 0 },
505
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
506
+ d.imageUrl ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { position: "relative", children: [
507
+ /* @__PURE__ */ jsxRuntime.jsx(
508
+ "img",
509
+ {
510
+ src: d.imageUrl,
511
+ alt: d.imageName ?? "",
512
+ style: {
513
+ display: "block",
514
+ width: "100%",
515
+ aspectRatio: "16 / 9",
516
+ objectFit: "cover",
517
+ borderRadius: 4
518
+ }
519
+ }
520
+ ),
521
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { position: "absolute", right: 4, bottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
522
+ designSystem.Button,
523
+ {
524
+ variant: "tertiary",
525
+ onClick: () => setPicker(true),
526
+ loading: busy === "imagen",
527
+ children: "Cambiar imagen"
528
+ }
529
+ ) })
530
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
531
+ designSystem.Flex,
532
+ {
533
+ direction: "column",
534
+ gap: 3,
535
+ justifyContent: "center",
536
+ hasRadius: true,
537
+ background: "neutral100",
538
+ borderColor: "neutral200",
539
+ borderStyle: "dashed",
540
+ borderWidth: "1px",
541
+ style: { aspectRatio: "16 / 9" },
542
+ children: [
543
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { color: "neutral500", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Image, { width: 32, height: 32 }) }),
544
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: "Sin imagen principal" }),
545
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Elige una de la biblioteca de Image Studio." }),
546
+ /* @__PURE__ */ jsxRuntime.jsx(
547
+ designSystem.Button,
548
+ {
549
+ variant: "secondary",
550
+ size: "L",
551
+ onClick: () => setPicker(true),
552
+ loading: busy === "imagen",
553
+ children: "Elegir imagen"
554
+ }
555
+ )
556
+ ]
557
+ }
558
+ ),
559
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, paddingLeft: 2, paddingRight: 2, children: [
560
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral700", children: d.descripcion }),
561
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral700", style: { lineHeight: 1.65 }, children: texto }),
562
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Vista previa del inicio del contenido. El texto completo se edita en el Content Manager." })
563
+ ] })
564
+ ] })
565
+ }
566
+ ),
567
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, style: { flex: "1 1 340px" }, children: [
568
+ d.estado === "borrador" && /* @__PURE__ */ jsxRuntime.jsxs(Tarjeta, { titulo: "Para publicar en el sitio", children: [
569
+ /* @__PURE__ */ jsxRuntime.jsx(Requisito, { ok: true, children: "Contenido y SEO" }),
570
+ /* @__PURE__ */ jsxRuntime.jsx(
571
+ Requisito,
572
+ {
573
+ ok: d.imagen,
574
+ accion: /* @__PURE__ */ jsxRuntime.jsx(
575
+ designSystem.Button,
576
+ {
577
+ variant: d.imagen ? "tertiary" : "secondary",
578
+ onClick: () => setPicker(true),
579
+ children: d.imagen ? "Cambiar" : "Elegir imagen"
580
+ }
581
+ ),
582
+ children: "Imagen principal"
583
+ }
584
+ ),
585
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "cuando", children: [
586
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "¿Cuándo?" }),
587
+ /* @__PURE__ */ jsxRuntime.jsxs(
588
+ designSystem.Radio.Group,
589
+ {
590
+ value: cuando,
591
+ onValueChange: (v) => setCuando(v),
592
+ "aria-label": "¿Cuándo?",
593
+ children: [
594
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { value: "ahora", children: "Publicar ahora" }),
595
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { value: "programar", children: "Programar" })
596
+ ]
597
+ }
598
+ )
599
+ ] }) }),
600
+ cuando === "programar" ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
601
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
602
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "fecha", children: [
603
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Fecha" }),
604
+ /* @__PURE__ */ jsxRuntime.jsx(
605
+ designSystem.TextInput,
606
+ {
607
+ type: "date",
608
+ value: fecha,
609
+ onChange: (e) => setFecha(e.target.value)
610
+ }
611
+ )
612
+ ] }) }),
613
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "hora", children: [
614
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Hora" }),
615
+ /* @__PURE__ */ jsxRuntime.jsx(
616
+ designSystem.TextInput,
617
+ {
618
+ type: "time",
619
+ value: hora,
620
+ onChange: (e) => setHora(e.target.value)
621
+ }
622
+ )
623
+ ] }) })
624
+ ] }),
625
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Hora del centro de México. La fecha de publicación del artículo será la programada." })
626
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "La fecha de publicación del artículo será hoy." }),
627
+ /* @__PURE__ */ jsxRuntime.jsx(
628
+ designSystem.Button,
629
+ {
630
+ size: "L",
631
+ fullWidth: true,
632
+ disabled: !d.imagen || cuando === "programar" && (!fecha || !hora),
633
+ loading: busy === "publicar",
634
+ onClick: publicar,
635
+ children: cuando === "programar" ? "Programar publicación" : "Publicar en el sitio"
636
+ }
637
+ ),
638
+ !d.imagen && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", textAlign: "center", children: "Elige una imagen para poder publicar." })
639
+ ] }),
640
+ d.estado === "programado" && d.programadaPara && /* @__PURE__ */ jsxRuntime.jsxs(Tarjeta, { titulo: "Publicación programada", children: [
641
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
642
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { color: "secondary600", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Clock, { width: 16, height: 16 }) }),
643
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `Se publicará el ${formatFechaHora(d.programadaPara)}` })
644
+ ] }),
645
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", paddingTop: 2, children: [
646
+ /* @__PURE__ */ jsxRuntime.jsx(
647
+ designSystem.Button,
648
+ {
649
+ variant: "secondary",
650
+ loading: busy === "publicar",
651
+ onClick: () => run(
652
+ "publicar",
653
+ () => api.publish(documentId),
654
+ "Artículo publicado en el sitio.",
655
+ "No se pudo publicar."
656
+ ),
657
+ children: "Publicar ahora"
658
+ }
659
+ ),
660
+ /* @__PURE__ */ jsxRuntime.jsx(
661
+ designSystem.Button,
662
+ {
663
+ variant: "tertiary",
664
+ loading: busy === "cancelar",
665
+ onClick: () => run(
666
+ "cancelar",
667
+ () => api.unschedule(documentId),
668
+ "Programación cancelada.",
669
+ "No se pudo cancelar."
670
+ ),
671
+ children: "Cancelar programación"
672
+ }
673
+ )
674
+ ] })
675
+ ] }),
676
+ d.estado === "publicado" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
677
+ /* @__PURE__ */ jsxRuntime.jsx(Tarjeta, { titulo: "Publicado en el sitio", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
678
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { color: "success600", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, { width: 16, height: 16 }) }),
679
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `Fecha de publicación: ${formatFecha(d.fechaPublicacion)}` })
680
+ ] }) }),
681
+ /* @__PURE__ */ jsxRuntime.jsx(Tarjeta, { titulo: "Compartir en redes", extra: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: "Opcional" }), children: REDES.map((red) => {
682
+ const postId = d[red.campo];
683
+ return /* @__PURE__ */ jsxRuntime.jsxs(
684
+ designSystem.Flex,
685
+ {
686
+ justifyContent: "space-between",
687
+ alignItems: "center",
688
+ gap: 3,
689
+ paddingTop: 2,
690
+ style: { minHeight: 44 },
691
+ children: [
692
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: red.nombre }),
693
+ postId ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: red.url(postId), isExternal: true, endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}), children: `Ver en ${red.nombre}` }) : /* @__PURE__ */ jsxRuntime.jsx(
694
+ designSystem.Button,
695
+ {
696
+ variant: "tertiary",
697
+ "aria-label": `Compartir en ${red.nombre}`,
698
+ loading: busy === red.id,
699
+ onClick: () => abrirCompartir(red.id),
700
+ children: "Compartir"
701
+ }
702
+ )
703
+ ]
704
+ },
705
+ red.id
706
+ );
707
+ }) })
708
+ ] }),
709
+ d.fuentes.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
710
+ Tarjeta,
711
+ {
712
+ titulo: "Fuentes",
713
+ extra: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Verifícalas antes de publicar" }),
714
+ children: [
715
+ referencias.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
716
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Tus referencias" }),
717
+ referencias.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: f.url, isExternal: true, children: f.titulo }, f.url))
718
+ ] }),
719
+ encontradas.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, paddingTop: 2, children: [
720
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Encontradas en Google" }),
721
+ encontradas.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: f.url, isExternal: true, children: f.titulo }, f.url))
722
+ ] })
723
+ ]
724
+ }
725
+ ),
726
+ /* @__PURE__ */ jsxRuntime.jsxs(Tarjeta, { titulo: "SEO", children: [
727
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 1, children: [
728
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Meta título" }),
729
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: d.seo?.metaTitle || "—" })
730
+ ] }),
731
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 1, children: [
732
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Meta descripción" }),
733
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: d.seo?.metaDescription || "—" })
734
+ ] }),
735
+ d.keywords.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, wrap: "wrap", children: d.keywords.map((k) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: k }, k)) })
736
+ ] })
737
+ ] })
738
+ ] }),
739
+ /* @__PURE__ */ jsxRuntime.jsx(
740
+ ImageStudioTabs.LibraryImagePicker,
741
+ {
742
+ open: picker,
743
+ max: 1,
744
+ title: "Elegir imagen principal",
745
+ onClose: () => setPicker(false),
746
+ onPick: elegirImagen,
747
+ footerStart: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: `/admin/${index.STUDIO_PATH}`, isExternal: true, endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}), children: "¿No está la que buscas? Créala en Image Studio" })
748
+ }
749
+ ),
750
+ /* @__PURE__ */ jsxRuntime.jsx(
751
+ designSystem.Modal.Root,
752
+ {
753
+ open: confirmar !== null,
754
+ onOpenChange: (open) => !open && setConfirmar(null),
755
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { style: { width: "min(1040px, 94vw)", maxWidth: "min(1040px, 94vw)" }, children: [
756
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: `Compartir en ${confirmar === "facebook" ? "Facebook" : "LinkedIn"}` }) }),
757
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 6, alignItems: "flex-start", wrap: "wrap", children: [
758
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 340px", minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
759
+ designSystem.Field.Root,
760
+ {
761
+ name: "mensaje",
762
+ hint: "Se publica con el enlace al artículo y su imagen. Una vez publicado no se puede borrar desde aquí.",
763
+ children: [
764
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "baseline", children: [
765
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Texto de la publicación" }),
766
+ /* @__PURE__ */ jsxRuntime.jsx(
767
+ designSystem.Typography,
768
+ {
769
+ variant: "pi",
770
+ textColor: [...mensaje].length > POST_MAX ? "danger600" : "neutral500",
771
+ children: `${[...mensaje].length}/${POST_MAX}`
772
+ }
773
+ )
774
+ ] }),
775
+ /* @__PURE__ */ jsxRuntime.jsx(
776
+ designSystem.Textarea,
777
+ {
778
+ value: mensaje,
779
+ onChange: (e) => setMensaje(e.target.value),
780
+ rows: 10
781
+ }
782
+ ),
783
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
784
+ ]
785
+ }
786
+ ) }),
787
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 340px", minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
788
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "Vista previa" }),
789
+ confirmar && /* @__PURE__ */ jsxRuntime.jsx(SharePreview, { red: confirmar, mensaje, article: d }),
790
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Aproximada: cada red ajusta el diseño y corta el texto largo con «ver más»." })
791
+ ] }) })
792
+ ] }) }),
793
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
794
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: () => setConfirmar(null), children: "Cancelar" }),
795
+ /* @__PURE__ */ jsxRuntime.jsx(
796
+ designSystem.Button,
797
+ {
798
+ disabled: !mensaje.trim() || [...mensaje].length > POST_MAX,
799
+ onClick: () => confirmar && compartir(confirmar),
800
+ children: "Compartir"
801
+ }
802
+ )
803
+ ] })
804
+ ] })
805
+ }
806
+ )
807
+ ] });
808
+ };
809
+ const MAX_URLS = 3;
810
+ const NUEVA = "nueva:";
811
+ const LABELS = { label: "Las instrucciones", empty: "Escribe las instrucciones del artículo." };
812
+ const Panel = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", padding: 6, hasRadius: true, shadow: "tableShadow", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children }) });
813
+ const Punto = ({ children }) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
814
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { color: "success600", paddingTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, { width: 14, height: 14 }) }),
815
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children })
816
+ ] });
817
+ const useElapsed = (running) => {
818
+ const [seconds, setSeconds] = react.useState(0);
819
+ react.useEffect(() => {
820
+ if (!running) return setSeconds(0);
821
+ const t = setInterval(() => setSeconds((s) => s + 1), 1e3);
822
+ return () => clearInterval(t);
823
+ }, [running]);
824
+ return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
825
+ };
826
+ const NewArticlePage = () => {
827
+ const { api, notify } = useArticlesApi();
828
+ const navigate = reactRouterDom.useNavigate();
829
+ const [categorias, setCategorias] = react.useState([]);
830
+ const [categoria, setCategoria] = react.useState();
831
+ const [titulo, setTitulo] = react.useState("");
832
+ const [temperatura, setTemperatura] = react.useState(0.5);
833
+ const [urls, setUrls] = react.useState([]);
834
+ const [busqueda, setBusqueda] = react.useState(false);
835
+ const [instrucciones, setInstrucciones] = react.useState("");
836
+ const [usarBase, setUsarBase] = react.useState(true);
837
+ const [guardado, setGuardado] = react.useState(null);
838
+ const [base, setBase] = react.useState("");
839
+ const [busy, setBusy] = react.useState(null);
840
+ const elapsed = useElapsed(busy === "generate");
841
+ const alive = react.useRef(true);
842
+ react.useEffect(() => {
843
+ alive.current = true;
844
+ api.categories().then(
845
+ (list) => alive.current && setCategorias(list),
846
+ () => notify("danger", "No se pudieron cargar las categorías.")
847
+ );
848
+ api.basePrompt().then(
849
+ (b) => {
850
+ if (!alive.current) return;
851
+ setGuardado(b);
852
+ setBase(b.prompt);
853
+ },
854
+ () => notify("danger", "No se pudo cargar el prompt base.")
855
+ );
856
+ return () => {
857
+ alive.current = false;
858
+ };
859
+ }, [api, notify]);
860
+ const baseEditado = Boolean(guardado) && base.trim() !== guardado?.prompt;
861
+ const estadoBase = !usarBase ? "sin prompt base (instrucciones tal cual)" : baseEditado ? "editado para este artículo" : guardado?.personalizado ? "personalizado" : "predeterminado";
862
+ const cambiarBase = async (work, ok) => {
863
+ setBusy("base");
864
+ try {
865
+ const b = await work();
866
+ if (!alive.current) return;
867
+ setGuardado(b);
868
+ setBase(b.prompt);
869
+ notify("success", ok);
870
+ } catch (err) {
871
+ notify("danger", errorMessage(err, "No se pudo actualizar el prompt base."));
872
+ } finally {
873
+ if (alive.current) setBusy(null);
874
+ }
875
+ };
876
+ const nueva = categoria?.startsWith(NUEVA) ? categoria.slice(NUEVA.length) : null;
877
+ const nombreCategoria = nueva ?? categorias.find((c) => c.id === categoria)?.nombre ?? "";
878
+ const crearCategoria = (value) => {
879
+ const nombre = value?.trim();
880
+ if (!nombre) return;
881
+ const existente = categorias.find((c) => c.nombre.toLowerCase() === nombre.toLowerCase());
882
+ setCategoria(existente ? existente.id : `${NUEVA}${nombre}`);
883
+ };
884
+ const elegirCategoria = (v) => {
885
+ if (!v) return setCategoria(void 0);
886
+ if (v.startsWith(NUEVA) || categorias.some((c) => c.id === v)) return setCategoria(v);
887
+ crearCategoria(v);
888
+ };
889
+ const enriquecer = async () => {
890
+ setBusy("enrich");
891
+ try {
892
+ const texto = await api.enrich(instrucciones.trim());
893
+ if (alive.current) setInstrucciones(texto);
894
+ } catch (err) {
895
+ notify("danger", errorMessage(err, "No se pudieron enriquecer las instrucciones."));
896
+ } finally {
897
+ if (alive.current) setBusy(null);
898
+ }
899
+ };
900
+ const generar = async (texto) => {
901
+ if (!categoria) return;
902
+ const body = {
903
+ titulo: titulo.trim(),
904
+ categoria: nueva ? { nueva } : { id: categoria },
905
+ temperatura,
906
+ instrucciones: texto,
907
+ urls: urls.map((u) => u.trim()).filter(Boolean),
908
+ busqueda,
909
+ usarPromptBase: usarBase,
910
+ ...usarBase && baseEditado && base.trim() ? { promptBase: base.trim() } : {}
911
+ };
912
+ setBusy("generate");
913
+ try {
914
+ const documentId = await api.generate(body);
915
+ notify("success", "Borrador generado. Elige la imagen y publícalo.");
916
+ navigate(articlePath(documentId));
917
+ } catch (err) {
918
+ notify("danger", errorMessage(err, "No se pudo generar el artículo. Intenta de nuevo."));
919
+ if (alive.current) setBusy(null);
920
+ }
921
+ };
922
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, paddingTop: 6, children: [
923
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 4, marginBottom: 6, children: [
924
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), onClick: () => navigate(".."), children: "Volver a artículos" }),
925
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "baseline", gap: 4, wrap: "wrap", width: "100%", children: [
926
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: "Nuevo artículo" }),
927
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Se guarda como borrador. Nada se publica sin tu revisión." })
928
+ ] })
929
+ ] }),
930
+ busy === "generate" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", padding: 8, hasRadius: true, shadow: "tableShadow", style: { width: "100%", maxWidth: 720 }, "aria-live": "polite", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
931
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
932
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", tag: "h2", children: "Redactando el artículo" }),
933
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: busqueda || urls.some(Boolean) ? "Gemini 2.5 Pro está investigando y escribiendo el borrador." : "Gemini 2.5 Pro está escribiendo el borrador con tus datos." })
934
+ ] }),
935
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", children: [
936
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true, children: "Generando" }),
937
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: `${elapsed} transcurridos` })
938
+ ] }),
939
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 5, hasRadius: true, borderColor: "neutral150", borderStyle: "solid", borderWidth: "1px", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
940
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Lo que pediste" }),
941
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `Categoría: ${nombreCategoria}${nueva ? " (nueva)" : ""}` }),
942
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `Creatividad: ${temperatura}` }),
943
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `Búsqueda de Google: ${busqueda ? "activada" : "no"} · URLs de referencia: ${urls.filter(Boolean).length}` }),
944
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `Prompt base: ${estadoBase}` })
945
+ ] }) }),
946
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Si sales de esta pantalla, el borrador aparecerá en Artículos al terminar." })
947
+ ] }) }) }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 6, alignItems: "flex-start", wrap: "wrap", children: [
948
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "999 1 560px", minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(Panel, { children: [
949
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: "¿De qué trata el artículo?" }),
950
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 6, alignItems: "flex-start", wrap: "wrap", children: [
951
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 280px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "titulo", hint: "Opcional. Si lo dejas vacío, Gemini propone uno.", children: [
952
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Título" }),
953
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TextInput, { placeholder: "Ej: Las 10 tendencias en IA para 2026", value: titulo, onChange: (e) => setTitulo(e.target.value) }),
954
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
955
+ ] }) }),
956
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 280px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "categoria", required: true, hint: nueva ? void 0 : "Elige una o escribe para crear una nueva.", children: [
957
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Categoría" }),
958
+ /* @__PURE__ */ jsxRuntime.jsxs(
959
+ designSystem.Combobox,
960
+ {
961
+ placeholder: "Busca o escribe una nueva",
962
+ value: categoria,
963
+ onChange: elegirCategoria,
964
+ creatable: true,
965
+ createMessage: (v) => `Crear categoría «${v}»`,
966
+ onCreateOption: crearCategoria,
967
+ onClear: () => setCategoria(void 0),
968
+ children: [
969
+ categorias.map((c) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: c.id, children: c.nombre }, c.id)),
970
+ nueva && /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: categoria, children: nueva })
971
+ ]
972
+ }
973
+ ),
974
+ nueva ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", paddingTop: 1, children: [
975
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: "primary", children: "Nueva" }),
976
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Se crea en Categorías al generar el artículo." })
977
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
978
+ ] }) })
979
+ ] }),
980
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "temperatura", children: [
981
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "baseline", children: [
982
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Creatividad" }),
983
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: temperatura })
984
+ ] }),
985
+ /* @__PURE__ */ jsxRuntime.jsx(
986
+ "input",
987
+ {
988
+ type: "range",
989
+ "aria-label": "Creatividad",
990
+ min: 0,
991
+ max: 2,
992
+ step: 0.05,
993
+ value: temperatura,
994
+ onChange: (e) => setTemperatura(Number(e.target.value)),
995
+ style: { width: "100%" }
996
+ }
997
+ ),
998
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
999
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Más precisa y sobria" }),
1000
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Más creativa y variada" })
1001
+ ] })
1002
+ ] }),
1003
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 5, hasRadius: true, borderColor: "neutral150", borderStyle: "solid", borderWidth: "1px", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 5, children: [
1004
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", tag: "h3", children: "Fuentes" }),
1005
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
1006
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: "URLs de referencia" }),
1007
+ urls.map((url, i) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1008
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1009
+ designSystem.TextInput,
1010
+ {
1011
+ "aria-label": `URL de referencia ${i + 1}`,
1012
+ type: "url",
1013
+ placeholder: "https://",
1014
+ value: url,
1015
+ onChange: (e) => setUrls((list) => list.map((u, j) => j === i ? e.target.value : u))
1016
+ }
1017
+ ) }),
1018
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: `Quitar URL ${i + 1}`, variant: "tertiary", onClick: () => setUrls((list) => list.filter((_, j) => j !== i)), children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) })
1019
+ ] }, i)),
1020
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 2, wrap: "wrap", children: [
1021
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: "Hasta 3 páginas: una nota, un caso, una página de servicio. Gemini escribe a partir de ellas." }),
1022
+ urls.length < MAX_URLS && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: () => setUrls((list) => [...list, ""]), children: "Agregar URL" })
1023
+ ] })
1024
+ ] }),
1025
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "busqueda", hint: "Usa información actual y muestra las fuentes en la revisión. Tiene un costo adicional por artículo.", children: [
1026
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Buscar en Google" }),
1027
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Toggle, { onLabel: "Sí", offLabel: "No", checked: busqueda, onChange: (e) => setBusqueda(e.target.checked) }),
1028
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
1029
+ ] })
1030
+ ] }) }),
1031
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 5, hasRadius: true, borderColor: "neutral150", borderStyle: "solid", borderWidth: "1px", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
1032
+ /* @__PURE__ */ jsxRuntime.jsxs(
1033
+ designSystem.Field.Root,
1034
+ {
1035
+ name: "usarPromptBase",
1036
+ hint: usarBase ? "Persona, tono, reglas y arco narrativo de InterWare. Lo que edites se usa en este artículo." : "Se mandan tus instrucciones tal cual, sin el prompt base.",
1037
+ children: [
1038
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Usar prompt base" }),
1039
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Toggle, { onLabel: "Sí", offLabel: "No", checked: usarBase, onChange: (e) => setUsarBase(e.target.checked) }),
1040
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
1041
+ ]
1042
+ }
1043
+ ),
1044
+ usarBase && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1045
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "promptBase", children: [
1046
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 2, children: [
1047
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Prompt base" }),
1048
+ baseEditado ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: "warning", children: "Con cambios sin guardar" }) : guardado?.personalizado && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: "primary", children: "Personalizado" })
1049
+ ] }),
1050
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Textarea, { value: base, onChange: (e) => setBase(e.target.value), rows: 14, disabled: !guardado })
1051
+ ] }),
1052
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", justifyContent: "flex-end", children: [
1053
+ baseEditado && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", disabled: busy !== null, onClick: () => setBase(guardado?.prompt ?? ""), children: "Descartar cambios" }),
1054
+ guardado?.personalizado && !baseEditado && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", loading: busy === "base", disabled: busy !== null, onClick: () => cambiarBase(() => api.resetBasePrompt(), "Se restauró el prompt base original."), children: "Restaurar el original" }),
1055
+ /* @__PURE__ */ jsxRuntime.jsx(
1056
+ designSystem.Button,
1057
+ {
1058
+ variant: "tertiary",
1059
+ size: "S",
1060
+ loading: busy === "base",
1061
+ disabled: busy !== null || !baseEditado || !base.trim(),
1062
+ onClick: () => cambiarBase(() => api.saveBasePrompt(base.trim()), "Prompt base guardado como predeterminado."),
1063
+ children: "Guardar como predeterminado"
1064
+ }
1065
+ )
1066
+ ] })
1067
+ ] })
1068
+ ] }) }),
1069
+ /* @__PURE__ */ jsxRuntime.jsx(
1070
+ ImageStudioTabs.PromptComposer,
1071
+ {
1072
+ label: "Instrucciones",
1073
+ value: instrucciones,
1074
+ onChange: setInstrucciones,
1075
+ onSubmit: generar,
1076
+ labels: LABELS,
1077
+ placeholder: "Tema, público, puntos que debe cubrir, tono, llamada a la acción…",
1078
+ actionLabel: "Generar artículo",
1079
+ actionIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Sparkle, {}),
1080
+ loading: false,
1081
+ disabled: !categoria || busy !== null,
1082
+ hint: categoria ? "«Enriquecer» convierte una idea corta en instrucciones completas. ⌘/Ctrl + Enter también genera." : "Elige una categoría para generar.",
1083
+ tools: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Sparkle, {}), loading: busy === "enrich", disabled: busy !== null || !instrucciones.trim(), onClick: enriquecer, children: "Enriquecer instrucciones" })
1084
+ }
1085
+ )
1086
+ ] }) }),
1087
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: "1 1 300px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(Panel, { children: [
1088
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", tag: "h2", children: "Qué vas a obtener" }),
1089
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1090
+ /* @__PURE__ */ jsxRuntime.jsx(Punto, { children: "Título, descripción y contenido completo" }),
1091
+ /* @__PURE__ */ jsxRuntime.jsx(Punto, { children: "Datos SEO y palabras clave" }),
1092
+ /* @__PURE__ */ jsxRuntime.jsx(Punto, { children: "Las fuentes consultadas, para verificarlas antes de publicar" }),
1093
+ /* @__PURE__ */ jsxRuntime.jsx(Punto, { children: "Un borrador listo para elegir imagen y publicar" })
1094
+ ] }),
1095
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
1096
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Modelo" }),
1097
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: "Gemini 2.5 Pro · Vertex AI" })
1098
+ ] })
1099
+ ] }) })
1100
+ ] })
1101
+ ] });
1102
+ };
1103
+ const sceneSchema = Yup__namespace.object().shape({
1104
+ prompt: Yup__namespace.string().trim().required("El prompt es obligatorio para esta escena"),
1105
+ durationSeconds: Yup__namespace.number().min(1, "La duración mínima es 1 segundo").max(90, "La duración máxima es 90 segundos").required("La duración es obligatoria"),
1106
+ referenceImages: Yup__namespace.array().of(
1107
+ Yup__namespace.object().shape({
1108
+ id: Yup__namespace.number().required(),
1109
+ file: Yup__namespace.mixed().optional(),
1110
+ previewUrl: Yup__namespace.string().optional()
1111
+ })
1112
+ ).max(3, "Se permiten máximo 3 imágenes de referencia por escena").optional()
1113
+ });
1114
+ const validationSchema = Yup__namespace.object().shape({
1115
+ aspectRatio: Yup__namespace.string().oneOf(["16:9", "9:16", "1:1"], "Aspect ratio no válido").required("El aspect ratio es obligatorio"),
1116
+ scenes: Yup__namespace.array().of(sceneSchema).min(1, "Debe haber al menos una escena").max(20, "No se permiten más de 20 escenas")
1117
+ });
1118
+ const initialValues = {
1119
+ aspectRatio: "16:9",
1120
+ scenes: [
1121
+ {
1122
+ prompt: ``,
1123
+ durationSeconds: 8,
1124
+ narrationText: ``,
1125
+ referenceImages: []
1126
+ }
1127
+ ]
1128
+ };
1129
+ const Form = ({ onSubmit }) => {
1130
+ const formik$1 = formik.useFormik({
1131
+ initialValues,
1132
+ validationSchema,
1133
+ validateOnMount: true,
1134
+ onSubmit
1135
+ });
1136
+ console.log("🚀 ~ Form ~ formik:", formik$1.errors);
1137
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", padding: 6, hasRadius: true, width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: formik$1.handleSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 4, children: [
1138
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { m: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: "aspectRatio", width: "100%", required: true, children: [
1139
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Relación de aspecto" }),
1140
+ /* @__PURE__ */ jsxRuntime.jsxs(
1141
+ designSystem.Combobox,
1142
+ {
1143
+ name: "aspectRatio",
1144
+ onChange: (value) => formik$1.setFieldValue("aspectRatio", value),
1145
+ onBlur: () => formik$1.setFieldTouched("aspectRatio", true),
1146
+ value: formik$1.values.aspectRatio,
1147
+ onClear: () => formik$1.setFieldValue("aspectRatio", ""),
1148
+ children: [
1149
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: "16:9", children: "16:9" }),
1150
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: "9:16", children: "9:16" }),
1151
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: "1:1", children: "1:1" })
1152
+ ]
1153
+ }
1154
+ ),
1155
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, { children: formik$1.errors.aspectRatio })
1156
+ ] }) }),
1157
+ formik$1.values.scenes.map((scene, index2) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { background: "neutral100", padding: 4, hasRadius: true, width: "100%", children: [
1158
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
1159
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
1160
+ "Escena ",
1161
+ index2 + 1
1162
+ ] }),
1163
+ formik$1.values.scenes.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
1164
+ designSystem.Button,
1165
+ {
1166
+ variant: "danger-light",
1167
+ onClick: () => {
1168
+ const updatedScenes = [...formik$1.values.scenes];
1169
+ updatedScenes.splice(index2, 1);
1170
+ formik$1.setFieldValue("scenes", updatedScenes);
1171
+ },
1172
+ children: "Eliminar Escena"
1173
+ }
1174
+ )
1175
+ ] }),
1176
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, children: [
1177
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: `scenes.${index2}.prompt`, width: "100%", required: true, children: [
1178
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Prompt de la escena" }),
1179
+ /* @__PURE__ */ jsxRuntime.jsx(
1180
+ designSystem.Textarea,
1181
+ {
1182
+ name: `scenes.${index2}.prompt`,
1183
+ placeholder: "Describe visualmente esta escena",
1184
+ onChange: formik$1.handleChange,
1185
+ onBlur: formik$1.handleBlur,
1186
+ value: formik$1.values.scenes[index2].prompt,
1187
+ rows: 6
1188
+ }
1189
+ )
1190
+ ] }),
1191
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: `scenes.${index2}.durationSeconds`, width: "100%", required: true, children: [
1192
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Duración (segundos)" }),
1193
+ /* @__PURE__ */ jsxRuntime.jsxs(
1194
+ designSystem.Combobox,
1195
+ {
1196
+ name: `scenes.${index2}.durationSeconds`,
1197
+ onChange: (value) => formik$1.setFieldValue(`scenes.${index2}.durationSeconds`, value),
1198
+ onBlur: formik$1.handleBlur,
1199
+ value: formik$1.values.scenes[index2].durationSeconds,
1200
+ onClear: () => formik$1.setFieldValue(`scenes.${index2}.durationSeconds`, ""),
1201
+ children: [
1202
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: 4, children: "4" }),
1203
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: 6, children: "6" }),
1204
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: 8, children: "8" })
1205
+ ]
1206
+ }
1207
+ )
1208
+ ] }),
1209
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { id: `scenes.${index2}.referenceImages`, width: "100%", children: [
1210
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Imagen(es) de referencia" }),
1211
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, width: "100%", children: [
1212
+ scene.referenceImages.map((image, imgIndex) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral200", padding: 2, hasRadius: true, width: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
1213
+ image.previewUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: image.previewUrl, alt: "preview", style: { width: 50, height: 50, objectFit: "cover" } }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 50, height: 50, backgroundColor: "#ccc" } }),
1214
+ /* @__PURE__ */ jsxRuntime.jsx(
1215
+ designSystem.IconButton,
1216
+ {
1217
+ onClick: () => {
1218
+ const imageToRemove = scene.referenceImages[imgIndex];
1219
+ if (imageToRemove.previewUrl) {
1220
+ URL.revokeObjectURL(imageToRemove.previewUrl);
1221
+ }
1222
+ const updatedImages = [...scene.referenceImages];
1223
+ updatedImages.splice(imgIndex, 1);
1224
+ formik$1.setFieldValue(`scenes.${index2}.referenceImages`, updatedImages);
1225
+ },
1226
+ label: "Eliminar imagen",
1227
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1228
+ }
1229
+ )
1230
+ ] }) }, image.id)),
1231
+ /* @__PURE__ */ jsxRuntime.jsx(
1232
+ designSystem.Button,
1233
+ {
1234
+ type: "button",
1235
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.PlusCircle, {}),
1236
+ variant: "secondary",
1237
+ fullWidth: true,
1238
+ onClick: () => {
1239
+ const input = document.createElement("input");
1240
+ input.type = "file";
1241
+ input.accept = "image/*";
1242
+ input.onchange = (e) => {
1243
+ const selectedFile = e.target.files?.[0];
1244
+ if (!selectedFile) return;
1245
+ const newImage = {
1246
+ id: Date.now(),
1247
+ file: selectedFile,
1248
+ previewUrl: URL.createObjectURL(selectedFile)
1249
+ };
1250
+ const newImages = [...scene.referenceImages, newImage];
1251
+ formik$1.setFieldValue(`scenes.${index2}.referenceImages`, newImages.slice(0, 5));
1252
+ };
1253
+ input.click();
1254
+ },
1255
+ disabled: scene.referenceImages.length >= 5,
1256
+ children: "Añadir imagen de referencia"
1257
+ }
1258
+ )
1259
+ ] })
1260
+ ] })
1261
+ ] })
1262
+ ] }, index2) }, `scene-${index2}`)),
1263
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { m: 12, children: /* @__PURE__ */ jsxRuntime.jsx(
1264
+ designSystem.Button,
1265
+ {
1266
+ type: "button",
1267
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.PlusCircle, {}),
1268
+ variant: "secondary",
1269
+ onClick: () => {
1270
+ formik$1.setFieldValue("scenes", [
1271
+ ...formik$1.values.scenes,
1272
+ { prompt: "", durationSeconds: 6, narrationText: "", referenceImages: [] }
1273
+ ]);
1274
+ },
1275
+ fullWidth: true,
1276
+ children: "Añadir Escena"
1277
+ }
1278
+ ) }),
1279
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { width: "100%", justifyContent: "end", alignItems: "end", children: /* @__PURE__ */ jsxRuntime.jsx(
1280
+ designSystem.Button,
1281
+ {
1282
+ type: "submit",
1283
+ fullWidth: true,
1284
+ disabled: !(formik$1.isValid && formik$1.dirty) || formik$1.isSubmitting,
1285
+ loading: formik$1.isSubmitting,
1286
+ children: "Generar Video"
1287
+ }
1288
+ ) }) })
1289
+ ] }) }) });
1290
+ };
1291
+ const useImages = () => {
1292
+ const { post } = admin.useFetchClient();
1293
+ const generateVideoWithScenes = react.useCallback(
1294
+ async (data, config) => {
1295
+ return await post("/strapi-content-generator-gemini/generate-video-with-scenes", data, config);
1296
+ },
1297
+ [post]
1298
+ );
1299
+ return {
1300
+ generateVideoWithScenes
1301
+ };
1302
+ };
1303
+ const GenerateVideoContainer = () => {
1304
+ const { toggleNotification } = admin.useNotification();
1305
+ const { generateVideoWithScenes } = useImages();
1306
+ const { formatMessage } = reactIntl.useIntl();
1307
+ const handleSubmit = async (values, { setSubmitting }) => {
1308
+ try {
1309
+ const formData = new FormData();
1310
+ formData.append("aspectRatio", values.aspectRatio);
1311
+ values.scenes.forEach((scene, index2) => {
1312
+ formData.append(`scenes[${index2}][prompt]`, scene.prompt);
1313
+ formData.append(`scenes[${index2}][durationSeconds]`, scene.durationSeconds.toString());
1314
+ formData.append(`scenes[${index2}][narrationText]`, scene.narrationText || "");
1315
+ scene.referenceImages.forEach((image, imgIndex) => {
1316
+ if (image.file) {
1317
+ formData.append(`scenes[${index2}][referenceImages][${imgIndex}]`, image.file);
1318
+ }
1319
+ });
1320
+ });
1321
+ const response = await generateVideoWithScenes(formData);
1322
+ console.log("🚀 ~ handleSubmit ~ response:", response);
1323
+ toggleNotification({
1324
+ type: "success",
1325
+ message: formatMessage({
1326
+ id: `${index.PLUGIN_ID}.config.load.success`,
1327
+ defaultMessage: "Video generado con éxito"
1328
+ })
1329
+ });
1330
+ } catch (error) {
1331
+ toggleNotification({
1332
+ type: "danger",
1333
+ message: formatMessage({
1334
+ id: `${index.PLUGIN_ID}.config.load.error`,
1335
+ defaultMessage: "Ocurrió un error al generar el video"
1336
+ })
1337
+ });
1338
+ } finally {
1339
+ setSubmitting(false);
1340
+ }
1341
+ };
1342
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1343
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { padding: 6, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", children: [
1344
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", children: "Generar video con IA" }),
1345
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
1346
+ ] }) }) }),
1347
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { padding: 6, children: [
1348
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { m: 2 }),
1349
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { m: 8, children: /* @__PURE__ */ jsxRuntime.jsx(Form, { onSubmit: handleSubmit }) })
1350
+ ] })
1351
+ ] });
1352
+ };
1353
+ const GenerateVideo = () => {
1354
+ return /* @__PURE__ */ jsxRuntime.jsxs(admin.Page.Main, { children: [
1355
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Title, { children: "Generar video con IA" }),
1356
+ /* @__PURE__ */ jsxRuntime.jsx(GenerateVideoContainer, {})
1357
+ ] });
1358
+ };
1359
+ const Screen = ({ title, children }) => /* @__PURE__ */ jsxRuntime.jsxs(admin.Page.Main, { children: [
1360
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Title, { children: title }),
1361
+ children
1362
+ ] });
1363
+ const App = () => {
1364
+ const { allowedActions, isLoading } = admin.useRBAC(index.pluginPermissions);
1365
+ if (isLoading) {
1366
+ return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Loading, {});
1367
+ }
1368
+ return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Protect, { permissions: [...index.pluginPermissions.access], children: /* @__PURE__ */ jsxRuntime.jsx(PluginProviders.PluginProviders, { children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
1369
+ allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(Screen, { title: "Generador de contenido", children: /* @__PURE__ */ jsxRuntime.jsx(ArticlesPage, {}) }) }),
1370
+ allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/generate-content", element: /* @__PURE__ */ jsxRuntime.jsx(Screen, { title: "Nuevo artículo", children: /* @__PURE__ */ jsxRuntime.jsx(NewArticlePage, {}) }) }),
1371
+ allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/articles/:documentId", element: /* @__PURE__ */ jsxRuntime.jsx(Screen, { title: "Artículo", children: /* @__PURE__ */ jsxRuntime.jsx(ArticlePage, {}) }) }),
1372
+ allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1373
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/image-studio", element: /* @__PURE__ */ jsxRuntime.jsx(ImageStudioTabs.StudioRedirect, {}) }),
1374
+ ImageStudioTabs.LEGACY_ROUTES.map((r) => /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: r.path, element: ImageStudioTabs.legacyRedirect(r.tab) }, r.path)),
1375
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/generate-video", element: /* @__PURE__ */ jsxRuntime.jsx(GenerateVideo, {}) })
1376
+ ] }),
1377
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
1378
+ ] }) }) });
1379
+ };
1380
+ exports.App = App;