@interware/content-generator 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/_chunks/{App-BO5dp398.mjs → App-Cbkccs2K.mjs} +585 -58
  2. package/dist/_chunks/{App-Z_jLmgEa.js → App-DCuBRwX8.js} +578 -51
  3. package/dist/_chunks/{Settings-C6a6AgQy.mjs → Settings-B60QGKK4.mjs} +48 -4
  4. package/dist/_chunks/{Settings-CT0AQ5Px.js → Settings-DuGbtyWC.js} +48 -4
  5. package/dist/_chunks/{getTranslation-CIMKEgGr.mjs → getTranslation-B97xbmEl.mjs} +1 -1
  6. package/dist/_chunks/{getTranslation-BdWuhMoN.js → getTranslation-CacX7ZX5.js} +1 -1
  7. package/dist/_chunks/{index-CMQYbwsn.mjs → index-B2RuvYul.mjs} +2 -2
  8. package/dist/_chunks/{index-CSMac09y.js → index-BVGbP3E-.js} +2 -2
  9. package/dist/admin/index.js +1 -1
  10. package/dist/admin/index.mjs +1 -1
  11. package/dist/admin/src/components/Navigation.d.ts +2 -0
  12. package/dist/admin/src/components/generate-content/Form.d.ts +1 -1
  13. package/dist/admin/src/components/generate-images/Image.d.ts +5 -0
  14. package/dist/admin/src/components/generate-images/Images.d.ts +2 -0
  15. package/dist/admin/src/components/generate-images/Modal.d.ts +9 -0
  16. package/dist/admin/src/components/home/table.d.ts +2 -1
  17. package/dist/admin/src/containers/generate-images/index.d.ts +2 -0
  18. package/dist/admin/src/containers/settings/index.d.ts +2 -0
  19. package/dist/admin/src/hooks/generate-images/useImages.d.ts +11 -0
  20. package/dist/admin/src/lib/use-cases/facebook/index.d.ts +5 -0
  21. package/dist/admin/src/lib/use-cases/images/index.d.ts +6 -0
  22. package/dist/admin/src/pages/GenerateImages.d.ts +2 -0
  23. package/dist/server/index.js +309 -147
  24. package/dist/server/index.mjs +309 -127
  25. package/dist/server/src/controllers/controller.d.ts +2 -0
  26. package/dist/server/src/controllers/index.d.ts +2 -0
  27. package/dist/server/src/index.d.ts +56 -1
  28. package/dist/server/src/services/index.d.ts +54 -1
  29. package/dist/server/src/services/service.d.ts +54 -1
  30. package/package.json +1 -2
@@ -4,12 +4,12 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const admin = require("@strapi/strapi/admin");
5
5
  const designSystem = require("@strapi/design-system");
6
6
  const reactRouterDom = require("react-router-dom");
7
+ const index = require("./index-BVGbP3E-.js");
7
8
  const reactIntl = require("react-intl");
8
9
  const react = require("react");
9
10
  const reactRedux = require("react-redux");
10
- const index = require("./index-CSMac09y.js");
11
11
  const icons = require("@strapi/icons");
12
- const getTranslation = require("./getTranslation-BdWuhMoN.js");
12
+ const getTranslation = require("./getTranslation-CacX7ZX5.js");
13
13
  const formik = require("formik");
14
14
  const Yup = require("yup");
15
15
  function _interopNamespace(e) {
@@ -30,6 +30,51 @@ function _interopNamespace(e) {
30
30
  return Object.freeze(n);
31
31
  }
32
32
  const Yup__namespace = /* @__PURE__ */ _interopNamespace(Yup);
33
+ const links = [
34
+ {
35
+ label: "Generador de contenido con IA",
36
+ required: "access",
37
+ subLinks: [
38
+ {
39
+ id: 1,
40
+ label: "Generador de contenido",
41
+ to: "/admin/plugins/strapi-content-generator-gemini",
42
+ required: "access"
43
+ },
44
+ {
45
+ id: 2,
46
+ label: "Generador de imágenes",
47
+ to: "/admin/plugins/strapi-content-generator-gemini/generate-images",
48
+ required: "access"
49
+ }
50
+ ]
51
+ }
52
+ ];
53
+ const Navigation = () => {
54
+ const location = reactRouterDom.useLocation();
55
+ const { allowedActions, isLoading } = admin.useRBAC(index.pluginPermissions);
56
+ if (isLoading) return null;
57
+ const canAccess = (required) => {
58
+ if (!required) return true;
59
+ if (required === "access") return allowedActions.canAccess;
60
+ return false;
61
+ };
62
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SubNav, { "aria-label": "Generador de contenido SubNav", children: [
63
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SubNavHeader, { label: "MKT" }),
64
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SubNavSections, { children: links.filter((section) => canAccess(section.required)).map(({ label, subLinks }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SubNavSection, { label, children: subLinks.filter((subLink) => canAccess(subLink.required)).map((subLink) => {
65
+ const active = location.pathname === subLink.to.replace(/^\/admin/, "");
66
+ return /* @__PURE__ */ jsxRuntime.jsx(
67
+ designSystem.SubNavLink,
68
+ {
69
+ href: subLink.to,
70
+ active,
71
+ children: subLink.label
72
+ },
73
+ subLink.id
74
+ );
75
+ }) }, label)) })
76
+ ] });
77
+ };
33
78
  const useDialog = (id) => {
34
79
  const dispatch = reactRedux.useDispatch();
35
80
  const state = reactRedux.useSelector((state2) => state2.content_generator_commonDialog.open[id] ?? false);
@@ -51,17 +96,48 @@ const useArticles = () => {
51
96
  getArticles
52
97
  };
53
98
  };
