Categories - the categories of each kind: expense, income, * referral and ticket, and the interface, asset status, software, rack and * note types. * * Each shows as it looks on records, with its description and what uses it * (opening the list of them, where there is one). A category can be * changed, merged into another of its kind, archived - it stays on its * records but isn't offered for new ones - or, while nothing uses it, * deleted. The kinds pickers list in an order set here are dragged into it. * The work is in functions/categories.php; the handlers are * admin/post/category.php. */ require_once "includes/inc_all_admin.php"; $kinds = categoryKinds(); $kind = isset($kinds[$_GET['category'] ?? '']) ? (string) $_GET['category'] : 'Expense'; $config = categoryKind($kind); $show_archived = isset($_GET['archived']); $search = trim((string) ($_GET['q'] ?? '')); // How the list is sorted - the order pickers use first, for the kinds that keep one $sorts = ($config['ordered'] ? ['order' => ['Order', 'fa-sort-numeric-down', 'The order they are offered in']] : []) + ['name' => ['Name', 'fa-sort-alpha-down', 'A to Z'], 'use' => ['Most used', 'fa-sort-amount-down', 'The ones on the most records first']]; $sort_by = isset($sorts[$_GET['sort'] ?? '']) ? (string) $_GET['sort'] : array_key_first($sorts); $default_sort = array_key_first($sorts); $usage = categoryUsage($kind); $empty_use = ['all' => 0, 'archived' => 0, 'uses' => []]; $categories = []; foreach (categoryRows($kind) as $category_id => $row) { if ((bool) $row['category_archived_at'] !== $show_archived) { continue; } if ($search !== '' && mb_stripos($row['category_name'], $search) === false && mb_stripos((string) $row['category_description'], $search) === false) { continue; } $row['used'] = $usage[$category_id] ?? $empty_use; $categories[$category_id] = $row; } if ($sort_by === 'name') { uasort($categories, function ($a, $b) { return strnatcasecmp($a['category_name'], $b['category_name']); }); } elseif ($sort_by === 'use') { uasort($categories, function ($a, $b) { return $b['used']['all'] <=> $a['used']['all'] ?: strnatcasecmp($a['category_name'], $b['category_name']); }); } // Names records have that aren't categories - a kind held by name, in the plain current view $loose = !$show_archived && $search === '' ? categoryLooseNames($kind) : []; // Dragged into order - in the order view, with the whole current list showing $can_reorder = $config['ordered'] && $sort_by === 'order' && !$show_archived && $search === '' && count($categories) > 1; // Current categories of each kind for the nav, and this kind's archived ones for the tab $kind_counts = []; $archived_count = 0; $sql = mysqli_query($mysqli, "SELECT category_type, SUM(category_archived_at IS NULL), SUM(category_archived_at IS NOT NULL) FROM categories GROUP BY category_type"); while ($row = mysqli_fetch_row($sql)) { $kind_counts[$row[0]] = intval($row[1]); if ($row[0] === $kind) { $archived_count = intval($row[2]); } } // This page with some of its query changed - null drops a key $page_url = function (array $change = []) use ($kind, $show_archived, $sort_by, $default_sort, $search) { $query = array_merge(['category' => $kind, 'q' => $search !== '' ? $search : null, 'sort' => $sort_by !== $default_sort ? $sort_by : null, 'archived' => $show_archived ? 1 : null], $change); return 'categories.php?' . http_build_query(array_filter($query, function ($v) { return $v !== null; })); }; $new_label = 'New ' . ucwords($config['one']); $add_url = 'modals/category/category_add.php?category=' . urlencode($kind); $csrf = $_SESSION['csrf_token']; ?>
= escapeHtml($config['about']) ?>
Records have these, but they aren't = escapeHtml($config['many']) ?> - so they can't be picked again, and have no color or icon. Add one to make it = escapeHtml(categoryKindPhrase($kind)) ?>; to fold it into another, add it, then merge it.