@interware/content-generator 0.1.4 → 0.2.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 (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
@@ -1,15 +1,60 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { useFetchClient, useNotification, Page, useRBAC } from "@strapi/strapi/admin";
3
- import { Box, Table, Thead, Tr, Th, Typography, Flex, Tbody, Td, Button, LinkButton, Dialog, Grid, Field, Textarea, TextInput, Combobox, ComboboxOption, DesignSystemProvider } from "@strapi/design-system";
4
- import { useNavigate, Routes, Route } from "react-router-dom";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { useRBAC, useFetchClient, useNotification, Page } from "@strapi/strapi/admin";
3
+ import { SubNav, SubNavHeader, SubNavSections, SubNavSection, SubNavLink, Box, Table, Thead, Tr, Th, Typography, Flex, Tbody, Td, Button, LinkButton, Dialog, Grid, Field, Textarea, TextInput, Combobox, ComboboxOption, Modal, Card, CardHeader, CardCheckbox, CardAsset, CardBody, CardContent, CardTitle, CardSubtitle, CardBadge, CardAction, IconButton, DesignSystemProvider } from "@strapi/design-system";
4
+ import { useLocation, useNavigate, Routes, Route } from "react-router-dom";
5
+ import { p as pluginPermissions, c as closeDialog, o as openDialog, P as PLUGIN_ID } from "./index-B2RuvYul.mjs";
5
6
  import { useIntl } from "react-intl";
6
- import { useCallback, memo, useMemo, useState, useRef, useEffect } from "react";
7
+ import { useCallback, memo, useMemo, useState, useRef, useEffect, createContext, useContext } from "react";
7
8
  import { useDispatch, useSelector } from "react-redux";
8
- import { c as closeDialog, o as openDialog, P as PLUGIN_ID, p as pluginPermissions } from "./index-B6aEv34B.mjs";
9
- import { Play, Image } from "@strapi/icons";
10
- import { g as getTranslation } from "./getTranslation-BcuPOeW7.mjs";
9
+ import { Play, ExternalLink, Image as Image$1, Download, Trash } from "@strapi/icons";
10
+ import { g as getTranslation } from "./getTranslation-B97xbmEl.mjs";
11
11
  import { useFormik } from "formik";
12
12
  import * as Yup from "yup";
13
+ const links = [
14
+ {
15
+ label: "Generador de contenido con IA",
16
+ required: "access",
17
+ subLinks: [
18
+ {
19
+ id: 1,
20
+ label: "Generador de contenido",
21
+ to: "/admin/plugins/strapi-content-generator-gemini",
22
+ required: "access"
23
+ },
24
+ {
25
+ id: 2,
26
+ label: "Generador de imágenes",
27
+ to: "/admin/plugins/strapi-content-generator-gemini/generate-images",
28
+ required: "access"
29
+ }
30
+ ]
31
+ }
32
+ ];
33
+ const Navigation = () => {
34
+ const location = useLocation();
35
+ const { allowedActions, isLoading } = useRBAC(pluginPermissions);
36
+ if (isLoading) return null;
37
+ const canAccess = (required) => {
38
+ if (!required) return true;
39
+ if (required === "access") return allowedActions.canAccess;
40
+ return false;
41
+ };
42
+ return /* @__PURE__ */ jsxs(SubNav, { "aria-label": "Generador de contenido SubNav", children: [
43
+ /* @__PURE__ */ jsx(SubNavHeader, { label: "MKT" }),
44
+ /* @__PURE__ */ jsx(SubNavSections, { children: links.filter((section) => canAccess(section.required)).map(({ label, subLinks }) => /* @__PURE__ */ jsx(SubNavSection, { label, children: subLinks.filter((subLink) => canAccess(subLink.required)).map((subLink) => {
45
+ const active = location.pathname === subLink.to.replace(/^\/admin/, "");
46
+ return /* @__PURE__ */ jsx(
47
+ SubNavLink,
48
+ {
49
+ href: subLink.to,
50
+ active,
51
+ children: subLink.label
52
+ },
53
+ subLink.id
54
+ );
55
+ }) }, label)) })
56
+ ] });
57
+ };
13
58
  const useDialog = (id) => {
14
59
  const dispatch = useDispatch();
15
60
  const state = useSelector((state2) => state2.content_generator_commonDialog.open[id] ?? false);
@@ -93,11 +138,24 @@ const usePublishLinkedIn = () => {
93
138
  publishToLinkedIn
94
139
  };
95
140
  };
141
+ const usePublishFacebook = () => {
142
+ const { post } = useFetchClient();
143
+ const publishToFacebook = useCallback(
144
+ async (data, config) => {
145
+ return post("/strapi-content-generator-gemini/publish-facebook", data, config);
146
+ },
147
+ [post]
148
+ );
149
+ return {
150
+ publishToFacebook
151
+ };
152
+ };
96
153
  const PublishingTableComponent = ({
97
154
  posts,
98
155
  currentPost,
99
156
  loadingImage,
100
157
  loadingPublishLinkedIn,
158
+ loadingPublishingToFacebook,
101
159
  onClickAction
102
160
  }) => {
103
161
  const rows = useMemo(() => {
@@ -112,8 +170,8 @@ const PublishingTableComponent = ({
112
170
  id: post.documentId,
113
171
  fecha,
114
172
  titulo: post.titulo && post.titulo.length > 30 ? `${post.titulo.slice(0, 30)}…` : post.titulo,
115
- autor: `${post.autor?.nombre ?? ""} ${post.autor?.apellidos ?? ""}`,
116
- linkedInURL: post.linkedInPostId,
173
+ linkedInURL: post.linkedInPostId ? `https://www.linkedin.com/feed/update/${post.linkedInPostId}` : "",
174
+ facebookURL: post.facebookPostId ? `https://www.facebook.com/${post.facebookPostId}` : "",
117
175
  hasImage: Boolean(post.image)
118
176
  };
119
177
  });
@@ -122,29 +180,60 @@ const PublishingTableComponent = ({
122
180
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
123
181
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Fecha de publicación" }) }),
124
182
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Blog Title" }) }),
125
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Autor" }) }),
126
183
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", justifyContent: "center", children: [
127
184
  /* @__PURE__ */ jsx("i", { className: "fa-brands fa-linkedin" }),
128
185
  /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "LinkedIn" })
129
186
  ] }) }),
