Skip to main content

Item Categories

Categories provide a hierarchical way to organize items in the directory. The template includes a full category management system with admin CRUD operations, a public-facing category navigation bar, and filtering integration.

Architecture Overview

components/
items-categories.tsx -- Public category navigation bar
categories-grid.tsx -- Grid layout for category cards
admin/categories/ -- Admin CRUD components
filters/components/categories/ -- Filter integration components

hooks/
use-admin-categories.ts -- Admin CRUD hook (React Query)
use-categories-enabled.ts -- Feature flag check
use-categories-exists.ts -- Data availability check

app/api/admin/categories/ -- API routes for category management

Category Data Model

Categories are represented with the following interface from the content layer:

interface Category {
id: string;
name: string;
icon_url?: string;
count?: number;
}

The admin interface uses an extended type:

// lib/types/category.ts
interface CategoryData {
id: string;
name: string;
slug: string;
description?: string;
iconUrl?: string;
isActive: boolean;
sortOrder: number;
createdAt: string;
updatedAt: string;
}

interface CategoryWithCount extends CategoryData {
itemCount: number;
}

Public Category Navigation

The ItemsCategories component at components/items-categories.tsx renders a horizontal scrollable category bar with optional sticky behavior:

// components/items-categories.tsx
export function ItemsCategories(props: {
categories: Category[];
basePath?: string;
resetPath?: string;
enableSticky?: boolean;
maxVisibleTags?: number;
}) {
const { categoriesEnabled } = useCategoriesEnabled();
const [showAllCategories, setShowAllCategories] = useState(false);
const pathname = usePathname();

if (!categoriesEnabled) return null;
if (!props.categories?.length) return null;

const MAX_VISIBLE = props.maxVisibleTags || 8;
const hasMore = props.categories.length > MAX_VISIBLE;

// Render logic...
}

Key Features

  • Feature flag gating: the component checks useCategoriesEnabled() and returns null if categories are disabled
  • Responsive overflow: in single-row mode, categories scroll horizontally with hidden scrollbar styling
  • Expand/collapse: a toggle button switches between single-row scrolling and wrapped multi-row layout
  • Active state detection: compares the current pathname against the category URL to highlight the active filter
  • "All Categories" button: always rendered first, acts as a reset filter with the total count
  • Sticky header: when enableSticky is true, the bar becomes sticky after scrolling past 250px, adding a blur backdrop

Usage Example

<ItemsCategories
categories={categories}
basePath="/categories"
resetPath="/"
enableSticky={true}
maxVisibleTags={10}
/>

Admin Category Management

useAdminCategories Hook

The hooks/use-admin-categories.ts hook provides full CRUD operations:

// hooks/use-admin-categories.ts
export function useAdminCategories(options = {}) {
const { params = {}, enabled = true } = options;

const { data, isLoading, refetch } = useQuery({
queryKey: QUERY_KEYS.categoriesList(params),
queryFn: () => fetchCategories(params),
staleTime: 5 * 60 * 1000,
});

const createCategoryMutation = useMutation({
mutationFn: createCategory,
onSuccess: () => {
toast.success('Category created successfully');
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.categories });
},
});

return {
categories: data?.categories || [],
total: data?.total || 0,
page: data?.page || 1,
totalPages: data?.totalPages || 1,
isLoading,
isSubmitting: createMutation.isPending || updateMutation.isPending,
createCategory: handleCreateCategory,
updateCategory: handleUpdateCategory,
deleteCategory: handleDeleteCategory,
refetch,
refreshData,
};
}

Query Key Factory

Categories use a structured query key hierarchy for precise cache invalidation:

const QUERY_KEYS = {
categories: ['admin', 'categories'] as const,
categoriesList: (params) =>
[...QUERY_KEYS.categories, 'list', params] as const,
allCategories: () =>
[...QUERY_KEYS.categories, 'all'] as const,
category: (id: string) =>
[...QUERY_KEYS.categories, 'detail', id] as const,
};

Single Category Hook

export function useCategory({ id, enabled = true }) {
const { data, isLoading, error, refetch } = useQuery({
queryKey: QUERY_KEYS.category(id),
queryFn: () => fetchCategory(id),
enabled: enabled && !!id,
});

return { category: data || null, isLoading, error, refetch };
}

Mutation-Only Hook

For components that only need write operations without the list query:

export function useCategoryMutations() {
return {
createCategory: handleCreate,
updateCategory: handleUpdate,
deleteCategory: handleDelete,
isSubmitting: anyMutationPending,
};
}

Category List Options

The admin list endpoint supports filtering and pagination:

interface CategoryListOptions {
includeInactive?: boolean;
page?: number;
limit?: number;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
}

API Endpoints

MethodEndpointPurpose
GET/api/admin/categoriesList categories with pagination
GET/api/admin/categories/allGet all categories without pagination
GET/api/admin/categories/:idGet a single category
POST/api/admin/categoriesCreate a new category
PUT/api/admin/categories/:idUpdate an existing category
DELETE/api/admin/categories/:idSoft-delete a category
DELETE/api/admin/categories/:id?hard=truePermanently delete a category

Filter Integration

Categories integrate with the filter system through the filters/ module:

// components/filters/index.ts
export { Categories } from './components/categories/categories-section';
export { CategoriesList, CategoryItem } from './components/categories';

The filter context tracks the selected category and applies it to item queries automatically.

Feature Flag

Categories can be enabled or disabled globally via the useCategoriesEnabled hook, which reads from the feature flags system:

const { categoriesEnabled } = useCategoriesEnabled();

When disabled, both the navigation bar and filter components return null.

File Reference

FilePurpose
components/items-categories.tsxPublic category navigation bar
components/categories-grid.tsxGrid layout for category display
components/admin/categories/Admin CRUD components
components/filters/components/categories/Filter integration
hooks/use-admin-categories.tsAdmin CRUD hook with React Query
hooks/use-categories-enabled.tsFeature flag check
hooks/use-categories-exists.tsData availability check
app/api/admin/categories/Backend API routes