@jhits/plugin-content 0.0.16 → 0.0.19
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/api/router.d.ts.map +1 -1
- package/dist/api/router.js +0 -1
- package/dist/components/DynamicLink.d.ts +1 -1
- package/dist/components/DynamicLink.d.ts.map +1 -1
- package/dist/components/DynamicLink.js +1 -1
- package/dist/components/TranslationEditor.d.ts +3 -1
- package/dist/components/TranslationEditor.d.ts.map +1 -1
- package/dist/components/TranslationEditor.js +11 -5
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/package.json +3 -3
- package/src/api/router.ts +0 -2
- package/src/components/DynamicLink.tsx +1 -1
- package/src/components/TranslationEditor.tsx +18 -7
- package/src/index.tsx +37 -8
- package/dist/views/LinkManager/LinkManager.d.ts +0 -9
- package/dist/views/LinkManager/LinkManager.d.ts.map +0 -1
- package/dist/views/LinkManager/LinkManager.js +0 -90
- package/dist/views/MediaManager/MediaManager.d.ts +0 -8
- package/dist/views/MediaManager/MediaManager.d.ts.map +0 -1
- package/dist/views/MediaManager/MediaManager.js +0 -93
- package/dist/views/index.d.ts +0 -10
- package/dist/views/index.d.ts.map +0 -1
- package/dist/views/index.js +0 -22
package/dist/api/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGxD,MAAM,WAAW,sBAAsB;IACnC,oEAAoE;IACpE,KAAK,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,GAAG,CAAA;KAAE,CAAC,CAAC;IACxC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAClC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,sBAAsB,GAC/B,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGxD,MAAM,WAAW,sBAAsB;IACnC,oEAAoE;IACpE,KAAK,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,GAAG,CAAA;KAAE,CAAC,CAAC;IACxC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAClC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,sBAAsB,GAC/B,OAAO,CAAC,YAAY,CAAC,CA6DvB"}
|
package/dist/api/router.js
CHANGED
|
@@ -16,7 +16,6 @@ export async function handleContentApi(req, path, config) {
|
|
|
16
16
|
const method = req.method;
|
|
17
17
|
const safePath = Array.isArray(path) ? path : [];
|
|
18
18
|
const route = safePath.length > 0 ? safePath[0] : '';
|
|
19
|
-
console.log(`[ContentApiRouter] method=${method}, path=${JSON.stringify(safePath)}, route=${route}, url=${req.url}`);
|
|
20
19
|
try {
|
|
21
20
|
// Route: /api/plugin-content/save
|
|
22
21
|
if (route === 'save') {
|
|
@@ -23,6 +23,6 @@ interface DynamicLinkProps {
|
|
|
23
23
|
type: 'url' | 'file';
|
|
24
24
|
}) => React.ReactNode;
|
|
25
25
|
}
|
|
26
|
-
export
|
|
26
|
+
export default function DynamicLink({ linkKey, siteId, locale, defaultLabel, defaultTarget, defaultType, className, isAdmin, apiBaseUrl, isButton, onClick, children }: DynamicLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
27
27
|
export {};
|
|
28
28
|
//# sourceMappingURL=DynamicLink.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DynamicLink.d.ts","sourceRoot":"","sources":["../../src/components/DynamicLink.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAKnD,UAAU,gBAAgB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;CACjG;AAED,
|
|
1
|
+
{"version":3,"file":"DynamicLink.d.ts","sourceRoot":"","sources":["../../src/components/DynamicLink.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAKnD,UAAU,gBAAgB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;CACjG;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAChC,OAAO,EACP,MAAkB,EAClB,MAAM,EACN,YAAY,EACZ,aAAa,EACb,WAAmB,EACnB,SAAc,EACd,OAAe,EACf,UAAU,EACV,QAAgB,EAChB,OAAO,EACP,QAAQ,EACX,EAAE,gBAAgB,2CA6GlB"}
|
|
@@ -8,7 +8,7 @@ import { useState } from 'react';
|
|
|
8
8
|
import { useLinks } from '../hooks/useLinks';
|
|
9
9
|
import { LinkSettingsModal } from './LinkSettingsModal';
|
|
10
10
|
import Link from 'next/link';
|
|
11
|
-
export function DynamicLink({ linkKey, siteId = 'default', locale, defaultLabel, defaultTarget, defaultType = 'url', className = '', isAdmin = false, apiBaseUrl, isButton = false, onClick, children }) {
|
|
11
|
+
export default function DynamicLink({ linkKey, siteId = 'default', locale, defaultLabel, defaultTarget, defaultType = 'url', className = '', isAdmin = false, apiBaseUrl, isButton = false, onClick, children }) {
|
|
12
12
|
const { getLink, refetch } = useLinks(siteId, apiBaseUrl);
|
|
13
13
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
14
14
|
const [isHovered, setIsHovered] = useState(false);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
interface TranslationEditorProps {
|
|
2
2
|
messages: Record<string, any>;
|
|
3
3
|
locale: string;
|
|
4
|
+
apiBaseUrl?: string;
|
|
5
|
+
siteId?: string;
|
|
4
6
|
}
|
|
5
|
-
declare function TranslationEditorInner({ messages: initialMessages, locale: initialLocale }: TranslationEditorProps): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
declare function TranslationEditorInner({ messages: initialMessages, locale: initialLocale, apiBaseUrl, siteId }: TranslationEditorProps): import("react/jsx-runtime").JSX.Element | null;
|
|
6
8
|
export default TranslationEditorInner;
|
|
7
9
|
export type { TranslationEditorProps };
|
|
8
10
|
//# sourceMappingURL=TranslationEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranslationEditor.d.ts","sourceRoot":"","sources":["../../src/components/TranslationEditor.tsx"],"names":[],"mappings":"AAQA,UAAU,sBAAsB;IAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"TranslationEditor.d.ts","sourceRoot":"","sources":["../../src/components/TranslationEditor.tsx"],"names":[],"mappings":"AAQA,UAAU,sBAAsB;IAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,iBAAS,sBAAsB,CAAC,EAC5B,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,aAAa,EACrB,UAAkC,EAClC,MAAkB,EACrB,EAAE,sBAAsB,kDAoTxB;AAeD,eAAe,sBAAsB,CAAC;AACtC,YAAY,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { useState, useEffect, useMemo, useRef } from 'react';
|
|
|
5
5
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
6
6
|
import { X, Globe, Search, ChevronRight, Plus, CornerDownRight, ShieldCheck, Lock, Bold, Italic, Type } from 'lucide-react';
|
|
7
7
|
import { parse } from '../utils/parser';
|
|
8
|
-
function TranslationEditorInner({ messages: initialMessages, locale: initialLocale }) {
|
|
8
|
+
function TranslationEditorInner({ messages: initialMessages, locale: initialLocale, apiBaseUrl = '/api/plugin-content', siteId = 'default' }) {
|
|
9
9
|
const router = useRouter();
|
|
10
10
|
const pathname = usePathname();
|
|
11
11
|
const [mounted, setMounted] = useState(false);
|
|
@@ -56,7 +56,9 @@ function TranslationEditorInner({ messages: initialMessages, locale: initialLoca
|
|
|
56
56
|
useEffect(() => {
|
|
57
57
|
async function checkAuth() {
|
|
58
58
|
try {
|
|
59
|
-
|
|
59
|
+
// Get the base dashboard API URL (strip plugin-content if present)
|
|
60
|
+
const baseApi = apiBaseUrl.replace('/plugin-content', '');
|
|
61
|
+
const res = await fetch(`${baseApi}/plugin-dep/me`);
|
|
60
62
|
const data = await res.json();
|
|
61
63
|
if (data.loggedIn)
|
|
62
64
|
setUserData(data.user);
|
|
@@ -69,7 +71,7 @@ function TranslationEditorInner({ messages: initialMessages, locale: initialLoca
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
checkAuth();
|
|
72
|
-
}, []);
|
|
74
|
+
}, [apiBaseUrl]);
|
|
73
75
|
useEffect(() => {
|
|
74
76
|
const handleLocaleUpdate = (event) => {
|
|
75
77
|
if (event.detail.locale === initialLocale) {
|
|
@@ -166,10 +168,14 @@ function TranslationEditorInner({ messages: initialMessages, locale: initialLoca
|
|
|
166
168
|
const handleSave = async () => {
|
|
167
169
|
setSaving(true);
|
|
168
170
|
try {
|
|
169
|
-
const res = await fetch(
|
|
171
|
+
const res = await fetch(`${apiBaseUrl}/save`, {
|
|
170
172
|
method: 'POST',
|
|
171
173
|
headers: { 'Content-Type': 'application/json' },
|
|
172
|
-
body: JSON.stringify({
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
locale: initialLocale,
|
|
176
|
+
messages: jsonData,
|
|
177
|
+
siteId
|
|
178
|
+
}),
|
|
173
179
|
});
|
|
174
180
|
if (res.ok) {
|
|
175
181
|
router.refresh();
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export interface ContentPluginProps {
|
|
|
9
9
|
locale?: string;
|
|
10
10
|
/** Messages object - can be passed explicitly or will be fetched from context */
|
|
11
11
|
messages?: Record<string, any>;
|
|
12
|
+
/** API base URL for the dashboard */
|
|
13
|
+
apiBaseUrl?: string;
|
|
14
|
+
/** Site identifier for multi-tenant setups */
|
|
15
|
+
siteId?: string;
|
|
12
16
|
}
|
|
13
17
|
/**
|
|
14
18
|
* Content Plugin Component
|
|
@@ -20,7 +24,7 @@ export { default as MultilineText } from './components/MultilineText';
|
|
|
20
24
|
export type { MultilineTextProps } from './components/MultilineText';
|
|
21
25
|
export { default as ParsedText } from './components/ParsedText';
|
|
22
26
|
export type { ParsedTextProps } from './components/ParsedText';
|
|
23
|
-
export { DynamicLink } from './components/DynamicLink';
|
|
27
|
+
export { default as DynamicLink } from './components/DynamicLink';
|
|
24
28
|
export type { TranslationEditorProps } from './components/TranslationEditor';
|
|
25
29
|
export { ParserConfigProvider, useParserConfig } from './context/ParserConfigContext';
|
|
26
30
|
export type { ParserConfigProviderProps } from './context/ParserConfigContext';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,kBAAkB;IAC/B,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,kBAAkB;IAC/B,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAmCD;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,KAAK,EAAE,kBAAkB,kDAyB9D;AAuBD,OAAO,EAAE,aAAa,EAAE,CAAC;AAGzB,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAG7E,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACtF,YAAY,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAG/E,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI5C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
7
7
|
import { useState, useEffect, lazy, Suspense } from 'react';
|
|
8
8
|
const TranslationEditor = lazy(() => import('./components/TranslationEditor'));
|
|
9
|
-
function ContentPluginWithIntl({ locale, messages }) {
|
|
10
|
-
return (_jsx(Suspense, { fallback: null, children: _jsx(TranslationEditor, { messages: messages, locale: locale }) }));
|
|
9
|
+
function ContentPluginWithIntl({ locale, messages, apiBaseUrl, siteId }) {
|
|
10
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(TranslationEditor, { messages: messages, locale: locale, apiBaseUrl: apiBaseUrl, siteId: siteId }) }));
|
|
11
11
|
}
|
|
12
12
|
function ClientOnly({ children }) {
|
|
13
13
|
const [mounted, setMounted] = useState(false);
|
|
@@ -21,29 +21,29 @@ function ClientOnly({ children }) {
|
|
|
21
21
|
* Renders the translation editor for editing website content
|
|
22
22
|
*/
|
|
23
23
|
export default function ContentPlugin(props) {
|
|
24
|
-
const { enabled = true, locale, messages } = props;
|
|
24
|
+
const { enabled = true, locale, messages, apiBaseUrl, siteId = 'default' } = props;
|
|
25
25
|
if (!enabled)
|
|
26
26
|
return null;
|
|
27
27
|
// If locale and messages are provided as props, use them directly
|
|
28
28
|
if (locale && messages) {
|
|
29
|
-
return (_jsx(ClientOnly, { children: _jsx(ContentPluginWithIntl, { locale: locale, messages: messages }) }));
|
|
29
|
+
return (_jsx(ClientOnly, { children: _jsx(ContentPluginWithIntl, { locale: locale, messages: messages, apiBaseUrl: apiBaseUrl, siteId: siteId }) }));
|
|
30
30
|
}
|
|
31
31
|
// Otherwise, try to use context (for backward compatibility)
|
|
32
|
-
return (_jsx(ClientOnly, { children: _jsx(ContentPluginWithIntlFallback, {}) }));
|
|
32
|
+
return (_jsx(ClientOnly, { children: _jsx(ContentPluginWithIntlFallback, { apiBaseUrl: apiBaseUrl, siteId: siteId }) }));
|
|
33
33
|
}
|
|
34
|
-
function ContentPluginWithIntlFallback() {
|
|
34
|
+
function ContentPluginWithIntlFallback({ apiBaseUrl, siteId }) {
|
|
35
35
|
// Dynamic import to avoid SSR issues with next-intl hooks
|
|
36
36
|
const { useMessages, useLocale, NextIntlClientProvider } = require('next-intl');
|
|
37
37
|
const messages = useMessages();
|
|
38
38
|
const locale = useLocale();
|
|
39
|
-
return (_jsx(NextIntlClientProvider, { locale: locale, messages: messages, children: _jsx(Suspense, { fallback: null, children: _jsx(TranslationEditor, { messages: messages, locale: locale }) }) }));
|
|
39
|
+
return (_jsx(NextIntlClientProvider, { locale: locale, messages: messages, children: _jsx(Suspense, { fallback: null, children: _jsx(TranslationEditor, { messages: messages, locale: locale, apiBaseUrl: apiBaseUrl, siteId: siteId }) }) }));
|
|
40
40
|
}
|
|
41
41
|
// Export named export for flexibility
|
|
42
42
|
export { ContentPlugin };
|
|
43
43
|
// Export components
|
|
44
44
|
export { default as MultilineText } from './components/MultilineText';
|
|
45
45
|
export { default as ParsedText } from './components/ParsedText';
|
|
46
|
-
export { DynamicLink } from './components/DynamicLink';
|
|
46
|
+
export { default as DynamicLink } from './components/DynamicLink';
|
|
47
47
|
// Export context
|
|
48
48
|
export { ParserConfigProvider, useParserConfig } from './context/ParserConfigContext';
|
|
49
49
|
// Export hooks
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jhits/plugin-content",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Content management and localization plugin for the JHITS ecosystem",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
-
"main": "./src/index.
|
|
9
|
-
"types": "./src/index.
|
|
8
|
+
"main": "./src/index.tsx",
|
|
9
|
+
"types": "./src/index.tsx",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"types": "./src/index.tsx",
|
package/src/api/router.ts
CHANGED
|
@@ -33,8 +33,6 @@ export async function handleContentApi(
|
|
|
33
33
|
const safePath = Array.isArray(path) ? path : [];
|
|
34
34
|
const route = safePath.length > 0 ? safePath[0] : '';
|
|
35
35
|
|
|
36
|
-
console.log(`[ContentApiRouter] method=${method}, path=${JSON.stringify(safePath)}, route=${route}, url=${req.url}`);
|
|
37
|
-
|
|
38
36
|
try {
|
|
39
37
|
// Route: /api/plugin-content/save
|
|
40
38
|
if (route === 'save') {
|
|
@@ -9,9 +9,16 @@ import { parse } from '../utils/parser';
|
|
|
9
9
|
interface TranslationEditorProps {
|
|
10
10
|
messages: Record<string, any>;
|
|
11
11
|
locale: string;
|
|
12
|
+
apiBaseUrl?: string;
|
|
13
|
+
siteId?: string;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
function TranslationEditorInner({
|
|
16
|
+
function TranslationEditorInner({
|
|
17
|
+
messages: initialMessages,
|
|
18
|
+
locale: initialLocale,
|
|
19
|
+
apiBaseUrl = '/api/plugin-content',
|
|
20
|
+
siteId = 'default'
|
|
21
|
+
}: TranslationEditorProps) {
|
|
15
22
|
const router = useRouter();
|
|
16
23
|
const pathname = usePathname();
|
|
17
24
|
const [mounted, setMounted] = useState(false);
|
|
@@ -65,7 +72,9 @@ function TranslationEditorInner({ messages: initialMessages, locale: initialLoca
|
|
|
65
72
|
useEffect(() => {
|
|
66
73
|
async function checkAuth() {
|
|
67
74
|
try {
|
|
68
|
-
|
|
75
|
+
// Get the base dashboard API URL (strip plugin-content if present)
|
|
76
|
+
const baseApi = apiBaseUrl.replace('/plugin-content', '');
|
|
77
|
+
const res = await fetch(`${baseApi}/plugin-dep/me`);
|
|
69
78
|
const data = await res.json();
|
|
70
79
|
if (data.loggedIn) setUserData(data.user);
|
|
71
80
|
} catch (err) {
|
|
@@ -75,7 +84,7 @@ function TranslationEditorInner({ messages: initialMessages, locale: initialLoca
|
|
|
75
84
|
}
|
|
76
85
|
}
|
|
77
86
|
checkAuth();
|
|
78
|
-
}, []);
|
|
87
|
+
}, [apiBaseUrl]);
|
|
79
88
|
|
|
80
89
|
useEffect(() => {
|
|
81
90
|
const handleLocaleUpdate = (event: CustomEvent) => {
|
|
@@ -162,10 +171,14 @@ function TranslationEditorInner({ messages: initialMessages, locale: initialLoca
|
|
|
162
171
|
const handleSave = async () => {
|
|
163
172
|
setSaving(true);
|
|
164
173
|
try {
|
|
165
|
-
const res = await fetch(
|
|
174
|
+
const res = await fetch(`${apiBaseUrl}/save`, {
|
|
166
175
|
method: 'POST',
|
|
167
176
|
headers: { 'Content-Type': 'application/json' },
|
|
168
|
-
body: JSON.stringify({
|
|
177
|
+
body: JSON.stringify({
|
|
178
|
+
locale: initialLocale,
|
|
179
|
+
messages: jsonData,
|
|
180
|
+
siteId
|
|
181
|
+
}),
|
|
169
182
|
});
|
|
170
183
|
if (res.ok) {
|
|
171
184
|
router.refresh();
|
|
@@ -330,5 +343,3 @@ function getFlattenedPaths(obj: any, prefix = ''): { path: string; value: string
|
|
|
330
343
|
|
|
331
344
|
export default TranslationEditorInner;
|
|
332
345
|
export type { TranslationEditorProps };
|
|
333
|
-
|
|
334
|
-
|
package/src/index.tsx
CHANGED
|
@@ -16,12 +16,31 @@ export interface ContentPluginProps {
|
|
|
16
16
|
locale?: string;
|
|
17
17
|
/** Messages object - can be passed explicitly or will be fetched from context */
|
|
18
18
|
messages?: Record<string, any>;
|
|
19
|
+
/** API base URL for the dashboard */
|
|
20
|
+
apiBaseUrl?: string;
|
|
21
|
+
/** Site identifier for multi-tenant setups */
|
|
22
|
+
siteId?: string;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
function ContentPluginWithIntl({
|
|
25
|
+
function ContentPluginWithIntl({
|
|
26
|
+
locale,
|
|
27
|
+
messages,
|
|
28
|
+
apiBaseUrl,
|
|
29
|
+
siteId
|
|
30
|
+
}: {
|
|
31
|
+
locale: string;
|
|
32
|
+
messages: Record<string, any>;
|
|
33
|
+
apiBaseUrl?: string;
|
|
34
|
+
siteId?: string;
|
|
35
|
+
}) {
|
|
22
36
|
return (
|
|
23
37
|
<Suspense fallback={null}>
|
|
24
|
-
<TranslationEditor
|
|
38
|
+
<TranslationEditor
|
|
39
|
+
messages={messages}
|
|
40
|
+
locale={locale}
|
|
41
|
+
apiBaseUrl={apiBaseUrl}
|
|
42
|
+
siteId={siteId}
|
|
43
|
+
/>
|
|
25
44
|
</Suspense>
|
|
26
45
|
);
|
|
27
46
|
}
|
|
@@ -41,7 +60,7 @@ function ClientOnly({ children }: { children: React.ReactNode }) {
|
|
|
41
60
|
* Renders the translation editor for editing website content
|
|
42
61
|
*/
|
|
43
62
|
export default function ContentPlugin(props: ContentPluginProps) {
|
|
44
|
-
const { enabled = true, locale, messages } = props;
|
|
63
|
+
const { enabled = true, locale, messages, apiBaseUrl, siteId = 'default' } = props;
|
|
45
64
|
|
|
46
65
|
if (!enabled) return null;
|
|
47
66
|
|
|
@@ -49,7 +68,12 @@ export default function ContentPlugin(props: ContentPluginProps) {
|
|
|
49
68
|
if (locale && messages) {
|
|
50
69
|
return (
|
|
51
70
|
<ClientOnly>
|
|
52
|
-
<ContentPluginWithIntl
|
|
71
|
+
<ContentPluginWithIntl
|
|
72
|
+
locale={locale}
|
|
73
|
+
messages={messages}
|
|
74
|
+
apiBaseUrl={apiBaseUrl}
|
|
75
|
+
siteId={siteId}
|
|
76
|
+
/>
|
|
53
77
|
</ClientOnly>
|
|
54
78
|
);
|
|
55
79
|
}
|
|
@@ -57,12 +81,12 @@ export default function ContentPlugin(props: ContentPluginProps) {
|
|
|
57
81
|
// Otherwise, try to use context (for backward compatibility)
|
|
58
82
|
return (
|
|
59
83
|
<ClientOnly>
|
|
60
|
-
<ContentPluginWithIntlFallback />
|
|
84
|
+
<ContentPluginWithIntlFallback apiBaseUrl={apiBaseUrl} siteId={siteId} />
|
|
61
85
|
</ClientOnly>
|
|
62
86
|
);
|
|
63
87
|
}
|
|
64
88
|
|
|
65
|
-
function ContentPluginWithIntlFallback() {
|
|
89
|
+
function ContentPluginWithIntlFallback({ apiBaseUrl, siteId }: { apiBaseUrl?: string; siteId?: string }) {
|
|
66
90
|
// Dynamic import to avoid SSR issues with next-intl hooks
|
|
67
91
|
const { useMessages, useLocale, NextIntlClientProvider } = require('next-intl');
|
|
68
92
|
const messages = useMessages();
|
|
@@ -71,7 +95,12 @@ function ContentPluginWithIntlFallback() {
|
|
|
71
95
|
return (
|
|
72
96
|
<NextIntlClientProvider locale={locale} messages={messages}>
|
|
73
97
|
<Suspense fallback={null}>
|
|
74
|
-
<TranslationEditor
|
|
98
|
+
<TranslationEditor
|
|
99
|
+
messages={messages}
|
|
100
|
+
locale={locale}
|
|
101
|
+
apiBaseUrl={apiBaseUrl}
|
|
102
|
+
siteId={siteId}
|
|
103
|
+
/>
|
|
75
104
|
</Suspense>
|
|
76
105
|
</NextIntlClientProvider>
|
|
77
106
|
);
|
|
@@ -85,7 +114,7 @@ export { default as MultilineText } from './components/MultilineText';
|
|
|
85
114
|
export type { MultilineTextProps } from './components/MultilineText';
|
|
86
115
|
export { default as ParsedText } from './components/ParsedText';
|
|
87
116
|
export type { ParsedTextProps } from './components/ParsedText';
|
|
88
|
-
export { DynamicLink } from './components/DynamicLink';
|
|
117
|
+
export { default as DynamicLink } from './components/DynamicLink';
|
|
89
118
|
export type { TranslationEditorProps } from './components/TranslationEditor';
|
|
90
119
|
|
|
91
120
|
// Export context
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Link Manager View
|
|
3
|
-
* Allows defining global, localized links and buttons
|
|
4
|
-
*/
|
|
5
|
-
export declare function LinkManagerView({ siteId, locale: defaultLocale }: {
|
|
6
|
-
siteId: string;
|
|
7
|
-
locale: string;
|
|
8
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
//# sourceMappingURL=LinkManager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LinkManager.d.ts","sourceRoot":"","sources":["../../../src/views/LinkManager/LinkManager.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAsBH,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,2CAuMpG"}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Link Manager View
|
|
3
|
-
* Allows defining global, localized links and buttons
|
|
4
|
-
*/
|
|
5
|
-
'use client';
|
|
6
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { useState, useEffect } from 'react';
|
|
8
|
-
import { Plus, Link2, Trash2, Globe, Save } from 'lucide-react';
|
|
9
|
-
export function LinkManagerView({ siteId, locale: defaultLocale }) {
|
|
10
|
-
const [links, setLinks] = useState([]);
|
|
11
|
-
const [loading, setLoading] = useState(true);
|
|
12
|
-
const [activeTab, setActiveTab] = useState('links');
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
// Fetch links from API
|
|
15
|
-
const fetchLinks = async () => {
|
|
16
|
-
try {
|
|
17
|
-
const res = await fetch(`/api/plugin-content/links?siteId=${siteId}`);
|
|
18
|
-
if (res.ok) {
|
|
19
|
-
const data = await res.json();
|
|
20
|
-
setLinks(data.links || []);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
console.error('Failed to fetch links:', err);
|
|
25
|
-
}
|
|
26
|
-
finally {
|
|
27
|
-
setLoading(false);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
fetchLinks();
|
|
31
|
-
}, [siteId]);
|
|
32
|
-
const handleAddLink = () => {
|
|
33
|
-
const newLink = {
|
|
34
|
-
key: `new-link-${Date.now()}`,
|
|
35
|
-
languages: {
|
|
36
|
-
[defaultLocale]: { label: 'New Link', target: '', type: 'url' }
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
setLinks([...links, newLink]);
|
|
40
|
-
};
|
|
41
|
-
const handleSaveLink = async (link) => {
|
|
42
|
-
try {
|
|
43
|
-
const res = await fetch('/api/plugin-content/links', {
|
|
44
|
-
method: 'POST',
|
|
45
|
-
headers: { 'Content-Type': 'application/json' },
|
|
46
|
-
body: JSON.stringify({ siteId, link }),
|
|
47
|
-
});
|
|
48
|
-
if (res.ok) {
|
|
49
|
-
// Refresh list or update local state
|
|
50
|
-
const result = await res.json();
|
|
51
|
-
setLinks(links.map(l => l.key === link.key ? result.link : l));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
console.error('Failed to save link:', err);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
const handleDeleteLink = async (key) => {
|
|
59
|
-
if (!confirm('Are you sure you want to delete this link?'))
|
|
60
|
-
return;
|
|
61
|
-
try {
|
|
62
|
-
const res = await fetch(`/api/plugin-content/links?siteId=${siteId}&key=${key}`, {
|
|
63
|
-
method: 'DELETE',
|
|
64
|
-
});
|
|
65
|
-
if (res.ok) {
|
|
66
|
-
setLinks(links.filter(l => l.key !== key));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
catch (err) {
|
|
70
|
-
console.error('Failed to delete link:', err);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
return (_jsxs("div", { className: "p-8 bg-white dark:bg-neutral-900 rounded-[2.5rem] h-full overflow-y-auto", children: [_jsxs("div", { className: "flex items-center justify-between mb-8", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-black uppercase tracking-tighter text-neutral-950 dark:text-white", children: "Link & Button Manager" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400", children: "Manage dynamic buttons and links across your application" })] }), _jsxs("button", { onClick: handleAddLink, className: "flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest hover:bg-primary/90 transition-all shadow-lg shadow-primary/20", children: [_jsx(Plus, { size: 16 }), "Define New Link"] })] }), _jsx("div", { className: "space-y-6", children: links.length === 0 && !loading ? (_jsxs("div", { className: "text-center py-20 bg-neutral-50 dark:bg-neutral-800/50 rounded-[2rem] border-2 border-dashed border-neutral-200 dark:border-neutral-700", children: [_jsx(Link2, { className: "mx-auto size-12 text-neutral-300 dark:text-neutral-600 mb-4" }), _jsx("p", { className: "text-neutral-500", children: "No dynamic links defined yet." })] })) : (links.map((link) => (_jsxs("div", { className: "bg-neutral-50 dark:bg-neutral-800/50 p-6 rounded-[2rem] border border-neutral-200 dark:border-neutral-700", children: [_jsxs("div", { className: "flex items-start justify-between mb-6", children: [_jsxs("div", { className: "flex-1 max-w-md", children: [_jsx("label", { className: "text-[10px] font-black uppercase tracking-widest text-neutral-400 block mb-2", children: "System Key (Unique ID)" }), _jsx("input", { type: "text", value: link.key, onChange: (e) => setLinks(links.map(l => l.key === link.key ? { ...l, key: e.target.value } : l)), className: "w-full bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700 p-3 rounded-xl text-sm font-bold outline-none focus:border-primary transition-all", placeholder: "e.g. hero-primary-button" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: () => handleSaveLink(link), className: "p-3 bg-white dark:bg-neutral-900 text-primary border border-neutral-200 dark:border-neutral-700 rounded-xl hover:border-primary transition-all", title: "Save Link", children: _jsx(Save, { size: 18 }) }), _jsx("button", { onClick: () => handleDeleteLink(link.key), className: "p-3 bg-white dark:bg-neutral-900 text-red-500 border border-neutral-200 dark:border-neutral-700 rounded-xl hover:border-red-500 transition-all", title: "Delete Link", children: _jsx(Trash2, { size: 18 }) })] })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: [Object.entries(link.languages).map(([locale, data]) => (_jsxs("div", { className: "bg-white dark:bg-neutral-900 p-5 rounded-2xl border border-neutral-100 dark:border-neutral-800", children: [_jsxs("div", { className: "flex items-center gap-2 mb-4", children: [_jsx(Globe, { size: 14, className: "text-primary" }), _jsxs("span", { className: "text-[10px] font-black uppercase tracking-widest text-neutral-500", children: [locale.toUpperCase(), " Translation"] })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "text-[9px] font-bold uppercase text-neutral-400 block mb-1", children: "Label" }), _jsx("input", { type: "text", value: data.label, onChange: (e) => {
|
|
74
|
-
const newLangs = { ...link.languages, [locale]: { ...data, label: e.target.value } };
|
|
75
|
-
setLinks(links.map(l => l.key === link.key ? { ...l, languages: newLangs } : l));
|
|
76
|
-
}, className: "w-full bg-neutral-50 dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 p-2 rounded-lg text-xs outline-none focus:border-primary transition-all" })] }), _jsxs("div", { children: [_jsx("label", { className: "text-[9px] font-bold uppercase text-neutral-400 block mb-1", children: "Target" }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("select", { value: data.type, onChange: (e) => {
|
|
77
|
-
const newType = e.target.value;
|
|
78
|
-
const newLangs = { ...link.languages, [locale]: { ...data, type: newType } };
|
|
79
|
-
setLinks(links.map(l => l.key === link.key ? { ...l, languages: newLangs } : l));
|
|
80
|
-
}, className: "bg-neutral-50 dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 p-2 rounded-lg text-[10px] outline-none", children: [_jsx("option", { value: "url", children: "URL" }), _jsx("option", { value: "file", children: "File" })] }), _jsx("input", { type: "text", value: data.target, onChange: (e) => {
|
|
81
|
-
const newLangs = { ...link.languages, [locale]: { ...data, target: e.target.value } };
|
|
82
|
-
setLinks(links.map(l => l.key === link.key ? { ...l, languages: newLangs } : l));
|
|
83
|
-
}, className: "flex-1 bg-neutral-50 dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 p-2 rounded-lg text-xs outline-none focus:border-primary transition-all", placeholder: data.type === 'url' ? "/about or https://..." : "file-id-..." })] })] })] })] }, locale))), _jsxs("button", { onClick: () => {
|
|
84
|
-
const newLocale = prompt('Enter locale (e.g. en, nl, sv):');
|
|
85
|
-
if (newLocale) {
|
|
86
|
-
const newLangs = { ...link.languages, [newLocale]: { label: '', target: '', type: 'url' } };
|
|
87
|
-
setLinks(links.map(l => l.key === link.key ? { ...l, languages: newLangs } : l));
|
|
88
|
-
}
|
|
89
|
-
}, className: "flex flex-col items-center justify-center p-5 rounded-2xl border-2 border-dashed border-neutral-200 dark:border-neutral-700 hover:border-primary transition-all text-neutral-400 hover:text-primary", children: [_jsx(Plus, { size: 20 }), _jsx("span", { className: "text-[10px] font-black uppercase mt-2", children: "Add translation" })] })] })] }, link.key)))) })] }));
|
|
90
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MediaManager.d.ts","sourceRoot":"","sources":["../../../src/views/MediaManager/MediaManager.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,2CAgL9D"}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Media Manager View
|
|
3
|
-
* Allows uploading and managing general files (PDFs, docs, etc.)
|
|
4
|
-
*/
|
|
5
|
-
'use client';
|
|
6
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
8
|
-
import { Upload, File, Trash2, Search, Copy, Check, FileText, ExternalLink } from 'lucide-react';
|
|
9
|
-
export function MediaManagerView({ siteId }) {
|
|
10
|
-
const [files, setFiles] = useState([]);
|
|
11
|
-
const [loading, setLoading] = useState(true);
|
|
12
|
-
const [uploading, setUploading] = useState(false);
|
|
13
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
14
|
-
const [copiedId, setCopiedId] = useState(null);
|
|
15
|
-
const fetchFiles = useCallback(async () => {
|
|
16
|
-
try {
|
|
17
|
-
setLoading(true);
|
|
18
|
-
const res = await fetch(`/api/plugin-content/files?siteId=${siteId}`);
|
|
19
|
-
if (res.ok) {
|
|
20
|
-
const data = await res.json();
|
|
21
|
-
setFiles(data.files || []);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
catch (err) {
|
|
25
|
-
console.error('Failed to fetch files:', err);
|
|
26
|
-
}
|
|
27
|
-
finally {
|
|
28
|
-
setLoading(false);
|
|
29
|
-
}
|
|
30
|
-
}, [siteId]);
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
fetchFiles();
|
|
33
|
-
}, [fetchFiles]);
|
|
34
|
-
const handleUpload = async (e) => {
|
|
35
|
-
const file = e.target.files?.[0];
|
|
36
|
-
if (!file)
|
|
37
|
-
return;
|
|
38
|
-
try {
|
|
39
|
-
setUploading(true);
|
|
40
|
-
const formData = new FormData();
|
|
41
|
-
formData.append('file', file);
|
|
42
|
-
formData.append('siteId', siteId);
|
|
43
|
-
const res = await fetch('/api/plugin-content/files/upload', {
|
|
44
|
-
method: 'POST',
|
|
45
|
-
body: formData,
|
|
46
|
-
});
|
|
47
|
-
if (res.ok) {
|
|
48
|
-
fetchFiles();
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
const error = await res.json();
|
|
52
|
-
alert(error.error || 'Upload failed');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch (err) {
|
|
56
|
-
console.error('Upload error:', err);
|
|
57
|
-
alert('An error occurred during upload');
|
|
58
|
-
}
|
|
59
|
-
finally {
|
|
60
|
-
setUploading(false);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
const handleDelete = async (id) => {
|
|
64
|
-
if (!confirm('Are you sure you want to delete this file?'))
|
|
65
|
-
return;
|
|
66
|
-
try {
|
|
67
|
-
const res = await fetch(`/api/plugin-content/files?siteId=${siteId}&id=${id}`, {
|
|
68
|
-
method: 'DELETE',
|
|
69
|
-
});
|
|
70
|
-
if (res.ok) {
|
|
71
|
-
setFiles(files.filter(f => f.id !== id));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch (err) {
|
|
75
|
-
console.error('Delete error:', err);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
const copyToClipboard = (text) => {
|
|
79
|
-
navigator.clipboard.writeText(text);
|
|
80
|
-
setCopiedId(text);
|
|
81
|
-
setTimeout(() => setCopiedId(null), 2000);
|
|
82
|
-
};
|
|
83
|
-
const filteredFiles = files.filter(f => f.filename.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
84
|
-
const formatSize = (bytes) => {
|
|
85
|
-
if (bytes === 0)
|
|
86
|
-
return '0 Bytes';
|
|
87
|
-
const k = 1024;
|
|
88
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
89
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
90
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
91
|
-
};
|
|
92
|
-
return (_jsxs("div", { className: "p-8 bg-white dark:bg-neutral-900 rounded-[2.5rem] h-full flex flex-col", children: [_jsxs("div", { className: "flex items-center justify-between mb-8", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-black uppercase tracking-tighter text-neutral-950 dark:text-white", children: "File Library" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400", children: "Upload and manage documents, PDFs, and other assets" })] }), _jsxs("label", { className: `flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest cursor-pointer hover:bg-primary/90 transition-all shadow-lg shadow-primary/20 ${uploading ? 'opacity-50 pointer-events-none' : ''}`, children: [_jsx(Upload, { size: 16 }), uploading ? 'Uploading...' : 'Upload File', _jsx("input", { type: "file", className: "hidden", onChange: handleUpload, disabled: uploading })] })] }), _jsxs("div", { className: "relative mb-6", children: [_jsx(Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 text-neutral-400 size-4" }), _jsx("input", { type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search files by name...", className: "w-full pl-11 pr-4 py-3 bg-neutral-50 dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-2xl text-sm outline-none focus:border-primary transition-all" })] }), _jsx("div", { className: "flex-1 overflow-y-auto custom-scrollbar", children: loading ? (_jsx("div", { className: "flex items-center justify-center py-20", children: _jsx("div", { className: "w-8 h-8 border-4 border-primary/20 border-t-primary rounded-full animate-spin" }) })) : filteredFiles.length === 0 ? (_jsxs("div", { className: "text-center py-20 bg-neutral-50 dark:bg-neutral-800/50 rounded-[2rem] border-2 border-dashed border-neutral-200 dark:border-neutral-700", children: [_jsx(FileText, { className: "mx-auto size-12 text-neutral-300 dark:text-neutral-600 mb-4" }), _jsx("p", { className: "text-neutral-500", children: searchQuery ? 'No files match your search.' : 'No files uploaded yet.' })] })) : (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6", children: filteredFiles.map((file) => (_jsxs("div", { className: "bg-neutral-50 dark:bg-neutral-800/50 p-5 rounded-[2rem] border border-neutral-200 dark:border-neutral-700 group transition-all hover:shadow-xl hover:border-primary/30", children: [_jsxs("div", { className: "flex items-center gap-4 mb-4", children: [_jsx("div", { className: "p-3 bg-white dark:bg-neutral-900 rounded-2xl shadow-sm group-hover:bg-primary/10 transition-colors", children: _jsx(File, { size: 24, className: "text-primary" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h3", { className: "font-bold text-sm text-neutral-950 dark:text-white truncate", title: file.filename, children: file.filename }), _jsxs("p", { className: "text-[10px] text-neutral-500 uppercase font-black tracking-tight", children: [formatSize(file.size), " \u2022 ", file.mimeType.split('/')[1].toUpperCase()] })] })] }), _jsxs("div", { className: "flex items-center gap-2 pt-4 border-t border-neutral-200 dark:border-neutral-700", children: [_jsxs("button", { onClick: () => copyToClipboard(file.id), className: "flex-1 flex items-center justify-center gap-2 py-2 bg-white dark:bg-neutral-900 rounded-xl text-[9px] font-black uppercase tracking-widest text-neutral-600 dark:text-neutral-400 hover:text-primary transition-all border border-neutral-200 dark:border-neutral-700", children: [copiedId === file.id ? _jsx(Check, { size: 12 }) : _jsx(Copy, { size: 12 }), copiedId === file.id ? 'Copied' : 'ID'] }), _jsx("a", { href: file.url, target: "_blank", rel: "noopener noreferrer", className: "p-2 bg-white dark:bg-neutral-900 rounded-xl text-neutral-600 dark:text-neutral-400 hover:text-blue-500 transition-all border border-neutral-200 dark:border-neutral-700", title: "View/Download", children: _jsx(ExternalLink, { size: 14 }) }), _jsx("button", { onClick: () => handleDelete(file.id), className: "p-2 bg-white dark:bg-neutral-900 rounded-xl text-neutral-600 dark:text-neutral-400 hover:text-red-500 transition-all border border-neutral-200 dark:border-neutral-700", title: "Delete", children: _jsx(Trash2, { size: 14 }) })] })] }, file.id))) })) })] }));
|
|
93
|
-
}
|
package/dist/views/index.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Asset Manager View
|
|
3
|
-
* Main entry point for managing links and files
|
|
4
|
-
*/
|
|
5
|
-
export declare function AssetManagerView({ siteId, locale, subPath }: {
|
|
6
|
-
siteId: string;
|
|
7
|
-
locale: string;
|
|
8
|
-
subPath: string[];
|
|
9
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
10
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/views/index.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AASH,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,2CAkElH"}
|
package/dist/views/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Asset Manager View
|
|
3
|
-
* Main entry point for managing links and files
|
|
4
|
-
*/
|
|
5
|
-
'use client';
|
|
6
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { Link2, FileText, ChevronRight } from 'lucide-react';
|
|
8
|
-
import { LinkManagerView } from './LinkManager/LinkManager';
|
|
9
|
-
import { MediaManagerView } from './MediaManager/MediaManager';
|
|
10
|
-
export function AssetManagerView({ siteId, locale, subPath }) {
|
|
11
|
-
const route = subPath[1] || 'links';
|
|
12
|
-
const navigate = (path) => {
|
|
13
|
-
window.history.pushState(null, '', `/dashboard/content/${path}`);
|
|
14
|
-
// Force re-render by triggering popstate or similar if needed,
|
|
15
|
-
// but for now we'll just handle it via state if we were in a single view
|
|
16
|
-
};
|
|
17
|
-
return (_jsxs("div", { className: "flex h-full w-full overflow-hidden", children: [_jsxs("aside", { className: "w-64 border-r border-dashboard-border bg-dashboard-sidebar flex flex-col p-6", children: [_jsx("h2", { className: "text-[10px] font-black uppercase tracking-[0.2em] text-neutral-400 mb-8", children: "Content Assets" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("button", { onClick: () => window.location.href = '/dashboard/content/links', className: `w-full flex items-center justify-between p-4 rounded-2xl transition-all ${route === 'links'
|
|
18
|
-
? 'bg-primary text-white shadow-lg shadow-primary/20'
|
|
19
|
-
: 'text-neutral-500 hover:bg-neutral-100 dark:hover:bg-neutral-800'}`, children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Link2, { size: 18 }), _jsx("span", { className: "text-sm font-bold", children: "Links & Buttons" })] }), _jsx(ChevronRight, { size: 14, className: route === 'links' ? 'opacity-100' : 'opacity-0' })] }), _jsxs("button", { onClick: () => window.location.href = '/dashboard/content/media', className: `w-full flex items-center justify-between p-4 rounded-2xl transition-all ${route === 'media'
|
|
20
|
-
? 'bg-primary text-white shadow-lg shadow-primary/20'
|
|
21
|
-
: 'text-neutral-500 hover:bg-neutral-100 dark:hover:bg-neutral-800'}`, children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(FileText, { size: 18 }), _jsx("span", { className: "text-sm font-bold", children: "File Library" })] }), _jsx(ChevronRight, { size: 14, className: route === 'media' ? 'opacity-100' : 'opacity-0' })] })] }), _jsx("div", { className: "mt-auto p-4 bg-dashboard-bg rounded-2xl border border-dashboard-border", children: _jsx("p", { className: "text-[10px] text-neutral-500 leading-relaxed font-medium", children: "Use these assets to make your application buttons and downloads dynamic." }) })] }), _jsx("main", { className: "flex-1 min-w-0 overflow-hidden", children: route === 'links' ? (_jsx(LinkManagerView, { siteId: siteId, locale: locale })) : (_jsx(MediaManagerView, { siteId: siteId })) })] }));
|
|
22
|
-
}
|