@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
@@ -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-CMQYbwsn.mjs";
9
- import { Play, Image } from "@strapi/icons";
10
- import { g as getTranslation } from "./getTranslation-CIMKEgGr.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);
@@ -31,17 +76,48 @@ const useArticles = () => {
31
76
  getArticles
32
77
  };
33
78
  };
79
+ const REQUEST_TIMEOUT = 12e4;
34
80
  const useGenerateContent = () => {
35
81
  const { post } = useFetchClient();
36
82
  const generateContent = useCallback(
37
83
  async (data, config) => {
38
- return post("/strapi-content-generator-gemini/generate-article", data, config);
84
+ const controller = new AbortController();
85
+ const timeoutId = setTimeout(() => {
86
+ controller.abort();
87
+ }, REQUEST_TIMEOUT);
88
+ try {
89
+ return await post(
90
+ "/strapi-content-generator-gemini/generate-article",
91
+ data,
92
+ {
93
+ ...config,
94
+ signal: controller.signal
95
+ }
96
+ );
97
+ } finally {
98
+ clearTimeout(timeoutId);
99
+ }
39
100
  },
40
101
  [post]
41
102
  );
42
103
  const generateImage = useCallback(
43
104
  async (data, config) => {
44
- return post("/strapi-content-generator-gemini/generate-image", data, config);
105
+ const controller = new AbortController();
106
+ const timeoutId = setTimeout(() => {
107
+ controller.abort();
108
+ }, REQUEST_TIMEOUT);
109
+ try {
110
+ return await post(
111
+ "/strapi-content-generator-gemini/generate-image",
112
+ data,
113
+ {
114
+ ...config,
115
+ signal: controller.signal
116
+ }
117
+ );
118
+ } finally {
119
+ clearTimeout(timeoutId);
120
+ }
45
121
  },
46
122
  [post]
47
123
  );
@@ -62,11 +138,24 @@ const usePublishLinkedIn = () => {
62
138
  publishToLinkedIn
63
139
  };
64
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
+ };
65
153
  const PublishingTableComponent = ({
66
154
  posts,
67
155
  currentPost,
68
156
  loadingImage,
69
157
  loadingPublishLinkedIn,
158
+ loadingPublishingToFacebook,
70
159
  onClickAction
71
160
  }) => {
72
161
  const rows = useMemo(() => {
@@ -81,8 +170,8 @@ const PublishingTableComponent = ({
81
170
  id: post.documentId,
82
171
  fecha,
83
172
  titulo: post.titulo && post.titulo.length > 30 ? `${post.titulo.slice(0, 30)}…` : post.titulo,
84
- autor: `${post.autor?.nombre ?? ""} ${post.autor?.apellidos ?? ""}`,
85
- 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}` : "",
86
175
  hasImage: Boolean(post.image)
87
176
  };
88
177
  });
@@ -91,29 +180,60 @@ const PublishingTableComponent = ({
91
180
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
92
181
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Fecha de publicación" }) }),
93
182
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Blog Title" }) }),
94
- /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Autor" }) }),
95
183
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", justifyContent: "center", children: [
96
184
  /* @__PURE__ */ jsx("i", { className: "fa-brands fa-linkedin" }),
97
185
  /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "LinkedIn" })
98
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
+ ] }) }),
99
191
  /* @__PURE__ */ jsx(Th, {}),
100
192
  /* @__PURE__ */ jsx(Th, {})
101
193
  ] }) }),
102
194
  /* @__PURE__ */ jsx(Tbody, { children: rows.map((row) => {
103
- const buttonLoadingImage = loadingImage && currentPost.current === row.id;
104
- 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;
105
198
  return /* @__PURE__ */ jsxs(Tr, { children: [
106
199
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: row.fecha }) }),
107
200
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: row.titulo }) }),
108
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: row.autor }) }),
109
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
201
+ /* @__PURE__ */ jsx(Td, { children: !row.linkedInURL ? /* @__PURE__ */ jsx(
110
202
  Button,
111
203
  {
112
204
  disabled: row.linkedInURL,
113
205
  onClick: () => onClickAction(row.id, "publish-to-linked-in-dialog"),
114
- loading: buttonLoadingPublishingLinkediN,
206
+ loading: buttonLoadingPublishingLinkedIn,
207
+ startIcon: /* @__PURE__ */ jsx(Play, {}),
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,
115
226
  startIcon: /* @__PURE__ */ jsx(Play, {}),
116
- children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingImage ? "publicando" : "publicar" })
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"
117
237
  }
118
238
  ) }),
119
239
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
@@ -122,7 +242,7 @@ const PublishingTableComponent = ({
122
242
  disabled: row.hasImage,
123
243
  onClick: () => onClickAction(row.id, "generate-image-dialog"),
124
244
  loading: buttonLoadingImage,
125
- startIcon: /* @__PURE__ */ jsx(Image, {}),
245
+ startIcon: /* @__PURE__ */ jsx(Image$1, {}),
126
246
  children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingImage ? "generando" : "generar" })
127
247
  }
128
248
  ) }),
@@ -207,13 +327,16 @@ const HomeContainer = () => {
207
327
  const [posts, setPosts] = useState([]);
208
328
  const [loadingImage, setIsLoadingImage] = useState(false);
209
329
  const [loadingPublishingToLinkedIn, setIsLoadingPublishingToLinkedIn] = useState(false);
330
+ const [loadingPublishingToFacebook, setIsLoadingPublishingToFacebook] = useState(false);
210
331
  const currentPostRef = useRef("");
211
332
  const { formatMessage } = useIntl();
212
333
  const { openDialog: openImageGenerateDialog, closeDialog: closeImageGenerateDialog } = useDialog("generate-image-dialog");
213
334
  const { openDialog: openPublishToLinkedInDialog, closeDialog: closePublishToLinkedInDialog } = useDialog("publish-to-linked-in-dialog");
335
+ const { openDialog: openPublishToFacebookDialog, closeDialog: closePublishToFacebookDialog } = useDialog("publish-to-facebook-dialog");
214
336
  const { getArticles } = useArticles();
215
337
  const { generateImage } = useGenerateContent();
216
338
  const { publishToLinkedIn } = usePublishLinkedIn();
339
+ const { publishToFacebook } = usePublishFacebook();
217
340
  const { toggleNotification } = useNotification();
218
341
  const fetchPosts = useCallback(async () => {
219
342
  let isMounted = true;
@@ -271,33 +394,65 @@ const HomeContainer = () => {
271
394
  },
272
395
  [generateImage, closeImageGenerateDialog, setIsLoadingImage]
273
396
  );
274
- const onConfirmPublishToLinedIn = useCallback(
275
- async () => {
276
- closePublishToLinkedInDialog();
277
- 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
+ }
278
409
  try {
279
- 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
+ }
280
420
  fetchPosts();
281
421
  toggleNotification({
282
422
  type: "success",
283
423
  message: formatMessage({
284
- id: `${PLUGIN_ID}.config.load.error`,
285
- 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"
286
426
  })
287
427
  });
288
428
  } catch (err) {
289
429
  toggleNotification({
290
430
  type: "danger",
291
431
  message: formatMessage({
292
- id: `${PLUGIN_ID}.config.load.error`,
293
- 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"
294
434
  })
295
435
  });
296
436
  } finally {
297
- setIsLoadingPublishingToLinkedIn(false);
437
+ if (isLinkedIn) {
438
+ setIsLoadingPublishingToLinkedIn(false);
439
+ }
440
+ if (isFacebook) {
441
+ setIsLoadingPublishingToFacebook(false);
442
+ }
298
443
  }
299
444
  },
300
- [publishToLinkedIn, closeImageGenerateDialog, setIsLoadingPublishingToLinkedIn]
445
+ [
446
+ publishToLinkedIn,
447
+ publishToFacebook,
448
+ closePublishToLinkedInDialog,
449
+ closePublishToFacebookDialog,
450
+ setIsLoadingPublishingToLinkedIn,
451
+ setIsLoadingPublishingToFacebook,
452
+ fetchPosts,
453
+ toggleNotification,
454
+ formatMessage
455
+ ]
301
456
  );
302
457
  const onCancel = useCallback((dialogId) => {
303
458
  currentPostRef.current = "";
@@ -308,10 +463,13 @@ const HomeContainer = () => {
308
463
  case "publish-to-linked-in-dialog":
309
464
  closePublishToLinkedInDialog();
310
465
  break;
466
+ case "publish-to-facebook-dialog":
467
+ closePublishToFacebookDialog();
468
+ break;
311
469
  default:
312
470
  return;
313
471
  }
314
- }, [closeImageGenerateDialog, closePublishToLinkedInDialog]);
472
+ }, [closeImageGenerateDialog, closePublishToLinkedInDialog, closePublishToFacebookDialog]);
315
473
  const onClickAction = useCallback(
316
474
  (postId, dialogId) => {
317
475
  currentPostRef.current = postId;
@@ -322,9 +480,12 @@ const HomeContainer = () => {
322
480
  case "publish-to-linked-in-dialog":
323
481
  openPublishToLinkedInDialog();
324
482
  break;
483
+ case "publish-to-facebook-dialog":
484
+ openPublishToFacebookDialog();
485
+ break;
325
486
  }
326
487
  },
327
- [openImageGenerateDialog, openPublishToLinkedInDialog]
488
+ [openImageGenerateDialog, openPublishToLinkedInDialog, openPublishToFacebookDialog]
328
489
  );
329
490
  return /* @__PURE__ */ jsxs(Fragment, { children: [
330
491
  /* @__PURE__ */ jsx(
@@ -334,13 +495,27 @@ const HomeContainer = () => {
334
495
  title: "Publicar en LinkedIn",
335
496
  confirmText: "Publicar",
336
497
  onCancelAction: () => onCancel("publish-to-linked-in-dialog"),
337
- onConfirmAction: onConfirmPublishToLinedIn,
498
+ onConfirmAction: () => onConfirmPublish("linkedin"),
338
499
  children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", children: [
339
500
  /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "¿Seguro que deseas publicar este artículo en LinkedIn?" }),
340
501
  /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "¡Esta acción es irreversible desde esta plataforma!" })
341
502
  ] })
342
503
  }
343
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
+ ),
344
519
  /* @__PURE__ */ jsx(GenerateImageDialog, { onConfirmAction: onConfirmGenerateImage, onCancelAction: () => onCancel("generate-image-dialog") }),
345
520
  /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
346
521
  /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
@@ -354,6 +529,7 @@ const HomeContainer = () => {
354
529
  posts,
355
530
  loadingImage,
356
531
  loadingPublishLinkedIn: loadingPublishingToLinkedIn,
532
+ loadingPublishingToFacebook,
357
533
  currentPost: currentPostRef,
358
534
  onClickAction
359
535
  }
@@ -396,14 +572,14 @@ const validationSchema = Yup.object().shape({
396
572
  titulo: Yup.string().notRequired(),
397
573
  autor: Yup.string().required("Este campo es obligatorio"),
398
574
  categoria: Yup.string().required("Este campo es obligatorio"),
399
- keywords: Yup.string().notRequired(),
575
+ temperatura: Yup.string().notRequired(),
400
576
  instrucciones: Yup.string().notRequired()
401
577
  });
402
578
  const initialValues = {
403
579
  titulo: "",
404
580
  autor: "",
405
581
  categoria: "",
406
- keywords: "",
582
+ temperatura: "0.2",
407
583
  instrucciones: ""
408
584
  };
409
585
  const Form = ({ onSubmit, categorias, users }) => {
@@ -494,25 +670,30 @@ const Form = ({ onSubmit, categorias, users }) => {
494
670
  ]
495
671
  }
496
672
  ) }),
497
- /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(
673
+ /* @__PURE__ */ jsx(Grid.Item, { xs: 12, s: 12, m: 6, children: /* @__PURE__ */ jsxs(
498
674
  Field.Root,
499
675
  {
500
- id: "keywords",
501
- error: touched.keywords && errors.keywords ? errors.keywords : void 0,
676
+ id: "temperatura",
677
+ error: touched.temperatura && errors.temperatura ? errors.temperatura : void 0,
502
678
  width: "100%",
503
- hint: "Ej: Innovación, Desarrollo, AI",
504
679
  children: [
505
- /* @__PURE__ */ jsx(Field.Label, { children: "Palabras clave" }),
506
- /* @__PURE__ */ jsx(
507
- TextInput,
508
- {
509
- name: "keywords",
510
- id: "keywords",
511
- placeholder: "Palabras clave separadas por comas",
512
- onChange: handleChange,
513
- value: values.keywords
514
- }
515
- ),
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
+ ] }),
516
697
  /* @__PURE__ */ jsx(Field.Hint, {})
517
698
  ]
518
699
  }
@@ -532,7 +713,8 @@ const Form = ({ onSubmit, categorias, users }) => {
532
713
  placeholder: "Escribe algunas instrucciones adicionales para el generador de contenido...",
533
714
  onChange: handleChange,
534
715
  value: values.instrucciones,
535
- rows: 8
716
+ resizable: false,
717
+ rows: 10
536
718
  }
537
719
  )
538
720
  ]
@@ -639,7 +821,6 @@ const GenerateContentContainer = () => {
639
821
  }
640
822
  }
641
823
  });
642
- console.log("🚀 ~ GenerateContentContainer ~ data:", data.length);
643
824
  if (isMounted) {
644
825
  setUsers(data);
645
826
  }
@@ -672,16 +853,362 @@ const GenerateContent = () => {
672
853
  /* @__PURE__ */ jsx(GenerateContentContainer, {})
673
854
  ] });
674
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
+ };
675
1187
  const App = () => {
676
1188
  const { allowedActions, isLoading } = useRBAC(pluginPermissions);
677
1189
  if (isLoading) {
678
1190
  return /* @__PURE__ */ jsx(Page.Loading, {});
679
1191
  }
680
- return /* @__PURE__ */ jsx(Page.Protect, { permissions: [...pluginPermissions.access], children: /* @__PURE__ */ jsx(DesignSystemProvider, { locale: "es-MX", children: /* @__PURE__ */ jsxs(Routes, { children: [
681
- allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
682
- allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { path: "/generate-content", element: /* @__PURE__ */ jsx(GenerateContent, {}) }),
683
- /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
684
- ] }) }) });
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
+ ) }) }) });
685
1212
  };
686
1213
  export {
687
1214
  App