@interware/content-generator 0.1.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/_chunks/App-6ntGE8c-.js +702 -0
  4. package/dist/_chunks/App-CUBxywy1.mjs +684 -0
  5. package/dist/_chunks/Settings-CwPVYHgw.js +218 -0
  6. package/dist/_chunks/Settings-DTPXHARn.mjs +200 -0
  7. package/dist/_chunks/en-B4KWt_jN.js +4 -0
  8. package/dist/_chunks/en-Byx4XI2L.mjs +4 -0
  9. package/dist/_chunks/es-Cb6O-nM6.js +4 -0
  10. package/dist/_chunks/es-DlGQb34u.mjs +4 -0
  11. package/dist/_chunks/getTranslation-AfxwBFKN.js +4 -0
  12. package/dist/_chunks/getTranslation-Di36t3DD.mjs +5 -0
  13. package/dist/_chunks/index-CkkBgYlg.mjs +126 -0
  14. package/dist/_chunks/index-Dcpjhvs5.js +125 -0
  15. package/dist/admin/index.js +3 -0
  16. package/dist/admin/index.mjs +4 -0
  17. package/dist/admin/src/components/Initializer.d.ts +5 -0
  18. package/dist/admin/src/components/PluginIcon.d.ts +2 -0
  19. package/dist/admin/src/components/commons/dialog/index.d.ts +12 -0
  20. package/dist/admin/src/components/commons/generate-image-dialog/index.d.ts +5 -0
  21. package/dist/admin/src/components/generate-content/Form.d.ts +15 -0
  22. package/dist/admin/src/components/home/table.d.ts +12 -0
  23. package/dist/admin/src/components/index.d.ts +12 -0
  24. package/dist/admin/src/components/settings/Form.d.ts +8 -0
  25. package/dist/admin/src/containers/generate-content/index.d.ts +2 -0
  26. package/dist/admin/src/containers/home/index.d.ts +2 -0
  27. package/dist/admin/src/containers/settings/index.d.ts +7 -0
  28. package/dist/admin/src/hooks/useDialog.d.ts +11 -0
  29. package/dist/admin/src/index.d.ts +10 -0
  30. package/dist/admin/src/lib/use-cases/articles/index.d.ts +5 -0
  31. package/dist/admin/src/lib/use-cases/categories/index.d.ts +5 -0
  32. package/dist/admin/src/lib/use-cases/generate-content/index.d.ts +6 -0
  33. package/dist/admin/src/lib/use-cases/linkedin/index.d.ts +5 -0
  34. package/dist/admin/src/lib/use-cases/settings/index.d.ts +6 -0
  35. package/dist/admin/src/lib/use-cases/users/index.d.ts +5 -0
  36. package/dist/admin/src/pages/App.d.ts +2 -0
  37. package/dist/admin/src/pages/GenerateContent.d.ts +2 -0
  38. package/dist/admin/src/pages/HomePage.d.ts +2 -0
  39. package/dist/admin/src/pages/Settings.d.ts +2 -0
  40. package/dist/admin/src/permissions.d.ts +7 -0
  41. package/dist/admin/src/pluginId.d.ts +1 -0
  42. package/dist/admin/src/reducers/dialog/index.d.ts +6 -0
  43. package/dist/admin/src/types/reducers.d.ts +4 -0
  44. package/dist/admin/src/utils/getTranslation.d.ts +2 -0
  45. package/dist/server/index.js +3149 -0
  46. package/dist/server/index.mjs +3130 -0
  47. package/dist/server/src/bootstrap.d.ts +5 -0
  48. package/dist/server/src/config/index.d.ts +5 -0
  49. package/dist/server/src/content-types/index.d.ts +2 -0
  50. package/dist/server/src/controllers/config.d.ts +8 -0
  51. package/dist/server/src/controllers/controller.d.ts +15 -0
  52. package/dist/server/src/controllers/index.d.ts +22 -0
  53. package/dist/server/src/destroy.d.ts +5 -0
  54. package/dist/server/src/index.d.ts +148 -0
  55. package/dist/server/src/middlewares/index.d.ts +2 -0
  56. package/dist/server/src/policies/index.d.ts +2 -0
  57. package/dist/server/src/register.d.ts +5 -0
  58. package/dist/server/src/routes/admin.d.ts +10 -0
  59. package/dist/server/src/routes/content-api.d.ts +10 -0
  60. package/dist/server/src/routes/index.d.ts +27 -0
  61. package/dist/server/src/services/index.d.ts +83 -0
  62. package/dist/server/src/services/service.d.ts +86 -0
  63. package/package.json +98 -0
