📄 Source: index.php
<?php
session_start();
// Allowed file types to show in dashboard
$allowed_extensions = ['php', 'html', 'htm', 'pdf', 'txt', 'md', 'json', 'xml', 'jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'css', 'js'];
// Files to ignore (don't show in standalone files)
$ignored_files = ['index.php', 'selector.php', 'style.css'];
// Theme handling
$theme = isset($_COOKIE['dashboard_theme']) ? $_COOKIE['dashboard_theme'] : 'light';
if (isset($_GET['theme']) && in_array($_GET['theme'], ['light', 'dark'])) {
$theme = $_GET['theme'];
setcookie('dashboard_theme', $theme, time() + (365 * 24 * 60 * 60), '/');
header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
exit;
}
// Handle AJAX refresh request
if (isset($_GET['ajax'])) {
header('Content-Type: application/json');
$items = scandir(__DIR__);
$projects = [];
$files = [];
foreach ($items as $item) {
if ($item == '.' || $item == '..' || $item == 'selector.php') continue;
$full_path = __DIR__ . '/' . $item;
if (is_dir($full_path)) {
$has_index = false;
$index_type = '';
foreach (['index.php', 'index.html', 'index.htm'] as $index) {
if (file_exists($full_path . '/' . $index)) {
$has_index = true;
$index_type = $index;
break;
}
}
$web_files = [];
if (is_dir($full_path)) {
foreach (scandir($full_path) as $file) {
if (in_array(pathinfo($file, PATHINFO_EXTENSION), $allowed_extensions)) {
$web_files[] = $file;
}
}
}
$projects[] = [
'name' => $item,
'has_index' => $has_index,
'index_type' => $index_type,
'file_count' => count($web_files),
'web_files' => $web_files
];
} elseif (in_array(pathinfo($item, PATHINFO_EXTENSION), $allowed_extensions) && !in_array($item, $ignored_files)) {
$files[] = [
'name' => $item,
'ext' => pathinfo($item, PATHINFO_EXTENSION)
];
}
}
usort($projects, function($a, $b) { return strcmp($a['name'], $b['name']); });
usort($files, function($a, $b) { return strcmp($a['name'], $b['name']); });
echo json_encode(['projects' => $projects, 'files' => $files]);
exit;
}
// Get all projects and files
$items = scandir(__DIR__);
$projects = [];
$files = [];
foreach ($items as $item) {
if ($item == '.' || $item == '..' || $item == 'selector.php') continue;
$full_path = __DIR__ . '/' . $item;
if (is_dir($full_path)) {
$has_index = false;
$index_type = '';
foreach (['index.php', 'index.html', 'index.htm'] as $index) {
if (file_exists($full_path . '/' . $index)) {
$has_index = true;
$index_type = $index;
break;
}
}
$web_files = [];
if (is_dir($full_path)) {
foreach (scandir($full_path) as $file) {
if (in_array(pathinfo($file, PATHINFO_EXTENSION), $allowed_extensions)) {
$web_files[] = $file;
}
}
}
$projects[] = [
'name' => $item,
'has_index' => $has_index,
'index_type' => $index_type,
'file_count' => count($web_files),
'web_files' => $web_files
];
} elseif (in_array(pathinfo($item, PATHINFO_EXTENSION), $allowed_extensions) && !in_array($item, $ignored_files)) {
$files[] = [
'name' => $item,
'ext' => pathinfo($item, PATHINFO_EXTENSION)
];
}
}
usort($projects, function($a, $b) { return strcmp($a['name'], $b['name']); });
usort($files, function($a, $b) { return strcmp($a['name'], $b['name']); });
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CreateCraft - Project Dashboard</title>
<link rel="stylesheet" href="style.css">
<style>
.refresh-btn {
background: var(--secondary-bg);
border: 1px solid var(--border-color);
padding: 10px 20px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
color: var(--text-color);
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.refresh-btn:hover {
background: var(--accent-color);
color: white;
}
.refresh-btn.spinning {
animation: spin 0.5s ease-in-out;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
background: #4caf50;
color: white;
padding: 12px 20px;
border-radius: 8px;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.toast.error {
background: #f44336;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
#liveClock {
font-family: monospace;
font-weight: bold;
background: var(--secondary-bg);
padding: 2px 8px;
border-radius: 5px;
display: inline-block;
}
/* Mobile improvements - more compact grid */
.project-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 15px;
}
/* Fully clickable card */
.project-card {
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.project-card:active {
transform: scale(0.98);
}
/* Hide the button text on mobile? No - keep but make card clickable */
.project-actions .btn {
pointer-events: auto;
position: relative;
z-index: 2;
}
/* Mobile specific */
@media (max-width: 768px) {
.project-grid {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 12px;
}
.project-card {
padding: 15px;
}
.project-icon {
font-size: 36px;
margin-bottom: 10px;
}
.project-name {
font-size: 16px;
}
.project-info {
font-size: 11px;
}
.project-actions .btn-sm {
padding: 4px 8px;
font-size: 10px;
}
}
/* Desktop hover effect */
@media (min-width: 769px) {
.project-card:hover {
transform: translateY(-3px);
}
}
</style>
</head>
<body class="<?php echo $theme; ?>">
<div class="container">
<div class="header">
<div class="header-top">
<div>
<h1>🚀 CreateCraft Development Server</h1>
<div class="subtitle">XAMPP-Style Project Dashboard</div>
</div>
<div style="display: flex; gap: 10px;">
<button class="refresh-btn" onclick="refreshDashboard()" id="refreshBtn">
🔄 Refresh
</button>
<button class="theme-toggle" onclick="toggleTheme()">
<?php echo $theme === 'dark' ? '☀️ Light Mode' : '🌙 Dark Mode'; ?>
</button>
</div>
</div>
<div class="server-info">
📂 Document Root: <?php echo __DIR__; ?><br>
🐘 PHP Version: <?php echo phpversion(); ?><br>
🌐 Server Time: <span id="liveClock">--:--:--</span>
</div>
</div>
<div id="dashboardContent">
<div class="section" id="projectsSection">
<h2>📁 Projects & Folders</h2>
<div class="project-grid" id="projectsGrid">
<?php foreach ($projects as $project): ?>
<div class="project-card" data-url="<?php echo $project['has_index'] ? htmlspecialchars($project['name']) . '/' : 'selector.php?dir=' . urlencode($project['name']); ?>">
<div class="project-icon"><?php echo $project['has_index'] ? '🌐' : '📁'; ?></div>
<div class="project-name"><?php echo htmlspecialchars($project['name']); ?></div>
<div class="project-info">
<?php if ($project['has_index']): ?>
<span class="badge badge-index">✓ Has index</span>
<span class="badge badge-folder"><?php echo $project['index_type']; ?></span>
<?php else: ?>
<span class="badge badge-noindex">⚠️ No index file</span>
<span class="badge badge-folder"><?php echo $project['file_count']; ?> files</span>
<?php endif; ?>
</div>
<div class="project-actions">
<?php if ($project['has_index']): ?>
<a href="<?php echo htmlspecialchars($project['name']); ?>/" class="btn btn-sm" onclick="event.stopPropagation()">🔗 Open Project</a>
<?php else: ?>
<a href="selector.php?dir=<?php echo urlencode($project['name']); ?>" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">📂 Browse</a>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="section" id="filesSection">
<h2>📄 Standalone Files</h2>
<div class="file-list" id="filesList">
<?php foreach ($files as $file): ?>
<div class="file-item">
<span class="file-name">
<?php
$icon = '📄';
if ($file['ext'] == 'php') $icon = '🐘';
elseif ($file['ext'] == 'pdf') $icon = '📑';
elseif (in_array($file['ext'], ['jpg', 'jpeg', 'png', 'gif'])) $icon = '🖼️';
elseif ($file['ext'] == 'txt') $icon = '📝';
elseif ($file['ext'] == 'css') $icon = '🎨';
elseif ($file['ext'] == 'js') $icon = '📜';
echo $icon;
?>
<?php echo htmlspecialchars($file['name']); ?>
</span>
<a href="<?php echo htmlspecialchars($file['name']); ?>" class="btn btn-sm" target="_blank">▶ Open</a>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<div class="quick-actions">
<h3>💡 Quick Tips</h3>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>🔄 Click Refresh to scan for new projects without reloading the page</li>
<li>🎨 Click the theme button to switch between Light/Dark mode</li>
<li>📁 Click anywhere on a project card to open it</li>
<li>🔗 The button works too - both do the same thing!</li>
<li>📂 Use FileZilla/SFTP to upload your projects to this directory</li>
</ul>
</div>
</div>
<script>
function updateClock() {
const now = new Date();
const formatter = new Intl.DateTimeFormat('ro-RO', {
timeZone: 'Europe/Bucharest',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
let formatted = formatter.format(now);
let parts = formatted.split(', ');
let date = parts[0].split('.').reverse().join('-');
let time = parts[1];
document.getElementById('liveClock').textContent = `${date} ${time}`;
}
// Make cards fully clickable
document.querySelectorAll('.project-card').forEach(card => {
const url = card.getAttribute('data-url');
if (url) {
card.addEventListener('click', function(e) {
// Don't trigger if clicking the button (button already has its own handler)
if (!e.target.closest('.btn')) {
window.location.href = url;
}
});
}
});
async function refreshDashboard() {
const refreshBtn = document.getElementById('refreshBtn');
refreshBtn.classList.add('spinning');
try {
const response = await fetch('?ajax=1');
const data = await response.json();
const projectsGrid = document.getElementById('projectsGrid');
projectsGrid.innerHTML = '';
data.projects.forEach(project => {
const card = document.createElement('div');
card.className = 'project-card';
const url = project.has_index ? `${escapeHtml(project.name)}/` : `selector.php?dir=${encodeURIComponent(project.name)}`;
card.setAttribute('data-url', url);
card.innerHTML = `
<div class="project-icon">${project.has_index ? '🌐' : '📁'}</div>
<div class="project-name">${escapeHtml(project.name)}</div>
<div class="project-info">
${project.has_index ?
`<span class="badge badge-index">✓ Has index</span>
<span class="badge badge-folder">${escapeHtml(project.index_type)}</span>` :
`<span class="badge badge-noindex">⚠️ No index file</span>
<span class="badge badge-folder">${project.file_count} files</span>`
}
</div>
<div class="project-actions">
${project.has_index ?
`<a href="${escapeHtml(project.name)}/" class="btn btn-sm" onclick="event.stopPropagation()">🔗 Open Project</a>` :
`<a href="selector.php?dir=${encodeURIComponent(project.name)}" class="btn btn-sm btn-outline" onclick="event.stopPropagation()">📂 Browse</a>`
}
</div>
`;
// Add click handler for the new card
card.addEventListener('click', function(e) {
if (!e.target.closest('.btn')) {
window.location.href = url;
}
});
projectsGrid.appendChild(card);
});
const filesList = document.getElementById('filesList');
filesList.innerHTML = '';
data.files.forEach(file => {
let icon = '📄';
if (file.ext == 'php') icon = '🐘';
else if (file.ext == 'pdf') icon = '📑';
else if (['jpg','jpeg','png','gif'].includes(file.ext)) icon = '🖼️';
else if (file.ext == 'txt') icon = '📝';
else if (file.ext == 'css') icon = '🎨';
else if (file.ext == 'js') icon = '📜';
const item = document.createElement('div');
item.className = 'file-item';
item.innerHTML = `
<span class="file-name">${icon} ${escapeHtml(file.name)}</span>
<a href="${escapeHtml(file.name)}" class="btn btn-sm" target="_blank">▶ Open</a>
`;
filesList.appendChild(item);
});
showToast('✅ Dashboard refreshed! New files detected.');
} catch (error) {
console.error('Refresh failed:', error);
showToast('❌ Refresh failed. Try manual reload (F5).', true);
} finally {
setTimeout(() => refreshBtn.classList.remove('spinning'), 500);
}
}
function showToast(message, isError = false) {
const toast = document.createElement('div');
toast.className = 'toast';
if (isError) toast.classList.add('error');
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function toggleTheme() {
const currentTheme = document.body.className;
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
window.location.href = '?theme=' + newTheme;
}
updateClock();
setInterval(updateClock, 1000);
</script>
</body>
</html>
← Back