187
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", justifyContent: "center", children: [
188
+ /* @__PURE__ */ jsx("i", { className: "fa-brands fa-facebook" }),
189
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Facebook" })
190
+ ] }) }),
130
191
  /* @__PURE__ */ jsx(Th, {}),
131
192
  /* @__PURE__ */ jsx(Th, {})
132
193
  ] }) }),
133
194
  /* @__PURE__ */ jsx(Tbody, { children: rows.map((row) => {
134
- const buttonLoadingImage = loadingImage && currentPost.current === row.id;
135
- const buttonLoadingPublishingLinkediN = loadingPublishLinkedIn && currentPost.current === row.id;
195
+ const buttonLoadingImage = (loadingImage && currentPost.current) === row.id;
196
+ const buttonLoadingPublishingLinkedIn = (loadingPublishLinkedIn && currentPost.current) === row.id;
197
+ const buttonLoadingPublishingFacebook = (loadingPublishingToFacebook && currentPost.current) === row.id;
136
198
  return /* @__PURE__ */ jsxs(Tr, { children: [
137
199
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: row.fecha }) }),
138
200
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: row.titulo }) }),
139
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: row.autor }) }),
140
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
201
+ /* @__PURE__ */ jsx(Td, { children: !row.linkedInURL ? /* @__PURE__ */ jsx(
141
202
  Button,
142
203
  {
143
204
  disabled: row.linkedInURL,
144
205
  onClick: () => onClickAction(row.id, "publish-to-linked-in-dialog"),
145
- loading: buttonLoadingPublishingLinkediN,
206
+ loading: buttonLoadingPublishingLinkedIn,
146
207
  startIcon: /* @__PURE__ */ jsx(Play, {}),
147
- children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingImage ? "publicando" : "publicar" })
208
+ children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingPublishingLinkedIn ? "publicando" : "publicar" })
209
+ }
210
+ ) : /* @__PURE__ */ jsx(
211
+ LinkButton,
212
+ {
213
+ variant: "success-light",
214
+ href: row.linkedInURL,
215
+ isExternal: true,
216
+ startIcon: /* @__PURE__ */ jsx(ExternalLink, {}),
217
+ children: "visitar"
218
+ }
219
+ ) }),
220
+ /* @__PURE__ */ jsx(Td, { children: !row.facebookURL ? /* @__PURE__ */ jsx(
221
+ Button,
222
+ {
223
+ disabled: row.facebookURL,
224
+ onClick: () => onClickAction(row.id, "publish-to-facebook-dialog"),
225
+ loading: buttonLoadingPublishingFacebook,
226
+ startIcon: /* @__PURE__ */ jsx(Play, {}),
227
+ children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingPublishingFacebook ? "publicando" : "publicar" })
228
+ }
229
+ ) : /* @__PURE__ */ jsx(
230
+ LinkButton,
231
+ {
232
+ variant: "success-light",
233
+ href: row.facebookURL,
234
+ isExternal: true,
235
+ startIcon: /* @__PURE__ */ jsx(ExternalLink, {}),
236
+ children: "visitar"
148
237
  }
149
238
  ) }),