99
+ const REQUEST_TIMEOUT = 12e4;
54
100
  const useGenerateContent = () => {
55
101
  const { post } = admin.useFetchClient();
56
102
  const generateContent = react.useCallback(
57
103
  async (data, config) => {
58
- return post("/strapi-content-generator-gemini/generate-article", data, config);
104
+ const controller = new AbortController();
105
+ const timeoutId = setTimeout(() => {
106
+ controller.abort();
107
+ }, REQUEST_TIMEOUT);
108
+ try {
109
+ return await post(
110
+ "/strapi-content-generator-gemini/generate-article",
111
+ data,
112
+ {
113
+ ...config,
114
+ signal: controller.signal
115
+ }
116
+ );
117
+ } finally {
118
+ clearTimeout(timeoutId);
119
+ }
59
120
  },
60
121
  [post]
61
122
  );
62
123
  const generateImage = react.useCallback(
63
124
  async (data, config) => {
64
- return post("/strapi-content-generator-gemini/generate-image", data, config);
125
+ const controller = new AbortController();
126
+ const timeoutId = setTimeout(() => {
127
+ controller.abort();
128
+ }, REQUEST_TIMEOUT);
129
+ try {
130
+ return await post(
131
+ "/strapi-content-generator-gemini/generate-image",
132
+ data,
133
+ {
134
+ ...config,
135
+ signal: controller.signal
136
+ }
137
+ );
138
+ } finally {
139
+ clearTimeout(timeoutId);
140
+ }
65
141
  },
66
142
  [post]
67
143
  );
@@ -82,11 +158,24 @@ const usePublishLinkedIn = () => {
82
158
  publishToLinkedIn
83
159
  };
84
160
  };
161
+ const usePublishFacebook = () => {
162
+ const { post } = admin.useFetchClient();
163
+ const publishToFacebook = react.useCallback(
164
+ async (data, config) => {
165
+ return post("/strapi-content-generator-gemini/publish-facebook", data, config);
166
+ },
167
+ [post]
168
+ );
169
+ return {
170
+ publishToFacebook
171
+ };
172
+ };
85
173
  const PublishingTableComponent = ({
86
174
  posts,
87
175
  currentPost,
88
176
  loadingImage,
89
177
  loadingPublishLinkedIn,
178
+ loadingPublishingToFacebook,
90
179
  onClickAction
91
180
  }) => {
92
181
  const rows = react.useMemo(() => {
@@ -101,8 +190,8 @@ const PublishingTableComponent = ({
101
190
  id: post.documentId,
102
191
  fecha,
103
192
  titulo: post.titulo && post.titulo.length > 30 ? `${post.titulo.slice(0, 30)}…` : post.titulo,
104
- autor: `${post.autor?.nombre ?? ""} ${post.autor?.apellidos ?? ""}`,
105
- linkedInURL: post.linkedInPostId,
193
+ linkedInURL: post.linkedInPostId ? `https://www.linkedin.com/feed/update/${post.linkedInPostId}` : "",
194
+ facebookURL: post.facebookPostId ? `https://www.facebook.com/${post.facebookPostId}` : "",
106
195
  hasImage: Boolean(post.image)
107
196
  };
108
197
  });
@@ -111,29 +200,60 @@ const PublishingTableComponent = ({
111
200
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
112
201
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Fecha de publicación" }) }),
113
202
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Blog Title" }) }),
114
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Autor" }) }),
115
203
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", justifyContent: "center", children: [
116
204
  /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fa-brands fa-linkedin" }),
117
205
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "LinkedIn" })
118
206
  ] }) }),
