@interware/content-generator 0.7.2 → 0.8.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.
- package/dist/_chunks/{App-CTh6owSM.mjs → App-2eX-mcfo.mjs} +2 -2
- package/dist/_chunks/{App-ugsCmlsC.js → App-CvZirjX_.js} +2 -2
- package/dist/_chunks/{ImageStudioTabs-Umf3GODW.mjs → ImageStudioTabs-B6fkCK-o.mjs} +353 -21
- package/dist/_chunks/{ImageStudioTabs-BFEF9ggK.js → ImageStudioTabs-CNktoRax.js} +351 -19
- package/dist/_chunks/{Settings-Sk78JUBS.js → Settings-B-QPodCh.js} +169 -1
- package/dist/_chunks/{Settings-CdGW2O8h.mjs → Settings-CaGruvIm.mjs} +170 -2
- package/dist/_chunks/{StudioApp-DoC0z4eh.js → StudioApp-BDoB31L0.js} +2 -2
- package/dist/_chunks/{StudioApp-CrGn-9Gg.mjs → StudioApp-CF03znuL.mjs} +2 -2
- package/dist/_chunks/{index-D6UAvh53.mjs → index-DlemRDzs.mjs} +3 -3
- package/dist/_chunks/{index-C2s9Jhcc.js → index-DwHGS-BJ.js} +3 -3
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/settings/DriveSettings.d.ts +77 -0
- package/dist/admin/src/features/drive/DriveTab.d.ts +42 -0
- package/dist/admin/src/features/image-studio/ImageStudioTabs.d.ts +3 -0
- package/dist/admin/src/features/library/FileCard.d.ts +5 -2
- package/dist/admin/src/features/library/LibraryTab.d.ts +5 -0
- package/dist/admin/src/features/library/driveSync.d.ts +41 -0
- package/dist/admin/src/features/templates/export/templateOrigin.d.ts +10 -0
- package/dist/admin/src/lib/use-cases/drive/index.d.ts +3 -0
- package/dist/server/index.js +1411 -49
- package/dist/server/index.mjs +1409 -47
- package/dist/server/src/bootstrap.d.ts +2 -0
- package/dist/server/src/content-types/drive-link/index.d.ts +62 -0
- package/dist/server/src/content-types/drive-link/schema.d.ts +68 -0
- package/dist/server/src/content-types/drive-op/index.d.ts +46 -0
- package/dist/server/src/content-types/drive-op/schema.d.ts +49 -0
- package/dist/server/src/content-types/index.d.ts +106 -0
- package/dist/server/src/controllers/drive.d.ts +30 -0
- package/dist/server/src/controllers/index.d.ts +130 -0
- package/dist/server/src/drive/client.d.ts +32 -0
- package/dist/server/src/drive/clientConfig.d.ts +27 -0
- package/dist/server/src/drive/common.d.ts +6 -0
- package/dist/server/src/drive/fakes.d.ts +104 -0
- package/dist/server/src/drive/files.d.ts +136 -0
- package/dist/server/src/drive/lifecycles.d.ts +7 -0
- package/dist/server/src/drive/links.d.ts +23 -0
- package/dist/server/src/drive/model.d.ts +99 -0
- package/dist/server/src/drive/oauth.d.ts +47 -0
- package/dist/server/src/drive/pull.d.ts +23 -0
- package/dist/server/src/drive/strapiPorts.d.ts +10 -0
- package/dist/server/src/drive/sync.d.ts +40 -0
- package/dist/server/src/index.d.ts +294 -0
- package/dist/server/src/services/drive.d.ts +141 -0
- package/dist/server/src/services/index.d.ts +55 -0
- package/package.json +1 -1
|
@@ -5,8 +5,8 @@ import { P as PluginProviders } from "./PluginProviders-DWMpcAIh.mjs";
|
|
|
5
5
|
import { useMemo, useState, useEffect, useRef, useCallback } from "react";
|
|
6
6
|
import { Box, Flex, Typography, Button, Badge, TextInput, Loader, Table, Thead, Tr, Th, VisuallyHidden, Tbody, Td, TextButton, IconButton, Link, LinkButton, Field, Radio, Modal, Textarea, Combobox, ComboboxOption, Toggle, Grid } from "@strapi/design-system";
|
|
7
7
|
import { Plus, Search, Check, Pencil, ExternalLink, ArrowLeft, Image, Clock, WarningCircle, Trash, Sparkle, PlusCircle } from "@strapi/icons";
|
|
8
|
-
import { P as PLUGIN_ID, S as STUDIO_PATH, p as pluginPermissions } from "./index-
|
|
9
|
-
import { L as LibraryImagePicker, P as PromptComposer, a as LEGACY_ROUTES, S as StudioRedirect, l as legacyRedirect } from "./ImageStudioTabs-
|
|
8
|
+
import { P as PLUGIN_ID, S as STUDIO_PATH, p as pluginPermissions } from "./index-DlemRDzs.mjs";
|
|
9
|
+
import { L as LibraryImagePicker, P as PromptComposer, a as LEGACY_ROUTES, S as StudioRedirect, l as legacyRedirect } from "./ImageStudioTabs-B6fkCK-o.mjs";
|
|
10
10
|
import { useIntl } from "react-intl";
|
|
11
11
|
import { useFormik } from "formik";
|
|
12
12
|
import * as Yup from "yup";
|
|
@@ -7,8 +7,8 @@ const PluginProviders = require("./PluginProviders-D79TeGSE.js");
|
|
|
7
7
|
const react = require("react");
|
|
8
8
|
const designSystem = require("@strapi/design-system");
|
|
9
9
|
const icons = require("@strapi/icons");
|
|
10
|
-
const index = require("./index-
|
|
11
|
-
const ImageStudioTabs = require("./ImageStudioTabs-
|
|
10
|
+
const index = require("./index-DwHGS-BJ.js");
|
|
11
|
+
const ImageStudioTabs = require("./ImageStudioTabs-CNktoRax.js");
|
|
12
12
|
const reactIntl = require("react-intl");
|
|
13
13
|
const formik = require("formik");
|
|
14
14
|
const Yup = require("yup");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Modal, Flex, Button, Loader, Typography, Grid, Box, lightTheme, Checkbox, Field, TextInput, Dialog, SingleSelect, SingleSelectOption, Link, Badge, IconButtonGroup, IconButton, Toggle, Menu, NumberInput, Divider, Textarea, Tabs } from "@strapi/design-system";
|
|
2
|
+
import { Modal, Flex, Button, Loader, Typography, Grid, Box, lightTheme, Checkbox, Field, TextInput, Dialog, SingleSelect, SingleSelectOption, Link, Badge, IconButtonGroup, IconButton, Toggle, Menu, Table, Thead, Tr, Th, VisuallyHidden, Tbody, Td, TextButton, LinkButton, ProgressBar, NumberInput, Divider, Textarea, Tabs } from "@strapi/design-system";
|
|
3
3
|
import { useLocation, Navigate, useSearchParams } from "react-router-dom";
|
|
4
|
-
import { S as STUDIO_PATH } from "./index-
|
|
4
|
+
import { S as STUDIO_PATH } from "./index-DlemRDzs.mjs";
|
|
5
5
|
import { useMemo, useState, useEffect, useId, useRef, useReducer, useCallback, useSyncExternalStore, useLayoutEffect, memo, forwardRef } from "react";
|
|
6
|
-
import { ChevronRight, Folder, Magic, Pencil, Upload, Sparkle, Plus, Cross, Download, ArrowClockwise, House, ChevronDown, FilePdf, File as File$1, More, Trash, User, Star, SquaresFour, Quotes, PriceTag, Paragraph, NumberList, Minus, Italic, Image as Image$1, HeadingTwo, HeadingOne, Hashtag, Feather, Drag, BulletList, ArrowLeft, Check, ArrowsCounterClockwise, ArrowLineLeft, ArrowLineRight, Expand, WarningCircle, Play, Phone, Mail, PinMap, Earth, PresentationChart, ChartPie, ChartBubble, Cog, Database, Cloud, Globe, Server, Lightbulb, Crown, SealCheck, Briefcase, Rocket, Shield, CheckCircle, Lightning, Heart, Collapse, Bold, ArrowUp, ArrowDown, Duplicate, Lock, EyeStriked, Eye } from "@strapi/icons";
|
|
6
|
+
import { ChevronRight, Folder, Magic, Pencil, Upload, Sparkle, Plus, Cross, Download, ArrowClockwise, House, ChevronDown, FilePdf, File as File$1, More, ExternalLink, Trash, User, Star, SquaresFour, Quotes, PriceTag, Paragraph, NumberList, Minus, Italic, Image as Image$1, HeadingTwo, HeadingOne, Hashtag, Feather, Drag, BulletList, ArrowLeft, Check, ArrowsCounterClockwise, ArrowLineLeft, ArrowLineRight, Expand, WarningCircle, Play, Phone, Mail, PinMap, Earth, PresentationChart, ChartPie, ChartBubble, Cog, Database, Cloud, Globe, Server, Lightbulb, Crown, SealCheck, Briefcase, Rocket, Shield, CheckCircle, Lightning, Heart, Collapse, Bold, ArrowUp, ArrowDown, Duplicate, Lock, EyeStriked, Eye } from "@strapi/icons";
|
|
7
7
|
import { useTheme } from "styled-components";
|
|
8
8
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
9
9
|
import { toPng, toSvg, toJpeg } from "html-to-image";
|
|
@@ -1171,7 +1171,7 @@ const closestRatio = (width, height, ratios) => {
|
|
|
1171
1171
|
};
|
|
1172
1172
|
return ratios.reduce((best, r) => Math.abs(value(r) - target) < Math.abs(value(best) - target) ? r : best);
|
|
1173
1173
|
};
|
|
1174
|
-
const BASE = "/strapi-content-generator-gemini/image-studio";
|
|
1174
|
+
const BASE$2 = "/strapi-content-generator-gemini/image-studio";
|
|
1175
1175
|
const toResult = ({ image, mimeType, model, grounding }) => ({
|
|
1176
1176
|
src: `data:${mimeType};base64,${image}`,
|
|
1177
1177
|
model,
|
|
@@ -1183,7 +1183,7 @@ const errorMessage = (err, fallback) => {
|
|
|
1183
1183
|
};
|
|
1184
1184
|
const createGeneratorApi = (client) => ({
|
|
1185
1185
|
async models() {
|
|
1186
|
-
const { data } = await client.get(`${BASE}/models`);
|
|
1186
|
+
const { data } = await client.get(`${BASE$2}/models`);
|
|
1187
1187
|
return data;
|
|
1188
1188
|
},
|
|
1189
1189
|
async generate(p) {
|
|
@@ -1196,7 +1196,7 @@ const createGeneratorApi = (client) => ({
|
|
|
1196
1196
|
form.append("allowPeople", String(p.allowPeople));
|
|
1197
1197
|
form.append("grounding", String(p.grounding));
|
|
1198
1198
|
p.references.forEach((f) => form.append("references", f, f.name));
|
|
1199
|
-
const { data } = await client.post(`${BASE}/generate`, form);
|
|
1199
|
+
const { data } = await client.post(`${BASE$2}/generate`, form);
|
|
1200
1200
|
return toResult(data);
|
|
1201
1201
|
},
|
|
1202
1202
|
async edit(p) {
|
|
@@ -1209,11 +1209,11 @@ const createGeneratorApi = (client) => ({
|
|
|
1209
1209
|
p.references.forEach((f) => form.append("references", f, f.name));
|
|
1210
1210
|
if (p.aspectRatio) form.append("aspectRatio", p.aspectRatio);
|
|
1211
1211
|
form.append("image", new File([blob], `base.${blob.type.split("/")[1] ?? "png"}`, { type: blob.type }));
|
|
1212
|
-
const { data } = await client.post(`${BASE}/edit`, form);
|
|
1212
|
+
const { data } = await client.post(`${BASE$2}/edit`, form);
|
|
1213
1213
|
return toResult(data);
|
|
1214
1214
|
},
|
|
1215
1215
|
async expand(prompt) {
|
|
1216
|
-
const { data } = await client.post(`${BASE}/expand`, { prompt });
|
|
1216
|
+
const { data } = await client.post(`${BASE$2}/expand`, { prompt });
|
|
1217
1217
|
return data.prompt;
|
|
1218
1218
|
}
|
|
1219
1219
|
});
|
|
@@ -1835,7 +1835,7 @@ const isImage = (f) => (f.mime ?? "").startsWith("image/");
|
|
|
1835
1835
|
const canRemoveBackground = (f) => isImage(f) && f.mime !== "image/svg+xml";
|
|
1836
1836
|
const isGenerated = (f) => (f.caption ?? "").startsWith("AI generated content");
|
|
1837
1837
|
const formatSize = (kb = 0) => kb >= 1024 ? `${(kb / 1024).toFixed(1)} MB` : `${Math.round(kb)} KB`;
|
|
1838
|
-
const FileCard = ({ file, onAction, busy }) => {
|
|
1838
|
+
const FileCard = ({ file, onAction, busy, drive }) => {
|
|
1839
1839
|
const c = useThemeColors();
|
|
1840
1840
|
const thumb2 = file.formats?.thumbnail?.url ?? file.formats?.small?.url ?? file.url;
|
|
1841
1841
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1874,7 +1874,8 @@ const FileCard = ({ file, onAction, busy }) => {
|
|
|
1874
1874
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
1875
1875
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: formatSize(file.size) }),
|
|
1876
1876
|
isGenerated(file) && /* @__PURE__ */ jsx(Badge, { children: "Generada" }),
|
|
1877
|
-
busy && /* @__PURE__ */ jsx(Badge, { active: true, children: busy })
|
|
1877
|
+
busy && /* @__PURE__ */ jsx(Badge, { active: true, children: busy }),
|
|
1878
|
+
drive?.label && /* @__PURE__ */ jsx(Badge, { variant: drive.tone, title: drive.title, children: drive.label })
|
|
1878
1879
|
] })
|
|
1879
1880
|
] }),
|
|
1880
1881
|
/* @__PURE__ */ jsxs(Menu.Root, { children: [
|
|
@@ -1886,6 +1887,8 @@ const FileCard = ({ file, onAction, busy }) => {
|
|
|
1886
1887
|
/* @__PURE__ */ jsx(Menu.Item, { onSelect: () => onAction("replace", file), children: "Reemplazar" }),
|
|
1887
1888
|
/* @__PURE__ */ jsx(Menu.Item, { onSelect: () => onAction("move", file), children: "Mover a…" }),
|
|
1888
1889
|
canRemoveBackground(file) && /* @__PURE__ */ jsx(Menu.Item, { onSelect: () => onAction("removeBackground", file), disabled: !!busy, children: "Quitar fondo" }),
|
|
1890
|
+
drive?.action === "mark" && /* @__PURE__ */ jsx(Menu.Item, { onSelect: () => onAction("syncDrive", file), children: "Sincronizar con Drive" }),
|
|
1891
|
+
drive?.action === "unmark" && /* @__PURE__ */ jsx(Menu.Item, { onSelect: () => onAction("unsyncDrive", file), children: "Dejar de sincronizar" }),
|
|
1889
1892
|
/* @__PURE__ */ jsx(Menu.Item, { onSelect: () => onAction("delete", file), variant: "danger", children: "Eliminar" })
|
|
1890
1893
|
] })
|
|
1891
1894
|
] })
|
|
@@ -1894,10 +1897,260 @@ const FileCard = ({ file, onAction, busy }) => {
|
|
|
1894
1897
|
}
|
|
1895
1898
|
);
|
|
1896
1899
|
};
|
|
1900
|
+
const BASE$1 = "/strapi-content-generator-gemini/drive";
|
|
1901
|
+
const FOLDER = "application/vnd.google-apps.folder";
|
|
1902
|
+
const NATIVE_TYPES = {
|
|
1903
|
+
"application/vnd.google-apps.document": "Documento de Google",
|
|
1904
|
+
"application/vnd.google-apps.spreadsheet": "Hoja de cálculo de Google",
|
|
1905
|
+
"application/vnd.google-apps.presentation": "Presentación de Google",
|
|
1906
|
+
"application/vnd.google-apps.form": "Formulario de Google",
|
|
1907
|
+
"application/vnd.google-apps.drawing": "Dibujo de Google",
|
|
1908
|
+
"application/vnd.google-apps.shortcut": "Acceso directo"
|
|
1909
|
+
};
|
|
1910
|
+
const typeLabel = (mime) => {
|
|
1911
|
+
if (mime === FOLDER) return "Carpeta";
|
|
1912
|
+
if (NATIVE_TYPES[mime]) return NATIVE_TYPES[mime];
|
|
1913
|
+
if (mime.startsWith("application/vnd.google-apps.")) return "Archivo de Google";
|
|
1914
|
+
if (mime === "application/pdf") return "PDF";
|
|
1915
|
+
if (mime.startsWith("image/")) return "Imagen";
|
|
1916
|
+
if (mime.startsWith("video/")) return "Video";
|
|
1917
|
+
if (mime.startsWith("audio/")) return "Audio";
|
|
1918
|
+
return "Archivo";
|
|
1919
|
+
};
|
|
1920
|
+
const TypeIcon = ({ mime }) => mime === FOLDER ? /* @__PURE__ */ jsx(Folder, { width: 16, height: 16, "aria-hidden": true }) : mime === "application/pdf" ? /* @__PURE__ */ jsx(FilePdf, { width: 16, height: 16, "aria-hidden": true }) : /* @__PURE__ */ jsx(File$1, { width: 16, height: 16, "aria-hidden": true });
|
|
1921
|
+
const formatDate = (iso) => iso ? new Date(iso).toLocaleString("es-MX", { dateStyle: "medium", timeStyle: "short", timeZone: "America/Mexico_City" }) : "—";
|
|
1922
|
+
const confirmText = (item, count) => {
|
|
1923
|
+
const parts = [`«${item.name}» irá a la papelera de Drive, donde se puede recuperar durante 30 días.`];
|
|
1924
|
+
if (item.mimeType === FOLDER && count) {
|
|
1925
|
+
const total = count.files + count.folders;
|
|
1926
|
+
parts.push(
|
|
1927
|
+
total === 0 ? "La carpeta está vacía." : `Contiene ${count.truncated ? "más de " : ""}${total.toLocaleString("es-MX")} ${total === 1 ? "elemento" : "elementos"} (${count.files} archivos y ${count.folders} carpetas), que también se van.`
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
if (item.synced || count?.synced) parts.push("Está sincronizado: también se borrará de la Biblioteca del CMS.");
|
|
1931
|
+
return parts.join(" ");
|
|
1932
|
+
};
|
|
1933
|
+
const serverMessage$1 = (error, fallback) => error?.response?.data?.error?.message ?? (error instanceof Error && error.message ? error.message : fallback);
|
|
1934
|
+
const useDriveBrowserApi = () => {
|
|
1935
|
+
const { get, del } = useFetchClient();
|
|
1936
|
+
return useMemo(
|
|
1937
|
+
() => ({
|
|
1938
|
+
browse: async (folderId, pageToken) => {
|
|
1939
|
+
try {
|
|
1940
|
+
return (await get(`${BASE$1}/folders/${encodeURIComponent(folderId)}`, { params: pageToken ? { pageToken } : {} })).data;
|
|
1941
|
+
} catch (error) {
|
|
1942
|
+
throw new Error(serverMessage$1(error, "No se pudo leer Google Drive."));
|
|
1943
|
+
}
|
|
1944
|
+
},
|
|
1945
|
+
count: async (id) => (await get(`${BASE$1}/items/${encodeURIComponent(id)}/count`)).data,
|
|
1946
|
+
trash: async (id) => {
|
|
1947
|
+
try {
|
|
1948
|
+
await del(`${BASE$1}/items/${encodeURIComponent(id)}`);
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
throw new Error(serverMessage$1(error, "No se pudo mandar a la papelera."));
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
}),
|
|
1954
|
+
[get, del]
|
|
1955
|
+
);
|
|
1956
|
+
};
|
|
1957
|
+
const DriveTab = ({ api: injected, notify }) => {
|
|
1958
|
+
const fallbackApi = useDriveBrowserApi();
|
|
1959
|
+
const api = injected ?? fallbackApi;
|
|
1960
|
+
const { toggleNotification } = useNotification();
|
|
1961
|
+
const say = notify ?? ((type, message) => toggleNotification({ type, message }));
|
|
1962
|
+
const [folderId, setFolderId] = useState("root");
|
|
1963
|
+
const [page, setPage] = useState(null);
|
|
1964
|
+
const [error, setError] = useState(null);
|
|
1965
|
+
const [loading2, setLoading] = useState(true);
|
|
1966
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
1967
|
+
const [doomed, setDoomed] = useState(null);
|
|
1968
|
+
const load = useCallback(async () => {
|
|
1969
|
+
setLoading(true);
|
|
1970
|
+
setError(null);
|
|
1971
|
+
try {
|
|
1972
|
+
setPage(await api.browse(folderId));
|
|
1973
|
+
} catch (err) {
|
|
1974
|
+
setError(err instanceof Error ? err.message : "No se pudo leer Google Drive.");
|
|
1975
|
+
} finally {
|
|
1976
|
+
setLoading(false);
|
|
1977
|
+
}
|
|
1978
|
+
}, [api, folderId]);
|
|
1979
|
+
useEffect(() => {
|
|
1980
|
+
load();
|
|
1981
|
+
}, [load]);
|
|
1982
|
+
const more = async () => {
|
|
1983
|
+
if (!page?.nextPageToken) return;
|
|
1984
|
+
setLoadingMore(true);
|
|
1985
|
+
try {
|
|
1986
|
+
const next = await api.browse(folderId, page.nextPageToken);
|
|
1987
|
+
setPage({ ...next, items: [...page.items, ...next.items] });
|
|
1988
|
+
} finally {
|
|
1989
|
+
setLoadingMore(false);
|
|
1990
|
+
}
|
|
1991
|
+
};
|
|
1992
|
+
const askDelete = async (item) => {
|
|
1993
|
+
setDoomed({ item, count: null });
|
|
1994
|
+
if (item.mimeType === FOLDER) {
|
|
1995
|
+
try {
|
|
1996
|
+
const count = await api.count(item.id);
|
|
1997
|
+
setDoomed((d) => d?.item.id === item.id ? { item, count } : d);
|
|
1998
|
+
} catch {
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
};
|
|
2002
|
+
const confirmDelete = async () => {
|
|
2003
|
+
if (!doomed) return;
|
|
2004
|
+
const { item } = doomed;
|
|
2005
|
+
setDoomed(null);
|
|
2006
|
+
try {
|
|
2007
|
+
await api.trash(item.id);
|
|
2008
|
+
say("success", `«${item.name}» se mandó a la papelera de Drive.`);
|
|
2009
|
+
await load();
|
|
2010
|
+
} catch (err) {
|
|
2011
|
+
say("danger", err instanceof Error ? err.message : "No se pudo mandar a la papelera.");
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
if (error) return /* @__PURE__ */ jsx(EmptyNote, { children: error });
|
|
2015
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
2016
|
+
/* @__PURE__ */ jsxs(Flex, { "data-testid": "drive-breadcrumb", gap: 1, alignItems: "center", wrap: "wrap", paddingBottom: 4, children: [
|
|
2017
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: () => setFolderId("root"), children: "Mi unidad" }),
|
|
2018
|
+
(page?.path ?? []).map((p, i, all) => /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
|
|
2019
|
+
/* @__PURE__ */ jsx(ChevronRight, { width: 10, height: 10, "aria-hidden": true }),
|
|
2020
|
+
i === all.length - 1 ? /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: p.name }) : /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "S", onClick: () => setFolderId(p.id), children: p.name })
|
|
2021
|
+
] }, p.id))
|
|
2022
|
+
] }),
|
|
2023
|
+
loading2 ? /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Cargando Google Drive…" }) }) : !page?.items.length ? /* @__PURE__ */ jsx(EmptyNote, { children: "Esta carpeta de Drive está vacía." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2024
|
+
/* @__PURE__ */ jsxs(Table, { colCount: 6, rowCount: page.items.length + 1, children: [
|
|
2025
|
+
/* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
|
|
2026
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Nombre" }) }),
|
|
2027
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Tipo" }) }),
|
|
2028
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Modificado" }) }),
|
|
2029
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Tamaño" }) }),
|
|
2030
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Estado" }) }),
|
|
2031
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(VisuallyHidden, { children: "Acciones" }) })
|
|
2032
|
+
] }) }),
|
|
2033
|
+
/* @__PURE__ */ jsx(Tbody, { children: page.items.map((item) => /* @__PURE__ */ jsxs(Tr, { children: [
|
|
2034
|
+
/* @__PURE__ */ jsx(Td, { style: { maxWidth: 420, whiteSpace: "normal" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
2035
|
+
/* @__PURE__ */ jsx(TypeIcon, { mime: item.mimeType }),
|
|
2036
|
+
item.mimeType === FOLDER ? /* @__PURE__ */ jsx(TextButton, { onClick: () => setFolderId(item.id), style: { textAlign: "left", height: "auto" }, children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", textColor: "neutral800", style: { overflowWrap: "anywhere" }, children: item.name }) }) : /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", style: { overflowWrap: "anywhere" }, children: item.name })
|
|
2037
|
+
] }) }),
|
|
2038
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral700", children: typeLabel(item.mimeType) }) }),
|
|
2039
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral700", style: { whiteSpace: "nowrap" }, children: formatDate(item.modifiedTime) }) }),
|
|
2040
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral700", children: item.size === null ? "—" : formatSize(item.size / 1e3) }) }),
|
|
2041
|
+
/* @__PURE__ */ jsx(Td, { children: item.synced && /* @__PURE__ */ jsx(Badge, { variant: "success", children: "Sincronizado" }) }),
|
|
2042
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Flex, { gap: 1, justifyContent: "end", children: [
|
|
2043
|
+
item.webViewLink && /* @__PURE__ */ jsx(
|
|
2044
|
+
LinkButton,
|
|
2045
|
+
{
|
|
2046
|
+
variant: "tertiary",
|
|
2047
|
+
size: "S",
|
|
2048
|
+
href: item.webViewLink,
|
|
2049
|
+
target: "_blank",
|
|
2050
|
+
rel: "noreferrer noopener",
|
|
2051
|
+
endIcon: /* @__PURE__ */ jsx(ExternalLink, {}),
|
|
2052
|
+
children: "Abrir en Drive"
|
|
2053
|
+
}
|
|
2054
|
+
),
|
|
2055
|
+
/* @__PURE__ */ jsx(IconButton, { label: `Eliminar ${item.name}`, variant: "ghost", onClick: () => askDelete(item), children: /* @__PURE__ */ jsx(Trash, {}) })
|
|
2056
|
+
] }) })
|
|
2057
|
+
] }, item.id)) })
|
|
2058
|
+
] }),
|
|
2059
|
+
page.nextPageToken && /* @__PURE__ */ jsx(Flex, { justifyContent: "center", paddingTop: 4, children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: more, loading: loadingMore, children: "Mostrar más" }) })
|
|
2060
|
+
] }),
|
|
2061
|
+
/* @__PURE__ */ jsx(
|
|
2062
|
+
ConfirmDialog,
|
|
2063
|
+
{
|
|
2064
|
+
open: !!doomed,
|
|
2065
|
+
title: "¿Mandar a la papelera de Drive?",
|
|
2066
|
+
confirmLabel: "Mandar a la papelera",
|
|
2067
|
+
onClose: () => setDoomed(null),
|
|
2068
|
+
onConfirm: confirmDelete,
|
|
2069
|
+
children: doomed ? confirmText(doomed.item, doomed.count) : ""
|
|
2070
|
+
}
|
|
2071
|
+
)
|
|
2072
|
+
] });
|
|
2073
|
+
};
|
|
2074
|
+
const BASE = "/strapi-content-generator-gemini/drive";
|
|
2075
|
+
const POLL_MS = 1e4;
|
|
2076
|
+
const LABELS = {
|
|
2077
|
+
synced: { label: "Drive", tone: "success" },
|
|
2078
|
+
pending: { label: "Drive: pendiente", tone: "neutral" },
|
|
2079
|
+
error: { label: "Drive: error", tone: "danger" },
|
|
2080
|
+
skipped: { label: "Drive: omitido", tone: "warning" }
|
|
2081
|
+
};
|
|
2082
|
+
const driveView = (links, key, parentInside) => {
|
|
2083
|
+
if (!links?.connected) return null;
|
|
2084
|
+
const item = links.items[key];
|
|
2085
|
+
if (item?.inside) {
|
|
2086
|
+
return { ...LABELS[item.status], title: item.error ?? void 0, action: item.root ? "unmark" : null };
|
|
2087
|
+
}
|
|
2088
|
+
if (parentInside) return { ...LABELS.pending, action: null };
|
|
2089
|
+
return { label: null, tone: "neutral", action: "mark" };
|
|
2090
|
+
};
|
|
2091
|
+
const serverMessage = (error, fallback) => error?.response?.data?.error?.message ?? fallback;
|
|
2092
|
+
const useDriveSync = (watching = false) => {
|
|
2093
|
+
const { get, post, del } = useFetchClient();
|
|
2094
|
+
const [links, setLinks] = useState(null);
|
|
2095
|
+
const refresh = useCallback(async () => {
|
|
2096
|
+
try {
|
|
2097
|
+
const { data } = await get(`${BASE}/links`);
|
|
2098
|
+
setLinks(data);
|
|
2099
|
+
} catch {
|
|
2100
|
+
setLinks(null);
|
|
2101
|
+
}
|
|
2102
|
+
}, [get]);
|
|
2103
|
+
useEffect(() => {
|
|
2104
|
+
refresh();
|
|
2105
|
+
}, [refresh]);
|
|
2106
|
+
const busy = !!links?.connected && (watching || links.pending > 0 || Object.values(links.items).some((i) => i.status === "pending"));
|
|
2107
|
+
useEffect(() => {
|
|
2108
|
+
if (!busy) return;
|
|
2109
|
+
const timer = setInterval(refresh, POLL_MS);
|
|
2110
|
+
return () => clearInterval(timer);
|
|
2111
|
+
}, [busy, refresh]);
|
|
2112
|
+
useEffect(() => {
|
|
2113
|
+
const onVisible = () => document.visibilityState === "visible" && refresh();
|
|
2114
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
2115
|
+
return () => document.removeEventListener("visibilitychange", onVisible);
|
|
2116
|
+
}, [refresh]);
|
|
2117
|
+
return useMemo(
|
|
2118
|
+
() => ({
|
|
2119
|
+
links,
|
|
2120
|
+
refresh,
|
|
2121
|
+
async mark(cmsType, cmsId) {
|
|
2122
|
+
try {
|
|
2123
|
+
await post(`${BASE}/links`, { cmsType, cmsId });
|
|
2124
|
+
} catch (error) {
|
|
2125
|
+
throw new Error(serverMessage(error, "No se pudo sincronizar con Drive."));
|
|
2126
|
+
} finally {
|
|
2127
|
+
await refresh();
|
|
2128
|
+
}
|
|
2129
|
+
},
|
|
2130
|
+
async unmark(cmsType, cmsId) {
|
|
2131
|
+
try {
|
|
2132
|
+
await del(`${BASE}/links/${cmsType}/${cmsId}`);
|
|
2133
|
+
} catch (error) {
|
|
2134
|
+
throw new Error(serverMessage(error, "No se pudo dejar de sincronizar."));
|
|
2135
|
+
} finally {
|
|
2136
|
+
await refresh();
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
}),
|
|
2140
|
+
[links, refresh, post, del]
|
|
2141
|
+
);
|
|
2142
|
+
};
|
|
2143
|
+
const uploadSummary = (total, failed) => {
|
|
2144
|
+
if (failed.length === 0) return { type: "success", message: total === 1 ? "Archivo subido" : `${total} archivos subidos` };
|
|
2145
|
+
if (failed.length === total) return { type: "danger", message: `No se pudo subir: ${failed.join(", ")}.` };
|
|
2146
|
+
return { type: "warning", message: `${total - failed.length} de ${total} archivos subidos. No se pudo subir: ${failed.join(", ")}.` };
|
|
2147
|
+
};
|
|
1897
2148
|
const status = (err) => err?.response?.status ?? err?.status;
|
|
1898
2149
|
const LibraryTab = () => {
|
|
1899
2150
|
const c = useThemeColors();
|
|
1900
2151
|
const { api, notify } = useLibraryApi();
|
|
2152
|
+
const [drivePending, setDrivePending] = useState(false);
|
|
2153
|
+
const drive = useDriveSync(drivePending);
|
|
1901
2154
|
const [tree, setTree] = useState([]);
|
|
1902
2155
|
const [counts, setCounts] = useState({});
|
|
1903
2156
|
const [current, setCurrent] = useState(null);
|
|
@@ -1908,7 +2161,10 @@ const LibraryTab = () => {
|
|
|
1908
2161
|
const [dragOver, setDragOver] = useState(false);
|
|
1909
2162
|
const [bgBusy, setBgBusy] = useState({});
|
|
1910
2163
|
const [bgTarget, setBgTarget] = useState(null);
|
|
2164
|
+
const [uploading, setUploading] = useState(null);
|
|
1911
2165
|
const uploadInput = useRef(null);
|
|
2166
|
+
const currentRef = useRef(current);
|
|
2167
|
+
currentRef.current = current;
|
|
1912
2168
|
const replaceInput = useRef(null);
|
|
1913
2169
|
const replaceTarget = useRef(null);
|
|
1914
2170
|
const path = pathTo(tree, current);
|
|
@@ -1941,7 +2197,20 @@ const LibraryTab = () => {
|
|
|
1941
2197
|
useEffect(() => {
|
|
1942
2198
|
loadFiles();
|
|
1943
2199
|
}, [loadFiles]);
|
|
1944
|
-
const refresh = () => Promise.all([loadTree(), loadFiles()]);
|
|
2200
|
+
const refresh = () => Promise.all([loadTree(), loadFiles(), drive.refresh()]);
|
|
2201
|
+
const toggleDrive = async (mode, cmsType, cmsId) => {
|
|
2202
|
+
try {
|
|
2203
|
+
if (mode === "mark") {
|
|
2204
|
+
await drive.mark(cmsType, cmsId);
|
|
2205
|
+
notify("success", "Se sincronizará con Drive en unos segundos.");
|
|
2206
|
+
} else {
|
|
2207
|
+
await drive.unmark(cmsType, cmsId);
|
|
2208
|
+
notify("success", "Dejó de sincronizarse. No se borró nada en Drive ni aquí.");
|
|
2209
|
+
}
|
|
2210
|
+
} catch (err) {
|
|
2211
|
+
notify("danger", err instanceof Error ? err.message : "No se pudo completar la operación con Drive.");
|
|
2212
|
+
}
|
|
2213
|
+
};
|
|
1945
2214
|
const run = async (action, ok) => {
|
|
1946
2215
|
try {
|
|
1947
2216
|
await action();
|
|
@@ -1953,9 +2222,30 @@ const LibraryTab = () => {
|
|
|
1953
2222
|
await refresh();
|
|
1954
2223
|
}
|
|
1955
2224
|
};
|
|
1956
|
-
|
|
2225
|
+
useEffect(() => {
|
|
2226
|
+
if (!uploading) return;
|
|
2227
|
+
const warn = (e) => e.preventDefault();
|
|
2228
|
+
window.addEventListener("beforeunload", warn);
|
|
2229
|
+
return () => window.removeEventListener("beforeunload", warn);
|
|
2230
|
+
}, [uploading]);
|
|
2231
|
+
const upload = async (list) => {
|
|
1957
2232
|
const arr = [...list ?? []];
|
|
1958
|
-
if (arr.length
|
|
2233
|
+
if (!arr.length || uploading) return;
|
|
2234
|
+
const folderId = current;
|
|
2235
|
+
const failed = [];
|
|
2236
|
+
for (const [i, file] of arr.entries()) {
|
|
2237
|
+
setUploading({ done: i, total: arr.length, name: file.name });
|
|
2238
|
+
try {
|
|
2239
|
+
const uploaded = await api.uploadFiles([file], folderId);
|
|
2240
|
+
if (currentRef.current === folderId) setFiles((prev) => [...uploaded, ...prev]);
|
|
2241
|
+
} catch {
|
|
2242
|
+
failed.push(file.name);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
setUploading(null);
|
|
2246
|
+
const { type, message } = uploadSummary(arr.length, failed);
|
|
2247
|
+
notify(type, message);
|
|
2248
|
+
await refresh();
|
|
1959
2249
|
};
|
|
1960
2250
|
const setBgStatus = (file, s) => setBgBusy((b) => {
|
|
1961
2251
|
const next = { ...b };
|
|
@@ -1998,12 +2288,24 @@ const LibraryTab = () => {
|
|
|
1998
2288
|
if (action === "rename") return setDialog({ kind: "renameFile", file });
|
|
1999
2289
|
if (action === "move") return setDialog({ kind: "moveFile", file });
|
|
2000
2290
|
if (action === "delete") return setDialog({ kind: "deleteFile", file });
|
|
2291
|
+
if (action === "syncDrive") return toggleDrive("mark", "file", file.id);
|
|
2292
|
+
if (action === "unsyncDrive") return toggleDrive("unmark", "file", file.id);
|
|
2001
2293
|
};
|
|
2002
2294
|
const validateFolder = (parent, except) => (name) => cleanFolderName(name) !== except && nameTaken(tree, parent, name) ? "Ya existe una carpeta con ese nombre aquí." : null;
|
|
2295
|
+
const pendingShown = (() => {
|
|
2296
|
+
const inside = current !== null && !!drive.links?.items[`folder:${current}`]?.inside;
|
|
2297
|
+
const isPending = (key) => driveView(drive.links, key, inside)?.label === "Drive: pendiente";
|
|
2298
|
+
return files.some((f) => isPending(`file:${f.id}`)) || childrenOf(tree, current).some((f) => isPending(`folder:${f.id}`));
|
|
2299
|
+
})();
|
|
2300
|
+
useEffect(() => {
|
|
2301
|
+
setDrivePending(pendingShown);
|
|
2302
|
+
}, [pendingShown]);
|
|
2003
2303
|
if (forbidden) {
|
|
2004
2304
|
return /* @__PURE__ */ jsx(EmptyNote, { children: "Para usar la biblioteca necesitas permiso de la Media Library de Strapi. Pídelo a un administrador." });
|
|
2005
2305
|
}
|
|
2006
2306
|
const subfolders = childrenOf(tree, current);
|
|
2307
|
+
const currentInside = current !== null && !!drive.links?.items[`folder:${current}`]?.inside;
|
|
2308
|
+
const folderDrive = current !== null ? driveView(drive.links, `folder:${current}`, !!(folder && path.length > 1 && drive.links?.items[`folder:${path[path.length - 2].id}`]?.inside)) : null;
|
|
2007
2309
|
return /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "stretch", children: [
|
|
2008
2310
|
/* @__PURE__ */ jsx(Box, { width: "280px", shrink: 0, background: "neutral0", padding: 3, hasRadius: true, style: { maxHeight: "calc(100vh - 220px)", overflowY: "auto" }, children: /* @__PURE__ */ jsx(
|
|
2009
2311
|
FolderTree,
|
|
@@ -2025,7 +2327,7 @@ const LibraryTab = () => {
|
|
|
2025
2327
|
hasRadius: true,
|
|
2026
2328
|
position: "relative",
|
|
2027
2329
|
onDragOver: (e) => {
|
|
2028
|
-
if (e.dataTransfer.types.includes("Files")) {
|
|
2330
|
+
if (e.dataTransfer.types.includes("Files") && !uploading) {
|
|
2029
2331
|
e.preventDefault();
|
|
2030
2332
|
setDragOver(true);
|
|
2031
2333
|
}
|
|
@@ -2037,7 +2339,7 @@ const LibraryTab = () => {
|
|
|
2037
2339
|
if (e.dataTransfer.files.length) {
|
|
2038
2340
|
e.preventDefault();
|
|
2039
2341
|
setDragOver(false);
|
|
2040
|
-
upload(e.dataTransfer.files);
|
|
2342
|
+
if (!uploading) upload(e.dataTransfer.files);
|
|
2041
2343
|
}
|
|
2042
2344
|
},
|
|
2043
2345
|
children: [
|
|
@@ -2050,12 +2352,15 @@ const LibraryTab = () => {
|
|
|
2050
2352
|
] }, f.id))
|
|
2051
2353
|
] }),
|
|
2052
2354
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
2355
|
+
folder && folderDrive?.label && /* @__PURE__ */ jsx(Badge, { variant: folderDrive.tone, title: folderDrive.title, children: folderDrive.label }),
|
|
2356
|
+
folder && folderDrive?.action === "mark" && /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => toggleDrive("mark", "folder", folder.id), children: "Sincronizar con Drive" }),
|
|
2357
|
+
folder && folderDrive?.action === "unmark" && /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: () => toggleDrive("unmark", "folder", folder.id), children: "Dejar de sincronizar" }),
|
|
2053
2358
|
folder && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2054
2359
|
/* @__PURE__ */ jsx(Button, { variant: "tertiary", startIcon: /* @__PURE__ */ jsx(Pencil, {}), onClick: () => setDialog({ kind: "renameFolder" }), children: "Renombrar carpeta" }),
|
|
2055
2360
|
/* @__PURE__ */ jsx(Button, { variant: "danger-light", startIcon: /* @__PURE__ */ jsx(Trash, {}), onClick: () => setDialog({ kind: "deleteFolder" }), children: "Eliminar carpeta" })
|
|
2056
2361
|
] }),
|
|
2057
2362
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: () => setDialog({ kind: "newFolder" }), children: "Nueva carpeta" }),
|
|
2058
|
-
/* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Upload, {}), onClick: () => uploadInput.current?.click(), children: "Subir archivos" })
|
|
2363
|
+
/* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Upload, {}), onClick: () => uploadInput.current?.click(), disabled: !!uploading, loading: !!uploading, children: "Subir archivos" })
|
|
2059
2364
|
] })
|
|
2060
2365
|
] }),
|
|
2061
2366
|
subfolders.length > 0 && /* @__PURE__ */ jsx(Grid.Root, { gap: 2, paddingBottom: 4, children: subfolders.map((f) => /* @__PURE__ */ jsx(Grid.Item, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(
|
|
@@ -2074,11 +2379,26 @@ const LibraryTab = () => {
|
|
|
2074
2379
|
children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
2075
2380
|
/* @__PURE__ */ jsx(Folder, { width: 16, height: 16, "aria-hidden": true }),
|
|
2076
2381
|
/* @__PURE__ */ jsx(Typography, { ellipsis: true, style: { flex: 1 }, children: f.name }),
|
|
2382
|
+
(() => {
|
|
2383
|
+
const view = driveView(drive.links, `folder:${f.id}`, currentInside);
|
|
2384
|
+
return view?.label ? /* @__PURE__ */ jsx(Badge, { size: "S", variant: view.tone, title: view.title, children: view.label }) : null;
|
|
2385
|
+
})(),
|
|
2077
2386
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: counts[f.id]?.files ?? 0 })
|
|
2078
2387
|
] })
|
|
2079
2388
|
}
|
|
2080
2389
|
) }, f.id)) }),
|
|
2081
|
-
|
|
2390
|
+
uploading && /* @__PURE__ */ jsx(Box, { padding: 3, marginBottom: 4, hasRadius: true, background: "primary100", borderColor: "primary200", role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
|
|
2391
|
+
/* @__PURE__ */ jsxs(Typography, { textColor: "primary700", children: [
|
|
2392
|
+
"Subiendo ",
|
|
2393
|
+
uploading.done + 1,
|
|
2394
|
+
" de ",
|
|
2395
|
+
uploading.total,
|
|
2396
|
+
" · ",
|
|
2397
|
+
uploading.name
|
|
2398
|
+
] }),
|
|
2399
|
+
/* @__PURE__ */ jsx(ProgressBar, { value: Math.round(uploading.done / uploading.total * 100), children: `${uploading.done} de ${uploading.total} archivos subidos` })
|
|
2400
|
+
] }) }),
|
|
2401
|
+
loading2 ? /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Cargando archivos…" }) }) : files.length === 0 ? /* @__PURE__ */ jsx(EmptyNote, { children: subfolders.length ? "No hay archivos sueltos en esta carpeta." : "Carpeta vacía. Arrastra archivos aquí o usa «Subir archivos»." }) : /* @__PURE__ */ jsx(Grid.Root, { gap: 4, children: files.map((f) => /* @__PURE__ */ jsx(Grid.Item, { col: 3, m: 4, s: 6, xs: 12, children: /* @__PURE__ */ jsx(FileCard, { file: f, onAction: onFileAction, busy: bgBusy[f.id], drive: driveView(drive.links, `file:${f.id}`, currentInside) }) }, f.id)) }),
|
|
2082
2402
|
dragOver && /* @__PURE__ */ jsx(Flex, { position: "absolute", justifyContent: "center", background: "primary100", hasRadius: true, style: { inset: 0, border: `2px dashed ${c.primary600}`, opacity: 0.95 }, children: /* @__PURE__ */ jsxs(Typography, { variant: "beta", textColor: "primary700", children: [
|
|
2083
2403
|
"Suelta para subir a «",
|
|
2084
2404
|
folder?.name ?? ROOT_LABEL,
|
|
@@ -2760,6 +3080,15 @@ const editorReducer = (state, action) => {
|
|
|
2760
3080
|
}
|
|
2761
3081
|
}
|
|
2762
3082
|
};
|
|
3083
|
+
const ORIGIN_URL = "/strapi-content-generator-gemini/drive/origin";
|
|
3084
|
+
const recordTemplateOrigin = async (post, files, template) => {
|
|
3085
|
+
for (const file of files) {
|
|
3086
|
+
try {
|
|
3087
|
+
await post(ORIGIN_URL, { fileId: file.id, templateDocumentId: template.documentId ?? template.id, templateName: template.name });
|
|
3088
|
+
} catch {
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
2763
3092
|
const useEditorIO = () => {
|
|
2764
3093
|
const { post } = useFetchClient();
|
|
2765
3094
|
const { toggleNotification } = useNotification();
|
|
@@ -2792,7 +3121,8 @@ const useEditorIO = () => {
|
|
|
2792
3121
|
if (!node) return false;
|
|
2793
3122
|
try {
|
|
2794
3123
|
const blob = await renderPiece(node, template, "png");
|
|
2795
|
-
await api.saveBlob(blob, /\.png$/i.test(name) ? name : `${name}.png`, folderId);
|
|
3124
|
+
const saved = await api.saveBlob(blob, /\.png$/i.test(name) ? name : `${name}.png`, folderId);
|
|
3125
|
+
await recordTemplateOrigin((u, body) => post(u, body), saved, template);
|
|
2796
3126
|
toggleNotification({ type: "success", message: "Pieza guardada en la biblioteca." });
|
|
2797
3127
|
return true;
|
|
2798
3128
|
} catch {
|
|
@@ -2800,7 +3130,7 @@ const useEditorIO = () => {
|
|
|
2800
3130
|
return false;
|
|
2801
3131
|
}
|
|
2802
3132
|
},
|
|
2803
|
-
[api, toggleNotification]
|
|
3133
|
+
[api, post, toggleNotification]
|
|
2804
3134
|
);
|
|
2805
3135
|
return { loadImage: loadImage2, exportAs, saveToLibrary };
|
|
2806
3136
|
};
|
|
@@ -5245,7 +5575,8 @@ const TemplatesTab = () => {
|
|
|
5245
5575
|
const STUDIO_TABS = [
|
|
5246
5576
|
{ value: "generator", label: "Generador" },
|
|
5247
5577
|
{ value: "templates", label: "Plantillas" },
|
|
5248
|
-
{ value: "library", label: "Imágenes y documentos" }
|
|
5578
|
+
{ value: "library", label: "Imágenes y documentos" },
|
|
5579
|
+
{ value: "drive", label: "Google Drive" }
|
|
5249
5580
|
];
|
|
5250
5581
|
const PANEL = { height: "100%", overflowY: "auto" };
|
|
5251
5582
|
const isStudioTab = (v) => STUDIO_TABS.some((t) => t.value === v);
|
|
@@ -5273,7 +5604,8 @@ const ImageStudioTabs = () => {
|
|
|
5273
5604
|
/* @__PURE__ */ jsxs(Box, { flex: "1", style: { minHeight: 0 }, children: [
|
|
5274
5605
|
/* @__PURE__ */ jsx(Tabs.Content, { value: "generator", style: PANEL, children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsx(GeneratorTab, {}) }) }),
|
|
5275
5606
|
/* @__PURE__ */ jsx(Tabs.Content, { value: "templates", style: PANEL, children: /* @__PURE__ */ jsx(TemplatesTab, {}) }),
|
|
5276
|
-
/* @__PURE__ */ jsx(Tabs.Content, { value: "library", style: PANEL, children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsx(LibraryTab, {}) }) })
|
|
5607
|
+
/* @__PURE__ */ jsx(Tabs.Content, { value: "library", style: PANEL, children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsx(LibraryTab, {}) }) }),
|
|
5608
|
+
/* @__PURE__ */ jsx(Tabs.Content, { value: "drive", style: PANEL, children: /* @__PURE__ */ jsx(Box, { padding: 6, children: tab === "drive" && /* @__PURE__ */ jsx(DriveTab, {}) }) })
|
|
5277
5609
|
] })
|
|
5278
5610
|
]
|
|
5279
5611
|
}
|