@@ -0,0 +1,684 @@
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";
5
+ import { useIntl } from "react-intl";
6
+ import { useCallback, memo, useMemo, useState, useRef, useEffect } from "react";
7
+ import { useDispatch, useSelector } from "react-redux";
8
+ import { c as closeDialog, o as openDialog, P as PLUGIN_ID, p as pluginPermissions } from "./index-CkkBgYlg.mjs";
9
+ import { Play, Image } from "@strapi/icons";
10
+ import { g as getTranslation } from "./getTranslation-Di36t3DD.mjs";
11
+ import { useFormik } from "formik";
12
+ import * as Yup from "yup";
13
+ const useDialog = (id) => {
14
+ const dispatch = useDispatch();
15
+ const state = useSelector((state2) => state2.content_generator_commonDialog.open[id] ?? false);
16
+ return {
17
+ open: state,
18
+ openDialog: () => dispatch(openDialog(id)),
19
+ closeDialog: () => dispatch(closeDialog(id))
20
+ };
21
+ };
22
+ const useArticles = () => {
23
+ const { get } = useFetchClient();
24
+ const getArticles = useCallback(
25
+ async (config) => {
26
+ return get("/strapi-content-generator-gemini/posts", config);
27
+ },
28
+ [get]
29
+ );
30
+ return {
31
+ getArticles
32
+ };
33
+ };
34
+ const useGenerateContent = () => {
35
+ const { post } = useFetchClient();
36
+ const generateContent = useCallback(
37
+ async (data, config) => {
38
+ return post("/strapi-content-generator-gemini/generate-article", data, config);
39
+ },
40
+ [post]
41
+ );
42
+ const generateImage = useCallback(
43
+ async (data, config) => {
44
+ return post("/strapi-content-generator-gemini/generate-image", data, config);
45
+ },
46
+ [post]
47
+ );
48
+ return {
49
+ generateContent,
50
+ generateImage
51
+ };
52
+ };
53
+ const usePublishLinkedIn = () => {
54
+ const { post } = useFetchClient();
55
+ const publishToLinkedIn = useCallback(
56
+ async (data, config) => {
57
+ return post("/strapi-content-generator-gemini/publish-linkedin", data, config);
58
+ },
59
+ [post]
60
+ );
61
+ return {
62
+ publishToLinkedIn
63
+ };
64
+ };
65
+ const PublishingTableComponent = ({
66
+ posts,
67
+ currentPost,
68
+ loadingImage,
69
+ loadingPublishLinkedIn,
70
+ onClickAction
71
+ }) => {
72
+ const rows = useMemo(() => {
73
+ const opciones = {
74
+ day: "2-digit",
75
+ month: "long",
76
+ year: "numeric"
77
+ };
78
+ return posts.map((post) => {
79
+ const fecha = post.fecha_publicacion ? new Date(post.fecha_publicacion).toLocaleDateString("es-ES", opciones) : "No definida";
80
+ return {
81
+ id: post.documentId,
82
+ fecha,
83
+ 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,
86
+ hasImage: Boolean(post.image)
87
+ };
88
+ });
89
+ }, [posts]);
90
+ return /* @__PURE__ */ jsx(Box, { padding: 6, width: "100%", children: /* @__PURE__ */ jsxs(Table, { colCount: 7, rowCount: rows.length + 1, children: [
91
+ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
92
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "Fecha de publicación" }) }),
93
+ /* @__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
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", justifyContent: "center", children: [
96
+ /* @__PURE__ */ jsx("i", { className: "fa-brands fa-linkedin" }),
97
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: "LinkedIn" })
98
+ ] }) }),
99
+ /* @__PURE__ */ jsx(Th, {}),
100
+ /* @__PURE__ */ jsx(Th, {})
101
+ ] }) }),
102
+ /* @__PURE__ */ jsx(Tbody, { children: rows.map((row) => {
103
+ const buttonLoadingImage = loadingImage && currentPost.current === row.id;
104
+ const buttonLoadingPublishingLinkediN = loadingPublishLinkedIn && currentPost.current === row.id;
105
+ return /* @__PURE__ */ jsxs(Tr, { children: [
106
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: row.fecha }) }),
107
+ /* @__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(
110
+ Button,
111
+ {
112
+ disabled: row.linkedInURL,
113
+ onClick: () => onClickAction(row.id, "publish-to-linked-in-dialog"),
114
+ loading: buttonLoadingPublishingLinkediN,
115
+ startIcon: /* @__PURE__ */ jsx(Play, {}),
116
+ children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingImage ? "publicando" : "publicar" })
117
+ }
118
+ ) }),
119
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
120
+ Button,
121
+ {
122
+ disabled: row.hasImage,
123
+ onClick: () => onClickAction(row.id, "generate-image-dialog"),
124
+ loading: buttonLoadingImage,
125
+ startIcon: /* @__PURE__ */ jsx(Image, {}),
126
+ children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: buttonLoadingImage ? "generando" : "generar" })
127
+ }
128
+ ) }),
129
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
130
+ LinkButton,
131
+ {
132
+ href: `/admin/content-manager/collection-types/api::articulo.articulo/${row.id}`,
133
+ children: /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "ver" })
134
+ }
135
+ ) })
136
+ ] }, row.id);
137
+ }) })
138
+ ] }) });
139
+ };
140
+ const PublishingTable = memo(PublishingTableComponent);
141
+ const DialogCommon = ({
142
+ id,
143
+ title = "Confirmación",
144
+ children,
145
+ cancelText = "Cancelar",
146
+ confirmText = "Confirmar",
147
+ onCancelAction,
148
+ onConfirmAction
149
+ }) => {
150
+ const { open } = useDialog(id);
151
+ return /* @__PURE__ */ jsx(Dialog.Root, { open, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
152
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
153
+ /* @__PURE__ */ jsx(Dialog.Body, { children }),
154
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
155
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { fullWidth: true, variant: "tertiary", onClick: onCancelAction, children: cancelText }) }),
156
+ /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button, { fullWidth: true, variant: "danger", onClick: onConfirmAction, children: confirmText }) })
157
+ ] })
158
+ ] }) });
159
+ };
160
+ const GenerateImageDialog = ({ onCancelAction, onConfirmAction }) => {
161
+ const [imageInstructions, setImageInstructions] = useState("");
162
+ return /* @__PURE__ */ jsx(
163
+ DialogCommon,
164
+ {
165
+ id: "generate-image-dialog",
166
+ title: "Generar imágen con Gemini",
167
+ cancelText: "Cancelar",
168
+ confirmText: "Confirmar",
169
+ onCancelAction,
170
+ onConfirmAction: () => {
171
+ onConfirmAction(imageInstructions);
172
+ },
173
+ children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 3, children: [
174
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", justifyContent: "center", width: "100%", children: [
175
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "¿Deseas generar la imágen para el artículo ahora?" }),
176
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "No te preocupes, también puedes hacerlo más adelante." })
177
+ ] }) }),
178
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsxs(
179
+ Field.Root,
180
+ {
181
+ id: "image-instructions",
182
+ width: "100%",
183
+ children: [
184
+ /* @__PURE__ */ jsx(Field.Label, { children: "Instrucciones adicionales" }),
185
+ /* @__PURE__ */ jsx(
186
+ Textarea,
187
+ {
188
+ name: "image-instructions",
189
+ value: imageInstructions,
190
+ placeholder: "Instrucciones adicionales para generar la imágen",
191
+ onChange: (e) => {
192
+ const { value } = e.target;
193
+ setImageInstructions(value);
194
+ },
195
+ resizable: false,
196
+ rows: 3
197
+ }
198
+ )
199
+ ]
200
+ }
201
+ ) })
202
+ ] })
203
+ }
204
+ );
205
+ };
206
+ const HomeContainer = () => {
207
+ const [posts, setPosts] = useState([]);
208
+ const [loadingImage, setIsLoadingImage] = useState(false);
209
+ const [loadingPublishingToLinkedIn, setIsLoadingPublishingToLinkedIn] = useState(false);
210
+ const currentPostRef = useRef("");
211
+ const { formatMessage } = useIntl();
212
+ const { openDialog: openImageGenerateDialog, closeDialog: closeImageGenerateDialog } = useDialog("generate-image-dialog");
213
+ const { openDialog: openPublishToLinkedInDialog, closeDialog: closePublishToLinkedInDialog } = useDialog("publish-to-linked-in-dialog");
214
+ const { getArticles } = useArticles();
215
+ const { generateImage } = useGenerateContent();
216
+ const { publishToLinkedIn } = usePublishLinkedIn();
217
+ const { toggleNotification } = useNotification();
218
+ const fetchPosts = useCallback(async () => {
219
+ let isMounted = true;
220
+ try {
221
+ const { data } = await getArticles({
222
+ params: {
223
+ populate: {
224
+ image: { fields: ["url", "caption", "alternativeText"] },
225
+ autor: { fields: ["nombre", "apellidos"] }
226
+ },
227
+ sort: ["createdAt:desc"]
228
+ }
229
+ });
230
+ if (isMounted) {
231
+ setPosts(data);
232
+ }
233
+ } catch (error) {
234
+ console.error("Error fetching posts:", error);
235
+ }
236
+ return () => {
237
+ isMounted = false;
238
+ };
239
+ }, [getArticles]);
240
+ useEffect(() => {
241
+ fetchPosts();
242
+ }, [fetchPosts]);
243
+ const onConfirmGenerateImage = useCallback(
244
+ async (imageInstructions) => {
245
+ closeImageGenerateDialog();
246
+ setIsLoadingImage(true);
247
+ try {
248
+ await generateImage({
249
+ articleId: currentPostRef.current,
250
+ imageInstructions
251
+ });
252
+ fetchPosts();
253
+ toggleNotification({
254
+ type: "success",
255
+ message: formatMessage({
256
+ id: `${PLUGIN_ID}.config.load.error`,
257
+ defaultMessage: "Imagen generada con éxito"
258
+ })
259
+ });
260
+ } catch (err) {
261
+ toggleNotification({
262
+ type: "danger",
263
+ message: formatMessage({
264
+ id: `${PLUGIN_ID}.config.load.error`,
265
+ defaultMessage: "Ocurrió un error al generar la imagen"
266
+ })
267
+ });
268
+ } finally {
269
+ setIsLoadingImage(false);
270
+ }
271
+ },
272
+ [generateImage, closeImageGenerateDialog, setIsLoadingImage]
273
+ );
274
+ const onConfirmPublishToLinedIn = useCallback(
275
+ async () => {
276
+ closePublishToLinkedInDialog();
277
+ setIsLoadingPublishingToLinkedIn(true);
278
+ try {
279
+ await publishToLinkedIn({ articleId: currentPostRef.current });
280
+ fetchPosts();
281
+ toggleNotification({
282
+ type: "success",
283
+ message: formatMessage({
284
+ id: `${PLUGIN_ID}.config.load.error`,
285
+ defaultMessage: "Artículo publicado en LinkedIn con éxito"
286
+ })
287
+ });
288
+ } catch (err) {
289
+ toggleNotification({
290
+ type: "danger",
291
+ message: formatMessage({
292
+ id: `${PLUGIN_ID}.config.load.error`,
293
+ defaultMessage: "Ocurrió un error al publicar el artículo en LinkedIn"
294
+ })
295
+ });
296
+ } finally {
297
+ setIsLoadingPublishingToLinkedIn(false);
298
+ }
299
+ },
300
+ [publishToLinkedIn, closeImageGenerateDialog, setIsLoadingPublishingToLinkedIn]
301
+ );
302
+ const onCancel = useCallback((dialogId) => {
303
+ currentPostRef.current = "";
304
+ switch (dialogId) {
305
+ case "generate-image-dialog":
306
+ closeImageGenerateDialog();
307
+ case "publish-to-linked-in-dialog":
308
+ closePublishToLinkedInDialog();
309
+ default:
310
+ return;
311
+ }
312
+ }, [closeImageGenerateDialog, closePublishToLinkedInDialog]);
313
+ const onClickAction = useCallback(
314
+ (postId, dialogId) => {
315
+ currentPostRef.current = postId;
316
+ switch (dialogId) {
317
+ case "generate-image-dialog":
318
+ openImageGenerateDialog();
319
+ case "publish-to-linked-in-dialog":
320
+ openPublishToLinkedInDialog();
321
+ }
322
+ },
323
+ [openImageGenerateDialog, openPublishToLinkedInDialog]
324
+ );
325
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
326
+ /* @__PURE__ */ jsx(
327
+ DialogCommon,
328
+ {
329
+ id: "publish-to-linked-in-dialog",
330
+ title: "Publicar en LinkedIn",
331
+ confirmText: "Publicar",
332
+ onCancelAction: () => onCancel("publish-to-linked-in-dialog"),
333
+ onConfirmAction: onConfirmPublishToLinedIn,
334
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", children: [
335
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "¿Seguro que deseas publicar este artículo en LinkedIn?" }),
336
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "¡Esta acción es irreversible desde esta plataforma!" })
337
+ ] })
338
+ }
339
+ ),
340
+ /* @__PURE__ */ jsx(GenerateImageDialog, { onConfirmAction: onConfirmGenerateImage, onCancelAction: () => onCancel("generate-image-dialog") }),
341
+ /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
342
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
343
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: formatMessage({ id: getTranslation("plugin.name") }) }),
344
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
345
+ ] }) }),
346
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "end", alignItems: "end", width: "100%", children: /* @__PURE__ */ jsx(LinkButton, { href: "/admin/plugins/strapi-content-generator-gemini/generate-content", children: "Generar nuevo artículo" }) }) }),
347
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsx(
348
+ PublishingTable,
349
+ {
350
+ posts,
351
+ loadingImage,
352
+ loadingPublishLinkedIn: loadingPublishingToLinkedIn,
353
+ currentPost: currentPostRef,
354
+ onClickAction
355
+ }
356
+ ) })
357
+ ] })
358
+ ] });
359
+ };
360
+ const HomePage = () => {
361
+ const { formatMessage } = useIntl();
362
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Page.Main, { children: [
363
+ /* @__PURE__ */ jsx(Page.Title, { children: formatMessage({ id: getTranslation("plugin.name") }) }),
364
+ /* @__PURE__ */ jsx(HomeContainer, {})
365
+ ] }) });
366
+ };
367
+ const useCategories = () => {
368
+ const { get } = useFetchClient();
369
+ const getCategories = useCallback(
370
+ async (config) => {
371
+ return get("/strapi-content-generator-gemini/categories", config);
372
+ },
373
+ [get]
374
+ );
375
+ return {
376
+ getCategories
377
+ };
378
+ };
379
+ const useUsers = () => {
380
+ const { get } = useFetchClient();
381
+ const getUsers = useCallback(
382
+ async (config) => {
383
+ return get("/strapi-content-generator-gemini/users", config);
384
+ },
385
+ [get]
386
+ );
387
+ return {
388
+ getUsers
389
+ };
390
+ };
391
+ const validationSchema = Yup.object().shape({
392
+ titulo: Yup.string().notRequired(),
393
+ autor: Yup.string().required("Este campo es obligatorio"),
394
+ categoria: Yup.string().required("Este campo es obligatorio"),
395
+ keywords: Yup.string().notRequired(),
396
+ instrucciones: Yup.string().notRequired()
397
+ });
398
+ const initialValues = {
399
+ titulo: "",
400
+ autor: "",
401
+ categoria: "",
402
+ keywords: "",
403
+ instrucciones: ""
404
+ };
405
+ const Form = ({ onSubmit, categorias, users }) => {
406
+ const formik = useFormik({
407
+ initialValues,
408
+ validationSchema,
409
+ onSubmit
410
+ });
411
+ const { values, errors, touched, isValid, dirty, isSubmitting, handleChange, setFieldValue, handleSubmit } = formik;
412
+ return /* @__PURE__ */ jsx(Box, { background: "neutral0", padding: 6, margin: 6, width: "100%", children: /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
413
+ /* @__PURE__ */ jsx(Grid.Item, { xs: 12, s: 12, m: 6, children: /* @__PURE__ */ jsxs(
414
+ Field.Root,
415
+ {
416
+ id: "titulo",
417
+ error: touched.titulo && errors.titulo ? errors.titulo : void 0,
418
+ hint: "Ej: Las 10 mejores tendencias en IA para 2026",
419
+ width: "100%",
420
+ children: [
421
+ /* @__PURE__ */ jsx(Field.Label, { children: "Título" }),
422
+ /* @__PURE__ */ jsx(
423
+ TextInput,
424
+ {
425
+ name: "titulo",
426
+ label: "Título",
427
+ placeholder: "Título sugerido para el blog",
428
+ onChange: handleChange,
429
+ type: "text",
430
+ value: values.titulo
431
+ }
432
+ ),
433
+ /* @__PURE__ */ jsx(Field.Error, {}),
434
+ /* @__PURE__ */ jsx(Field.Hint, {})
435
+ ]
436
+ }
437
+ ) }),
438
+ /* @__PURE__ */ jsx(Grid.Item, { xs: 12, s: 12, m: 6, children: /* @__PURE__ */ jsxs(
439
+ Field.Root,
440
+ {
441
+ id: "categoria",
442
+ error: touched.categoria && errors.categoria ? errors.categoria : void 0,
443
+ hint: "Puedes agregar más desde el Content Type: Categorías",
444
+ width: "100%",
445
+ required: true,
446
+ children: [
447
+ /* @__PURE__ */ jsx(Field.Label, { children: "Categoría" }),
448
+ /* @__PURE__ */ jsx(
449
+ Combobox,
450
+ {
451
+ name: "categoria",
452
+ placeholder: "Selecciona o escribe una categoría",
453
+ value: values.categoria,
454
+ onChange: (v) => setFieldValue("categoria", v),
455
+ onCreateOption: (v) => {
456
+ setFieldValue("categoria", v);
457
+ },
458
+ required: true,
459
+ children: categorias.map((c) => /* @__PURE__ */ jsx(ComboboxOption, { value: c.documentId, children: c.categoria }, c.documentId))
460
+ }
461
+ ),
462
+ /* @__PURE__ */ jsx(Field.Hint, {})
463
+ ]
464
+ }
465
+ ) }),
466
+ /* @__PURE__ */ jsx(Grid.Item, { xs: 12, s: 12, m: 6, children: /* @__PURE__ */ jsxs(
467
+ Field.Root,
468
+ {
469
+ id: "autor",
470
+ error: touched.autor && errors.autor ? errors.autor : void 0,
471
+ width: "100%",
472
+ required: true,
473
+ children: [
474
+ /* @__PURE__ */ jsx(Field.Label, { children: "Autor" }),
475
+ /* @__PURE__ */ jsx(
476
+ Combobox,
477
+ {
478
+ name: "autor",
479
+ placeholder: "Selecciona o escribe una categoría",
480
+ value: values.autor,
481
+ onChange: (v) => setFieldValue("autor", v),
482
+ onCreateOption: (v) => {
483
+ setFieldValue("autor", v);
484
+ },
485
+ required: true,
486
+ children: users.map((c) => /* @__PURE__ */ jsx(ComboboxOption, { value: c.documentId, children: c.nombre_completo }, c.documentId))
487
+ }
488
+ ),
489
+ /* @__PURE__ */ jsx(Field.Hint, {})
490
+ ]
491
+ }
492
+ ) }),
493
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(
494
+ Field.Root,
495
+ {
496
+ id: "keywords",
497
+ error: touched.keywords && errors.keywords ? errors.keywords : void 0,
498
+ width: "100%",
499
+ hint: "Ej: Innovación, Desarrollo, AI",
500
+ children: [
501
+ /* @__PURE__ */ jsx(Field.Label, { children: "Palabras clave" }),
502
+ /* @__PURE__ */ jsx(
503
+ TextInput,
504
+ {
505
+ name: "keywords",
506
+ id: "keywords",
507
+ placeholder: "Palabras clave separadas por comas",
508
+ onChange: handleChange,
509
+ value: values.keywords
510
+ }
511
+ ),
512
+ /* @__PURE__ */ jsx(Field.Hint, {})
513
+ ]
514
+ }
515
+ ) }),
516
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(
517
+ Field.Root,
518
+ {
519
+ id: "instrucciones",
520
+ error: touched.instrucciones && errors.instrucciones ? errors.instrucciones : void 0,
521
+ width: "100%",
522
+ children: [
523
+ /* @__PURE__ */ jsx(Field.Label, { children: "Instrucciones adicionales" }),
524
+ /* @__PURE__ */ jsx(
525
+ Textarea,
526
+ {
527
+ name: "instrucciones",
528
+ placeholder: "Escribe algunas instrucciones adicionales para el generador de contenido...",
529
+ onChange: handleChange,
530
+ value: values.instrucciones,
531
+ rows: 8
532
+ }
533
+ )
534
+ ]
535
+ }
536
+ ) }),
537
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsx(Flex, { width: "100%", justifyContent: { initial: "end" }, alignItems: { initial: "end" }, children: /* @__PURE__ */ jsx(
538
+ Button,
539
+ {
540
+ disabled: !(isValid && dirty) || isSubmitting,
541
+ loading: isSubmitting,
542
+ type: "submit",
543
+ children: "Generar contenido"
544
+ }
545
+ ) }) })
546
+ ] }) }) });
547
+ };
548
+ const GenerateContentContainer = () => {
549
+ const [documentId, setDocumentId] = useState("");
550
+ const [categorias, setCategorias] = useState([]);
551
+ const [users, setUsers] = useState([]);
552
+ const { getCategories } = useCategories();
553
+ const { getUsers } = useUsers();
554
+ const navigate = useNavigate();
555
+ const { closeDialog: closeDialog2, openDialog: openDialog2 } = useDialog("generate-image-dialog");
556
+ const { generateContent, generateImage } = useGenerateContent();
557
+ const { formatMessage } = useIntl();
558
+ const { toggleNotification } = useNotification();
559
+ const onSubmit = useCallback(
560
+ async (values, { setSubmitting }) => {
561
+ try {
562
+ const { data } = await generateContent(values);
563
+ setDocumentId(data.documentId);
564
+ openDialog2();
565
+ toggleNotification({
566
+ type: "success",
567
+ message: formatMessage({
568
+ id: `${PLUGIN_ID}.config.load.error`,
569
+ defaultMessage: "Artículo generado con éxito"
570
+ })
571
+ });
572
+ } catch (error) {
573
+ console.error("Error generating content:", error);
574
+ toggleNotification({
575
+ type: "danger",
576
+ message: formatMessage({
577
+ id: `${PLUGIN_ID}.config.load.error`,
578
+ defaultMessage: "Ocurrió un error al generar el artículo"
579
+ })
580
+ });
581
+ } finally {
582
+ setSubmitting(false);
583
+ }
584
+ },
585
+ [generateContent, openDialog2]
586
+ );
587
+ const goToArticle = useCallback(() => {
588
+ navigate(
589
+ `/content-manager/collection-types/api::articulo.articulo/${documentId}`
590
+ );
591
+ }, [navigate, documentId]);
592
+ const onConfirmGenerateImage = useCallback(
593
+ async (imageInstructions) => {
594
+ try {
595
+ await generateImage({
596
+ articleId: documentId,
597
+ imageInstructions
598
+ });
599
+ goToArticle();
600
+ } catch (err) {
601
+ console.error("Error generating image:", err);
602
+ } finally {
603
+ closeDialog2();
604
+ }
605
+ },
606
+ [generateImage, documentId, closeDialog2, goToArticle]
607
+ );
608
+ const onCancelGenerateImage = useCallback(() => {
609
+ closeDialog2();
610
+ goToArticle();
611
+ }, [closeDialog2, goToArticle]);
612
+ const fetchCategories = useCallback(async () => {
613
+ let isMounted = true;
614
+ try {
615
+ const { data } = await getCategories();
616
+ if (isMounted) {
617
+ setCategorias(data);
618
+ }
619
+ } catch (error) {
620
+ console.error("Error fetching posts:", error);
621
+ }
622
+ return () => {
623
+ isMounted = false;
624
+ };
625
+ }, [getCategories]);
626
+ const fetchUsers = useCallback(async () => {
627
+ let isMounted = true;
628
+ try {
629
+ const { data } = await getUsers({
630
+ params: {
631
+ filters: {
632
+ blocked: {
633
+ $eq: false
634
+ }
635
+ }
636
+ }
637
+ });
638
+ console.log("🚀 ~ GenerateContentContainer ~ data:", data.length);
639
+ if (isMounted) {
640
+ setUsers(data);
641
+ }
642
+ } catch (error) {
643
+ console.error("Error fetching posts:", error);
644
+ }
645
+ return () => {
646
+ isMounted = false;
647
+ };
648
+ }, [getUsers]);
649
+ useEffect(() => {
650
+ fetchCategories();
651
+ fetchUsers();
652
+ }, [fetchCategories]);
653
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
654
+ /* @__PURE__ */ jsx(GenerateImageDialog, { onCancelAction: onCancelGenerateImage, onConfirmAction: onConfirmGenerateImage }),
655
+ /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
656
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
657
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Generar nuevo artículo" }),
658
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
659
+ ] }) }),
660
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsx(Flex, { alignItems: "end", width: "100%", justifyContent: "end", children: /* @__PURE__ */ jsx(LinkButton, { href: "/admin/plugins/strapi-content-generator-gemini", children: "Regresar" }) }) }),
661
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsx(Form, { categorias, users, onSubmit }) })
662
+ ] })
663
+ ] });
664
+ };
665
+ const GenerateContent = () => {
666
+ return /* @__PURE__ */ jsxs(Page.Main, { children: [
667
+ /* @__PURE__ */ jsx(Page.Title, { children: "Generar nuevo artículo" }),
668
+ /* @__PURE__ */ jsx(GenerateContentContainer, {})
669
+ ] });
670
+ };
671
+ const App = () => {
672
+ const { allowedActions, isLoading } = useRBAC(pluginPermissions);
673
+ if (isLoading) {
674
+ return /* @__PURE__ */ jsx(Page.Loading, {});
675
+ }
676
+ return /* @__PURE__ */ jsx(Page.Protect, { permissions: [...pluginPermissions.access], children: /* @__PURE__ */ jsx(DesignSystemProvider, { locale: "es-MX", children: /* @__PURE__ */ jsxs(Routes, { children: [
677
+ allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
678
+ allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { path: "/generate-content", element: /* @__PURE__ */ jsx(GenerateContent, {}) }),
679
+ /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
680
+ ] }) }) });
681
+ };
682
+ export {
683
+ App
684
+ };