Skip to main content

Item Type Definitions

Source: lib/types/item.ts

Items are the core content entities in the template. This module defines the data structures for creating, reading, updating, and listing items, along with validation constants and status management types.

Interfaces

ItemLocationData

Location data for items that can be geocoded. Stored in YAML and indexed in item_location_index for fast geo queries.

import type { MapProvider } from './location';

interface ItemLocationData {
address?: string; // Full address string for geocoding
city?: string;
state?: string;
country?: string;
postal_code?: string;
latitude?: number; // Pre-geocoded latitude
longitude?: number; // Pre-geocoded longitude
service_area?: string; // e.g., "Nationwide", "New York Metro"
is_remote?: boolean; // Whether this item operates remotely
geocoded_by?: MapProvider; // Which geocoding provider was used
}

ItemData

The primary item data structure returned by read operations.

interface ItemData {
id: string;
name: string;
slug: string;
description: string;
source_url: string;
category: string | string[];
tags: string[];
collections?: string[];
featured?: boolean;
icon_url?: string;
updated_at: string;
status: 'draft' | 'pending' | 'approved' | 'rejected';
submitted_by?: string;
submitted_at?: string;
reviewed_by?: string;
reviewed_at?: string;
review_notes?: string;
deleted_at?: string; // ISO timestamp for soft delete
action?: 'visit-website' | 'start-survey' | 'buy';
showSurveys?: boolean; // Whether to show surveys section
publisher?: string; // Publisher name for display
location?: ItemLocationData;
}

Key details:

  • category supports both a single string and an array for multi-category items
  • status uses a four-state approval flow: draft, pending, approved, rejected
  • deleted_at enables soft deletion without removing data
  • action defines the CTA button type on the item detail page

CreateItemRequest

Input payload for creating a new item (POST endpoint).

interface CreateItemRequest {
id: string;
name: string;
slug: string;
description: string;
source_url: string;
category: string | string[];
tags: string[];
collections?: string[];
brand?: string;
featured?: boolean;
icon_url?: string;
status?: 'draft' | 'pending' | 'approved' | 'rejected';
submitted_by?: string;
location?: ItemLocationData;
}

UpdateItemRequest

Input payload for updating an existing item. Extends Partial<CreateItemRequest> so all fields except id are optional.

interface UpdateItemRequest extends Partial<CreateItemRequest> {
id: string;
status?: 'draft' | 'pending' | 'approved' | 'rejected';
review_notes?: string;
deleted_at?: string; // For soft delete operations
}

ItemListOptions

Query parameters for filtering and paginating item lists.

interface ItemListOptions {
status?: 'draft' | 'pending' | 'approved' | 'rejected';
categories?: string[]; // Multi-category filtering
tags?: string[]; // Multi-tag filtering
page?: number;
limit?: number;
sortBy?: SortField;
sortOrder?: SortOrder;
includeDeleted?: boolean; // Include soft-deleted items (default: false)
submittedBy?: string; // Filter by submitting user
search?: string; // Search by name or description
city?: string; // Filter by city
country?: string; // Filter by country
includeRemote?: boolean; // Include remote items in location queries
}

ItemListResponse

Paginated response for item list queries.

interface ItemListResponse {
items: ItemData[];
total: number;
page: number;
limit: number;
totalPages: number;
}

ItemResponse

Response envelope for single-item operations.

interface ItemResponse {
success: boolean;
item?: ItemData;
error?: string;
message?: string;
}

ReviewRequest

Payload for approving or rejecting an item during the review process.

interface ReviewRequest {
status: 'approved' | 'rejected';
review_notes?: string;
}

Type Aliases

SortField

Valid fields for sorting item lists:

type SortField = 'name' | 'updated_at' | 'status' | 'submitted_at';

SortOrder

Sort direction:

type SortOrder = 'asc' | 'desc';

ItemStatus

A union type derived from ITEM_STATUSES:

type ItemStatus = (typeof ITEM_STATUSES)[keyof typeof ITEM_STATUSES];
// Resolves to: 'draft' | 'pending' | 'approved' | 'rejected'

Constants

ITEM_VALIDATION

Validation constraints for item fields:

const ITEM_VALIDATION = {
NAME_MIN_LENGTH: 3,
NAME_MAX_LENGTH: 100,
DESCRIPTION_MIN_LENGTH: 10,
DESCRIPTION_MAX_LENGTH: 500,
SLUG_MIN_LENGTH: 3,
SLUG_MAX_LENGTH: 50,
} as const;

ITEM_STATUSES

Canonical status values for the item approval workflow:

const ITEM_STATUSES = {
DRAFT: 'draft',
PENDING: 'pending',
APPROVED: 'approved',
REJECTED: 'rejected',
} as const;

ITEM_STATUS_LABELS

Human-readable labels for each status:

const ITEM_STATUS_LABELS = {
draft: 'Draft',
pending: 'Pending Review',
approved: 'Approved',
rejected: 'Rejected',
} as const;

ITEM_STATUS_COLORS

UI color mappings for each status:

const ITEM_STATUS_COLORS = {
draft: 'gray',
pending: 'yellow',
approved: 'green',
rejected: 'red',
} as const;

Usage Examples

Creating an item

import type { CreateItemRequest } from '@/lib/types/item';
import { ITEM_VALIDATION } from '@/lib/types/item';

function validateItemName(name: string): boolean {
return (
name.length >= ITEM_VALIDATION.NAME_MIN_LENGTH &&
name.length <= ITEM_VALIDATION.NAME_MAX_LENGTH
);
}

const newItem: CreateItemRequest = {
id: 'unique-id-123',
name: 'My Tool',
slug: 'my-tool',
description: 'A description of my tool that is at least 10 characters.',
source_url: 'https://example.com',
category: ['productivity', 'developer-tools'],
tags: ['open-source', 'free'],
status: 'pending',
};

Filtering items

import type { ItemListOptions } from '@/lib/types/item';

const options: ItemListOptions = {
status: 'approved',
categories: ['productivity'],
tags: ['open-source'],
page: 1,
limit: 20,
sortBy: 'updated_at',
sortOrder: 'desc',
includeRemote: true,
};

Rendering status badges

import { ITEM_STATUS_LABELS, ITEM_STATUS_COLORS } from '@/lib/types/item';
import type { ItemStatus } from '@/lib/types/item';

function getStatusBadge(status: ItemStatus) {
return {
label: ITEM_STATUS_LABELS[status],
color: ITEM_STATUS_COLORS[status],
};
}

// getStatusBadge('pending')
// => { label: 'Pending Review', color: 'yellow' }
  • ItemLocationData references MapProvider from the location module
  • ClientSubmissionData in client-item.ts extends ItemData with engagement metrics
  • CategoryData defines the category values referenced in ItemData.category
  • TagData defines the tag values referenced in ItemData.tags