Radius/add network structure

This commit is contained in:
Luca Haid
2025-12-09 05:34:24 +00:00
parent 60556e5d63
commit 167b038c20
37 changed files with 6833 additions and 2920 deletions

View File

@@ -0,0 +1,133 @@
<?php
if (!isset($vueViewName)) die("vueViewName is not set");
if (!isset($mfLayoutPackage)) die("mfLayoutPackage is not set");
$additionalCSS = $additionalCSS ?? [];
$additionalJS = $additionalJS ?? [];
$vueViewPath = BASEDIR . "/public/js/pages/$vueViewName";
// Load page-specific CSS and JS files
if (is_dir($vueViewPath)) {
foreach (scandir($vueViewPath) as $file) {
if ($file === '.' || $file === '..') continue;
$fileExtension = pathinfo($file, PATHINFO_EXTENSION);
if ($fileExtension === 'css') $additionalCSS[] = "js/pages/$vueViewName/$file";
else if ($fileExtension === 'js') $additionalJS[] = "js/pages/$vueViewName/$file";
}
}
// Add TT-Core CSS
$additionalCSS = [
"plugins/vue/tt-core/styles/tt-core.css",
...$additionalCSS,
];
/**
* Convert PascalCase to snake_case (e.g. PascalCase to pascal-case)
* @param string $str PascalCase string
* @return string snake-case string
*/
function pascalToSnakeCase(string $str): string {
return strtolower(preg_replace('/(?<!^)([A-Z])/', '-$1', $str));
}
$vueTagName = pascalToSnakeCase($vueViewName);
$vueHeaderPath = realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/vueHeader3.php";
if (!file_exists($vueHeaderPath))
$vueHeaderPath = realpath(dirname(__FILE__) . "/../../default") . "/vueHeader3.php";
include($vueHeaderPath); ?>
<div id="app">
<<?php echo $vueTagName; ?>>
</<?php echo $vueTagName; ?>>
</div>
<!-- TT-Core Library -->
<script src="<?php echo mfBaseController::getUrl(""); ?>plugins/vue/tt-core/index.js" type="module"></script>
<!-- Vue 3 Initialization -->
<script>
// TT-Core components to load
const ttCoreComponents = [
'plugins/vue/tt-core/components/data-display/TtDataTable.js',
'plugins/vue/tt-core/components/data-display/TtStatusChip.js',
'plugins/vue/tt-core/components/display/TtInfoCard.js',
'plugins/vue/tt-core/components/feedback/TtLoadingIndicator.js',
'plugins/vue/tt-core/components/feedback/TtSkeleton.js',
'plugins/vue/tt-core/components/forms/TtSmartAutocomplete.js',
'plugins/vue/tt-core/components/forms/TtFileDropzone.js',
'plugins/vue/tt-core/components/forms/TtCopyButton.js',
'plugins/vue/tt-core/components/overlays/TtDialog.js',
'plugins/vue/tt-core/components/navigation/TtViewSwitcher.js'
];
// All additional scripts
const allScripts = <?php echo json_encode($additionalJS); ?>;
// Separate Chart.js libraries (need to load first)
const chartLibs = allScripts.filter(s => s.includes('chart.js/chart.'));
const chartAdapters = allScripts.filter(s => s.includes('chartjs-adapter'));
const pageScripts = allScripts.filter(s => !s.includes('chart.js/') && !s.includes('chartjs-adapter'));
// Wait for TT_CORE to be loaded (since index.js is a module and loads async)
function initVueApp() {
if (typeof window.TT_CORE === 'undefined') {
// TT_CORE not loaded yet, wait a bit and try again
setTimeout(initVueApp, 50);
return;
}
const { createApp } = Vue;
const app = createApp({
data() {
return {
window: window
};
}
});
// CRITICAL: Register TT-Core components and set window.VueApp
window.TT_CORE.registerComponents(app);
// Load scripts in order:
// 1. TT-Core components
// 2. Chart.js library (if needed)
// 3. Chart.js adapters (after Chart.js)
// 4. Page-specific components
loadScripts(ttCoreComponents)
.then(() => chartLibs.length ? loadScripts(chartLibs) : Promise.resolve())
.then(() => chartAdapters.length ? loadScripts(chartAdapters) : Promise.resolve())
.then(() => loadScripts(pageScripts))
.then(() => {
// Mount the app after all components are loaded and registered
app.mount('#app');
})
.catch(err => {
console.error('Failed to load components:', err);
});
}
// Dynamically load scripts
function loadScripts(scriptPaths) {
const baseUrl = '<?php echo mfBaseController::getUrl(""); ?>';
const promises = scriptPaths.map(src => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = baseUrl + src;
script.onload = resolve;
script.onerror = () => reject(new Error(`Failed to load ${src}`));
document.body.appendChild(script);
});
});
return Promise.all(promises);
}
// Start initialization
initVueApp();
</script>
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>