150
239
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
@@ -153,7 +242,7 @@ const PublishingTableComponent = ({
153
242
  disabled: row.hasImage,
154
243
  onClick: () => onClickAction(row.id, "generate-image-dialog"),
155
244
  loading: buttonLoadingImage,
156
- startIcon: /* @__PURE__ */ jsx(Image, {}),
245
+ startIcon: /* @__PURE__ */ jsx(Image$1, {}),
157
246
  children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingImage ? "generando" : "generar" })
158
247
  }
159
248
  ) }),
@@ -238,13 +327,16 @@ const HomeContainer = () => {
238
327
  const [posts, setPosts] = useState([]);
239
328
  const [loadingImage, setIsLoadingImage] = useState(false);
240
329
  const [loadingPublishingToLinkedIn, setIsLoadingPublishingToLinkedIn] = useState(false);
330
+ const [loadingPublishingToFacebook, setIsLoadingPublishingToFacebook] = useState(false);
241
331
  const currentPostRef = useRef("");
242
332
  const { formatMessage } = useIntl();
243
333
  const { openDialog: openImageGenerateDialog, closeDialog: closeImageGenerateDialog } = useDialog("generate-image-dialog");
244
334
  const { openDialog: openPublishToLinkedInDialog, closeDialog: closePublishToLinkedInDialog } = useDialog("publish-to-linked-in-dialog");
335
+ const { openDialog: openPublishToFacebookDialog, closeDialog: closePublishToFacebookDialog } = useDialog("publish-to-facebook-dialog");
245
336
  const { getArticles } = useArticles();
246
337
  const { generateImage } = useGenerateContent();
247
338
  const { publishToLinkedIn } = usePublishLinkedIn();
339
+ const { publishToFacebook } = usePublishFacebook();
248
340
  const { toggleNotification } = useNotification();
249
341
  const fetchPosts = useCallback(async () => {
250
342
  let isMounted = true;
@@ -302,33 +394,65 @@ const HomeContainer = () => {
302
394
  },
303
395
  [generateImage, closeImageGenerateDialog, setIsLoadingImage]
304
396
  );
305
- const onConfirmPublishToLinedIn = useCallback(
306
- async () => {
307
- closePublishToLinkedInDialog();
308
- setIsLoadingPublishingToLinkedIn(true);
397
+ const onConfirmPublish = useCallback(
398
+ async (type) => {
399
+ const isLinkedIn = type === "linkedin";
400
+ const isFacebook = type === "facebook";
401
+ if (isLinkedIn) {
402
+ closePublishToLinkedInDialog();
403
+ setIsLoadingPublishingToLinkedIn(true);
404
+ }
405
+ if (isFacebook) {
406
+ closePublishToFacebookDialog();
407
+ setIsLoadingPublishingToFacebook(true);
408
+ }
309
409
  try {
310
- await publishToLinkedIn({ articleId: currentPostRef.current });
410
+ switch (type) {
411
+ case "linkedin":
412
+ await publishToLinkedIn({ articleId: currentPostRef.current });
413
+ break;
414
+ case "facebook":
415
+ await publishToFacebook({ articleId: currentPostRef.current });
416
+ break;
417
+ default:
418
+ return;
419
+ }
311
420
  fetchPosts();
312
421
  toggleNotification({
313
422
  type: "success",
314
423
  message: formatMessage({
315
- id: `${PLUGIN_ID}.config.load.error`,
316
- defaultMessage: "Artículo publicado en LinkedIn con éxito"
424
+ id: `${PLUGIN_ID}.config.publish.success.${type}`,
425
+ defaultMessage: type === "linkedin" ? "Artículo publicado en LinkedIn con éxito" : "Artículo publicado en Facebook con éxito"
317
426
  })
318
427
  });
319
428
  } catch (err) {
320
429
  toggleNotification({
321
430
  type: "danger",
322
431
  message: formatMessage({
323
- id: `${PLUGIN_ID}.config.load.error`,
324
- defaultMessage: "Ocurrió un error al publicar el artículo en LinkedIn"
432
+ id: `${PLUGIN_ID}.config.publish.error.${type}`,
433
+ defaultMessage: type === "linkedin" ? "Ocurrió un error al publicar el artículo en LinkedIn" : "Ocurrió un error al publicar el artículo en Facebook"
325
434
  })
326
435
  });
327
436
  } finally {
328
- setIsLoadingPublishingToLinkedIn(false);
437
+ if (isLinkedIn) {
438
+ setIsLoadingPublishingToLinkedIn(false);
439
+ }
440
+ if (isFacebook) {
441
+ setIsLoadingPublishingToFacebook(false);
442
+ }
329
443
  }
330
444
  },
331
- [publishToLinkedIn, closeImageGenerateDialog, setIsLoadingPublishingToLinkedIn]
445
+ [
446
+ publishToLinkedIn,
447
+ publishToFacebook,
448
+ closePublishToLinkedInDialog,
449
+ closePublishToFacebookDialog,
450
+ setIsLoadingPublishingToLinkedIn,
451
+ setIsLoadingPublishingToFacebook,
452
+ fetchPosts,
453
+ toggleNotification,
454
+ formatMessage
455
+ ]
332
456
  );
333
457
  const onCancel = useCallback((dialogId) => {
334
458
  currentPostRef.current = "";
@@ -339,10 +463,13 @@ const HomeContainer = () => {
339
463
  case "publish-to-linked-in-dialog":
340
464
  closePublishToLinkedInDialog();
341
465
  break;
466
+ case "publish-to-facebook-dialog":
467
+ closePublishToFacebookDialog();
468
+ break;
342
469
  default:
343
470
  return;
344
471
  }
345
- }, [closeImageGenerateDialog, closePublishToLinkedInDialog]);
472
+ }, [closeImageGenerateDialog, closePublishToLinkedInDialog, closePublishToFacebookDialog]);
346
473
  const onClickAction = useCallback(
347
474
  (postId, dialogId) => {
348
475
  currentPostRef.current = postId;
@@ -353,9 +480,12 @@ const HomeContainer = () => {
353
480
  case "publish-to-linked-in-dialog":
354
481
  openPublishToLinkedInDialog();
355
482
  break;
483
+ case "publish-to-facebook-dialog":
484
+ openPublishToFacebookDialog();
485
+ break;
356
486
  }
357
487
  },
358
- [openImageGenerateDialog, openPublishToLinkedInDialog]
488
+ [openImageGenerateDialog, openPublishToLinkedInDialog, openPublishToFacebookDialog]
359
489
  );
360
490
  return /* @__PURE__ */ jsxs(Fragment, { children: [
361
491
  /* @__PURE__ */ jsx(
@@ -365,13 +495,27 @@ const HomeContainer = () => {
365
495
  title: "Publicar en LinkedIn",
366
496
  confirmText: "Publicar",
367
497
  onCancelAction: () => onCancel("publish-to-linked-in-dialog"),
368
- onConfirmAction: onConfirmPublishToLinedIn,
498
+ onConfirmAction: () => onConfirmPublish("linkedin"),
369
499
  children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", children: [
370
500
  /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "¿Seguro que deseas publicar este artículo en LinkedIn?" }),
371
501
  /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "¡Esta acción es irreversible desde esta plataforma!" })
372
502
  ] })