207
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", justifyContent: "center", children: [
208
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fa-brands fa-facebook" }),
209
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Facebook" })
210
+ ] }) }),
119
211
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, {}),
120
212
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, {})
121
213
  ] }) }),
122
214
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rows.map((row) => {
123
- const buttonLoadingImage = loadingImage && currentPost.current === row.id;
124
- const buttonLoadingPublishingLinkediN = loadingPublishLinkedIn && currentPost.current === row.id;
215
+ const buttonLoadingImage = (loadingImage && currentPost.current) === row.id;
216
+ const buttonLoadingPublishingLinkedIn = (loadingPublishLinkedIn && currentPost.current) === row.id;
217
+ const buttonLoadingPublishingFacebook = (loadingPublishingToFacebook && currentPost.current) === row.id;
125
218
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
126
219
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: row.fecha }) }),
127
220
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: row.titulo }) }),
128
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: row.autor }) }),
129
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
221
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: !row.linkedInURL ? /* @__PURE__ */ jsxRuntime.jsx(
130
222
  designSystem.Button,
131
223
  {
132
224
  disabled: row.linkedInURL,
133
225
  onClick: () => onClickAction(row.id, "publish-to-linked-in-dialog"),
134
- loading: buttonLoadingPublishingLinkediN,
226
+ loading: buttonLoadingPublishingLinkedIn,
227
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
228
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: buttonLoadingPublishingLinkedIn ? "publicando" : "publicar" })
229
+ }
230
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
231
+ designSystem.LinkButton,
232
+ {
233
+ variant: "success-light",
234
+ href: row.linkedInURL,
235
+ isExternal: true,
236
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}),
237
+ children: "visitar"
238
+ }
239
+ ) }),
240
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: !row.facebookURL ? /* @__PURE__ */ jsxRuntime.jsx(
241
+ designSystem.Button,
242
+ {
243
+ disabled: row.facebookURL,
244
+ onClick: () => onClickAction(row.id, "publish-to-facebook-dialog"),
245
+ loading: buttonLoadingPublishingFacebook,
135
246
  startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
136
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: buttonLoadingImage ? "publicando" : "publicar" })
247
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: buttonLoadingPublishingFacebook ? "publicando" : "publicar" })
248
+ }
249
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
250
+ designSystem.LinkButton,
251
+ {
252
+ variant: "success-light",
253
+ href: row.facebookURL,
254
+ isExternal: true,
255
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}),
256
+ children: "visitar"
137
257
  }
138
258
  ) }),
