backups/manual-20260417-retire-indexhtml/structure-map.pre-retire.php

Code: DEV-FCE42BFF Size: 11.7 KB Lines: 263 Path: /home/prodconfig.wenesthosting.com/dev.solargroup.wenest.se/backups/manual-20260417-retire-indexhtml/structure-map.pre-retire.php

Task / Comment

Open report form
<?php
declare(strict_types=1);

function buildStructureMap(string $rootPath): array
{
    $rootPath = rtrim($rootPath, '/');

    $treeRoots = [
        'pages',
        'assets',
        'css',
        'js',
        'api',
        'setup',
        'maps',
    ];

    $tree = ['ROOT/'];
    foreach (['index.php', 'index.html', 'login.php', 'menu.php'] as $rootFile) {
        $rootFilePath = $rootPath . '/' . $rootFile;
        $tree[] = '  ' . (is_file($rootFilePath)
            ? $rootFile . ' [' . formatKb((int) filesize($rootFilePath)) . ']'
            : $rootFile . ' (missing)');
    }

    foreach ($treeRoots as $dir) {
        $path = $rootPath . '/' . $dir;
        if (is_dir($path)) {
            $tree[] = $dir . '/';
            appendTree($tree, $path, '  ', 2);
        }
    }

    $pageDefinitions = [
        ['name' => 'Dashboard', 'php' => 'pages/dashboard.php', 'js' => 'js/dashboard.js', 'css' => 'css/dashboard.css', 'tags' => ['OLD', 'ACTIVE']],
        ['name' => 'Deals', 'php' => 'pages/deals.php', 'js' => 'js/deals.js', 'css' => 'css/deals.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'ProjectFlow', 'php' => 'pages/project-flow.php', 'js' => 'js/project-flow.js', 'css' => 'css/project-flow.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Contractors', 'php' => 'pages/contractors.php', 'js' => 'js/contractors.js', 'css' => 'css/contractors.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Purchasing', 'php' => 'pages/purchasing.php', 'js' => 'js/purchasing.js', 'css' => 'css/purchasing.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Suppliers', 'php' => 'pages/suppliers.php', 'js' => 'js/suppliers.js', 'css' => 'css/suppliers.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'FieldSales', 'php' => 'pages/fieldsales.php', 'js' => 'js/fieldsales.js', 'css' => 'css/fieldsales.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Leads', 'php' => 'pages/leads.php', 'js' => 'js/leads.js', 'css' => 'css/leads.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Customers', 'php' => 'pages/customer.php', 'js' => 'js/customer.js', 'css' => 'css/customer.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'ProductList', 'php' => 'pages/productlist.php', 'js' => 'js/productlist.js', 'css' => 'css/productlist.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'CalcList', 'php' => 'pages/calc-list.php', 'js' => 'js/calc-list.js', 'css' => 'css/calc-list.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'NewCalc', 'php' => 'pages/kalkyl-ny-shell.php', 'js' => 'js/kalkyl-ny.js', 'css' => 'css/kalkyl-ny.css', 'tags' => ['OLD', 'ACTIVE']],
        ['name' => 'Calendar', 'php' => 'pages/calendar.php', 'js' => 'js/calendar.js', 'css' => 'css/calendar.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Inbox', 'php' => 'pages/inbox.php', 'js' => 'js/inbox.js', 'css' => 'css/inbox.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'DailyReport', 'php' => 'pages/daily-report.php', 'js' => 'js/daily-report.js', 'css' => 'css/daily-report.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'MySalary', 'php' => 'pages/my-salary.php', 'js' => 'js/my-salary.js', 'css' => 'css/my-salary.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Profile', 'php' => 'pages/profile.php', 'js' => 'js/profile.js', 'css' => 'css/profile.css', 'tags' => ['NEW', 'ACTIVE']],
        ['name' => 'Settings', 'php' => 'pages/settings.php', 'js' => 'js/settings.js', 'css' => 'css/settings.css', 'tags' => ['NEW', 'ACTIVE']],
    ];

    $pages = [];
    foreach ($pageDefinitions as $definition) {
        $phpPath = $rootPath . '/' . $definition['php'];
        $jsPath = $rootPath . '/' . $definition['js'];
        $cssPath = $rootPath . '/' . $definition['css'];
        $exists = [
            'php' => is_file($phpPath),
            'js' => is_file($jsPath),
            'css' => is_file($cssPath),
        ];

        $count = count(array_filter($exists));
        $status = 'Missing';
        $statusClass = 'status-missing';
        if ($count === 3) {
            $status = 'OK';
            $statusClass = 'status-ok';
        } elseif ($count > 0) {
            $status = 'Partial';
            $statusClass = 'status-partial';
        }

        $pages[] = [
            'name' => $definition['name'],
            'php' => formatFileEntry($definition['php'], $phpPath, $definition['tags']),
            'js' => formatFileEntry($definition['js'], $jsPath, $definition['tags']),
            'css' => formatFileEntry($definition['css'], $cssPath, $definition['tags']),
            'status' => $status,
            'statusClass' => $statusClass,
        ];
    }

    $keyFiles = [];
    foreach ([
        ['path' => 'index.php', 'tags' => ['OLD', 'ACTIVE']],
        ['path' => 'index.html', 'tags' => ['OLD', 'ACTIVE']],
        ['path' => 'login.php', 'tags' => ['NEW', 'TARGET']],
        ['path' => 'menu.php', 'tags' => ['NEW', 'TARGET']],
    ] as $fileDef) {
        $keyFiles[] = formatFileEntry($fileDef['path'], $rootPath . '/' . $fileDef['path'], $fileDef['tags']);
    }

    $counts = [];
    foreach (['pages', 'js', 'css', 'api', 'setup', 'maps'] as $dir) {
        $dirPath = $rootPath . '/' . $dir;
        $counts[] = [
            'dir' => $dir,
            'count' => is_dir($dirPath) ? countPhpJsCssFiles($dirPath) : 0,
            'size' => is_dir($dirPath) ? formatKb((int) directorySize($dirPath)) : '0 KB',
        ];
    }

    $targetStructure = [
        formatTargetEntry('index.php', ['NEW', 'TARGET'], 'Shell entry point'),
        formatTargetEntry('login.php', ['NEW', 'TARGET'], 'Login controls user identity and page permissions'),
        formatTargetEntry('menu.php', ['NEW', 'TARGET'], 'Shared menu shell for all pages'),
        formatTargetEntry('pages/dashboard.php', ['NEW', 'TARGET'], 'Dashboard filtered by logged-in user later'),
        formatTargetEntry('pages/deals.php', ['NEW', 'TARGET'], 'Deals page'),
        formatTargetEntry('pages/project-flow.php', ['NEW', 'TARGET'], 'ProjectFlow page'),
        formatTargetEntry('pages/contractors.php', ['NEW', 'TARGET'], 'Contractors page'),
        formatTargetEntry('pages/purchasing.php', ['NEW', 'TARGET'], 'Purchasing page'),
        formatTargetEntry('pages/suppliers.php', ['NEW', 'TARGET'], 'Suppliers page'),
        formatTargetEntry('pages/fieldsales.php', ['NEW', 'TARGET'], 'FieldSales page'),
        formatTargetEntry('pages/leads.php', ['NEW', 'TARGET'], 'Leads page'),
        formatTargetEntry('pages/customer.php', ['NEW', 'TARGET'], 'Customers page'),
        formatTargetEntry('pages/productlist.php', ['NEW', 'TARGET'], 'ProductList page'),
        formatTargetEntry('pages/calc-list.php', ['NEW', 'TARGET'], 'CalcList page'),
        formatTargetEntry('pages/new-calc.php', ['NEW', 'TARGET'], 'NewCalc page'),
        formatTargetEntry('pages/calendar.php', ['NEW', 'TARGET'], 'Calendar tied to logged-in user'),
        formatTargetEntry('pages/inbox.php', ['NEW', 'TARGET'], 'Inbox tied to logged-in user'),
        formatTargetEntry('pages/daily-report.php', ['NEW', 'TARGET'], 'Personal DailyReport for logged-in user'),
        formatTargetEntry('pages/my-salary.php', ['NEW', 'TARGET'], 'Personal MySalary for logged-in user'),
        formatTargetEntry('pages/profile.php', ['NEW', 'TARGET'], 'Profile page in bottom area with user settings'),
        formatTargetEntry('pages/settings.php', ['NEW', 'TARGET'], 'Settings page with company, AI, Google, notifications and finance tabs'),
        formatTargetEntry('assets/js/', ['NEW', 'TARGET'], 'Canonical JS folder'),
        formatTargetEntry('assets/css/', ['NEW', 'TARGET'], 'Canonical CSS folder'),
        formatTargetEntry('api/', ['NEW', 'TARGET'], 'Shared API directory'),
        formatTargetEntry('setup/', ['NEW', 'TARGET'], 'Settings and structure scripts'),
        formatTargetEntry('maps/', ['NEW', 'TARGET'], 'Structure map, tasks, and bug tracking'),
    ];

    $preferences = [
        'Profile page should allow user to edit profile data',
        'Profile page should allow toggling Calendar on/off',
        'Profile page should allow toggling Inbox on/off',
        'Calendar and Inbox are created from login identity',
        'DailyReport and MySalary are always personal to the logged-in user',
    ];

    $legacyFiles = [];
    foreach (findLegacyFiles($rootPath) as $legacyFile) {
        $legacyFiles[] = formatFileEntry($legacyFile, $rootPath . '/' . $legacyFile, ['OLD', 'BACKUP']);
    }

    return [
        'rootLabel' => basename($rootPath),
        'tree' => implode("\n", $tree),
        'pages' => $pages,
        'keyFiles' => $keyFiles,
        'counts' => $counts,
        'targetStructure' => $targetStructure,
        'preferences' => $preferences,
        'legacyFiles' => $legacyFiles,
    ];
}

function appendTree(array &$lines, string $path, string $prefix, int $depth): void
{
    if ($depth < 0 || !is_dir($path)) {
        return;
    }
    $items = scandir($path);
    if ($items === false) {
        return;
    }
    $items = array_values(array_filter($items, static function (string $item): bool {
        return $item !== '.' && $item !== '..';
    }));
    sort($items);
    foreach ($items as $item) {
        $fullPath = $path . '/' . $item;
        $isDir = is_dir($fullPath);
        $label = $isDir ? $item . '/' : $item . ' [' . formatKb(is_file($fullPath) ? (int) filesize($fullPath) : 0) . ']';
        $lines[] = $prefix . $label;
        if ($isDir) {
            appendTree($lines, $fullPath, $prefix . '  ', $depth - 1);
        }
    }
}

function countPhpJsCssFiles(string $path): int
{
    $count = 0;
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS));
    foreach ($iterator as $fileInfo) {
        if ($fileInfo->isFile()) {
            $extension = strtolower($fileInfo->getExtension());
            if (in_array($extension, ['php', 'js', 'css'], true)) {
                $count++;
            }
        }
    }
    return $count;
}