373
503
  }
374
504
  ),
505
+ /* @__PURE__ */ jsx(
506
+ DialogCommon,
507
+ {
508
+ id: "publish-to-facebook-dialog",
509
+ title: "Publicar en Facebook",
510
+ confirmText: "Publicar",
511
+ onCancelAction: () => onCancel("publish-to-facebook-dialog"),
512
+ onConfirmAction: () => onConfirmPublish("facebook"),
513
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", children: [
514
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "¿Seguro que deseas publicar este artículo en Facebook?" }),
515
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "¡Esta acción es irreversible desde esta plataforma!" })
516
+ ] })
517
+ }
518
+ ),
375
519
  /* @__PURE__ */ jsx(GenerateImageDialog, { onConfirmAction: onConfirmGenerateImage, onCancelAction: () => onCancel("generate-image-dialog") }),
376
520
  /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
377
521
  /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
@@ -385,6 +529,7 @@ const HomeContainer = () => {
385
529
  posts,
386
530
  loadingImage,
387
531
  loadingPublishLinkedIn: loadingPublishingToLinkedIn,
532
+ loadingPublishingToFacebook,
388
533
  currentPost: currentPostRef,
389
534
  onClickAction
390
535
  }
@@ -427,14 +572,14 @@ const validationSchema = Yup.object().shape({
427
572
  titulo: Yup.string().notRequired(),
428
573
  autor: Yup.string().required("Este campo es obligatorio"),
429
574
  categoria: Yup.string().required("Este campo es obligatorio"),
430
- keywords: Yup.string().notRequired(),
575
+ temperatura: Yup.string().notRequired(),
431
576
  instrucciones: Yup.string().notRequired()
432
577
  });
