main #2
@ -12,16 +12,49 @@ const refreshPage = () => {
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
// Fast loading logo - fetch during SSR to prevent hydration flash
|
||||
const { data: quickLoadingData } = await useLazyFetch("/api/devtool/config/loading-logo", {
|
||||
default: () => ({
|
||||
data: {
|
||||
siteLoadingLogo: '',
|
||||
siteName: 'Loading...'
|
||||
}
|
||||
}),
|
||||
transform: (response) => response.data || {
|
||||
siteLoadingLogo: '',
|
||||
siteName: 'Loading...'
|
||||
}
|
||||
});
|
||||
|
||||
const loadingLogoSrc = computed(() => {
|
||||
return 'http://localhost:3003/uploads/site-settings/loading-logo.png';
|
||||
// First priority: Quick loading data if available
|
||||
if (quickLoadingData.value?.siteLoadingLogo) {
|
||||
return quickLoadingData.value.siteLoadingLogo;
|
||||
}
|
||||
|
||||
// Second priority: Full site settings if loaded
|
||||
if (!siteSettingsLoading.value && siteSettings.value.siteLoadingLogo) {
|
||||
return siteSettings.value.siteLoadingLogo;
|
||||
}
|
||||
|
||||
// Fallback: Default logo
|
||||
return '/img/logo/corradAF-logo.svg';
|
||||
});
|
||||
|
||||
// Get site name with fallback
|
||||
const getSiteName = () => {
|
||||
if (siteSettingsLoading.value) {
|
||||
return 'Loading Logo';
|
||||
// First priority: Quick loading data
|
||||
if (quickLoadingData.value?.siteName) {
|
||||
return quickLoadingData.value.siteName;
|
||||
}
|
||||
return siteSettings.value?.siteName || 'Loading Logo';
|
||||
|
||||
// Second priority: Full site settings
|
||||
if (!siteSettingsLoading.value && siteSettings.value.siteName) {
|
||||
return siteSettings.value.siteName;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return 'Loading...';
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -9,18 +9,6 @@ export default [
|
||||
"icon": "ic:outline-dashboard",
|
||||
"child": [],
|
||||
"meta": {}
|
||||
},
|
||||
{
|
||||
"title": "Notes",
|
||||
"path": "/notes",
|
||||
"icon": "",
|
||||
"child": []
|
||||
},
|
||||
{
|
||||
"title": "Metabase",
|
||||
"path": "/metabase",
|
||||
"icon": "",
|
||||
"child": []
|
||||
}
|
||||
],
|
||||
"meta": {}
|
||||
|
@ -5,7 +5,7 @@ definePageMeta({
|
||||
requiresAuth: true,
|
||||
});
|
||||
|
||||
const { $swal, $toast } = useNuxtApp();
|
||||
const { $swal } = useNuxtApp();
|
||||
const { siteSettings, updateSiteSettings, applyThemeSettings, updateGlobalMeta } = useSiteSettings();
|
||||
|
||||
// Reactive data
|
||||
@ -139,7 +139,7 @@ const loadSettings = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading settings:", error);
|
||||
$toast.error("Failed to load site settings");
|
||||
alert("Failed to load site settings");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@ -148,7 +148,7 @@ const loadSettings = async () => {
|
||||
// Save settings
|
||||
const saveSettings = async () => {
|
||||
if (!validateForm()) {
|
||||
$toast.error("Please fix the validation errors");
|
||||
alert("Please fix the validation errors");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ const saveSettings = async () => {
|
||||
|
||||
if (result && result.success) {
|
||||
originalSettings.value = { ...settings.value };
|
||||
$toast.success("Settings saved successfully");
|
||||
alert("Settings saved successfully");
|
||||
|
||||
// Apply changes
|
||||
// applyChanges(); // Temporarily commented out to isolate the error source
|
||||
@ -175,7 +175,7 @@ const saveSettings = async () => {
|
||||
console.error("[SiteSettingsPage] 'result' from updateSiteSettings was undefined.");
|
||||
}
|
||||
|
||||
$toast.error(errorMsg);
|
||||
alert(errorMsg);
|
||||
|
||||
if (result && result.error && result.error.details) {
|
||||
console.error("[SiteSettingsPage] Save settings error details:", result.error.details);
|
||||
@ -189,7 +189,7 @@ const saveSettings = async () => {
|
||||
// This catch block is for unexpected errors during the saveSettings execution itself,
|
||||
// or if updateSiteSettings somehow re-throws an error not caught by its own try-catch.
|
||||
console.error("Critical error saving settings:", error);
|
||||
$toast.error("A critical error occurred. Failed to save settings.");
|
||||
alert("A critical error occurred. Failed to save settings.");
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
@ -220,7 +220,7 @@ const applyFontFromSource = () => {
|
||||
}
|
||||
}
|
||||
|
||||
$toast.success('Font applied successfully');
|
||||
alert('Font applied successfully');
|
||||
}
|
||||
};
|
||||
|
||||
@ -241,7 +241,7 @@ const uploadFile = async (file, type) => {
|
||||
return response.data.url;
|
||||
} catch (error) {
|
||||
console.error(`Error uploading ${type}:`, error);
|
||||
$toast.error(`Failed to upload ${type}`);
|
||||
alert(`Failed to upload ${type}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@ -253,7 +253,7 @@ const handleLogoUpload = async (event) => {
|
||||
const url = await uploadFile(file, 'logo');
|
||||
if (url) {
|
||||
settings.value.siteLogo = url;
|
||||
$toast.success('Logo uploaded successfully');
|
||||
alert('Logo uploaded successfully');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -264,7 +264,7 @@ const handleLoadingLogoUpload = async (event) => {
|
||||
const url = await uploadFile(file, 'loading-logo');
|
||||
if (url) {
|
||||
settings.value.siteLoadingLogo = url;
|
||||
$toast.success('Loading logo uploaded successfully');
|
||||
alert('Loading logo uploaded successfully');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -275,7 +275,7 @@ const handleFaviconUpload = async (event) => {
|
||||
const url = await uploadFile(file, 'favicon');
|
||||
if (url) {
|
||||
settings.value.siteFavicon = url;
|
||||
$toast.success('Favicon uploaded successfully');
|
||||
alert('Favicon uploaded successfully');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -286,7 +286,7 @@ const handleLoginLogoUpload = async (event) => {
|
||||
const url = await uploadFile(file, 'login-logo');
|
||||
if (url) {
|
||||
settings.value.siteLoginLogo = url;
|
||||
$toast.success('Login logo uploaded successfully');
|
||||
alert('Login logo uploaded successfully');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -295,14 +295,14 @@ const handleCSSUpload = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
if (!file.name.endsWith('.css')) {
|
||||
$toast.error('Please upload a valid CSS file');
|
||||
alert('Please upload a valid CSS file');
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
settings.value.customCSS = e.target.result;
|
||||
$toast.success('CSS file loaded successfully');
|
||||
alert('CSS file loaded successfully');
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
@ -314,7 +314,7 @@ const handleOgImageUpload = async (event) => {
|
||||
const url = await uploadFile(file, 'og-image');
|
||||
if (url) {
|
||||
settings.value.seoOgImage = url;
|
||||
$toast.success('OG image uploaded successfully');
|
||||
alert('OG image uploaded successfully');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -332,7 +332,7 @@ const resetSettings = () => {
|
||||
settings.value = { ...originalSettings.value };
|
||||
errors.value = {};
|
||||
applyChanges();
|
||||
$toast.info('Settings reset to last saved state');
|
||||
alert('Settings reset to last saved state');
|
||||
};
|
||||
|
||||
// Check for changes
|
||||
@ -364,7 +364,7 @@ const applyGoogleFont = (font) => {
|
||||
settings.value.fontSource = googleFontUrl;
|
||||
settings.value.currentFont = font.name;
|
||||
applyFontFromSource();
|
||||
$toast.success(`${font.name} font applied successfully`);
|
||||
alert(`${font.name} font applied successfully`);
|
||||
// Reset the dropdown after selection
|
||||
selectedGoogleFont.value = '';
|
||||
}
|
||||
@ -451,7 +451,7 @@ watch(() => settings.value.showSiteNameInHeader, (newValue) => {
|
||||
<div v-else>
|
||||
<!-- Tab Navigation -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 mb-8">
|
||||
<nav class="flex space-x-8" role="tablist">
|
||||
<nav class="flex space-x-4" role="tablist">
|
||||
<button
|
||||
v-for="tab in [
|
||||
{ id: 'basic', name: 'Basic', icon: 'ic:outline-info' },
|
||||
@ -466,7 +466,7 @@ watch(() => settings.value.showSiteNameInHeader, (newValue) => {
|
||||
activeTab === tab.id
|
||||
? 'border-primary text-primary bg-primary/5'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300',
|
||||
'whitespace-nowrap py-3 px-3 border-b-2 font-medium text-sm flex items-center space-x-2 rounded-t-lg transition-all duration-200'
|
||||
'whitespace-nowrap py-2 px-2 border-b-2 font-medium text-sm flex items-center space-x-2 rounded-t-lg transition-all duration-200'
|
||||
]"
|
||||
:aria-selected="activeTab === tab.id"
|
||||
role="tab"
|
||||
|
@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<LayoutsBreadcrumb />
|
||||
|
||||
<section class="flex flex-col h-screen">
|
||||
<div class="mb-4 flex-shrink-0">
|
||||
<h3>Metabase</h3>
|
||||
<p>
|
||||
Metabase is a powerful data visualization and analytics tool that allows you to
|
||||
create and share dashboards, reports, and visualizations with your team.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="pending" class="flex justify-center items-center flex-1">
|
||||
<div class="text-lg">Loading Metabase dashboard...</div>
|
||||
</div>
|
||||
<div v-else-if="error" class="flex justify-center items-center flex-1">
|
||||
<div class="text-red-500">Error loading dashboard: {{ error.message }}</div>
|
||||
</div>
|
||||
<iframe
|
||||
v-else
|
||||
:src="iframeUrl"
|
||||
frameborder="0"
|
||||
width="100%"
|
||||
class="flex-1"
|
||||
allowtransparency
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// Fetch the JWT token from our server API
|
||||
const { data: tokenData, pending, error } = await useFetch("/api/metabase/token");
|
||||
|
||||
const iframeUrl = computed(() => {
|
||||
if (tokenData.value?.token && tokenData.value?.siteUrl) {
|
||||
return (
|
||||
tokenData.value.siteUrl +
|
||||
"/embed/dashboard/" +
|
||||
tokenData.value.token +
|
||||
"#bordered=true&titled=true"
|
||||
);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -1,30 +0,0 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Notes",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<LayoutsBreadcrumb />
|
||||
<rs-card>
|
||||
<template #header>
|
||||
<div>
|
||||
Notes
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div>
|
||||
Content for Notes
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Add your styles here */
|
||||
</style>
|
||||
|
44
server/api/devtool/config/loading-logo.js
Normal file
44
server/api/devtool/config/loading-logo.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const method = getMethod(event);
|
||||
|
||||
try {
|
||||
if (method === "GET") {
|
||||
// Get only the loading logo and site name for faster loading
|
||||
const settings = await prisma.site_settings.findFirst({
|
||||
select: {
|
||||
siteLoadingLogo: true,
|
||||
siteName: true,
|
||||
},
|
||||
orderBy: { settingID: "desc" },
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Success",
|
||||
data: {
|
||||
siteLoadingLogo: settings?.siteLoadingLogo || '',
|
||||
siteName: settings?.siteName || 'corradAF',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 405,
|
||||
message: "Method not allowed",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Loading logo API error:", error);
|
||||
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal server error",
|
||||
error: error.message,
|
||||
};
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user