function directorySize(string $path): int
{
    $size = 0;
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS));
    foreach ($iterator as $fileInfo) {
        if ($fileInfo->isFile()) {
            $size += (int) $fileInfo->getSize();
        }
    }
    return $size;
}

function formatFileEntry(string $relativePath, string $absolutePath, array $tags): array
{
    return [
        'path' => $relativePath,
        'code' => buildFileCode($relativePath),
        'label' => is_file($absolutePath) ? $relativePath . ' [' . formatKb((int) filesize($absolutePath)) . ']' : $relativePath . ' (missing)',
        'exists' => is_file($absolutePath) || is_dir($absolutePath),
        'isFile' => is_file($absolutePath),
        'tags' => $tags,
    ];
}

function formatTargetEntry(string $path, array $tags, string $note): array
{
    return [
        'path' => $path,
        'code' => buildFileCode($path),
        'tags' => $tags,
        'note' => $note,
    ];
}

function buildFileCode(string $relativePath): string
{
    return 'DEV-' . strtoupper(substr(sha1($relativePath), 0, 8));
}

function formatKb(int $bytes): string
{
    if ($bytes <= 0) {
        return '0 KB';
    }
    return number_format($bytes / 1024, 1, '.', '') . ' KB';
}

function findLegacyFiles(string $rootPath): array
{
    $legacy = [];
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($rootPath, FilesystemIterator::SKIP_DOTS));
    foreach ($iterator as $fileInfo) {
        if (!$fileInfo->isFile()) {
            continue;
        }
        $relative = ltrim(str_replace($rootPath, '', $fileInfo->getPathname()), '/');
        if (preg_match('/(\.bak|backup|index-broken|index-today)/i', $relative)) {
            $legacy[] = $relative;
        }
    }
    sort($legacy);
    return $legacy;
}