139
259
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -227,13 +347,16 @@ const HomeContainer = () => {
227
347
  const [posts, setPosts] = react.useState([]);
228
348
  const [loadingImage, setIsLoadingImage] = react.useState(false);
229
349
  const [loadingPublishingToLinkedIn, setIsLoadingPublishingToLinkedIn] = react.useState(false);
350
+ const [loadingPublishingToFacebook, setIsLoadingPublishingToFacebook] = react.useState(false);
230
351
  const currentPostRef = react.useRef("");
231
352
  const { formatMessage } = reactIntl.useIntl();
232
353
  const { openDialog: openImageGenerateDialog, closeDialog: closeImageGenerateDialog } = useDialog("generate-image-dialog");
233
354
  const { openDialog: openPublishToLinkedInDialog, closeDialog: closePublishToLinkedInDialog } = useDialog("publish-to-linked-in-dialog");
355
+ const { openDialog: openPublishToFacebookDialog, closeDialog: closePublishToFacebookDialog } = useDialog("publish-to-facebook-dialog");
234
356
  const { getArticles } = useArticles();
235
357
  const { generateImage } = useGenerateContent();
236
358
  const { publishToLinkedIn } = usePublishLinkedIn();
359
+ const { publishToFacebook } = usePublishFacebook();
237
360
  const { toggleNotification } = admin.useNotification();
238
361
  const fetchPosts = react.useCallback(async () => {
239
362
  let isMounted = true;
@@ -291,33 +414,65 @@ const HomeContainer = () => {
291
414
  },
292
415
  [generateImage, closeImageGenerateDialog, setIsLoadingImage]
293
416
  );
294
- const onConfirmPublishToLinedIn = react.useCallback(
295
- async () => {
296
- closePublishToLinkedInDialog();
297
- setIsLoadingPublishingToLinkedIn(true);
417
+ const onConfirmPublish = react.useCallback(
418
+ async (type) => {
419
+ const isLinkedIn = type === "linkedin";
420
+ const isFacebook = type === "facebook";
421
+ if (isLinkedIn) {
422
+ closePublishToLinkedInDialog();
423
+ setIsLoadingPublishingToLinkedIn(true);
424
+ }
425
+ if (isFacebook) {
426
+ closePublishToFacebookDialog();
427
+ setIsLoadingPublishingToFacebook(true);
428
+ }
298
429
  try {
299
- await publishToLinkedIn({ articleId: currentPostRef.current });
430
+ switch (type) {
431
+ case "linkedin":
432
+ await publishToLinkedIn({ articleId: currentPostRef.current });
433
+ break;
434
+ case "facebook":
435
+ await publishToFacebook({ articleId: currentPostRef.current });
436
+ break;
437
+ default:
438
+ return;
439
+ }
300
440
  fetchPosts();
301
441
  toggleNotification({
302
442
  type: "success",
303
443
  message: formatMessage({
304
- id: `${index.PLUGIN_ID}.config.load.error`,
305
- defaultMessage: "Artículo publicado en LinkedIn con éxito"
444
+ id: `${index.PLUGIN_ID}.config.publish.success.${type}`,
445
+ defaultMessage: type === "linkedin" ? "Artículo publicado en LinkedIn con éxito" : "Artículo publicado en Facebook con éxito"
306
446
  })
307
447
  });
308
448
  } catch (err) {
309
449
  toggleNotification({
310
450
  type: "danger",
311
451
  message: formatMessage({
312
- id: `${index.PLUGIN_ID}.config.load.error`,
313
- defaultMessage: "Ocurrió un error al publicar el artículo en LinkedIn"
452
+ id: `${index.PLUGIN_ID}.config.publish.error.${type}`,
453
+ defaultMessage: type === "linkedin" ? "Ocurrió un error al publicar el artículo en LinkedIn" : "Ocurrió un error al publicar el artículo en Facebook"
314
454
  })
315
455
  });
316
456
  } finally {
317
- setIsLoadingPublishingToLinkedIn(false);
457
+ if (isLinkedIn) {
458
+ setIsLoadingPublishingToLinkedIn(false);
459
+ }
460
+ if (isFacebook) {
461
+ setIsLoadingPublishingToFacebook(false);
462
+ }
318
463
  }
319
464
  },
320
- [publishToLinkedIn, closeImageGenerateDialog, setIsLoadingPublishingToLinkedIn]
465
+ [
466
+ publishToLinkedIn,
467
+ publishToFacebook,
468
+ closePublishToLinkedInDialog,
469
+ closePublishToFacebookDialog,
470
+ setIsLoadingPublishingToLinkedIn,
471
+ setIsLoadingPublishingToFacebook,
472
+ fetchPosts,
473
+ toggleNotification,
474
+ formatMessage
475
+ ]
321
476
  );
322
477
  const onCancel = react.useCallback((dialogId) => {
323
478
  currentPostRef.current = "";
@@ -328,10 +483,13 @@ const HomeContainer = () => {
328
483
  case "publish-to-linked-in-dialog":
329
484
  closePublishToLinkedInDialog();
330
485
  break;
486
+ case "publish-to-facebook-dialog":
487
+ closePublishToFacebookDialog();
488
+ break;
331
489
  default:
332
490
  return;
333
491
  }
334
- }, [closeImageGenerateDialog, closePublishToLinkedInDialog]);
492
+ }, [closeImageGenerateDialog, closePublishToLinkedInDialog, closePublishToFacebookDialog]);
335
493
  const onClickAction = react.useCallback(
336
494
  (postId, dialogId) => {
337
495
  currentPostRef.current = postId;
@@ -342,9 +500,12 @@ const HomeContainer = () => {
342
500
  case "publish-to-linked-in-dialog":
343
501
  openPublishToLinkedInDialog();
344
502
  break;
503
+ case "publish-to-facebook-dialog":
504
+ openPublishToFacebookDialog();
505
+ break;
345
506
  }
346
507
  },
347
- [openImageGenerateDialog, openPublishToLinkedInDialog]
508
+ [openImageGenerateDialog, openPublishToLinkedInDialog, openPublishToFacebookDialog]
348
509
  );
349
510
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
350
511
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -354,13 +515,27 @@ const HomeContainer = () => {
354
515
  title: "Publicar en LinkedIn",
355
516
  confirmText: "Publicar",
356
517
  onCancelAction: () => onCancel("publish-to-linked-in-dialog"),
357
- onConfirmAction: onConfirmPublishToLinedIn,
518
+ onConfirmAction: () => onConfirmPublish("linkedin"),
358
519
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", children: [
359
520
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: "¿Seguro que deseas publicar este artículo en LinkedIn?" }),
360
521
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "¡Esta acción es irreversible desde esta plataforma!" })
361
522
  ] })
362
523
  }
363
524
  ),
