| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- <?php
- /** @var \App\Domain\Sprint $sprint */
- /** @var \App\Domain\User $currentUser */
- /** @var string $csrfToken */
- /** @var list<\App\Domain\SprintWeek> $weeks */
- /** @var list<\App\Domain\SprintWorker> $sprintWorkers */
- /** @var list<\App\Domain\Worker> $availableWorkers */
- use App\Domain\SprintWeek;
- use function App\Http\e;
- ?>
- <section class="space-y-6"
- data-sprint-root
- data-sprint-id="<?= (int) $sprint->id ?>"
- data-csrf="<?= e($csrfToken) ?>">
- <header class="flex items-end justify-between gap-4">
- <div>
- <nav class="text-xs text-slate-500 dark:text-slate-400">
- <a href="/" class="hover:underline">Sprints</a> /
- <a href="/sprints/<?= (int) $sprint->id ?>" class="hover:underline"><?= e($sprint->name) ?></a> /
- </nav>
- <h1 class="text-2xl font-semibold tracking-tight">Settings</h1>
- </div>
- <div data-status
- class="text-sm border rounded px-3 py-1 opacity-0 transition-opacity duration-200 border-slate-200 bg-slate-50 text-slate-700 dark:bg-slate-800 dark:border-slate-700 dark:text-slate-300">
- </div>
- </header>
- <!-- Sprint meta -->
- <section class="rounded-lg border bg-white p-5 space-y-4 dark:bg-slate-800 dark:border-slate-700">
- <h2 class="text-sm font-semibold text-slate-700 uppercase tracking-wider dark:text-slate-200">Sprint</h2>
- <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
- <label class="md:col-span-2 block">
- <span class="text-sm text-slate-700 dark:text-slate-300">Name</span>
- <input data-meta name="name" type="text" value="<?= e($sprint->name) ?>"
- class="mt-1 block w-full rounded-md border border-slate-300 shadow-sm px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:focus:ring-slate-500">
- </label>
- <label class="block">
- <span class="text-sm text-slate-700 dark:text-slate-300">Start date</span>
- <input data-meta name="start_date" type="date" value="<?= e($sprint->startDate) ?>"
- class="mt-1 block w-full rounded-md border border-slate-300 shadow-sm px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:focus:ring-slate-500">
- </label>
- <label class="block">
- <span class="text-sm text-slate-700 dark:text-slate-300">End date</span>
- <input data-meta name="end_date" type="date" value="<?= e($sprint->endDate) ?>"
- class="mt-1 block w-full rounded-md border border-slate-300 shadow-sm px-3 py-2 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:focus:ring-slate-500">
- </label>
- <label class="block">
- <span class="text-sm text-slate-700 dark:text-slate-300">Reserve (%)</span>
- <input data-meta name="reserve_fraction" type="number" min="0" max="100" step="1"
- value="<?= e(number_format($sprint->reserveFraction * 100, 0)) ?>"
- class="mt-1 block w-full rounded-md border border-slate-300 shadow-sm px-3 py-2 font-mono focus:outline-none focus:ring-2 focus:ring-slate-400 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:focus:ring-slate-500">
- </label>
- </div>
- <p class="text-xs text-slate-500 dark:text-slate-400">Changes save automatically.</p>
- </section>
- <!-- Weeks -->
- <section class="rounded-lg border bg-white p-5 space-y-4 dark:bg-slate-800 dark:border-slate-700">
- <div class="flex items-end justify-between gap-4">
- <div>
- <h2 class="text-sm font-semibold text-slate-700 uppercase tracking-wider dark:text-slate-200">Weeks</h2>
- <p class="text-xs text-slate-500 mt-1 dark:text-slate-400">
- Current: <?= count($weeks) ?> week<?= count($weeks) === 1 ? '' : 's' ?>.
- Tick the weekdays that are workdays for each week; the count feeds
- the Arbeitstage header on the sprint page.
- </p>
- </div>
- <form data-weeks-form class="flex items-end gap-2">
- <label class="block">
- <span class="text-xs text-slate-600 dark:text-slate-400">Set to</span>
- <input name="n_weeks" type="number" min="1" max="26" step="1"
- value="<?= count($weeks) ?>"
- class="mt-1 w-24 rounded-md border border-slate-300 shadow-sm px-3 py-2 font-mono focus:outline-none focus:ring-2 focus:ring-slate-400 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:focus:ring-slate-500">
- </label>
- <button type="submit"
- class="rounded-md bg-slate-900 text-white px-3 py-2 text-sm font-medium hover:bg-slate-800 dark:bg-slate-700 dark:hover:bg-slate-600">
- Apply
- </button>
- </form>
- </div>
- <div class="overflow-hidden rounded border border-slate-200 dark:border-slate-700">
- <table class="min-w-full text-sm" data-weeks-table>
- <thead class="bg-slate-50 text-slate-600 text-xs uppercase tracking-wider dark:bg-slate-700 dark:text-slate-300">
- <tr>
- <th class="text-left px-3 py-2 font-semibold">#</th>
- <th class="text-left px-3 py-2 font-semibold">KW</th>
- <th class="text-left px-3 py-2 font-semibold">Start</th>
- <?php foreach (SprintWeek::DAY_LABELS as $label): ?>
- <th class="text-center px-2 py-2 font-semibold w-10"><?= e($label) ?></th>
- <?php endforeach; ?>
- <th class="text-right px-3 py-2 font-semibold">Arbeitstage</th>
- </tr>
- </thead>
- <tbody class="divide-y divide-slate-100 dark:divide-slate-700">
- <?php foreach ($weeks as $w): ?>
- <tr data-week-row data-week-id="<?= (int) $w->id ?>">
- <td class="px-3 py-2 font-mono"><?= (int) $w->sortOrder ?></td>
- <td class="px-3 py-2 font-mono">KW<?= (int) $w->isoWeek ?></td>
- <td class="px-3 py-2 font-mono"><?= e($w->startDate) ?></td>
- <?php foreach (SprintWeek::DAY_LABELS as $bit => $label): ?>
- <td class="px-2 py-2 text-center">
- <input type="checkbox"
- data-day-toggle
- data-bit="<?= (int) $bit ?>"
- data-label="<?= e($label) ?>"
- aria-label="<?= e($label) ?>"
- <?= $w->hasDay($label) ? 'checked' : '' ?>
- class="rounded border-slate-300 focus:ring-slate-400 dark:border-slate-600 dark:focus:ring-slate-500">
- </td>
- <?php endforeach; ?>
- <td class="px-3 py-2 font-mono text-right" data-week-count>
- <?= (int) $w->maxWorkingDays ?>
- </td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
- </div>
- <p class="text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-3 py-2 dark:bg-amber-900 dark:border-amber-800 dark:text-amber-200">
- Reducing the week count deletes trailing weeks and any data attached to them.
- </p>
- </section>
- <!-- Worker picker -->
- <section class="rounded-lg border bg-white p-5 space-y-4 dark:bg-slate-800 dark:border-slate-700">
- <h2 class="text-sm font-semibold text-slate-700 uppercase tracking-wider dark:text-slate-200">Workers</h2>
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
- <div>
- <h3 class="text-xs font-semibold text-slate-500 uppercase mb-2 dark:text-slate-400">Available</h3>
- <div class="rounded border border-slate-200 dark:border-slate-700">
- <ul data-available class="divide-y divide-slate-100 dark:divide-slate-700">
- <?php foreach ($availableWorkers as $w): ?>
- <li class="flex items-center gap-2 px-3 py-2 border-b last:border-b-0 dark:border-slate-700"
- data-worker-id="<?= (int) $w->id ?>">
- <span class="flex-1"><?= e($w->name) ?></span>
- <button type="button" data-add class="text-sm text-blue-700 hover:underline dark:text-blue-400 dark:hover:text-blue-300">Add →</button>
- </li>
- <?php endforeach; ?>
- </ul>
- <div data-empty-available
- class="p-3 text-center text-xs text-slate-500 dark:text-slate-400"
- <?= $availableWorkers === [] ? '' : 'style="display:none"' ?>>
- No other active workers.
- </div>
- </div>
- </div>
- <div>
- <h3 class="text-xs font-semibold text-slate-500 uppercase mb-2 dark:text-slate-400">In sprint (drag to reorder)</h3>
- <div class="rounded border border-slate-200 dark:border-slate-700">
- <ul data-in-sprint class="divide-y divide-slate-100 dark:divide-slate-700">
- <?php foreach ($sprintWorkers as $sw): ?>
- <li class="flex items-center gap-2 px-3 py-2 border-b bg-white last:border-b-0 dark:bg-slate-800 dark:border-slate-700"
- data-sw-id="<?= (int) $sw->id ?>"
- data-worker-id="<?= (int) $sw->workerId ?>">
- <span class="handle cursor-grab text-slate-400 select-none dark:text-slate-500">≡</span>
- <span class="flex-1"><?= e($sw->workerName) ?></span>
- <input type="number" step="0.05" min="0" max="1"
- value="<?= e(number_format($sw->rtb, 2, '.', '')) ?>"
- data-rtb
- data-stepper
- class="w-20 rounded border border-slate-300 px-2 py-1 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-slate-400 dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:focus:ring-slate-500">
- <button type="button" data-remove class="text-sm text-red-600 hover:underline dark:text-red-400">Remove</button>
- </li>
- <?php endforeach; ?>
- </ul>
- <div data-empty-sprint
- class="p-3 text-center text-xs text-slate-500 dark:text-slate-400"
- <?= $sprintWorkers === [] ? '' : 'style="display:none"' ?>>
- No workers assigned yet.
- </div>
- </div>
- </div>
- </div>
- <p class="text-xs text-slate-500 dark:text-slate-400">
- RTB (Run-the-Business) is informational and does not reduce computed capacity.
- </p>
- </section>
- </section>
- <script src="/assets/js/sprint-settings.js" defer></script>
|