433
578
  const initialValues = {
434
579
  titulo: "",
435
580
  autor: "",
436
581
  categoria: "",
437
- keywords: "",
582
+ temperatura: "0.2",
438
583
  instrucciones: ""
439
584
  };
440
585
  const Form = ({ onSubmit, categorias, users }) => {
@@ -525,25 +670,30 @@ const Form = ({ onSubmit, categorias, users }) => {
525
670
  ]
526
671
  }
527
672
  ) }),
528
- /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(
673
+ /* @__PURE__ */ jsx(Grid.Item, { xs: 12, s: 12, m: 6, children: /* @__PURE__ */ jsxs(
529
674
  Field.Root,
530
675
  {
531
- id: "keywords",
532
- error: touched.keywords && errors.keywords ? errors.keywords : void 0,
676
+ id: "temperatura",
677
+ error: touched.temperatura && errors.temperatura ? errors.temperatura : void 0,
533
678
  width: "100%",
534
- hint: "Ej: Innovación, Desarrollo, AI",
535
679
  children: [
536
- /* @__PURE__ */ jsx(Field.Label, { children: "Palabras clave" }),
537
- /* @__PURE__ */ jsx(
538
- TextInput,
539
- {
540
- name: "keywords",
541
- id: "keywords",
542
- placeholder: "Palabras clave separadas por comas",
543
- onChange: handleChange,
544
- value: values.keywords
545
- }
546
- ),
680
+ /* @__PURE__ */ jsx(Field.Label, { children: "Temperatura" }),
681
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", gap: 4, children: [
682
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
683
+ "input",
684
+ {
685
+ name: "temperatura",
686
+ value: values.temperatura,
687
+ type: "range",
688
+ min: "0",
689
+ max: "2",
690
+ step: "0.05",
691
+ onChange: handleChange,
692
+ style: { width: "100%" }
693
+ }
694
+ ) }),
695
+ /* @__PURE__ */ jsx(Box, { width: "100px", children: /* @__PURE__ */ jsx(Field.Input, { value: values.temperatura, readOnly: true }) })
696
+ ] }),
547
697
  /* @__PURE__ */ jsx(Field.Hint, {})
548
698
  ]
549
699
  }
@@ -563,7 +713,8 @@ const Form = ({ onSubmit, categorias, users }) => {
563
713
  placeholder: "Escribe algunas instrucciones adicionales para el generador de contenido...",
564
714
  onChange: handleChange,
565
715
  value: values.instrucciones,
566
- rows: 8
716
+ resizable: false,
717
+ rows: 10
567
718
  }
568
719
  )
569
720
  ]
@@ -702,16 +853,362 @@ const GenerateContent = () => {
702
853
  /* @__PURE__ */ jsx(GenerateContentContainer, {})
703
854
  ] });
704
855
  };
