create-nextblock 0.13.10 → 0.13.11
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/docker-template/.dockerignore +23 -23
- package/docker-template/.env.docker.example +69 -69
- package/docker-template/docker/db/init/99-jwt.sql +6 -6
- package/docker-template/docker/db/init/99-roles.sql +25 -25
- package/docker-template/docker/kong/kong.yml +112 -112
- package/docker-template/scripts/docker-setup.mjs +310 -310
- package/libs/db/tsconfig.lib.json +3 -3
- package/libs/editor/tsconfig.lib.json +3 -3
- package/libs/ui/tsconfig.lib.json +3 -3
- package/libs/utils/tsconfig.json +3 -3
- package/package.json +1 -1
- package/project.json +19 -19
- package/templates/nextblock-template/.browserslistrc +11 -11
- package/templates/nextblock-template/.dockerignore +23 -23
- package/templates/nextblock-template/README.md +34 -34
- package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
- package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
- package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
- package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -51
- package/templates/nextblock-template/app/ToasterProvider.tsx +17 -17
- package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
- package/templates/nextblock-template/app/actions/consent.ts +57 -57
- package/templates/nextblock-template/app/actions/postActions.ts +146 -146
- package/templates/nextblock-template/app/actions/productGridActions.ts +40 -40
- package/templates/nextblock-template/app/actions/twoFactorEmail.ts +22 -22
- package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
- package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
- package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
- package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
- package/templates/nextblock-template/app/api/cms/ecommerce/product-picker/route.ts +151 -151
- package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
- package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
- package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3490 -3490
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +5749 -5609
- package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
- package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
- package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
- package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
- package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
- package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
- package/templates/nextblock-template/app/api/search/route.ts +43 -43
- package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +441 -441
- package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
- package/templates/nextblock-template/app/cart/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
- package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
- package/templates/nextblock-template/app/cms/blocks/components/MultiEntityPicker.tsx +263 -263
- package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
- package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +90 -90
- package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
- package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/EcommerceActiveContext.tsx +27 -27
- package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
- package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
- package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
- package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
- package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
- package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
- package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
- package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
- package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
- package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
- package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
- package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
- package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
- package/templates/nextblock-template/app/cms/interactions/EmailRecipientsInput.tsx +189 -189
- package/templates/nextblock-template/app/cms/layout.tsx +73 -73
- package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
- package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
- package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
- package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
- package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
- package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
- package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
- package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
- package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
- package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
- package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
- package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
- package/templates/nextblock-template/app/cms/revisions/service.ts +19 -19
- package/templates/nextblock-template/app/cms/revisions/utils.ts +132 -132
- package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
- package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
- package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
- package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +497 -497
- package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
- package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
- package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
- package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
- package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
- package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
- package/templates/nextblock-template/app/cms/settings/languages/components/LanguageDetectionPanel.tsx +188 -188
- package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
- package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
- package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +20 -20
- package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
- package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
- package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
- package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -122
- package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
- package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
- package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
- package/templates/nextblock-template/app/cms/users/components/CreateUserForm.tsx +217 -217
- package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
- package/templates/nextblock-template/app/cms/users/new/page.tsx +44 -44
- package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
- package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
- package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
- package/templates/nextblock-template/app/profile/account-links.ts +22 -22
- package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
- package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
- package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
- package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
- package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
- package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
- package/templates/nextblock-template/app/setup/layout.tsx +13 -13
- package/templates/nextblock-template/app/setup/page.tsx +111 -111
- package/templates/nextblock-template/app/sitemap.ts +130 -130
- package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
- package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
- package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
- package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
- package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
- package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
- package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
- package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
- package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
- package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
- package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
- package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
- package/templates/nextblock-template/components/blocks/ProductGridClient.tsx +114 -114
- package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
- package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +8 -1
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +249 -249
- package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +29 -23
- package/templates/nextblock-template/components/blocks/types.ts +7 -7
- package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
- package/templates/nextblock-template/components/form-message.tsx +32 -32
- package/templates/nextblock-template/components/media/YouTubeFacade.tsx +69 -0
- package/templates/nextblock-template/components/media/youtube-embed-replace.tsx +32 -0
- package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
- package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +471 -464
- package/templates/nextblock-template/components/submit-button.tsx +23 -23
- package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
- package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
- package/templates/nextblock-template/context/AuthContext.tsx +23 -23
- package/templates/nextblock-template/context/language-rest-client.ts +32 -32
- package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
- package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
- package/templates/nextblock-template/docker/kong/kong.yml +112 -112
- package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
- package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -202
- package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +246 -246
- package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +176 -176
- package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
- package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
- package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -173
- package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +170 -170
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +151 -151
- package/templates/nextblock-template/docs/README.md +39 -39
- package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
- package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
- package/templates/nextblock-template/index.d.ts +7 -7
- package/templates/nextblock-template/lib/app-secrets.ts +39 -39
- package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
- package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
- package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
- package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
- package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +78 -78
- package/templates/nextblock-template/lib/blocks/README.md +13 -13
- package/templates/nextblock-template/lib/blocks/product-grid-data.ts +210 -210
- package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
- package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -2243
- package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
- package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
- package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
- package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
- package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
- package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
- package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
- package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
- package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
- package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
- package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
- package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
- package/templates/nextblock-template/lib/email/branding.ts +76 -76
- package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
- package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
- package/templates/nextblock-template/lib/full-backup/server.ts +743 -743
- package/templates/nextblock-template/lib/i18n/country-languages.ts +247 -247
- package/templates/nextblock-template/lib/i18n/detection.test.ts +197 -197
- package/templates/nextblock-template/lib/i18n/detection.ts +192 -192
- package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
- package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +54 -54
- package/templates/nextblock-template/lib/media/youtube.ts +81 -0
- package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
- package/templates/nextblock-template/lib/onboarding/status.ts +222 -222
- package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
- package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
- package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
- package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
- package/templates/nextblock-template/lib/privacy/types.ts +69 -69
- package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
- package/templates/nextblock-template/lib/promotions/server.ts +741 -741
- package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
- package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
- package/templates/nextblock-template/lib/search/types.ts +27 -27
- package/templates/nextblock-template/lib/setup/actions.ts +460 -460
- package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
- package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +87 -82
- package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
- package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
- package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
- package/templates/nextblock-template/lib/setup/types.ts +18 -18
- package/templates/nextblock-template/lib/site-url.ts +48 -48
- package/templates/nextblock-template/lib/storage/provider.ts +66 -66
- package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
- package/templates/nextblock-template/lib/updates/check-upstream.ts +441 -441
- package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
- package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
- package/templates/nextblock-template/package.json +1 -1
- package/templates/nextblock-template/postcss.config.js +6 -6
- package/templates/nextblock-template/scripts/backup.js +115 -115
- package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
- package/templates/nextblock-template/scripts/restore.js +385 -385
- package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
- package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
- package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
- package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
- package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
- package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
- package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
- package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
- package/templates/nextblock-template/tailwind.config.js +25 -25
- package/templates/nextblock-template/tools/build-migrate.mjs +209 -209
- package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
- package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
- package/templates/nextblock-template/types/jsdom.d.ts +6 -6
- package/tsconfig.base.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
4
4
|
import { Editor } from '@tiptap/react';
|
|
5
5
|
import Image from 'next/image';
|
|
6
6
|
import { Image as ImageIconLucide, Search, CheckCircle } from 'lucide-react';
|
|
@@ -14,113 +14,113 @@ import {
|
|
|
14
14
|
DialogFooter,
|
|
15
15
|
DialogClose,
|
|
16
16
|
} from '@nextblock-cms/ui';
|
|
17
|
-
import { Input } from '@nextblock-cms/ui';
|
|
18
|
-
import type { Database } from '@nextblock-cms/db';
|
|
19
|
-
import { resolveMediaUrl } from '../../../../lib/media/resolveMediaUrl';
|
|
20
|
-
|
|
21
|
-
type Media = Database['public']['Tables']['media']['Row'];
|
|
22
|
-
|
|
23
|
-
const MEDIA_REQUEST_TIMEOUT_MS = 8000;
|
|
24
|
-
const MEDIA_LIBRARY_LIMIT = 20;
|
|
25
|
-
|
|
26
|
-
function resolveMediaPreviewPath(media: Media) {
|
|
27
|
-
return media.file_path || media.object_key || null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function resolveMediaPreviewSrc(path: string) {
|
|
31
|
-
return resolveMediaUrl(path);
|
|
32
|
-
}
|
|
17
|
+
import { Input } from '@nextblock-cms/ui';
|
|
18
|
+
import type { Database } from '@nextblock-cms/db';
|
|
19
|
+
import { resolveMediaUrl } from '../../../../lib/media/resolveMediaUrl';
|
|
20
|
+
|
|
21
|
+
type Media = Database['public']['Tables']['media']['Row'];
|
|
22
|
+
|
|
23
|
+
const MEDIA_REQUEST_TIMEOUT_MS = 8000;
|
|
24
|
+
const MEDIA_LIBRARY_LIMIT = 20;
|
|
25
|
+
|
|
26
|
+
function resolveMediaPreviewPath(media: Media) {
|
|
27
|
+
return media.file_path || media.object_key || null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveMediaPreviewSrc(path: string) {
|
|
31
|
+
return resolveMediaUrl(path);
|
|
32
|
+
}
|
|
33
33
|
|
|
34
34
|
interface MediaLibraryModalProps {
|
|
35
35
|
editor: Editor | null;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export const MediaLibraryModal = ({ editor }: MediaLibraryModalProps) => {
|
|
39
|
-
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
40
|
-
const [mediaLibrary, setMediaLibrary] = useState<Media[]>([]);
|
|
41
|
-
const [isLoadingMedia, setIsLoadingMedia] = useState(false);
|
|
42
|
-
const [loadError, setLoadError] = useState<string | null>(null);
|
|
43
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
44
|
-
const requestIdRef = useRef(0);
|
|
45
|
-
|
|
46
|
-
const fetchLibrary = useCallback(async () => {
|
|
47
|
-
if (!isModalOpen) return;
|
|
48
|
-
const requestId = ++requestIdRef.current;
|
|
49
|
-
setIsLoadingMedia(true);
|
|
50
|
-
setLoadError(null);
|
|
51
|
-
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
52
|
-
try {
|
|
53
|
-
const controller = new AbortController();
|
|
54
|
-
timeoutId = setTimeout(
|
|
55
|
-
() => controller.abort(new Error('Media library request timed out.')),
|
|
56
|
-
MEDIA_REQUEST_TIMEOUT_MS
|
|
57
|
-
);
|
|
58
|
-
const params = new URLSearchParams({
|
|
59
|
-
limit: MEDIA_LIBRARY_LIMIT.toString(),
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
if (searchTerm.trim()) {
|
|
63
|
-
params.set('q', searchTerm.trim());
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const response = await fetch(`/api/media/library?${params.toString()}`, {
|
|
67
|
-
method: 'GET',
|
|
68
|
-
cache: 'no-store',
|
|
69
|
-
signal: controller.signal,
|
|
70
|
-
});
|
|
71
|
-
clearTimeout(timeoutId);
|
|
72
|
-
|
|
73
|
-
const payload = (await response.json()) as {
|
|
74
|
-
items?: Media[];
|
|
75
|
-
error?: string;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
if (requestId !== requestIdRef.current) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!response.ok) {
|
|
83
|
-
console.error('Error fetching media library:', payload.error);
|
|
84
|
-
setMediaLibrary([]);
|
|
85
|
-
setLoadError(payload.error || 'We could not load the media library. Please retry.');
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
setMediaLibrary(payload.items || []);
|
|
90
|
-
} catch (error: any) {
|
|
91
|
-
if (requestId !== requestIdRef.current) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
console.error('Error fetching media library:', error);
|
|
96
|
-
setMediaLibrary([]);
|
|
97
|
-
if (error?.name === 'AbortError' || error?.message === 'Media library request timed out.') {
|
|
98
|
-
setLoadError('The media library took too long to respond. Please retry.');
|
|
99
|
-
} else {
|
|
100
|
-
setLoadError('We could not load the media library. Please retry.');
|
|
101
|
-
}
|
|
102
|
-
} finally {
|
|
103
|
-
if (timeoutId) {
|
|
104
|
-
clearTimeout(timeoutId);
|
|
105
|
-
}
|
|
106
|
-
if (requestId === requestIdRef.current) {
|
|
107
|
-
setIsLoadingMedia(false);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}, [isModalOpen, searchTerm]);
|
|
38
|
+
export const MediaLibraryModal = ({ editor }: MediaLibraryModalProps) => {
|
|
39
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
40
|
+
const [mediaLibrary, setMediaLibrary] = useState<Media[]>([]);
|
|
41
|
+
const [isLoadingMedia, setIsLoadingMedia] = useState(false);
|
|
42
|
+
const [loadError, setLoadError] = useState<string | null>(null);
|
|
43
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
44
|
+
const requestIdRef = useRef(0);
|
|
45
|
+
|
|
46
|
+
const fetchLibrary = useCallback(async () => {
|
|
47
|
+
if (!isModalOpen) return;
|
|
48
|
+
const requestId = ++requestIdRef.current;
|
|
49
|
+
setIsLoadingMedia(true);
|
|
50
|
+
setLoadError(null);
|
|
51
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
52
|
+
try {
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
timeoutId = setTimeout(
|
|
55
|
+
() => controller.abort(new Error('Media library request timed out.')),
|
|
56
|
+
MEDIA_REQUEST_TIMEOUT_MS
|
|
57
|
+
);
|
|
58
|
+
const params = new URLSearchParams({
|
|
59
|
+
limit: MEDIA_LIBRARY_LIMIT.toString(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (searchTerm.trim()) {
|
|
63
|
+
params.set('q', searchTerm.trim());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const response = await fetch(`/api/media/library?${params.toString()}`, {
|
|
67
|
+
method: 'GET',
|
|
68
|
+
cache: 'no-store',
|
|
69
|
+
signal: controller.signal,
|
|
70
|
+
});
|
|
71
|
+
clearTimeout(timeoutId);
|
|
72
|
+
|
|
73
|
+
const payload = (await response.json()) as {
|
|
74
|
+
items?: Media[];
|
|
75
|
+
error?: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (requestId !== requestIdRef.current) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
console.error('Error fetching media library:', payload.error);
|
|
84
|
+
setMediaLibrary([]);
|
|
85
|
+
setLoadError(payload.error || 'We could not load the media library. Please retry.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setMediaLibrary(payload.items || []);
|
|
90
|
+
} catch (error: any) {
|
|
91
|
+
if (requestId !== requestIdRef.current) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.error('Error fetching media library:', error);
|
|
96
|
+
setMediaLibrary([]);
|
|
97
|
+
if (error?.name === 'AbortError' || error?.message === 'Media library request timed out.') {
|
|
98
|
+
setLoadError('The media library took too long to respond. Please retry.');
|
|
99
|
+
} else {
|
|
100
|
+
setLoadError('We could not load the media library. Please retry.');
|
|
101
|
+
}
|
|
102
|
+
} finally {
|
|
103
|
+
if (timeoutId) {
|
|
104
|
+
clearTimeout(timeoutId);
|
|
105
|
+
}
|
|
106
|
+
if (requestId === requestIdRef.current) {
|
|
107
|
+
setIsLoadingMedia(false);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}, [isModalOpen, searchTerm]);
|
|
111
111
|
|
|
112
112
|
useEffect(() => {
|
|
113
113
|
fetchLibrary();
|
|
114
114
|
}, [fetchLibrary]);
|
|
115
115
|
|
|
116
|
-
const handleSelectMedia = (mediaItem: Media) => {
|
|
117
|
-
const previewPath = resolveMediaPreviewPath(mediaItem);
|
|
118
|
-
const imageUrl = previewPath ? resolveMediaPreviewSrc(previewPath) : null;
|
|
119
|
-
if (editor && mediaItem.file_type?.startsWith("image/") && imageUrl) {
|
|
120
|
-
editor.chain().focus().insertContent(`<img src="${imageUrl}" alt="${mediaItem.description || mediaItem.file_name}" />`).run();
|
|
121
|
-
}
|
|
122
|
-
setIsModalOpen(false);
|
|
123
|
-
};
|
|
116
|
+
const handleSelectMedia = (mediaItem: Media) => {
|
|
117
|
+
const previewPath = resolveMediaPreviewPath(mediaItem);
|
|
118
|
+
const imageUrl = previewPath ? resolveMediaPreviewSrc(previewPath) : null;
|
|
119
|
+
if (editor && mediaItem.file_type?.startsWith("image/") && imageUrl) {
|
|
120
|
+
editor.chain().focus().insertContent(`<img src="${imageUrl}" alt="${mediaItem.description || mediaItem.file_name}" />`).run();
|
|
121
|
+
}
|
|
122
|
+
setIsModalOpen(false);
|
|
123
|
+
};
|
|
124
124
|
|
|
125
125
|
return (
|
|
126
126
|
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
|
|
@@ -143,54 +143,54 @@ export const MediaLibraryModal = ({ editor }: MediaLibraryModalProps) => {
|
|
|
143
143
|
/>
|
|
144
144
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
145
145
|
</div>
|
|
146
|
-
{isLoadingMedia ? (
|
|
147
|
-
<div className="flex-grow flex items-center justify-center"><p>Loading media...</p></div>
|
|
148
|
-
) : loadError ? (
|
|
149
|
-
<div className="flex-grow flex flex-col items-center justify-center gap-3 text-center">
|
|
150
|
-
<p className="max-w-sm text-sm text-muted-foreground">{loadError}</p>
|
|
151
|
-
<Button type="button" variant="outline" size="sm" onClick={() => void fetchLibrary()}>
|
|
152
|
-
Retry
|
|
153
|
-
</Button>
|
|
154
|
-
</div>
|
|
155
|
-
) : mediaLibrary.length === 0 ? (
|
|
156
|
-
<div className="flex-grow flex items-center justify-center"><p>No media found.</p></div>
|
|
157
|
-
) : (
|
|
158
|
-
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 gap-3 overflow-y-auto flex-grow pr-2">
|
|
159
|
-
{mediaLibrary.filter(m => m.file_type?.startsWith("image/")).map((media) => {
|
|
160
|
-
const previewPath = resolveMediaPreviewPath(media);
|
|
161
|
-
const previewSrc = previewPath ? resolveMediaPreviewSrc(previewPath) : null;
|
|
162
|
-
|
|
163
|
-
return (
|
|
164
|
-
<button
|
|
165
|
-
key={media.id}
|
|
166
|
-
type="button"
|
|
167
|
-
className="relative aspect-square border rounded-md overflow-hidden group focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
|
168
|
-
onClick={() => handleSelectMedia(media)}
|
|
169
|
-
>
|
|
170
|
-
{previewSrc ? (
|
|
171
|
-
<Image
|
|
172
|
-
src={previewSrc}
|
|
173
|
-
alt={media.description || media.file_name}
|
|
174
|
-
width={200}
|
|
175
|
-
height={200}
|
|
176
|
-
className="h-full w-full object-cover"
|
|
177
|
-
/>
|
|
178
|
-
) : (
|
|
179
|
-
<div className="flex h-full w-full items-center justify-center bg-muted text-xs text-muted-foreground">
|
|
180
|
-
Preview unavailable
|
|
181
|
-
</div>
|
|
182
|
-
)}
|
|
183
|
-
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 group-focus:opacity-100 transition-opacity flex items-center justify-center">
|
|
184
|
-
<CheckCircle className="h-8 w-8 text-white" />
|
|
185
|
-
</div>
|
|
186
|
-
<p className="absolute bottom-0 left-0 right-0 bg-black/50 text-white text-xs p-1 truncate text-center">
|
|
187
|
-
{media.file_name}
|
|
188
|
-
</p>
|
|
189
|
-
</button>
|
|
190
|
-
);
|
|
191
|
-
})}
|
|
192
|
-
</div>
|
|
193
|
-
)}
|
|
146
|
+
{isLoadingMedia ? (
|
|
147
|
+
<div className="flex-grow flex items-center justify-center"><p>Loading media...</p></div>
|
|
148
|
+
) : loadError ? (
|
|
149
|
+
<div className="flex-grow flex flex-col items-center justify-center gap-3 text-center">
|
|
150
|
+
<p className="max-w-sm text-sm text-muted-foreground">{loadError}</p>
|
|
151
|
+
<Button type="button" variant="outline" size="sm" onClick={() => void fetchLibrary()}>
|
|
152
|
+
Retry
|
|
153
|
+
</Button>
|
|
154
|
+
</div>
|
|
155
|
+
) : mediaLibrary.length === 0 ? (
|
|
156
|
+
<div className="flex-grow flex items-center justify-center"><p>No media found.</p></div>
|
|
157
|
+
) : (
|
|
158
|
+
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 gap-3 overflow-y-auto flex-grow pr-2">
|
|
159
|
+
{mediaLibrary.filter(m => m.file_type?.startsWith("image/")).map((media) => {
|
|
160
|
+
const previewPath = resolveMediaPreviewPath(media);
|
|
161
|
+
const previewSrc = previewPath ? resolveMediaPreviewSrc(previewPath) : null;
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<button
|
|
165
|
+
key={media.id}
|
|
166
|
+
type="button"
|
|
167
|
+
className="relative aspect-square border rounded-md overflow-hidden group focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
|
168
|
+
onClick={() => handleSelectMedia(media)}
|
|
169
|
+
>
|
|
170
|
+
{previewSrc ? (
|
|
171
|
+
<Image
|
|
172
|
+
src={previewSrc}
|
|
173
|
+
alt={media.description || media.file_name}
|
|
174
|
+
width={200}
|
|
175
|
+
height={200}
|
|
176
|
+
className="h-full w-full object-cover"
|
|
177
|
+
/>
|
|
178
|
+
) : (
|
|
179
|
+
<div className="flex h-full w-full items-center justify-center bg-muted text-xs text-muted-foreground">
|
|
180
|
+
Preview unavailable
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 group-focus:opacity-100 transition-opacity flex items-center justify-center">
|
|
184
|
+
<CheckCircle className="h-8 w-8 text-white" />
|
|
185
|
+
</div>
|
|
186
|
+
<p className="absolute bottom-0 left-0 right-0 bg-black/50 text-white text-xs p-1 truncate text-center">
|
|
187
|
+
{media.file_name}
|
|
188
|
+
</p>
|
|
189
|
+
</button>
|
|
190
|
+
);
|
|
191
|
+
})}
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
194
|
<DialogFooter className="mt-4">
|
|
195
195
|
<DialogClose asChild>
|
|
196
196
|
<Button type="button" variant="outline">Cancel</Button>
|
|
@@ -199,4 +199,4 @@ export const MediaLibraryModal = ({ editor }: MediaLibraryModalProps) => {
|
|
|
199
199
|
</DialogContent>
|
|
200
200
|
</Dialog>
|
|
201
201
|
);
|
|
202
|
-
};
|
|
202
|
+
};
|