# How User Missions Are Tracked in TCG Pocket Collection Tracker: JSON Structure and Storage Logic

> Discover how the TCG Pocket Collection Tracker uses JSON to store user missions and track completions. Learn about the structure for objectives and rewards storage.

- Repository: [Marcel Panse/tcg-pocket-collection-tracker](https://github.com/marcelpanse/tcg-pocket-collection-tracker)
- Tags: internals
- Published: 2026-03-06

---

**The TCG Pocket Collection Tracker tracks user missions through a hybrid system that combines runtime calculation of card ownership with persistent manual completion flags stored in the `completed_missions` array of the user account record.**

The application helps players manage their Pokémon TCG Pocket collections and monitor progress toward themed mission objectives. Understanding how the system tracks mission completion requires examining both the JSON schema that defines mission requirements and the dual-state storage mechanism that handles automatic and manual progress tracking.

## How User Missions Are Tracked

The application employs a dual-layer approach to determine mission completion status. This system balances automatic detection based on card ownership with user-controlled manual overrides.

### Persistent Storage of Manual Completions

Manual completion states are stored in the user account record within the Supabase database. The `AccountRow` interface in [`src/types/index.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/types/index.ts) defines this storage:

```typescript
completed_missions?: string[]   // Line 34-35

```

Each mission is identified by a unique key formatted as `{expansionId}_{mission.name}`, such as `A1_First Choice`. When users click the check-mark icon in the `Mission` component, the application updates this array via the `useUpdateAccount` mutation:

```tsx
const handleManualComplete = () => {
  const currentCompletedMissions = account.completed_missions || []
  const updatedMissions = isManuallyCompleted
    ? currentCompletedMissions.filter(m => m !== missionKey)
    : [...currentCompletedMissions, missionKey]

  const mergedAccount = { ...account, completed_missions: updatedMissions }
  delete mergedAccount.trade_rarity_settings
  updateAccount(mergedAccount)
}

```

This code appears in [`src/components/Mission.tsx`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/components/Mission.tsx) (lines 96-104) and ensures that manual completions persist across sessions.

### Runtime Calculation of Mission Progress

In addition to manual flags, the application calculates mission completion dynamically based on the user's current card collection. The mission definitions are loaded from JSON files in `assets/themed-collections/` and attached to expansion objects in [`src/lib/CardsDB.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/lib/CardsDB.ts).

The `Mission` component evaluates progress by iterating over `mission.requiredCards` and checking each `options` array against owned cards via the `useCollection` hook. The completion logic combines both calculated and manual states:

```typescript
mission.completed = isMissionCompleted || isManuallyCompleted

```

This line in [`src/components/Mission.tsx`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/components/Mission.tsx) (line 83) produces the final boolean that drives UI filtering and visual indicators.

## JSON Structure for Mission Objectives and Rewards

Mission definitions follow a strict JSON schema stored in expansion-specific files such as [`frontend/assets/themed-collections/A1-missions.json`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/frontend/assets/themed-collections/A1-missions.json). Each mission object contains the following properties:

```json
{
  "expansionId": "A1",
  "name": "First Choice",
  "requiredCards": [
    { "options": ["A1-1", "A1-227"], "amount": 1 },
    { "options": ["A1-33", "A1-230"], "amount": 1 },
    { "options": ["A1-53", "A1-232"], "amount": 1 }
  ],
  "reward": "Wonder Hourglass ×12<br />Shop Ticket ×2<br />Emblem Ticket (Genetic Apex) ×1"
}

```

The TypeScript interfaces in [`src/types/index.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/types/index.ts) formalize this structure:

```typescript
export interface Mission {
  name: string
  requiredCards: MissionCard[]
  expansionId: ExpansionId
  reward?: string
  completed?: boolean
}

export interface MissionCard {
  amount: number
  options: string[]
  owned?: number
}

```

Key characteristics of this schema include:

- **Flexible card requirements**: The `options` array allows missions to accept any one of several card IDs, supporting variants and promos.
- **Quantitative thresholds**: The `amount` field specifies exactly how many cards from the options list are required.
- **Runtime enrichment**: The `owned` and `completed` properties are populated dynamically and never serialized to the JSON files.

## Filtering Missions by Completion Status

The `Missions` page in [`src/pages/collection/Missions.tsx`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/pages/collection/Missions.tsx) provides filtering capabilities that leverage both the calculated and manual completion states. The filtering logic (lines 32-44) checks the `completed_missions` array alongside the runtime `mission.completed` flag:

```tsx
if (ownedFilter === 'owned') {
  missions = missions.filter(m => {
    const key = `${m.expansionId}_${m.name}`
    const manual = account?.completed_missions?.includes(key) ?? false
    return m.completed || manual
  })
} else if (ownedFilter === 'missing') {
  missions = missions.filter(m => {
    const key = `${m.expansionId}_${m.name}`
    const manual = account?.completed_missions?.includes(key) ?? false
    return !m.completed && !manual
  })
}

```

This dual-check ensures that users see missions they have completed either by owning the required cards or by manually marking them as done.

## Summary

- The TCG Pocket Collection Tracker tracks user missions through a hybrid system combining **runtime card ownership calculation** with **persistent manual completion flags**.
- Manual completions are stored in the `completed_missions` string array within the user account record, using keys formatted as `{expansionId}_{mission.name}`.
- Mission objectives and rewards are defined in static JSON files (e.g., [`assets/themed-collections/A1-missions.json`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/assets/themed-collections/A1-missions.json)) following a strict schema with `requiredCards`, `options`, and `amount` fields.
- The `Mission` component in [`src/components/Mission.tsx`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/components/Mission.tsx) calculates completion by checking owned cards against required options and merges this with manual flags to produce the final state.
- The `Missions` page filters results by checking both the runtime `mission.completed` flag and the `completed_missions` array to display owned, missing, or all missions.

## Frequently Asked Questions

### How does the TCG Pocket Collection Tracker determine if a mission is completed?

The application uses a dual-layer approach. First, it calculates completion dynamically by checking if the user owns the required cards specified in the mission's `requiredCards` array. Second, it checks the `completed_missions` array stored in the user's account record for manual completion flags. If either condition is true, the mission is considered complete.

### What is the format of the mission key stored in the completed_missions array?

Mission keys follow the pattern `{expansionId}_{mission.name}`. For example, a mission named "First Choice" from expansion "A1" would have the key `A1_First Choice`. This string is added to or removed from the `completed_missions` array when users toggle the check-mark icon in the mission UI.

### Can missions require specific alternative cards or variants?

Yes, the JSON structure supports flexible requirements through the `options` array within each `requiredCards` entry. Each requirement can list multiple card IDs in the `options` array, and the user needs to own the specified `amount` of cards from that list. This design accommodates variant cards, promos, or alternative rarities that can satisfy the same mission slot.

### Where are the mission definitions stored in the codebase?

Static mission definitions reside in JSON files within the `assets/themed-collections/` directory (e.g., [`A1-missions.json`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/A1-missions.json)). These files are imported by [`src/lib/CardsDB.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/lib/CardsDB.ts) and attached to their respective `Expansion` objects. The TypeScript interfaces defining the schema are located in [`src/types/index.ts`](https://github.com/marcelpanse/tcg-pocket-collection-tracker/blob/main/src/types/index.ts).