856
+ const useImages$1 = () => {
857
+ const { get, del } = useFetchClient();
858
+ const getImages = useCallback(
859
+ async () => {
860
+ return await get("/strapi-content-generator-gemini/images");
861
+ },
862
+ [get]
863
+ );
864
+ const deleteImage = useCallback(
865
+ async (config) => {
866
+ return await del("/strapi-content-generator-gemini/delete-image", config);
867
+ },
868
+ [del]
869
+ );
870
+ return {
871
+ getImages,
872
+ deleteImage
873
+ };
874
+ };
875
+ const PRINCIPAL_KEY = "principal";
876
+ const ModalImagesSizes = ({
877
+ open,
878
+ setOpen,
879
+ image,
880
+ handleDownload
881
+ }) => {
882
+ const [selectedImages, setSelectedImages] = useState(
883
+ () => /* @__PURE__ */ new Set()
884
+ );
885
+ const formats = useMemo(
886
+ () => Object.entries(image?.formats ?? {}),
887
+ [image?.formats]
888
+ );
889
+ const toggleImageSelection = useCallback((key) => {
890
+ setSelectedImages((prev) => {
891
+ const next = new Set(prev);
892
+ next.has(key) ? next.delete(key) : next.add(key);
893
+ return next;
894
+ });
895
+ }, []);
896
+ const isPrincipalSelected = useMemo(
897
+ () => selectedImages.has(PRINCIPAL_KEY),
898
+ [selectedImages]
899
+ );
900
+ const isDownloadDisabled = useMemo(
901
+ () => selectedImages.size === 0,
902
+ [selectedImages]
903
+ );
904
+ const handleClose = useCallback(() => {
905
+ setOpen(false);
906
+ }, [setOpen]);
907
+ useEffect(() => {
908
+ if (!open) {
909
+ setSelectedImages(/* @__PURE__ */ new Set());
910
+ }
911
+ }, [open]);
912
+ if (!image) return null;
913
+ return /* @__PURE__ */ jsx(
914
+ Modal.Root,
915
+ {
916
+ id: "modal-image-formats",
917
+ open,
918
+ onOpenChange: handleClose,
919
+ children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
920
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: "Descargar imágenes" }) }),
921
+ /* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 3, children: [
922
+ /* @__PURE__ */ jsx(Grid.Item, { m: 3, children: /* @__PURE__ */ jsxs(
923
+ Card,
924
+ {
925
+ id: "modal-principal",
926
+ onClick: () => toggleImageSelection(PRINCIPAL_KEY),
927
+ children: [
928
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
929
+ /* @__PURE__ */ jsx(CardCheckbox, { checked: isPrincipalSelected }),
930
+ /* @__PURE__ */ jsx(CardAsset, { src: image.url })
931
+ ] }),
932
+ /* @__PURE__ */ jsxs(CardBody, { children: [
933
+ /* @__PURE__ */ jsxs(CardContent, { children: [
934
+ /* @__PURE__ */ jsx(CardTitle, { children: "Principal" }),
935
+ /* @__PURE__ */ jsx(CardSubtitle, { children: `${image.width}x${image.height}` })
936
+ ] }),
937
+ /* @__PURE__ */ jsx(CardBadge, { children: image.mime })
938
+ ] })
939
+ ]
940
+ }
941
+ ) }),
942
+ formats.map(([format, img]) => {
943
+ const isSelected = selectedImages.has(format);
944
+ return /* @__PURE__ */ jsx(Grid.Item, { m: 3, children: /* @__PURE__ */ jsxs(
945
+ Card,
946
+ {
947
+ id: `modal-${format}`,
948
+ onClick: () => toggleImageSelection(format),
949
+ children: [
950
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
951
+ /* @__PURE__ */ jsx(CardCheckbox, { checked: isSelected }),
952
+ /* @__PURE__ */ jsx(CardAsset, { src: img.url })
953
+ ] }),
954
+ /* @__PURE__ */ jsxs(CardBody, { children: [
955
+ /* @__PURE__ */ jsxs(CardContent, { children: [
956
+ /* @__PURE__ */ jsx(CardTitle, { children: format }),
957
+ /* @__PURE__ */ jsx(CardSubtitle, { children: `${img.width}x${img.height}` })
958
+ ] }),
959
+ /* @__PURE__ */ jsx(CardBadge, { children: image.mime })
960
+ ] })
961
+ ]
962
+ }
963
+ ) }, format);
964
+ })
965
+ ] }) }),
966
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
967
+ /* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: handleClose, children: "Cancelar" }) }),
968
+ /* @__PURE__ */ jsx(
969
+ Button,
970
+ {
971
+ disabled: isDownloadDisabled,
972
+ onClick: () => {
973
+ handleDownload(selectedImages);
974
+ },
975
+ children: "Descargar"
976
+ }
977
+ )
978
+ ] })
979
+ ] })
980
+ }
981
+ );
982
+ };
983
+ const ImagesContext = createContext(void 0);
984
+ const ImagesProvider = ({ children }) => {
985
+ const { getImages, deleteImage } = useImages$1();
986
+ const { openDialog: openDialog2, closeDialog: closeDialog2 } = useDialog("dialog-delete-image");
987
+ const { formatMessage } = useIntl();
988
+ const { toggleNotification } = useNotification();
989
+ const [images, setImages] = useState([]);
990
+ const [loading, setLoading] = useState(false);
991
+ const [open, setOpen] = useState(false);
992
+ const currentImageRef = useRef();
993
+ const fetchImages = useCallback(async () => {
994
+ let isMounted = true;
995
+ try {
996
+ const { data } = await getImages();
997
+ if (isMounted) {
998
+ setImages(data);
999
+ }
1000
+ } catch (error) {
1001
+ console.error("Error fetching images:", error);
1002
+ }
1003
+ return () => {
1004
+ isMounted = false;
1005
+ };
1006
+ }, [getImages]);
1007
+ const handleDeleteImage = async () => {
1008
+ closeDialog2();
1009
+ try {
1010
+ await deleteImage({ params: { documentId: currentImageRef.current?.documentId } });
1011
+ toggleNotification({
1012
+ type: "success",
1013
+ message: formatMessage({
1014
+ id: `${PLUGIN_ID}.config.load.error`,
1015
+ defaultMessage: "Imagen eliminada con éxito"
1016
+ })
1017
+ });
1018
+ fetchImages();
1019
+ } catch (err) {
1020
+ toggleNotification({
1021
+ type: "danger",
1022
+ message: formatMessage({
1023
+ id: `${PLUGIN_ID}.config.load.error`,
1024
+ defaultMessage: "Ocurrió un error al eliminar la imagen"
1025
+ })
1026
+ });
1027
+ }
1028
+ };
1029
+ const onCancelAction = useCallback((action) => {
1030
+ switch (action) {
1031
+ case "delete":
1032
+ closeDialog2();
1033
+ break;
1034
+ case "download":
1035
+ setOpen(false);
1036
+ break;
1037
+ default:
1038
+ return;
1039
+ }
1040
+ currentImageRef.current = void 0;
1041
+ closeDialog2();
1042
+ }, [openDialog2]);
1043
+ const onClickAction = useCallback((image, action) => {
1044
+ currentImageRef.current = image;
1045
+ switch (action) {
1046
+ case "delete":
1047
+ openDialog2();
1048
+ break;
1049
+ case "download":
1050
+ setOpen(true);
1051
+ break;
1052
+ default:
1053
+ return;
1054
+ }
1055
+ }, [openDialog2, setOpen]);
1056
+ const downloadImage = async (url, filename) => {
1057
+ const response = await fetch(url);
1058
+ const blob = await response.blob();
1059
+ const objectUrl = URL.createObjectURL(blob);
1060
+ const link = document.createElement("a");
1061
+ link.href = objectUrl;
1062
+ link.download = filename;
1063
+ document.body.appendChild(link);
1064
+ link.click();
1065
+ document.body.removeChild(link);
1066
+ URL.revokeObjectURL(objectUrl);
1067
+ };
1068
+ const handleDownload = async (selectedKeys) => {
1069
+ const image = currentImageRef.current;
1070
+ if (!image) return;
1071
+ setOpen(false);
1072
+ const keys = Array.isArray(selectedKeys) ? selectedKeys : Array.from(selectedKeys);
1073
+ const extension = image.ext?.replace(".", "") ?? "jpg";
1074
+ const formats = image.formats;
1075
+ const items = keys.map((key) => {
1076
+ if (key === "principal") {
1077
+ return {
1078
+ url: image.url,
1079
+ filename: `image-principal-${image.documentId}.${extension}`
1080
+ };
1081
+ }
1082
+ const formatImage = formats?.[key];
1083
+ if (!formatImage) return null;
1084
+ return {
1085
+ url: formatImage.url,
1086
+ filename: `image-${key}-${image.documentId}.${extension}`
1087
+ };
1088
+ }).filter(Boolean);
1089
+ for (const item of items) {
1090
+ await downloadImage(item.url, item.filename);
1091
+ }
1092
+ };
1093
+ useEffect(() => {
1094
+ fetchImages();
1095
+ }, [fetchImages]);
1096
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1097
+ currentImageRef.current && /* @__PURE__ */ jsx(
1098
+ ModalImagesSizes,
1099
+ {
1100
+ open,
1101
+ setOpen,
1102
+ image: currentImageRef.current,
1103
+ handleDownload
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ jsx(
1107
+ DialogCommon,
1108
+ {
1109
+ title: "Eliminar imagen",
1110
+ id: "dialog-delete-image",
1111
+ onConfirmAction: handleDeleteImage,
1112
+ onCancelAction: () => onCancelAction("delete"),
1113
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", justifyContent: "center", children: [
1114
+ /* @__PURE__ */ jsx(Typography, { children: "¿Seguro que deseas eliminar esta imagen?" }),
1115
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Esta acción es irreversible" })
1116
+ ] })
1117
+ }
1118
+ ),
1119
+ /* @__PURE__ */ jsx(ImagesContext.Provider, { value: { images, onClickAction }, children })
1120
+ ] });
1121
+ };
1122
+ const useImages = () => {
1123
+ const context = useContext(ImagesContext);
1124
+ if (!context) {
1125
+ throw new Error("useImages must be used within an ImagesProvider");
1126
+ }
1127
+ return context;
1128
+ };
1129
+ const Image = ({ image }) => {
1130
+ const { onClickAction } = useImages();
1131
+ return /* @__PURE__ */ jsxs(Card, { id: image.id, children: [
1132
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
1133
+ /* @__PURE__ */ jsxs(CardAction, { position: "end", children: [
1134
+ /* @__PURE__ */ jsx(
1135
+ IconButton,
1136
+ {
1137
+ label: "Descargar",
1138
+ onClick: () => {
1139
+ onClickAction(image, "download");
1140
+ },
1141
+ children: /* @__PURE__ */ jsx(Download, {})
1142
+ }
1143
+ ),
1144
+ /* @__PURE__ */ jsx(
1145
+ IconButton,
1146
+ {
1147
+ label: "Eliminar",
1148
+ onClick: () => {
1149
+ onClickAction(image, "delete");
1150
+ },
1151
+ children: /* @__PURE__ */ jsx(Trash, {})
1152
+ }
1153
+ )
1154
+ ] }),
1155
+ /* @__PURE__ */ jsx(CardAsset, { src: image.url, style: { borderTopLeftRadius: "4px", borderTopRightRadius: "4px" } })
1156
+ ] }),
1157
+ /* @__PURE__ */ jsxs(CardBody, { children: [
1158
+ /* @__PURE__ */ jsxs(CardContent, { children: [
1159
+ /* @__PURE__ */ jsx(CardTitle, { children: `${image.caption?.replace("generada por IA", "").slice(0, 80)}...` }),
1160
+ /* @__PURE__ */ jsx(CardSubtitle, { children: `${image.width}X${image.height}` })
1161
+ ] }),
1162
+ /* @__PURE__ */ jsx(CardBadge, { children: image.mime })
1163
+ ] })
1164
+ ] });
1165
+ };
1166
+ const Images = () => {
1167
+ const { images } = useImages();
1168
+ return /* @__PURE__ */ jsx(Box, { background: "neutral0", padding: 6, margin: 6, width: "100%", children: /* @__PURE__ */ jsx(Grid.Root, { gap: 3, children: images.map((image) => {
1169
+ return /* @__PURE__ */ jsx(Grid.Item, { m: 3, children: /* @__PURE__ */ jsx(Image, { image }) }, image.id);
1170
+ }) }) });
1171
+ };
1172
+ const GenerateImagesContainer = () => {
1173
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ImagesProvider, { children: /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
1174
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
1175
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Generar imagenes con IA" }),
1176
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
1177
+ ] }) }),
1178
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsx(Images, {}) })
1179
+ ] }) }) });
1180
+ };
1181
+ const GenerateImages = () => {
1182
+ return /* @__PURE__ */ jsxs(Page.Main, { children: [
1183
+ /* @__PURE__ */ jsx(Page.Title, { children: "Generar imagenes con IA" }),
1184
+ /* @__PURE__ */ jsx(GenerateImagesContainer, {})
1185
+ ] });
1186
+ };
705
1187
  const App = () => {
706
1188
  const { allowedActions, isLoading } = useRBAC(pluginPermissions);
707
1189
  if (isLoading) {
708
1190
  return /* @__PURE__ */ jsx(Page.Loading, {});
709
1191
  }
710
- return /* @__PURE__ */ jsx(Page.Protect, { permissions: [...pluginPermissions.access], children: /* @__PURE__ */ jsx(DesignSystemProvider, { locale: "es-MX", children: /* @__PURE__ */ jsxs(Routes, { children: [
711
- allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
712
- allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { path: "/generate-content", element: /* @__PURE__ */ jsx(GenerateContent, {}) }),
713
- /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
714
- ] }) }) });
1192
+ return /* @__PURE__ */ jsx(Page.Protect, { permissions: [...pluginPermissions.access], children: /* @__PURE__ */ jsx(DesignSystemProvider, { locale: "es-MX", children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
1193
+ "div",
1194
+ {
1195
+ style: {
1196
+ display: "grid",
1197
+ gridTemplateColumns: "auto 1fr",
1198
+ width: "100%",
1199
+ height: "100vh"
1200
+ },
1201
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
1202
+ /* @__PURE__ */ jsx(Navigation, {}),
1203
+ /* @__PURE__ */ jsxs(Routes, { children: [
1204
+ allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
1205
+ allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { path: "/generate-content", element: /* @__PURE__ */ jsx(GenerateContent, {}) }),
1206
+ allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { path: "/generate-images", element: /* @__PURE__ */ jsx(GenerateImages, {}) }),
1207
+ /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
1208
+ ] })
1209
+ ] })
1210
+ }
1211
+ ) }) }) });
715
1212
  };
716
1213
  export {
717
1214
  App