strapi-content-embeddings 0.1.7 → 0.1.9
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/README.md +69 -2
- package/dist/_chunks/{App-CA5bQnKQ.js → App-ByRBbkZn.js} +430 -7
- package/dist/_chunks/{App-C5NFY1UT.mjs → App-MjsTrWRS.mjs} +432 -9
- package/dist/_chunks/{index-CVCA8dDp.js → index-TWbcT-zJ.js} +110 -42
- package/dist/_chunks/{index-CIpGvEcJ.mjs → index-ifqYByO5.mjs} +111 -43
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/custom/SyncModal.d.ts +7 -0
- package/dist/admin/src/utils/api.d.ts +48 -0
- package/dist/server/index.js +197 -64
- package/dist/server/index.mjs +195 -64
- package/dist/server/src/config/index.d.ts +3 -0
- package/dist/server/src/index.d.ts +1 -0
- package/dist/server/src/services/embeddings.d.ts +2 -2
- package/dist/server/src/services/sync.d.ts +1 -0
- package/dist/server/src/utils/preprocessing.d.ts +26 -0
- package/package.json +3 -1
|
@@ -2,10 +2,10 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
|
2
2
|
import { useFetchClient, Layouts, useNotification, Page } from "@strapi/strapi/admin";
|
|
3
3
|
import { Link, useNavigate, NavLink, useParams, Routes, Route } from "react-router-dom";
|
|
4
4
|
import { useRef, useState, useEffect, useCallback, useMemo } from "react";
|
|
5
|
-
import { EmptyStateLayout, Button, Tr, Box, Table, Thead, Th, Typography, VisuallyHidden, Tbody, Td, Flex, IconButton, Modal, TextInput, Link as Link$1, Accordion,
|
|
6
|
-
import { Plus, ArrowRight,
|
|
5
|
+
import { EmptyStateLayout, Button, Tr, Box, Table, Thead, Th, Typography, VisuallyHidden, Tbody, Td, Flex, IconButton, Modal, TextInput, Link as Link$1, Accordion, Alert, Loader, Card, SingleSelect, SingleSelectOption, Checkbox, Badge, Divider, Main, Pagination, PreviousLink, PageLink, NextLink, Field, Textarea, Grid, Dialog } from "@strapi/design-system";
|
|
6
|
+
import { Plus, ArrowRight, ArrowClockwise, WarningCircle, Check, Database, Cross, Search, ArrowLeft, Pencil, Trash } from "@strapi/icons";
|
|
7
7
|
import qs from "qs";
|
|
8
|
-
import { P as PLUGIN_ID, R as RobotIcon, M as MarkdownEditor } from "./index-
|
|
8
|
+
import { P as PLUGIN_ID, R as RobotIcon, M as MarkdownEditor } from "./index-ifqYByO5.mjs";
|
|
9
9
|
import styled from "styled-components";
|
|
10
10
|
import ReactMarkdown from "react-markdown";
|
|
11
11
|
import { useIntl } from "react-intl";
|
|
@@ -462,6 +462,383 @@ function ChatModal() {
|
|
|
462
462
|
] }) })
|
|
463
463
|
] });
|
|
464
464
|
}
|
|
465
|
+
const SYNC_BASE = `/${PLUGIN_ID}`;
|
|
466
|
+
const syncApi = {
|
|
467
|
+
getStatus: async (fetchClient) => {
|
|
468
|
+
const response = await fetchClient.get(`${SYNC_BASE}/sync/status`);
|
|
469
|
+
return response.data;
|
|
470
|
+
},
|
|
471
|
+
syncFromNeon: async (fetchClient, options) => {
|
|
472
|
+
const queryString = options ? `?${qs.stringify(options)}` : "";
|
|
473
|
+
const response = await fetchClient.post(`${SYNC_BASE}/sync${queryString}`);
|
|
474
|
+
return response.data;
|
|
475
|
+
},
|
|
476
|
+
recreateAll: async (fetchClient) => {
|
|
477
|
+
const response = await fetchClient.post(`${SYNC_BASE}/recreate`);
|
|
478
|
+
return response.data;
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
function StatCard({ label, value, subtitle, variant = "default" }) {
|
|
482
|
+
const bgColors = {
|
|
483
|
+
default: "neutral100",
|
|
484
|
+
success: "success100",
|
|
485
|
+
warning: "warning100",
|
|
486
|
+
danger: "danger100"
|
|
487
|
+
};
|
|
488
|
+
const textColors = {
|
|
489
|
+
default: "neutral800",
|
|
490
|
+
success: "success700",
|
|
491
|
+
warning: "warning700",
|
|
492
|
+
danger: "danger700"
|
|
493
|
+
};
|
|
494
|
+
return /* @__PURE__ */ jsxs(
|
|
495
|
+
Box,
|
|
496
|
+
{
|
|
497
|
+
padding: 4,
|
|
498
|
+
background: bgColors[variant],
|
|
499
|
+
hasRadius: true,
|
|
500
|
+
borderColor: "neutral200",
|
|
501
|
+
style: { textAlign: "center", minWidth: "120px" },
|
|
502
|
+
children: [
|
|
503
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { textTransform: "uppercase", fontSize: "11px" }, children: label }),
|
|
504
|
+
/* @__PURE__ */ jsx(Box, { paddingTop: 1, children: /* @__PURE__ */ jsx(Typography, { variant: "alpha", textColor: textColors[variant], fontWeight: "bold", children: value }) }),
|
|
505
|
+
subtitle && /* @__PURE__ */ jsx(Box, { paddingTop: 1, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: subtitle }) })
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
function SyncModal({ isOpen, onClose, onSyncComplete }) {
|
|
511
|
+
const fetchClient = useFetchClient();
|
|
512
|
+
const [operation, setOperation] = useState("status");
|
|
513
|
+
const [removeOrphans, setRemoveOrphans] = useState(false);
|
|
514
|
+
const [dryRun, setDryRun] = useState(true);
|
|
515
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
516
|
+
const [status, setStatus] = useState(null);
|
|
517
|
+
const [syncResult, setSyncResult] = useState(null);
|
|
518
|
+
const [recreateResult, setRecreateResult] = useState(null);
|
|
519
|
+
const [error, setError] = useState(null);
|
|
520
|
+
useEffect(() => {
|
|
521
|
+
if (isOpen) {
|
|
522
|
+
fetchStatus();
|
|
523
|
+
}
|
|
524
|
+
}, [isOpen]);
|
|
525
|
+
useEffect(() => {
|
|
526
|
+
if (!isOpen) {
|
|
527
|
+
setSyncResult(null);
|
|
528
|
+
setRecreateResult(null);
|
|
529
|
+
setError(null);
|
|
530
|
+
setOperation("status");
|
|
531
|
+
setDryRun(true);
|
|
532
|
+
setRemoveOrphans(false);
|
|
533
|
+
}
|
|
534
|
+
}, [isOpen]);
|
|
535
|
+
const fetchStatus = async () => {
|
|
536
|
+
setIsLoading(true);
|
|
537
|
+
setError(null);
|
|
538
|
+
try {
|
|
539
|
+
const result = await syncApi.getStatus(fetchClient);
|
|
540
|
+
setStatus(result);
|
|
541
|
+
} catch (err) {
|
|
542
|
+
setError(err.message || "Failed to fetch sync status");
|
|
543
|
+
} finally {
|
|
544
|
+
setIsLoading(false);
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
const handleExecute = async () => {
|
|
548
|
+
setIsLoading(true);
|
|
549
|
+
setError(null);
|
|
550
|
+
setSyncResult(null);
|
|
551
|
+
setRecreateResult(null);
|
|
552
|
+
try {
|
|
553
|
+
if (operation === "status") {
|
|
554
|
+
await fetchStatus();
|
|
555
|
+
} else if (operation === "sync") {
|
|
556
|
+
const result = await syncApi.syncFromNeon(fetchClient, { removeOrphans, dryRun });
|
|
557
|
+
setSyncResult(result);
|
|
558
|
+
if (!dryRun && onSyncComplete) {
|
|
559
|
+
onSyncComplete();
|
|
560
|
+
}
|
|
561
|
+
} else if (operation === "recreate") {
|
|
562
|
+
const result = await syncApi.recreateAll(fetchClient);
|
|
563
|
+
setRecreateResult(result);
|
|
564
|
+
if (onSyncComplete) {
|
|
565
|
+
onSyncComplete();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} catch (err) {
|
|
569
|
+
setError(err.message || "Operation failed");
|
|
570
|
+
} finally {
|
|
571
|
+
setIsLoading(false);
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const renderStatus = () => {
|
|
575
|
+
if (!status) return null;
|
|
576
|
+
return /* @__PURE__ */ jsx(Card, { padding: 5, background: "neutral0", shadow: "tableShadow", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
|
|
577
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
578
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
579
|
+
/* @__PURE__ */ jsx(Database, {}),
|
|
580
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "Sync Status" })
|
|
581
|
+
] }),
|
|
582
|
+
/* @__PURE__ */ jsx(Badge, { active: status.inSync, size: "S", children: status.inSync ? "In Sync" : "Out of Sync" })
|
|
583
|
+
] }),
|
|
584
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
585
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 4, justifyContent: "center", wrap: "wrap", children: [
|
|
586
|
+
/* @__PURE__ */ jsx(
|
|
587
|
+
StatCard,
|
|
588
|
+
{
|
|
589
|
+
label: "Neon DB",
|
|
590
|
+
value: status.neonCount,
|
|
591
|
+
subtitle: "embeddings"
|
|
592
|
+
}
|
|
593
|
+
),
|
|
594
|
+
/* @__PURE__ */ jsx(
|
|
595
|
+
StatCard,
|
|
596
|
+
{
|
|
597
|
+
label: "Strapi DB",
|
|
598
|
+
value: status.strapiCount,
|
|
599
|
+
subtitle: "embeddings"
|
|
600
|
+
}
|
|
601
|
+
)
|
|
602
|
+
] }),
|
|
603
|
+
!status.inSync && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
604
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
605
|
+
/* @__PURE__ */ jsx(Box, { padding: 3, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
|
|
606
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "warning700", children: "Differences Found" }),
|
|
607
|
+
status.missingInStrapi > 0 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "warning700", children: [
|
|
608
|
+
"• ",
|
|
609
|
+
status.missingInStrapi,
|
|
610
|
+
" embeddings missing in Strapi (will be created)"
|
|
611
|
+
] }),
|
|
612
|
+
status.missingInNeon > 0 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", children: [
|
|
613
|
+
"• ",
|
|
614
|
+
status.missingInNeon,
|
|
615
|
+
" orphaned entries in Strapi (not in Neon)"
|
|
616
|
+
] }),
|
|
617
|
+
status.contentDifferences > 0 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "warning700", children: [
|
|
618
|
+
"• ",
|
|
619
|
+
status.contentDifferences,
|
|
620
|
+
" entries with content differences"
|
|
621
|
+
] })
|
|
622
|
+
] }) })
|
|
623
|
+
] })
|
|
624
|
+
] }) });
|
|
625
|
+
};
|
|
626
|
+
const renderSyncResult = () => {
|
|
627
|
+
if (!syncResult) return null;
|
|
628
|
+
return /* @__PURE__ */ jsx(Card, { padding: 5, background: syncResult.success ? "success100" : "danger100", shadow: "tableShadow", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
|
|
629
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
630
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: syncResult.dryRun ? "Preview Results" : "Sync Results" }),
|
|
631
|
+
/* @__PURE__ */ jsx(Badge, { active: syncResult.success, children: syncResult.success ? /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
|
|
632
|
+
/* @__PURE__ */ jsx(Check, { width: 12, height: 12 }),
|
|
633
|
+
"Success"
|
|
634
|
+
] }) : /* @__PURE__ */ jsxs(Flex, { gap: 1, alignItems: "center", children: [
|
|
635
|
+
/* @__PURE__ */ jsx(Cross, { width: 12, height: 12 }),
|
|
636
|
+
"Failed"
|
|
637
|
+
] }) })
|
|
638
|
+
] }),
|
|
639
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
640
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 3, justifyContent: "center", wrap: "wrap", children: [
|
|
641
|
+
/* @__PURE__ */ jsx(
|
|
642
|
+
StatCard,
|
|
643
|
+
{
|
|
644
|
+
label: "Created",
|
|
645
|
+
value: syncResult.actions.created,
|
|
646
|
+
variant: syncResult.actions.created > 0 ? "success" : "default"
|
|
647
|
+
}
|
|
648
|
+
),
|
|
649
|
+
/* @__PURE__ */ jsx(
|
|
650
|
+
StatCard,
|
|
651
|
+
{
|
|
652
|
+
label: "Updated",
|
|
653
|
+
value: syncResult.actions.updated,
|
|
654
|
+
variant: syncResult.actions.updated > 0 ? "warning" : "default"
|
|
655
|
+
}
|
|
656
|
+
),
|
|
657
|
+
/* @__PURE__ */ jsx(
|
|
658
|
+
StatCard,
|
|
659
|
+
{
|
|
660
|
+
label: "Removed",
|
|
661
|
+
value: syncResult.actions.orphansRemoved,
|
|
662
|
+
variant: syncResult.actions.orphansRemoved > 0 ? "danger" : "default"
|
|
663
|
+
}
|
|
664
|
+
)
|
|
665
|
+
] }),
|
|
666
|
+
syncResult.errors.length > 0 && /* @__PURE__ */ jsxs(Box, { padding: 3, background: "danger100", hasRadius: true, children: [
|
|
667
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "danger700", children: "Errors" }),
|
|
668
|
+
/* @__PURE__ */ jsxs(Box, { paddingTop: 2, children: [
|
|
669
|
+
syncResult.errors.slice(0, 3).map((err, i) => /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", children: [
|
|
670
|
+
"• ",
|
|
671
|
+
err
|
|
672
|
+
] }, i)),
|
|
673
|
+
syncResult.errors.length > 3 && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", fontWeight: "bold", children: [
|
|
674
|
+
"+ ",
|
|
675
|
+
syncResult.errors.length - 3,
|
|
676
|
+
" more errors"
|
|
677
|
+
] })
|
|
678
|
+
] })
|
|
679
|
+
] }),
|
|
680
|
+
syncResult.dryRun && /* @__PURE__ */ jsx(Box, { paddingTop: 2, children: /* @__PURE__ */ jsx(Alert, { variant: "default", closeLabel: "Close", children: 'This was a preview. No changes were made. Click "Apply Changes" below to execute the sync.' }) })
|
|
681
|
+
] }) });
|
|
682
|
+
};
|
|
683
|
+
const renderRecreateResult = () => {
|
|
684
|
+
if (!recreateResult) return null;
|
|
685
|
+
return /* @__PURE__ */ jsx(Card, { padding: 5, background: recreateResult.success ? "success100" : "danger100", shadow: "tableShadow", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
|
|
686
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
687
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "Recreate Results" }),
|
|
688
|
+
/* @__PURE__ */ jsx(Badge, { active: recreateResult.success, children: recreateResult.success ? "Success" : "Failed" })
|
|
689
|
+
] }),
|
|
690
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
691
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 3, justifyContent: "center", wrap: "wrap", children: [
|
|
692
|
+
/* @__PURE__ */ jsx(
|
|
693
|
+
StatCard,
|
|
694
|
+
{
|
|
695
|
+
label: "Processed",
|
|
696
|
+
value: recreateResult.totalProcessed
|
|
697
|
+
}
|
|
698
|
+
),
|
|
699
|
+
/* @__PURE__ */ jsx(
|
|
700
|
+
StatCard,
|
|
701
|
+
{
|
|
702
|
+
label: "Created",
|
|
703
|
+
value: recreateResult.created,
|
|
704
|
+
variant: "success"
|
|
705
|
+
}
|
|
706
|
+
),
|
|
707
|
+
/* @__PURE__ */ jsx(
|
|
708
|
+
StatCard,
|
|
709
|
+
{
|
|
710
|
+
label: "Failed",
|
|
711
|
+
value: recreateResult.failed,
|
|
712
|
+
variant: recreateResult.failed > 0 ? "danger" : "default"
|
|
713
|
+
}
|
|
714
|
+
)
|
|
715
|
+
] }),
|
|
716
|
+
recreateResult.errors.length > 0 && /* @__PURE__ */ jsxs(Box, { padding: 3, background: "danger100", hasRadius: true, children: [
|
|
717
|
+
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "danger700", children: "Errors" }),
|
|
718
|
+
/* @__PURE__ */ jsx(Box, { paddingTop: 2, children: recreateResult.errors.slice(0, 3).map((err, i) => /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", children: [
|
|
719
|
+
"• ",
|
|
720
|
+
err
|
|
721
|
+
] }, i)) })
|
|
722
|
+
] })
|
|
723
|
+
] }) });
|
|
724
|
+
};
|
|
725
|
+
const getButtonLabel = () => {
|
|
726
|
+
if (operation === "status") return "Refresh Status";
|
|
727
|
+
if (operation === "sync") return dryRun ? "Preview Sync" : "Run Sync";
|
|
728
|
+
if (operation === "recreate") return "Recreate All";
|
|
729
|
+
return "Execute";
|
|
730
|
+
};
|
|
731
|
+
const handleApplyChanges = async () => {
|
|
732
|
+
setIsLoading(true);
|
|
733
|
+
setError(null);
|
|
734
|
+
setSyncResult(null);
|
|
735
|
+
try {
|
|
736
|
+
const result = await syncApi.syncFromNeon(fetchClient, { removeOrphans, dryRun: false });
|
|
737
|
+
setSyncResult(result);
|
|
738
|
+
if (onSyncComplete) {
|
|
739
|
+
onSyncComplete();
|
|
740
|
+
}
|
|
741
|
+
} catch (err) {
|
|
742
|
+
setError(err.message || "Failed to apply changes");
|
|
743
|
+
} finally {
|
|
744
|
+
setIsLoading(false);
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
const showApplyButton = syncResult?.dryRun && syncResult?.success;
|
|
748
|
+
return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
|
|
749
|
+
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
750
|
+
/* @__PURE__ */ jsx(ArrowClockwise, {}),
|
|
751
|
+
/* @__PURE__ */ jsx(Modal.Title, { children: "Database Sync" })
|
|
752
|
+
] }) }),
|
|
753
|
+
/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 5, children: [
|
|
754
|
+
error && /* @__PURE__ */ jsx(Alert, { variant: "danger", closeLabel: "Close", onClose: () => setError(null), children: error }),
|
|
755
|
+
isLoading && !status ? /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 6, children: /* @__PURE__ */ jsx(Loader, { children: "Loading status..." }) }) : renderStatus(),
|
|
756
|
+
/* @__PURE__ */ jsx(Card, { padding: 5, background: "neutral0", shadow: "tableShadow", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
|
|
757
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "Select Operation" }),
|
|
758
|
+
/* @__PURE__ */ jsxs(
|
|
759
|
+
SingleSelect,
|
|
760
|
+
{
|
|
761
|
+
value: operation,
|
|
762
|
+
onChange: (value) => setOperation(value),
|
|
763
|
+
disabled: isLoading,
|
|
764
|
+
size: "M",
|
|
765
|
+
children: [
|
|
766
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "status", children: "Check Status" }),
|
|
767
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "sync", children: "Sync from Neon" }),
|
|
768
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "recreate", children: "Recreate All (Danger)" })
|
|
769
|
+
]
|
|
770
|
+
}
|
|
771
|
+
),
|
|
772
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral500", children: [
|
|
773
|
+
operation === "status" && "Compare Neon and Strapi databases without making changes.",
|
|
774
|
+
operation === "sync" && "Import embeddings from Neon DB to Strapi. Use this to restore missing entries.",
|
|
775
|
+
operation === "recreate" && "Delete all Neon embeddings and recreate them from Strapi data."
|
|
776
|
+
] })
|
|
777
|
+
] }) }),
|
|
778
|
+
operation === "sync" && /* @__PURE__ */ jsx(Card, { padding: 5, background: "neutral0", shadow: "tableShadow", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
|
|
779
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "Sync Options" }),
|
|
780
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
|
|
781
|
+
/* @__PURE__ */ jsx(
|
|
782
|
+
Checkbox,
|
|
783
|
+
{
|
|
784
|
+
checked: dryRun,
|
|
785
|
+
onCheckedChange: (checked) => setDryRun(checked),
|
|
786
|
+
children: /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
|
|
787
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: "Dry Run" }),
|
|
788
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "Preview changes without applying them" })
|
|
789
|
+
] })
|
|
790
|
+
}
|
|
791
|
+
),
|
|
792
|
+
/* @__PURE__ */ jsx(
|
|
793
|
+
Checkbox,
|
|
794
|
+
{
|
|
795
|
+
checked: removeOrphans,
|
|
796
|
+
onCheckedChange: (checked) => setRemoveOrphans(checked),
|
|
797
|
+
children: /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
|
|
798
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: "Remove Orphans" }),
|
|
799
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "Delete Strapi entries that don't exist in Neon" })
|
|
800
|
+
] })
|
|
801
|
+
}
|
|
802
|
+
)
|
|
803
|
+
] })
|
|
804
|
+
] }) }),
|
|
805
|
+
operation === "recreate" && /* @__PURE__ */ jsx(Alert, { variant: "danger", closeLabel: "Close", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
|
|
806
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
807
|
+
/* @__PURE__ */ jsx(WarningCircle, {}),
|
|
808
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "Destructive Operation" })
|
|
809
|
+
] }),
|
|
810
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", children: "This will delete ALL embeddings in Neon and recreate them from Strapi data. This operation cannot be undone. Only use if embeddings are corrupted." })
|
|
811
|
+
] }) }),
|
|
812
|
+
syncResult && renderSyncResult(),
|
|
813
|
+
recreateResult && renderRecreateResult()
|
|
814
|
+
] }) }),
|
|
815
|
+
/* @__PURE__ */ jsx(Modal.Footer, { children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", width: "100%", children: [
|
|
816
|
+
/* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: "Cancel" }) }),
|
|
817
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
818
|
+
showApplyButton && /* @__PURE__ */ jsx(
|
|
819
|
+
Button,
|
|
820
|
+
{
|
|
821
|
+
onClick: handleApplyChanges,
|
|
822
|
+
loading: isLoading,
|
|
823
|
+
startIcon: /* @__PURE__ */ jsx(Check, {}),
|
|
824
|
+
variant: "success",
|
|
825
|
+
children: "Apply Changes"
|
|
826
|
+
}
|
|
827
|
+
),
|
|
828
|
+
/* @__PURE__ */ jsx(
|
|
829
|
+
Button,
|
|
830
|
+
{
|
|
831
|
+
onClick: handleExecute,
|
|
832
|
+
loading: isLoading,
|
|
833
|
+
startIcon: /* @__PURE__ */ jsx(ArrowClockwise, {}),
|
|
834
|
+
variant: operation === "recreate" ? "danger" : "secondary",
|
|
835
|
+
children: getButtonLabel()
|
|
836
|
+
}
|
|
837
|
+
)
|
|
838
|
+
] })
|
|
839
|
+
] }) })
|
|
840
|
+
] }) });
|
|
841
|
+
}
|
|
465
842
|
const PAGE_SIZE = 10;
|
|
466
843
|
function debounce(func, wait) {
|
|
467
844
|
let timeout;
|
|
@@ -477,6 +854,7 @@ function HomePage() {
|
|
|
477
854
|
const [search, setSearch] = useState("");
|
|
478
855
|
const [isLoading, setIsLoading] = useState(true);
|
|
479
856
|
const [currentPage, setCurrentPage] = useState(1);
|
|
857
|
+
const [isSyncModalOpen, setIsSyncModalOpen] = useState(false);
|
|
480
858
|
const totalPages = embeddings ? Math.ceil(embeddings.totalCount / PAGE_SIZE) : 0;
|
|
481
859
|
const buildQuery = (searchTerm, page) => qs.stringify({
|
|
482
860
|
page,
|
|
@@ -513,18 +891,55 @@ function HomePage() {
|
|
|
513
891
|
const handleCreateNew = () => {
|
|
514
892
|
navigate(`/plugins/${PLUGIN_ID}/embeddings`);
|
|
515
893
|
};
|
|
894
|
+
const handleSyncComplete = () => {
|
|
895
|
+
fetchData(search, currentPage);
|
|
896
|
+
};
|
|
897
|
+
const headerActions = /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
898
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsx(ArrowClockwise, {}), onClick: () => setIsSyncModalOpen(true), children: "Sync" }),
|
|
899
|
+
/* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: handleCreateNew, children: "Create new embedding" })
|
|
900
|
+
] });
|
|
516
901
|
if (isLoading && !embeddings) {
|
|
517
902
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
518
|
-
/* @__PURE__ */ jsx(
|
|
903
|
+
/* @__PURE__ */ jsx(
|
|
904
|
+
Layouts.Header,
|
|
905
|
+
{
|
|
906
|
+
title: "Content Embeddings",
|
|
907
|
+
subtitle: "Manage your content embeddings",
|
|
908
|
+
primaryAction: headerActions
|
|
909
|
+
}
|
|
910
|
+
),
|
|
519
911
|
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading..." }) }) }),
|
|
520
|
-
/* @__PURE__ */ jsx(ChatModal, {})
|
|
912
|
+
/* @__PURE__ */ jsx(ChatModal, {}),
|
|
913
|
+
/* @__PURE__ */ jsx(
|
|
914
|
+
SyncModal,
|
|
915
|
+
{
|
|
916
|
+
isOpen: isSyncModalOpen,
|
|
917
|
+
onClose: () => setIsSyncModalOpen(false),
|
|
918
|
+
onSyncComplete: handleSyncComplete
|
|
919
|
+
}
|
|
920
|
+
)
|
|
521
921
|
] });
|
|
522
922
|
}
|
|
523
923
|
if (embeddings?.totalCount === 0 && !search) {
|
|
524
924
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
525
|
-
/* @__PURE__ */ jsx(
|
|
925
|
+
/* @__PURE__ */ jsx(
|
|
926
|
+
Layouts.Header,
|
|
927
|
+
{
|
|
928
|
+
title: "Content Embeddings",
|
|
929
|
+
subtitle: "Manage your content embeddings",
|
|
930
|
+
primaryAction: headerActions
|
|
931
|
+
}
|
|
932
|
+
),
|
|
526
933
|
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(EmptyState, {}) }),
|
|
527
|
-
/* @__PURE__ */ jsx(ChatModal, {})
|
|
934
|
+
/* @__PURE__ */ jsx(ChatModal, {}),
|
|
935
|
+
/* @__PURE__ */ jsx(
|
|
936
|
+
SyncModal,
|
|
937
|
+
{
|
|
938
|
+
isOpen: isSyncModalOpen,
|
|
939
|
+
onClose: () => setIsSyncModalOpen(false),
|
|
940
|
+
onSyncComplete: handleSyncComplete
|
|
941
|
+
}
|
|
942
|
+
)
|
|
528
943
|
] });
|
|
529
944
|
}
|
|
530
945
|
const renderEmbeddingsContent = () => {
|
|
@@ -606,7 +1021,7 @@ function HomePage() {
|
|
|
606
1021
|
{
|
|
607
1022
|
title: "Content Embeddings",
|
|
608
1023
|
subtitle: `${embeddings?.totalCount || 0} embeddings total`,
|
|
609
|
-
primaryAction:
|
|
1024
|
+
primaryAction: headerActions
|
|
610
1025
|
}
|
|
611
1026
|
),
|
|
612
1027
|
/* @__PURE__ */ jsxs(Layouts.Content, { children: [
|
|
@@ -623,7 +1038,15 @@ function HomePage() {
|
|
|
623
1038
|
renderEmbeddingsContent(),
|
|
624
1039
|
renderPagination()
|
|
625
1040
|
] }),
|
|
626
|
-
/* @__PURE__ */ jsx(ChatModal, {})
|
|
1041
|
+
/* @__PURE__ */ jsx(ChatModal, {}),
|
|
1042
|
+
/* @__PURE__ */ jsx(
|
|
1043
|
+
SyncModal,
|
|
1044
|
+
{
|
|
1045
|
+
isOpen: isSyncModalOpen,
|
|
1046
|
+
onClose: () => setIsSyncModalOpen(false),
|
|
1047
|
+
onSyncComplete: handleSyncComplete
|
|
1048
|
+
}
|
|
1049
|
+
)
|
|
627
1050
|
] });
|
|
628
1051
|
}
|
|
629
1052
|
function CreateEmbeddingsForm({
|