Files
IronKanban/public/index.php
T

317 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// phpcs:disable PEAR.Commenting.FileComment,PEAR.Commenting.ClassComment
/**
* Main application entrypoint.
*/
declare(strict_types=1);
require_once dirname(__DIR__) . '/bootstrap.php';
use IronKanban\Repository\ProjectRepository;
use IronKanban\Service\BoardService;
$projectRepository = new ProjectRepository();
$projects = $projectRepository->getAll();
$requestedProject = isset($_GET['project']) ? trim((string) $_GET['project']) : '';
$activeProjectId = $requestedProject;
$boardService = new BoardService();
$initialState = null;
$loadError = null;
$showDashboard = $requestedProject === '';
if ($activeProjectId !== '') {
try {
$initialState = $boardService->getBoardState($activeProjectId);
$showDashboard = false;
} catch (Throwable $exception) {
$showDashboard = true;
$loadError = 'The requested project could not be loaded. Pick another project or create a new one.';
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="<?php echo e(csrf_token()); ?>">
<title><?php echo e((string) config('app_name')); ?></title>
<link rel="stylesheet" href="/assets/app.css">
</head>
<body>
<div class="app-shell">
<aside class="sidebar">
<div>
<p class="eyebrow">Git-backed markdown kanban</p>
<h1>IronKanban</h1>
<p class="sidebar-copy">Projects stay as flat files, while the UI gives you drag-and-drop columns, quick edits, notes, and polling-based refreshes.</p>
</div>
<div class="sidebar-actions">
<a class="button ghost" href="/">All Projects</a>
<?php if ($showDashboard) : ?>
<a class="button primary" href="#project-create-form">New Project</a>
<?php elseif ($initialState !== null) : ?>
<a class="button primary" href="/?project=<?php echo rawurlencode((string) $initialState['project']['id']); ?>">Open Current Board</a>
<?php endif; ?>
</div>
<section class="project-list">
<div class="section-heading">
<h2>Projects</h2>
<span><?php echo count($projects); ?></span>
</div>
<?php if ($projects === []) : ?>
<div class="empty-state compact">
<p>No projects found in <code><?php echo e(project_root()); ?></code>.</p>
</div>
<?php else : ?>
<?php foreach ($projects as $project) : ?>
<a class="project-link<?php echo $project->id === $activeProjectId && !$showDashboard ? ' active' : ''; ?>" href="/?project=<?php echo rawurlencode($project->id); ?>">
<strong><?php echo e($project->title); ?></strong>
<span><?php echo e($project->id); ?></span>
</a>
<?php endforeach; ?>
<?php endif; ?>
</section>
<section class="project-root">
<div class="section-heading">
<h2>Storage</h2>
</div>
<p><code><?php echo e(project_root()); ?></code></p>
</section>
</aside>
<main class="workspace" id="app" data-project-id="<?php echo e($showDashboard ? '' : $activeProjectId); ?>" data-view="<?php echo e($showDashboard ? 'dashboard' : 'board'); ?>">
<?php if ($showDashboard) : ?>
<section class="dashboard-intro hero">
<div>
<p class="eyebrow">Project Dashboard</p>
<h2>Choose a project or start a fresh one</h2>
<p class="project-description">Each project stays as markdown on disk, with its own notes, tasks, board definition, and revision tracking.</p>
</div>
<div class="dashboard-intro-meta">
<span><?php echo count($projects); ?> project<?php echo count($projects) === 1 ? '' : 's'; ?></span>
<span>Stored in <code><?php echo e(project_root()); ?></code></span>
</div>
</section>
<?php if ($loadError !== null) : ?>
<section class="hero error-state">
<h2>Project unavailable</h2>
<p><?php echo e($loadError); ?></p>
</section>
<?php endif; ?>
<section class="dashboard-grid">
<section class="project-create-panel">
<div class="section-heading">
<h2>Create Project</h2>
</div>
<form class="project-create-form" id="project-create-form" data-project-form>
<label>
<span>Title</span>
<input type="text" name="title" maxlength="200" required placeholder="Release Planning">
</label>
<label>
<span>Slug</span>
<input type="text" name="slug" maxlength="200" placeholder="release-planning">
</label>
<label>
<span>Description</span>
<textarea name="body" rows="7" placeholder="Describe the project, goals, and context."></textarea>
</label>
<div class="project-create-actions">
<p class="form-hint">Leave the slug blank to generate it automatically.</p>
<button type="submit" class="button primary">Create Project</button>
</div>
</form>
</section>
<section class="projects-panel">
<div class="section-heading">
<h2>Projects</h2>
<span><?php echo count($projects); ?></span>
</div>
<?php if ($projects === []) : ?>
<div class="empty-state">
<h3>No projects yet</h3>
<p>Create your first project to generate a board with default kanban columns.</p>
</div>
<?php else : ?>
<div class="project-card-grid">
<?php foreach ($projects as $project) : ?>
<?php
$preview = trim(preg_replace('/\s+/', ' ', $project->body) ?? '');
$preview = $preview !== '' ? $preview : 'No project description yet.';
$preview = strlen($preview) > 180 ? substr($preview, 0, 177) . '...' : $preview;
?>
<a class="project-card" href="/?project=<?php echo rawurlencode($project->id); ?>">
<p class="eyebrow">Project</p>
<h3><?php echo e($project->title); ?></h3>
<p class="project-card-body"><?php echo e($preview); ?></p>
<div class="project-card-footer">
<span class="project-card-slug"><?php echo e($project->id); ?></span>
<span class="project-card-cta">Open Board</span>
</div>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
</section>
<?php else : ?>
<header class="workspace-header">
<div>
<p class="eyebrow">Project Board</p>
<h2><?php echo e((string) $initialState['project']['title']); ?></h2>
<p class="project-description"><?php echo nl2br(e((string) $initialState['project']['body'])); ?></p>
</div>
<div class="header-actions">
<a class="button ghost" href="/">All Projects</a>
<button type="button" class="button ghost" data-action="new-column">New Column</button>
<button type="button" class="button primary" data-action="new-task">New Task</button>
</div>
</header>
<section class="workspace-stack">
<section class="notes-panel is-collapsible is-collapsed" id="notes-panel" data-collapsible>
<div class="notes-panel-header">
<div>
<p class="eyebrow">Project Notes</p>
<h2>Notes</h2>
</div>
<div class="notes-panel-actions">
<button type="button" class="button ghost compact" data-action="new-note">Add Note</button>
<button
type="button"
class="button ghost compact collapse-toggle"
data-action="toggle-notes"
data-target="notes-list"
aria-expanded="false"
aria-controls="notes-list"
>
Show Notes
</button>
</div>
</div>
<div id="notes-list" class="notes-list" hidden></div>
</section>
<section class="board-panel">
<div class="board-scroll">
<div class="board-columns" id="board-columns"></div>
</div>
</section>
<section class="trash-panel" id="trash-panel">
<div class="trash-panel-header">
<div>
<p class="eyebrow">Discarded Tasks</p>
<h2>Trash</h2>
</div>
<button
type="button"
class="button ghost compact collapse-toggle"
data-action="toggle-trash"
aria-expanded="false"
aria-controls="trash-columns-wrap"
>
Show Trash
</button>
</div>
<div class="trash-dropzone" id="trash-dropzone" data-column-id="trash">
<p class="trash-dropzone-title">Drop tasks here to discard them</p>
<p class="trash-dropzone-meta" id="trash-dropzone-meta">Trash is empty.</p>
</div>
<div id="trash-columns-wrap" class="trash-columns-wrap" hidden>
<div class="board-scroll">
<div class="board-columns board-columns-trash" id="trash-columns"></div>
</div>
</div>
</section>
</section>
<?php endif; ?>
</main>
</div>
<dialog id="task-dialog" class="dialog">
<form method="dialog" class="dialog-card" id="task-form">
<div class="dialog-header">
<h3 id="task-dialog-title">Task</h3>
<button type="submit" class="icon-button" aria-label="Close">×</button>
</div>
<input type="hidden" name="id">
<label>
<span>Title</span>
<input type="text" name="title" maxlength="200" required>
</label>
<label>
<span>Column</span>
<select name="column"></select>
</label>
<label>
<span>Priority</span>
<select name="priority">
<option value="low">Low</option>
<option value="normal">Normal</option>
<option value="high">High</option>
<option value="urgent">Urgent</option>
</select>
</label>
<label class="checkbox-row">
<input type="checkbox" name="completed">
<span>Completed</span>
</label>
<label class="checkbox-row">
<input type="checkbox" name="is_active" checked>
<span>Active</span>
</label>
<label>
<span>Markdown</span>
<textarea name="body" rows="12"></textarea>
</label>
<div class="dialog-actions">
<button type="button" class="button ghost" data-action="trash-task">Move To Trash</button>
<button type="submit" class="button primary">Save Task</button>
</div>
</form>
</dialog>
<dialog id="note-dialog" class="dialog">
<form method="dialog" class="dialog-card" id="note-form">
<div class="dialog-header">
<h3 id="note-dialog-title">Note</h3>
<button type="submit" class="icon-button" aria-label="Close">×</button>
</div>
<input type="hidden" name="id">
<label>
<span>Title</span>
<input type="text" name="title" maxlength="200" required>
</label>
<label>
<span>Markdown</span>
<textarea name="body" rows="14"></textarea>
</label>
<div class="dialog-actions">
<button type="submit" class="button primary">Save Note</button>
</div>
</form>
</dialog>
<script id="initial-state" type="application/json"><?php echo
$initialState !== null
? (string) json_encode(
$initialState,
JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
)
: 'null';
?></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
<script src="/assets/app.js"></script>
</body>
</html>