@interware/content-generator 0.1.4 → 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-KtJGFVFQ.mjs → App-Cbkccs2K.mjs} +552 -55
  2. package/dist/_chunks/{App-Bt1yydY9.js → App-DCuBRwX8.js} +545 -48
  3. package/dist/_chunks/{Settings-CFag78zw.mjs → Settings-B60QGKK4.mjs} +48 -4
  4. package/dist/_chunks/{Settings-BbIpY74m.js → Settings-DuGbtyWC.js} +48 -4
  5. package/dist/_chunks/{getTranslation-BcuPOeW7.mjs → getTranslation-B97xbmEl.mjs} +1 -1
  6. package/dist/_chunks/{getTranslation-DA3AxQPb.js → getTranslation-CacX7ZX5.js} +1 -1
  7. package/dist/_chunks/{index-B6aEv34B.mjs → index-B2RuvYul.mjs} +2 -2
  8. package/dist/_chunks/{index-JZA3FSFH.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-JZA3FSFH.js");
11
11
  const icons = require("@strapi/icons");
12
- const getTranslation = require("./getTranslation-DA3AxQPb.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);
@@ -113,11 +158,24 @@ const usePublishLinkedIn = () => {
113
158
  publishToLinkedIn
114
159
  };
115
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
+ };
116
173
  const PublishingTableComponent = ({
117
174
  posts,
118
175
  currentPost,
119
176
  loadingImage,
120
177
  loadingPublishLinkedIn,
178
+ loadingPublishingToFacebook,
121
179
  onClickAction
122
180
  }) => {
123
181
  const rows = react.useMemo(() => {
@@ -132,8 +190,8 @@ const PublishingTableComponent = ({
132
190
  id: post.documentId,
133
191
  fecha,
134
192
  titulo: post.titulo && post.titulo.length > 30 ? `${post.titulo.slice(0, 30)}…` : post.titulo,
135
- autor: `${post.autor?.nombre ?? ""} ${post.autor?.apellidos ?? ""}`,
136
- 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}` : "",
137
195
  hasImage: Boolean(post.image)
138
196
  };
139
197
  });
@@ -142,29 +200,60 @@ const PublishingTableComponent = ({
142
200
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
143
201
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Fecha de publicación" }) }),
144
202
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Blog Title" }) }),
145
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "Autor" }) }),
146
203
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", justifyContent: "center", children: [
147
204
  /* @__PURE__ */ jsxRuntime.jsx("i", { className: "fa-brands fa-linkedin" }),
148
205
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: "LinkedIn" })
149
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
+ ] }) }),
150
211
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, {}),
151
212
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, {})
152
213
  ] }) }),
153
214
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rows.map((row) => {
154
- const buttonLoadingImage = loadingImage && currentPost.current === row.id;
155
- 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;
156
218
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
157
219
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: row.fecha }) }),
158
220
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: row.titulo }) }),
159
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: row.autor }) }),
160
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
221
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: !row.linkedInURL ? /* @__PURE__ */ jsxRuntime.jsx(
161
222
  designSystem.Button,
162
223
  {
163
224
  disabled: row.linkedInURL,
164
225
  onClick: () => onClickAction(row.id, "publish-to-linked-in-dialog"),
165
- loading: buttonLoadingPublishingLinkediN,
226
+ loading: buttonLoadingPublishingLinkedIn,
166
227
  startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
167
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: buttonLoadingImage ? "publicando" : "publicar" })
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,
246
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Play, {}),
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"
168
257
  }
169
258
  ) }),
170
259
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -258,13 +347,16 @@ const HomeContainer = () => {
258
347
  const [posts, setPosts] = react.useState([]);
259
348
  const [loadingImage, setIsLoadingImage] = react.useState(false);
260
349
  const [loadingPublishingToLinkedIn, setIsLoadingPublishingToLinkedIn] = react.useState(false);
350
+ const [loadingPublishingToFacebook, setIsLoadingPublishingToFacebook] = react.useState(false);
261
351
  const currentPostRef = react.useRef("");
262
352
  const { formatMessage } = reactIntl.useIntl();
263
353
  const { openDialog: openImageGenerateDialog, closeDialog: closeImageGenerateDialog } = useDialog("generate-image-dialog");
264
354
  const { openDialog: openPublishToLinkedInDialog, closeDialog: closePublishToLinkedInDialog } = useDialog("publish-to-linked-in-dialog");
355
+ const { openDialog: openPublishToFacebookDialog, closeDialog: closePublishToFacebookDialog } = useDialog("publish-to-facebook-dialog");
265
356
  const { getArticles } = useArticles();
266
357
  const { generateImage } = useGenerateContent();
267
358
  const { publishToLinkedIn } = usePublishLinkedIn();
359
+ const { publishToFacebook } = usePublishFacebook();
268
360
  const { toggleNotification } = admin.useNotification();
269
361
  const fetchPosts = react.useCallback(async () => {
270
362
  let isMounted = true;
@@ -322,33 +414,65 @@ const HomeContainer = () => {
322
414
  },
323
415
  [generateImage, closeImageGenerateDialog, setIsLoadingImage]
324
416
  );
325
- const onConfirmPublishToLinedIn = react.useCallback(
326
- async () => {
327
- closePublishToLinkedInDialog();
328
- 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
+ }
329
429
  try {
330
- 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
+ }
331
440
  fetchPosts();
332
441
  toggleNotification({
333
442
  type: "success",
334
443
  message: formatMessage({
335
- id: `${index.PLUGIN_ID}.config.load.error`,
336
- 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"
337
446
  })
338
447
  });
339
448
  } catch (err) {
340
449
  toggleNotification({
341
450
  type: "danger",
342
451
  message: formatMessage({
343
- id: `${index.PLUGIN_ID}.config.load.error`,
344
- 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"
345
454
  })
346
455
  });
347
456
  } finally {
348
- setIsLoadingPublishingToLinkedIn(false);
457
+ if (isLinkedIn) {
458
+ setIsLoadingPublishingToLinkedIn(false);
459
+ }
460
+ if (isFacebook) {
461
+ setIsLoadingPublishingToFacebook(false);
462
+ }
349
463
  }
350
464
  },
351
- [publishToLinkedIn, closeImageGenerateDialog, setIsLoadingPublishingToLinkedIn]
465
+ [
466
+ publishToLinkedIn,
467
+ publishToFacebook,
468
+ closePublishToLinkedInDialog,
469
+ closePublishToFacebookDialog,
470
+ setIsLoadingPublishingToLinkedIn,
471
+ setIsLoadingPublishingToFacebook,
472
+ fetchPosts,
473
+ toggleNotification,
474
+ formatMessage
475
+ ]
352
476
  );
353
477
  const onCancel = react.useCallback((dialogId) => {
354
478
  currentPostRef.current = "";
@@ -359,10 +483,13 @@ const HomeContainer = () => {
359
483
  case "publish-to-linked-in-dialog":
360
484
  closePublishToLinkedInDialog();
361
485
  break;
486
+ case "publish-to-facebook-dialog":
487
+ closePublishToFacebookDialog();
488
+ break;
362
489
  default:
363
490
  return;
364
491
  }
365
- }, [closeImageGenerateDialog, closePublishToLinkedInDialog]);
492
+ }, [closeImageGenerateDialog, closePublishToLinkedInDialog, closePublishToFacebookDialog]);
366
493
  const onClickAction = react.useCallback(
367
494
  (postId, dialogId) => {
368
495
  currentPostRef.current = postId;
@@ -373,9 +500,12 @@ const HomeContainer = () => {
373
500
  case "publish-to-linked-in-dialog":
374
501
  openPublishToLinkedInDialog();
375
502
  break;
503
+ case "publish-to-facebook-dialog":
504
+ openPublishToFacebookDialog();
505
+ break;
376
506
  }
377
507
  },
378
- [openImageGenerateDialog, openPublishToLinkedInDialog]
508
+ [openImageGenerateDialog, openPublishToLinkedInDialog, openPublishToFacebookDialog]
379
509
  );
380
510
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
381
511
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -385,13 +515,27 @@ const HomeContainer = () => {
385
515
  title: "Publicar en LinkedIn",
386
516
  confirmText: "Publicar",
387
517
  onCancelAction: () => onCancel("publish-to-linked-in-dialog"),
388
- onConfirmAction: onConfirmPublishToLinedIn,
518
+ onConfirmAction: () => onConfirmPublish("linkedin"),
389
519
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", children: [
390
520
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: "¿Seguro que deseas publicar este artículo en LinkedIn?" }),
391
521
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "¡Esta acción es irreversible desde esta plataforma!" })
392
522
  ] })
393
523
  }
394
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
+ ),
395
539
  /* @__PURE__ */ jsxRuntime.jsx(GenerateImageDialog, { onConfirmAction: onConfirmGenerateImage, onCancelAction: () => onCancel("generate-image-dialog") }),
396
540
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { padding: 6, children: [
397
541
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", children: [
@@ -405,6 +549,7 @@ const HomeContainer = () => {
405
549
  posts,
406
550
  loadingImage,
407
551
  loadingPublishLinkedIn: loadingPublishingToLinkedIn,
552
+ loadingPublishingToFacebook,
408
553
  currentPost: currentPostRef,
409
554
  onClickAction
410
555
  }
@@ -447,14 +592,14 @@ const validationSchema = Yup__namespace.object().shape({
447
592
  titulo: Yup__namespace.string().notRequired(),
448
593
  autor: Yup__namespace.string().required("Este campo es obligatorio"),
449
594
  categoria: Yup__namespace.string().required("Este campo es obligatorio"),
450
- keywords: Yup__namespace.string().notRequired(),
595
+ temperatura: Yup__namespace.string().notRequired(),
451
596
  instrucciones: Yup__namespace.string().notRequired()
452
597
  });
453
598
  const initialValues = {
454
599
  titulo: "",
455
600
  autor: "",
456
601
  categoria: "",
457
- keywords: "",
602
+ temperatura: "0.2",
458
603
  instrucciones: ""
459
604
  };
460
605
  const Form = ({ onSubmit, categorias, users }) => {
@@ -545,25 +690,30 @@ const Form = ({ onSubmit, categorias, users }) => {
545
690
  ]
546
691
  }
547
692
  ) }),
548
- /* @__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(
549
694
  designSystem.Field.Root,
550
695
  {
551
- id: "keywords",
552
- error: touched.keywords && errors.keywords ? errors.keywords : void 0,
696
+ id: "temperatura",
697
+ error: touched.temperatura && errors.temperatura ? errors.temperatura : void 0,
553
698
  width: "100%",
554
- hint: "Ej: Innovación, Desarrollo, AI",
555
699
  children: [
556
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Palabras clave" }),
557
- /* @__PURE__ */ jsxRuntime.jsx(
558
- designSystem.TextInput,
559
- {
560
- name: "keywords",
561
- id: "keywords",
562
- placeholder: "Palabras clave separadas por comas",
563
- onChange: handleChange,
564
- value: values.keywords
565
- }
566
- ),
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
+ ] }),
567
717
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
568
718
  ]
569
719
  }
@@ -583,7 +733,8 @@ const Form = ({ onSubmit, categorias, users }) => {
583
733
  placeholder: "Escribe algunas instrucciones adicionales para el generador de contenido...",
584
734
  onChange: handleChange,
585
735
  value: values.instrucciones,
586
- rows: 8
736
+ resizable: false,
737
+ rows: 10
587
738
  }
588
739
  )
589
740
  ]
@@ -722,15 +873,361 @@ const GenerateContent = () => {
722
873
  /* @__PURE__ */ jsxRuntime.jsx(GenerateContentContainer, {})
723
874
  ] });
724
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
+ };
725
1207
  const App = () => {
726
1208
  const { allowedActions, isLoading } = admin.useRBAC(index.pluginPermissions);
727
1209
  if (isLoading) {
728
1210
  return /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Loading, {});
729
1211
  }
730
- 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: [
731
- allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
732
- allowedActions.canAccess && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "/generate-content", element: /* @__PURE__ */ jsxRuntime.jsx(GenerateContent, {}) }),
733
- /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
734
- ] }) }) });
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
+ ) }) }) });
735
1232
  };
736
1233
  exports.App = App;