525
+ /* @__PURE__ */ jsxRuntime.jsx(
526
+ DialogCommon,
527
+ {
528
+ id: "publish-to-facebook-dialog",
529
+ title: "Publicar en Facebook",
530
+ confirmText: "Publicar",
531
+ onCancelAction: () => onCancel("publish-to-facebook-dialog"),
532
+ onConfirmAction: () => onConfirmPublish("facebook"),
533
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", children: [
534
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: "¿Seguro que deseas publicar este artículo en Facebook?" }),
535
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "¡Esta acción es irreversible desde esta plataforma!" })
536
+ ] })
537
+ }
538
+ ),
364
539
  /* @__PURE__ */ jsxRuntime.jsx(GenerateImageDialog, { onConfirmAction: onConfirmGenerateImage, onCancelAction: () => onCancel("generate-image-dialog") }),
365
540
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { padding: 6, children: [
366
541
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", children: [
@@ -374,6 +549,7 @@ const HomeContainer = () => {
374
549
  posts,
375
550
  loadingImage,
376
551
  loadingPublishLinkedIn: loadingPublishingToLinkedIn,
552
+ loadingPublishingToFacebook,
377
553
  currentPost: currentPostRef,
378
554
  onClickAction
379
555
  }
@@ -416,14 +592,14 @@ const validationSchema = Yup__namespace.object().shape({
416
592
  titulo: Yup__namespace.string().notRequired(),
417
593
  autor: Yup__namespace.string().required("Este campo es obligatorio"),
418
594
  categoria: Yup__namespace.string().required("Este campo es obligatorio"),
419
- keywords: Yup__namespace.string().notRequired(),
595
+ temperatura: Yup__namespace.string().notRequired(),
420
596
  instrucciones: Yup__namespace.string().notRequired()
421
597
  });
422
598
  const initialValues = {
423
599
  titulo: "",
424
600
  autor: "",
425
601
  categoria: "",
426
- keywords: "",
602
+ temperatura: "0.2",
427
603
  instrucciones: ""
428
604
  };
429
605
  const Form = ({ onSubmit, categorias, users }) => {
@@ -514,25 +690,30 @@ const Form = ({ onSubmit, categorias, users }) => {
514
690
  ]
515
691
  }
516
692
  ) }),
517
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs(
693
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { xs: 12, s: 12, m: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(
518
694
  designSystem.Field.Root,
519
695
  {
520
- id: "keywords",
521
- error: touched.keywords && errors.keywords ? errors.keywords : void 0,
696
+ id: "temperatura",
697
+ error: touched.temperatura && errors.temperatura ? errors.temperatura : void 0,
522
698
  width: "100%",
523
- hint: "Ej: Innovación, Desarrollo, AI",
524
699
  children: [
525
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Palabras clave" }),
526
- /* @__PURE__ */ jsxRuntime.jsx(
527
- designSystem.TextInput,
528
- {
529
- name: "keywords",
530
- id: "keywords",
531
- placeholder: "Palabras clave separadas por comas",
532
- onChange: handleChange,
533
- value: values.keywords
534
- }
535
- ),
700
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Temperatura" }),
701
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", gap: 4, children: [
702
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
703
+ "input",
704
+ {
705
+ name: "temperatura",
706
+ value: values.temperatura,
707
+ type: "range",
708
+ min: "0",
709
+ max: "2",
710
+ step: "0.05",
711
+ onChange: handleChange,
712
+ style: { width: "100%" }
713
+ }
714
+ ) }),
715
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100px", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Input, { value: values.temperatura, readOnly: true }) })
716
+ ] }),
536
717
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
537
718
  ]
538
719
  }
@@ -552,7 +733,8 @@ const Form = ({ onSubmit, categorias, users }) => {
552
733
  placeholder: "Escribe algunas instrucciones adicionales para el generador de contenido...",
553
734
  onChange: handleChange,
554
735
  value: values.instrucciones,
555
- rows: 8
736
+ resizable: false,
737
+ rows: 10
556
738
  }
557
739
  )
558
740
  ]
@@ -659,7 +841,6 @@ const GenerateContentContainer = () => {
659
841
  }
660
842
  }
661
843
  });
662
- console.log("🚀 ~ GenerateContentContainer ~ data:", data.length);
663
844
  if (isMounted) {
664
845
  setUsers(data);
665
846
  }
@@ -692,15 +873,361 @@ const GenerateContent = () => {
692
873
  /* @__PURE__ */ jsxRuntime.jsx(GenerateContentContainer, {})
693
874
  ] });
694
875
  };
