363 lines
14 KiB
JavaScript
363 lines
14 KiB
JavaScript
export const useSiteSettings = () => {
|
|
// Global site settings state
|
|
const siteSettings = useState('siteSettings', () => ({
|
|
siteName: 'corradAF',
|
|
siteDescription: 'corradAF Base Project',
|
|
siteLogo: '',
|
|
siteLoginLogo: '',
|
|
siteLoadingLogo: '',
|
|
siteFavicon: '',
|
|
showSiteNameInHeader: true,
|
|
siteNameFontSize: 18,
|
|
customCSS: '',
|
|
selectedTheme: 'biasa', // Use existing theme system
|
|
customThemeFile: '',
|
|
currentFont: '',
|
|
fontSource: '',
|
|
// SEO fields
|
|
seoTitle: '',
|
|
seoDescription: '',
|
|
seoKeywords: '',
|
|
seoAuthor: '',
|
|
seoOgImage: '',
|
|
seoTwitterCard: 'summary_large_image',
|
|
seoCanonicalUrl: '',
|
|
seoRobots: 'index, follow',
|
|
seoGoogleAnalytics: '',
|
|
seoGoogleTagManager: '',
|
|
seoFacebookPixel: ''
|
|
}));
|
|
|
|
// Loading state
|
|
const loading = useState('siteSettingsLoading', () => false);
|
|
|
|
// Load site settings from API
|
|
const loadSiteSettings = async () => {
|
|
loading.value = true;
|
|
try {
|
|
const response = await $fetch("/api/devtool/config/site-settings", {
|
|
method: "GET",
|
|
});
|
|
|
|
if (response && response.data) {
|
|
siteSettings.value = { ...siteSettings.value, ...response.data };
|
|
applyThemeSettings();
|
|
updateGlobalMeta();
|
|
}
|
|
} catch (error) {
|
|
console.error("Error loading site settings:", error);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
// Update global meta tags and SEO
|
|
const updateGlobalMeta = () => {
|
|
if (process.client) {
|
|
// Update page title - use SEO title if available
|
|
const title = siteSettings.value.seoTitle || siteSettings.value.siteName;
|
|
if (title) {
|
|
document.title = title;
|
|
|
|
// Update meta description - use SEO description if available
|
|
let metaDescription = document.querySelector('meta[name="description"]');
|
|
if (!metaDescription) {
|
|
metaDescription = document.createElement('meta');
|
|
metaDescription.name = 'description';
|
|
document.head.appendChild(metaDescription);
|
|
}
|
|
metaDescription.content = siteSettings.value.seoDescription || siteSettings.value.siteDescription || title;
|
|
|
|
// Update keywords meta tag
|
|
if (siteSettings.value.seoKeywords) {
|
|
let keywordsMeta = document.querySelector('meta[name="keywords"]');
|
|
if (!keywordsMeta) {
|
|
keywordsMeta = document.createElement('meta');
|
|
keywordsMeta.name = 'keywords';
|
|
document.head.appendChild(keywordsMeta);
|
|
}
|
|
keywordsMeta.content = siteSettings.value.seoKeywords;
|
|
}
|
|
|
|
// Update author meta tag
|
|
if (siteSettings.value.seoAuthor) {
|
|
let authorMeta = document.querySelector('meta[name="author"]');
|
|
if (!authorMeta) {
|
|
authorMeta = document.createElement('meta');
|
|
authorMeta.name = 'author';
|
|
document.head.appendChild(authorMeta);
|
|
}
|
|
authorMeta.content = siteSettings.value.seoAuthor;
|
|
}
|
|
|
|
// Update robots meta tag
|
|
let robotsMeta = document.querySelector('meta[name="robots"]');
|
|
if (!robotsMeta) {
|
|
robotsMeta = document.createElement('meta');
|
|
robotsMeta.name = 'robots';
|
|
document.head.appendChild(robotsMeta);
|
|
}
|
|
robotsMeta.content = siteSettings.value.seoRobots;
|
|
|
|
// Update Open Graph tags
|
|
let ogTitle = document.querySelector('meta[property="og:title"]');
|
|
if (!ogTitle) {
|
|
ogTitle = document.createElement('meta');
|
|
ogTitle.setAttribute('property', 'og:title');
|
|
document.head.appendChild(ogTitle);
|
|
}
|
|
ogTitle.content = title;
|
|
|
|
let ogDescription = document.querySelector('meta[property="og:description"]');
|
|
if (!ogDescription) {
|
|
ogDescription = document.createElement('meta');
|
|
ogDescription.setAttribute('property', 'og:description');
|
|
document.head.appendChild(ogDescription);
|
|
}
|
|
ogDescription.content = siteSettings.value.seoDescription || siteSettings.value.siteDescription || title;
|
|
|
|
// Update OG image
|
|
if (siteSettings.value.seoOgImage) {
|
|
let ogImage = document.querySelector('meta[property="og:image"]');
|
|
if (!ogImage) {
|
|
ogImage = document.createElement('meta');
|
|
ogImage.setAttribute('property', 'og:image');
|
|
document.head.appendChild(ogImage);
|
|
}
|
|
ogImage.content = siteSettings.value.seoOgImage;
|
|
}
|
|
|
|
// Update Twitter Card tags
|
|
let twitterCard = document.querySelector('meta[name="twitter:card"]');
|
|
if (!twitterCard) {
|
|
twitterCard = document.createElement('meta');
|
|
twitterCard.name = 'twitter:card';
|
|
document.head.appendChild(twitterCard);
|
|
}
|
|
twitterCard.content = siteSettings.value.seoTwitterCard;
|
|
|
|
let twitterTitle = document.querySelector('meta[name="twitter:title"]');
|
|
if (!twitterTitle) {
|
|
twitterTitle = document.createElement('meta');
|
|
twitterTitle.name = 'twitter:title';
|
|
document.head.appendChild(twitterTitle);
|
|
}
|
|
twitterTitle.content = title;
|
|
|
|
let twitterDescription = document.querySelector('meta[name="twitter:description"]');
|
|
if (!twitterDescription) {
|
|
twitterDescription = document.createElement('meta');
|
|
twitterDescription.name = 'twitter:description';
|
|
document.head.appendChild(twitterDescription);
|
|
}
|
|
twitterDescription.content = siteSettings.value.seoDescription || siteSettings.value.siteDescription || title;
|
|
|
|
// Update canonical URL
|
|
if (siteSettings.value.seoCanonicalUrl) {
|
|
let canonicalLink = document.querySelector('link[rel="canonical"]');
|
|
if (!canonicalLink) {
|
|
canonicalLink = document.createElement('link');
|
|
canonicalLink.rel = 'canonical';
|
|
document.head.appendChild(canonicalLink);
|
|
}
|
|
canonicalLink.href = siteSettings.value.seoCanonicalUrl;
|
|
}
|
|
}
|
|
|
|
// Update favicon
|
|
if (siteSettings.value.siteFavicon) {
|
|
let faviconLink = document.querySelector("link[rel*='icon']");
|
|
if (!faviconLink) {
|
|
faviconLink = document.createElement('link');
|
|
faviconLink.rel = 'icon';
|
|
document.head.appendChild(faviconLink);
|
|
}
|
|
faviconLink.href = siteSettings.value.siteFavicon;
|
|
|
|
// Update apple touch icon
|
|
let appleTouchIcon = document.querySelector("link[rel='apple-touch-icon']");
|
|
if (!appleTouchIcon) {
|
|
appleTouchIcon = document.createElement('link');
|
|
appleTouchIcon.rel = 'apple-touch-icon';
|
|
document.head.appendChild(appleTouchIcon);
|
|
}
|
|
appleTouchIcon.href = siteSettings.value.siteFavicon;
|
|
}
|
|
|
|
// Apply analytics scripts
|
|
if (siteSettings.value.seoGoogleAnalytics) {
|
|
// Add Google Analytics
|
|
const script = document.createElement('script');
|
|
script.async = true;
|
|
script.src = `https://www.googletagmanager.com/gtag/js?id=${siteSettings.value.seoGoogleAnalytics}`;
|
|
document.head.appendChild(script);
|
|
|
|
const gtag = document.createElement('script');
|
|
gtag.textContent = `
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
gtag('config', '${siteSettings.value.seoGoogleAnalytics}');
|
|
`;
|
|
document.head.appendChild(gtag);
|
|
}
|
|
|
|
if (siteSettings.value.seoGoogleTagManager) {
|
|
// Add Google Tag Manager
|
|
const gtmScript = document.createElement('script');
|
|
gtmScript.textContent = `
|
|
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
})(window,document,'script','dataLayer','${siteSettings.value.seoGoogleTagManager}');
|
|
`;
|
|
document.head.appendChild(gtmScript);
|
|
}
|
|
|
|
if (siteSettings.value.seoFacebookPixel) {
|
|
// Add Facebook Pixel
|
|
const fbScript = document.createElement('script');
|
|
fbScript.textContent = `
|
|
!function(f,b,e,v,n,t,s)
|
|
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
|
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
|
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
|
n.queue=[];t=b.createElement(e);t.async=!0;
|
|
t.src=v;s=b.getElementsByTagName(e)[0];
|
|
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
|
'https://connect.facebook.net/en_US/fbevents.js');
|
|
fbq('init', '${siteSettings.value.seoFacebookPixel}');
|
|
fbq('track', 'PageView');
|
|
`;
|
|
document.head.appendChild(fbScript);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Apply theme settings to the document
|
|
const applyThemeSettings = () => {
|
|
if (process.client) {
|
|
// Apply selected theme using existing theme system
|
|
if (siteSettings.value.selectedTheme) {
|
|
document.documentElement.setAttribute("data-theme", siteSettings.value.selectedTheme);
|
|
localStorage.setItem("theme", siteSettings.value.selectedTheme);
|
|
}
|
|
|
|
// Apply custom theme file if exists (append to theme.css)
|
|
if (siteSettings.value.customThemeFile) {
|
|
let customThemeElement = document.getElementById('custom-theme-file');
|
|
if (!customThemeElement) {
|
|
customThemeElement = document.createElement('link');
|
|
customThemeElement.id = 'custom-theme-file';
|
|
customThemeElement.rel = 'stylesheet';
|
|
customThemeElement.type = 'text/css';
|
|
document.head.appendChild(customThemeElement);
|
|
}
|
|
customThemeElement.href = siteSettings.value.customThemeFile;
|
|
} else {
|
|
// Remove custom theme file if it exists
|
|
const existingThemeElement = document.getElementById('custom-theme-file');
|
|
if (existingThemeElement) {
|
|
existingThemeElement.remove();
|
|
}
|
|
}
|
|
|
|
// Apply custom CSS
|
|
let customStyleElement = document.getElementById('custom-site-styles');
|
|
if (!customStyleElement) {
|
|
customStyleElement = document.createElement('style');
|
|
customStyleElement.id = 'custom-site-styles';
|
|
document.head.appendChild(customStyleElement);
|
|
}
|
|
customStyleElement.textContent = siteSettings.value.customCSS || '';
|
|
}
|
|
};
|
|
|
|
// Set theme (integrate with existing theme system)
|
|
const setTheme = (theme) => {
|
|
siteSettings.value.selectedTheme = theme;
|
|
applyThemeSettings();
|
|
// Optionally save to server
|
|
updateSiteSettings(siteSettings.value);
|
|
};
|
|
|
|
// Get current theme
|
|
const getCurrentTheme = () => {
|
|
return siteSettings.value.selectedTheme || 'biasa';
|
|
};
|
|
|
|
// Update site settings
|
|
const updateSiteSettings = async (newSettings) => {
|
|
console.log("[useSiteSettings] updateSiteSettings called with:", JSON.parse(JSON.stringify(newSettings)));
|
|
try {
|
|
const response = await $fetch("/api/devtool/config/site-settings", {
|
|
method: "POST",
|
|
body: newSettings,
|
|
});
|
|
console.log("[useSiteSettings] API response received:", JSON.parse(JSON.stringify(response)));
|
|
|
|
if (response && response.data) {
|
|
siteSettings.value = { ...siteSettings.value, ...response.data };
|
|
applyThemeSettings();
|
|
updateGlobalMeta();
|
|
console.log("[useSiteSettings] Returning success from updateSiteSettings.");
|
|
return { success: true, data: response.data };
|
|
}
|
|
|
|
let errorMessage = "Update operation failed: No data returned from server.";
|
|
if (response && typeof response === 'object' && response !== null && 'message' in response) {
|
|
errorMessage = response.message;
|
|
} else if (response) {
|
|
errorMessage = "Update operation failed: Unexpected server response format on success.";
|
|
} else if (response === undefined) {
|
|
errorMessage = "Update failed: Server returned no content (e.g. 204). Treating as failure as data is expected for settings.";
|
|
console.log("[useSiteSettings] Returning failure (204 or undefined response) from updateSiteSettings.");
|
|
return { success: false, error: { message: errorMessage, details: response } };
|
|
}
|
|
|
|
console.log("[useSiteSettings] Returning failure (general case) from updateSiteSettings:", errorMessage);
|
|
return { success: false, error: { message: errorMessage, details: response } };
|
|
} catch (error) {
|
|
console.error("[useSiteSettings] Error in updateSiteSettings catch block:", error);
|
|
let detailedMessage = "An unexpected error occurred during update.";
|
|
if (error.data && error.data.message) {
|
|
detailedMessage = error.data.message;
|
|
} else if (error.message) {
|
|
detailedMessage = error.message;
|
|
}
|
|
console.log("[useSiteSettings] Returning failure (catch block) from updateSiteSettings:", detailedMessage);
|
|
return { success: false, error: { message: detailedMessage, details: error } };
|
|
}
|
|
};
|
|
|
|
// Add custom theme to theme.css file
|
|
const addCustomThemeToFile = async (themeName, themeCSS) => {
|
|
try {
|
|
const response = await $fetch("/api/devtool/config/add-custom-theme", {
|
|
method: "POST",
|
|
body: {
|
|
themeName,
|
|
themeCSS
|
|
}
|
|
});
|
|
|
|
return response;
|
|
} catch (error) {
|
|
console.error("Error adding custom theme:", error);
|
|
return { success: false, error };
|
|
}
|
|
};
|
|
|
|
return {
|
|
siteSettings,
|
|
loading: readonly(loading),
|
|
loadSiteSettings,
|
|
updateSiteSettings,
|
|
applyThemeSettings,
|
|
updateGlobalMeta,
|
|
getCurrentTheme,
|
|
setTheme,
|
|
addCustomThemeToFile
|
|
};
|
|
};
|