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 listed in it, and * dragged into it. The work is in functions/categories.php; the handlers are * admin/post/category.php. */ // Default Column Sortby Filter - the column is the kind's, set below $order = "ASC"; require_once "includes/inc_all_admin.php"; $kinds = categoryKinds(); $kind = isset($kinds[$_GET['category'] ?? '']) ? (string) $_GET['category'] : 'Expense'; $config = categoryKind($kind); // ?archived=1 is how older links asked for the archived ones $show_archived = ($_GET['show'] ?? '') === 'archived' || !empty($_GET['archived']); $search = trim((string) ($_GET['q'] ?? '')); // Only these sort - by name, by use, and in the order pickers use for the kinds that keep one (and are listed in it) $sort_first = ['category_name' => 'ASC', 'used' => 'DESC'] + ($config['ordered'] ? ['category_order' => 'ASC'] : []); $default_sort = $config['ordered'] ? 'category_order' : 'category_name'; if (!isset($sort) || !isset($sort_first[$sort])) { $sort = $default_sort; } // The kind's categories, what the search leaves - counted for the pills, then this pill's $usage = categoryUsage($kind); $empty_use = ['all' => 0, 'archived' => 0, 'uses' => []]; $categories = []; $counts = [0, 0]; foreach (categoryRows($kind) as $category_id => $row) { if ($search !== '' && mb_stripos($row['category_name'], $search) === false && mb_stripos((string) $row['category_description'], $search) === false) { continue; } $counts[$row['category_archived_at'] ? 1 : 0]++; if ((bool) $row['category_archived_at'] !== $show_archived) { continue; } $row['used'] = $usage[$category_id] ?? $empty_use; $categories[$category_id] = $row; } // categoryRows() has them in the order pickers use $direction = $order === 'DESC' ? -1 : 1; if ($sort === 'category_name') { uasort($categories, function ($a, $b) use ($direction) { return $direction * strnatcasecmp($a['category_name'], $b['category_name']); }); } elseif ($sort === 'used') { uasort($categories, function ($a, $b) use ($direction) { return $direction * ($a['used']['all'] <=> $b['used']['all']) ?: strnatcasecmp($a['category_name'], $b['category_name']); }); } elseif ($direction === -1) { $categories = array_reverse($categories, true); } // A page of them $num_rows = [count($categories)]; $categories = array_slice($categories, $record_from, $record_to, true); // 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 order, first to last, with every current one in it $can_reorder = $config['ordered'] && $sort === 'category_order' && $order === 'ASC' && !$show_archived && $search === '' && $num_rows[0] > 1; // Current categories of each kind for the nav $kind_counts = []; $sql = mysqli_query($mysqli, "SELECT category_type, SUM(category_archived_at IS NULL) FROM categories GROUP BY category_type"); while ($row = mysqli_fetch_row($sql)) { $kind_counts[$row[0]] = intval($row[1]); } // A column first sorts the way it reads best - A to Z, most used, first to last - then flips $sort_link = function ($column, $label) use ($url_query_strings_sort, $sort, $disp, $order_icon, $sort_first) { return '' . $label . ($sort === $column ? " $order_icon" : '') . ''; }; $pill_url = function ($archived) use ($kind) { return '?' . http_build_query(array_filter(['category' => $kind, 'q' => $_GET['q'] ?? '', 'show' => $archived ? 'archived' : ''])); }; $new_label = 'New ' . ucwords($config['one']); $add_url = 'modals/category/category_add.php?category=' . urlencode($kind); $csrf = $_SESSION['csrf_token']; ?>

Categories

"> > $row) { $position++; $name = escapeHtml($row['category_name']); $used = $row['used']; $edit_url = "modals/category/category_edit.php?id=$category_id"; $used_html = $used['all'] ? categoryUsageHtml($row, $used) : '' . ($row['category_archived_at'] ? 'Nothing uses it' : 'Nothing yet') . ''; $blocker = categoryDeleteBlocker($row, $used); ?>
Order
Archived
Drag them into the order they're offered in.
Clear the search to drag them into order.
$config['many'], 'icon' => $config['icon'], 'filtered' => $search !== '', 'archived' => $show_archived, 'views' => ['category', 'show'], 'text' => escapeHtml($config['about']), 'actions' => [['label' => $new_label, 'modal' => $add_url]], ]; require_once "../includes/filter_footer.php"; ?>
In use, but not set up here

Records have these, but they aren't - so they can't be picked again, and have no color or icon. Add one to make it ; to fold it into another, add it, then merge it.

    $loose_used) { ?>
  • 0, 'category_name' => $loose_name, 'category_type' => $kind], $loose_used) ?>