876
+ const useImages$1 = () => {
877
+ const { get, del } = admin.useFetchClient();
878
+ const getImages = react.useCallback(
879
+ async () => {
880
+ return await get("/strapi-content-generator-gemini/images");
881
+ },
882
+ [get]
883
+ );
884
+ const deleteImage = react.useCallback(
885
+ async (config) => {
886
+ return await del("/strapi-content-generator-gemini/delete-image", config);
887
+ },
888
+ [del]
889
+ );
890
+ return {
891
+ getImages,
892
+ deleteImage
893
+ };
894
+ };
895
+ const PRINCIPAL_KEY = "principal";
896
+ const ModalImagesSizes = ({
897
+ open,
898
+ setOpen,
899
+ image,
900
+ handleDownload
901
+ }) => {
902
+ const [selectedImages, setSelectedImages] = react.useState(
903
+ () => /* @__PURE__ */ new Set()
904
+ );
905
+ const formats = react.useMemo(
906
+ () => Object.entries(image?.formats ?? {}),
907
+ [image?.formats]
908
+ );
909
+ const toggleImageSelection = react.useCallback((key) => {
910
+ setSelectedImages((prev) => {
911
+ const next = new Set(prev);
912
+ next.has(key) ? next.delete(key) : next.add(key);
913
+ return next;
914
+ });
915
+ }, []);
916
+ const isPrincipalSelected = react.useMemo(
917
+ () => selectedImages.has(PRINCIPAL_KEY),
918
+ [selectedImages]
919
+ );
920
+ const isDownloadDisabled = react.useMemo(
921
+ () => selectedImages.size === 0,
922
+ [selectedImages]
923
+ );
924
+ const handleClose = react.useCallback(() => {
925
+ setOpen(false);
926
+ }, [setOpen]);
927
+ react.useEffect(() => {
928
+ if (!open) {
929
+ setSelectedImages(/* @__PURE__ */ new Set());
930
+ }
931
+ }, [open]);
932
+ if (!image) return null;
933
+ return /* @__PURE__ */ jsxRuntime.jsx(
934
+ designSystem.Modal.Root,
935
+ {
936
+ id: "modal-image-formats",
937
+ open,
938
+ onOpenChange: handleClose,
939
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
940
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: "Descargar imágenes" }) }),
941
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 3, children: [
942
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { m: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(
943
+ designSystem.Card,
944
+ {
945
+ id: "modal-principal",
946
+ onClick: () => toggleImageSelection(PRINCIPAL_KEY),
947
+ children: [
948
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardHeader, { children: [
949
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardCheckbox, { checked: isPrincipalSelected }),
950
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardAsset, { src: image.url })
951
+ ] }),
952
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardBody, { children: [
953
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardContent, { children: [
954
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardTitle, { children: "Principal" }),
955
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardSubtitle, { children: `${image.width}x${image.height}` })
956
+ ] }),
957
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardBadge, { children: image.mime })
958
+ ] })
959
+ ]
960
+ }
961
+ ) }),
962
+ formats.map(([format, img]) => {
963
+ const isSelected = selectedImages.has(format);
964
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { m: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(
965
+ designSystem.Card,
966
+ {
967
+ id: `modal-${format}`,
968
+ onClick: () => toggleImageSelection(format),
969
+ children: [
970
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardHeader, { children: [
971
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardCheckbox, { checked: isSelected }),
972
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardAsset, { src: img.url })
973
+ ] }),
974
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardBody, { children: [
975
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardContent, { children: [
976
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardTitle, { children: format }),
977
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardSubtitle, { children: `${img.width}x${img.height}` })
978
+ ] }),
979
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardBadge, { children: image.mime })
980
+ ] })
981
+ ]
982
+ }
983
+ ) }, format);
984
+ })
985
+ ] }) }),
986
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
987
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Close, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: handleClose, children: "Cancelar" }) }),
988
+ /* @__PURE__ */ jsxRuntime.jsx(
989
+ designSystem.Button,
990
+ {
991
+ disabled: isDownloadDisabled,
992
+ onClick: () => {
993
+ handleDownload(selectedImages);
994
+ },
995
+ children: "Descargar"
996
+ }
997
+ )
998
+ ] })
999
+ ] })
1000
+ }
1001
+ );
1002
+ };
1003
+ const ImagesContext = react.createContext(void 0);
1004
+ const ImagesProvider = ({ children }) => {
1005
+ const { getImages, deleteImage } = useImages$1();
1006
+ const { openDialog, closeDialog } = useDialog("dialog-delete-image");
1007
+ const { formatMessage } = reactIntl.useIntl();
1008
+ const { toggleNotification } = admin.useNotification();
1009
+ const [images, setImages] = react.useState([]);
1010
+ const [loading, setLoading] = react.useState(false);
1011
+ const [open, setOpen] = react.useState(false);
1012
+ const currentImageRef = react.useRef();
1013
+ const fetchImages = react.useCallback(async () => {
1014
+ let isMounted = true;
1015
+ try {
1016
+ const { data } = await getImages();
1017
+ if (isMounted) {
1018
+ setImages(data);
1019
+ }
1020
+ } catch (error) {
1021
+ console.error("Error fetching images:", error);
1022
+ }
1023
+ return () => {
1024
+ isMounted = false;
1025
+ };
1026
+ }, [getImages]);
1027
+ const handleDeleteImage = async () => {
1028
+ closeDialog();
1029
+ try {
1030
+ await deleteImage({ params: { documentId: currentImageRef.current?.documentId } });
1031
+ toggleNotification({
1032
+ type: "success",
1033
+ message: formatMessage({
1034
+ id: `${index.PLUGIN_ID}.config.load.error`,
1035
+ defaultMessage: "Imagen eliminada con éxito"
1036
+ })
1037
+ });
1038
+ fetchImages();
1039
+ } catch (err) {
1040
+ toggleNotification({
1041
+ type: "danger",
1042
+ message: formatMessage({
1043
+ id: `${index.PLUGIN_ID}.config.load.error`,
1044
+ defaultMessage: "Ocurrió un error al eliminar la imagen"
1045
+ })
1046
+ });
1047
+ }
1048
+ };
1049
+ const onCancelAction = react.useCallback((action) => {
1050
+ switch (action) {
1051
+ case "delete":
1052
+ closeDialog();
1053
+ break;
1054
+ case "download":
1055
+ setOpen(false);
1056
+ break;
1057
+ default:
1058
+ return;
1059
+ }
1060
+ currentImageRef.current = void 0;
1061
+ closeDialog();
1062
+ }, [openDialog]);
1063
+ const onClickAction = react.useCallback((image, action) => {
1064
+ currentImageRef.current = image;
1065
+ switch (action) {
1066
+ case "delete":
1067
+ openDialog();
1068
+ break;
1069
+ case "download":
1070
+ setOpen(true);
1071
+ break;
1072
+ default:
1073
+ return;
1074
+ }
1075
+ }, [openDialog, setOpen]);
1076
+ const downloadImage = async (url, filename) => {
1077
+ const response = await fetch(url);
1078
+ const blob = await response.blob();
1079
+ const objectUrl = URL.createObjectURL(blob);
1080
+ const link = document.createElement("a");
1081
+ link.href = objectUrl;
1082
+ link.download = filename;
1083
+ document.body.appendChild(link);
1084
+ link.click();
1085
+ document.body.removeChild(link);
1086
+ URL.revokeObjectURL(objectUrl);
1087
+ };
1088
+ const handleDownload = async (selectedKeys) => {
1089
+ const image = currentImageRef.current;
1090
+ if (!image) return;
1091
+ setOpen(false);
1092
+ const keys = Array.isArray(selectedKeys) ? selectedKeys : Array.from(selectedKeys);
1093
+ const extension = image.ext?.replace(".", "") ?? "jpg";
1094
+ const formats = image.formats;
1095
+ const items = keys.map((key) => {
1096
+ if (key === "principal") {
1097
+ return {
1098
+ url: image.url,
1099
+ filename: `image-principal-${image.documentId}.${extension}`
1100
+ };
1101
+ }
1102
+ const formatImage = formats?.[key];
1103
+ if (!formatImage) return null;
1104
+ return {
1105
+ url: formatImage.url,
1106
+ filename: `image-${key}-${image.documentId}.${extension}`
1107
+ };
1108
+ }).filter(Boolean);
1109
+ for (const item of items) {
1110
+ await downloadImage(item.url, item.filename);
1111
+ }
1112
+ };
1113
+ react.useEffect(() => {
1114
+ fetchImages();
1115
+ }, [fetchImages]);
1116
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1117
+ currentImageRef.current && /* @__PURE__ */ jsxRuntime.jsx(
1118
+ ModalImagesSizes,
1119
+ {
1120
+ open,
1121
+ setOpen,
1122
+ image: currentImageRef.current,
1123
+ handleDownload
1124
+ }
1125
+ ),
1126
+ /* @__PURE__ */ jsxRuntime.jsx(
1127
+ DialogCommon,
1128
+ {
1129
+ title: "Eliminar imagen",
1130
+ id: "dialog-delete-image",
1131
+ onConfirmAction: handleDeleteImage,
1132
+ onCancelAction: () => onCancelAction("delete"),
1133
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", justifyContent: "center", children: [
1134
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: "¿Seguro que deseas eliminar esta imagen?" }),
1135
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "Esta acción es irreversible" })
1136
+ ] })
1137
+ }
1138
+ ),
1139
+ /* @__PURE__ */ jsxRuntime.jsx(ImagesContext.Provider, { value: { images, onClickAction }, children })
1140
+ ] });
1141
+ };
1142
+ const useImages = () => {
1143
+ const context = react.useContext(ImagesContext);
1144
+ if (!context) {
1145
+ throw new Error("useImages must be used within an ImagesProvider");
1146
+ }
1147
+ return context;
1148
+ };
1149
+ const Image = ({ image }) => {
1150
+ const { onClickAction } = useImages();
1151
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Card, { id: image.id, children: [
1152
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardHeader, { children: [
1153
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardAction, { position: "end", children: [
1154
+ /* @__PURE__ */ jsxRuntime.jsx(
1155
+ designSystem.IconButton,
1156
+ {
1157
+ label: "Descargar",
1158
+ onClick: () => {
1159
+ onClickAction(image, "download");
1160
+ },
1161
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {})
1162
+ }
1163
+ ),
1164
+ /* @__PURE__ */ jsxRuntime.jsx(
1165
+ designSystem.IconButton,
1166
+ {
1167
+ label: "Eliminar",
1168
+ onClick: () => {
1169
+ onClickAction(image, "delete");
1170
+ },
1171
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1172
+ }
1173
+ )
1174
+ ] }),
1175
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardAsset, { src: image.url, style: { borderTopLeftRadius: "4px", borderTopRightRadius: "4px" } })
1176
+ ] }),
1177
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardBody, { children: [
1178
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.CardContent, { children: [
1179
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardTitle, { children: `${image.caption?.replace("generada por IA", "").slice(0, 80)}...` }),
1180
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardSubtitle, { children: `${image.width}X${image.height}` })
1181
+ ] }),
1182
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.CardBadge, { children: image.mime })
1183
+ ] })
1184
+ ] });
1185
+ };
1186
+ const Images = () => {
1187
+ const { images } = useImages();
1188
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", padding: 6, margin: 6, width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { gap: 3, children: images.map((image) => {
1189
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { m: 3, children: /* @__PURE__ */ jsxRuntime.jsx(Image, { image }) }, image.id);
1190
+ }) }) });
1191
+ };
1192
+ const GenerateImagesContainer = () => {
1193
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(ImagesProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { padding: 6, children: [
1194
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", children: [
1195
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", children: "Generar imagenes con IA" }),
1196
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
1197
+ ] }) }),
1198
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { children: /* @__PURE__ */ jsxRuntime.jsx(Images, {}) })
1199
+ ] }) }) });
1200
+ };
1201
+ const GenerateImages = () => {
1202
+ return /* @__PURE__ */ jsxRuntime.jsxs(admin.Page.Main, { children: [
1203
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Title, { children: "Generar imagenes con IA" }),
1204
+ /* @__PURE__ */ jsxRuntime.jsx(GenerateImagesContainer, {})
1205
+ ] });
1206
+ };
695
1207
  const App = () => {
696
1208
  const { allowedActions, isLoading } = admin.useRBAC(index.pluginPermissions);
697
1209
  if (isLoading) {
698
1210
  return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Loading, {});
699
1211
  }
700
- return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Protect, { permissions: [...index.pluginPermissions.access], children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.DesignSystemProvider, { locale: "es-MX", children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
701
- allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
702
- allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/generate-content", element: /* @__PURE__ */ jsxRuntime.jsx(GenerateContent, {}) }),
703
- /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
704
- ] }) }) });
1212
+ return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Protect, { permissions: [...index.pluginPermissions.access], children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.DesignSystemProvider, { locale: "es-MX", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
1213
+ "div",
1214
+ {
1215
+ style: {
1216
+ display: "grid",
1217
+ gridTemplateColumns: "auto 1fr",
1218
+ width: "100%",
1219
+ height: "100vh"
1220
+ },
1221
+ children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1222
+ /* @__PURE__ */ jsxRuntime.jsx(Navigation, {}),
1223
+ /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
1224
+ allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
1225
+ allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/generate-content", element: /* @__PURE__ */ jsxRuntime.jsx(GenerateContent, {}) }),
1226
+ allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/generate-images", element: /* @__PURE__ */ jsxRuntime.jsx(GenerateImages, {}) }),
1227
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
1228
+ ] })
1229
+ ] })
1230
+ }
1231
+ ) }) }) });
705
1232
  };
706
1233
  exports.App = App;