View File

@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?= MFAPPNAME_FULL ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="shortcut icon" href="<?= self::getResourcePath() ?>assets/images/favicon.ico">
<link href="<?= self::getResourcePath() ?>cssbundler.php?<?= $git_merge_ts ?>" rel="stylesheet">
<link href="<?= self::getResourcePath() ?>fontawesome/css/fontawesome.min.css?<?= $git_merge_ts ?>"
rel="stylesheet">
<link href="<?= self::getResourcePath() ?>fontawesome/css/solid.min.css?<?= $git_merge_ts ?>" rel="stylesheet">
<link href="<?= self::getResourcePath() ?>fontawesome/css/regular.min.css?<?= $git_merge_ts ?>" rel="stylesheet">
<link href="<?=self::getResourcePath()?>fontawesome/css/duotone.min.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
<link href="<?=self::getResourcePath()?>fontawesome/css/duotone-regular.min.css?<?=$git_merge_ts?>" rel="stylesheet" type="text/css" />
<link href="<?= self::getResourcePath() ?>fontawesome/css/sharp-light.min.css?<?= $git_merge_ts ?>"
rel="stylesheet">
<?php if (!empty($additionalCSS)):
foreach ($additionalCSS as $css): ?>
<link rel="stylesheet" href="<?= self::getResourcePath() ?><?= $css ?>?<?= $git_merge_ts ?>">
<?php endforeach;
endif;
if (!empty($additionalHead)):
foreach ($additionalHead as $head):
echo $head;
endforeach;
endif; ?>
<script>
const baseurl = '<?=self::getResourcePath()?>';
window.mfNotify = <?=json_encode($mfNotify ?? null)?>;
window.TT_CONFIG = {};
<?php
if(isset($JSGlobals) && is_array($JSGlobals) && count($JSGlobals)):
foreach($JSGlobals as $key => $value): ?>
window.TT_CONFIG.<?=$key?> = <?=is_array($value) ? json_encode($value) : "'$value'"; ?>;
<?php endforeach; endif;?>
</script>
<script type="text/javascript" src="<?=self::getResourcePath()?>js/jquery.min.js"></script>
<script type="text/javascript" src="<?=self::getResourcePath()?>js/popper.min.js"></script>
<script type="text/javascript" src="<?=self::getResourcePath()?>js/bootstrap.min.js"></script>
<!-- Vue 3 CDN -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- Axios for HTTP requests -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- Moment.js for date handling -->
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script>
<script src="<?= self::getResourcePath() ?>plugins/notification/notify.js" defer></script>
<script src="<?= self::getResourcePath() ?>plugins/bookstack/bookstackIntegration.js" defer></script>
<style>
body {
min-height: 100vh;
}
<?php if (MFAPPNAME === "devthetool"): ?>
body {
border-left: 8px dashed #f672a7;
}
<?php endif; ?>
</style>
</head>
<body>
<header id="topnav">
<?php
include(__DIR__ . "/topbar.php");
include(__DIR__ . "/menu.php");
?>
</header>
<div class="wrapper pl-0 pl-lg-1 pr-0 pr-lg-1">
<div class="container-fluid">