Refactor: Dynamic Generation Of Category Equipment

by Alex Johnson 51 views

Overview

In this article, we will discuss the refactoring of category representative equipment to improve data consistency. Currently, the system allows users to select a category representative (type="Category") when adding equipment. This can lead to the existence of multiple category representative equipment for the same category. To address this issue fundamentally, we propose changing the category representative equipment to be dynamically generated, ensuring data consistency.

Background of the Problem

Before diving into the solution, let's understand the current design's problems.

Current Design Issues

  1. Data Duplication: Currently, category definitions (categories.json) and category representative equipment (equipments.json where type="Category") are managed separately, leading to potential inconsistencies.
  2. Potential Duplication: Users can select type="Category" when adding equipment, which may result in multiple category representatives for the same category. This duplication creates confusion and makes data management difficult.
  3. Calculation Logic Problems: When category representatives are duplicated, they are treated as separate equipment in the scrapping list calculation. Ideally, the system should consider only the maximum value for each category, but this isn't possible with the current design.
  4. UI Display Issues: Multiple category representatives can compete for display in the user interface, causing a cluttered and confusing experience for the user.

Impact Areas

These issues impact several areas of the application:

  • src/data/equipments.json: This file contains 11 category representative equipment entries, which need to be refactored.
  • src/components/EquipmentModal.jsx:176-177: The UI allows users to select the type of equipment, which needs to be modified to prevent the selection of category representatives.
  • src/utils/calculateScrapList.js: The calculation logic treats duplicated category representatives as separate items, which needs to be updated to handle dynamically generated representatives correctly.
  • src/utils/scrapListFormatters.js:38: The UI display might overwrite category representatives due to the potential for conflicts, requiring adjustments.

Proposed Solution

To address these problems, we propose a solution that structurally guarantees data consistency by clearly separating categories and items and dynamically generating category representative equipment.

Basic Policy

Our proposed solution revolves around a few core principles:

  1. Dynamic Generation of Category Representatives: Generate category representatives for all categories (official + user-defined) automatically at runtime. This ensures that there is always a single, consistent representative for each category.
  2. Data Schema Simplification: Remove the type field and category representative equipment from equipments.json. This simplifies the data structure and eliminates the possibility of manual duplication.
  3. Mission Schema Clarification: Add a targetType field to the required equipment in missions.json. This makes it clear whether a mission requires a specific item or a category representative.
  4. UI Improvement: Clearly separate "Add Equipment" and "Add Category" options in the equipment management modal. This helps users understand the distinction between creating new equipment and defining new categories.

Deletion Rules (Maintaining Current Behavior)

To maintain the current functionality and user expectations, we will keep the existing deletion rules:

  • Official Masters (isMaster: true): Cannot be deleted. These are core items and categories that should always be available.
  • User-Defined (isMaster: false): Can be deleted.
    • When a user category is deleted, all equipment within that category will also be automatically deleted (with a warning). This ensures that orphaned equipment does not remain in the system.

Detailed Specifications

Let's delve into the specifics of how we plan to implement this solution.

1. Data Schema Changes

To support dynamic generation and simplify the data structure, we need to modify the existing schemas.

equipments.json (Official Master)

Before:

{
  "version": "1.0.0",
  "equipments": [
    {
      "id": "m_eq_cat_gun_s",
      "name": "Small Caliber Main Gun (Any Type)",
      "categoryId": "m_cat_gun_s",
      "type": "Category",
      "order": 1
    },
    {
      "id": "m_eq_gun_12cm",
      "name": "12cm Single Gun",
      "categoryId": "m_cat_gun_s",
      "type": "Item",
      "order": 100
    }
  ]
}

After:

{
  "version": "1.0.0",
  "equipments": [
    {
      "id": "m_eq_gun_12cm",
      "name": "12cm Single Gun",
      "categoryId": "m_cat_gun_s",
      "order": 100
    }
  ]
}
  • Remove the 11 category representative equipment entries (type="Category").
  • Remove the type field entirely.

missions.json

Before:

{
  "reqs": [
    {
      "id": "req_1",
      "targetId": "m_eq_cat_gun_s",
      "count": 2
    }
  ]
}

After:

{
  "reqs": [
    {
      "id": "req_1",
      "targetId": "m_cat_gun_s",
      "targetType": "category",
      "count": 2
    },
    {
      "id": "req_2",
      "targetId": "m_eq_gun_12cm",
      "targetType": "item",
      "count": 1
    }
  ]
}
  • Add a targetType field ("category" | "item"). This clearly indicates whether the requirement is for a category or a specific item.
  • When referencing a category representative, specify the category ID directly in targetId.

categories.json

No changes are required for this file.

2. Runtime Schema

At runtime, the system will work with the following schema:

// Category Representative (Dynamically Generated)
{
  id: "m_cat_gun_s",           // Use the category ID directly
  name: "Small Caliber Main Gun (Any Type)",
  categoryId: "m_cat_gun_s",
  isMaster: true,              // Inherit the category's isMaster (remove on save)
  type: "Category",            // Assigned during dynamic generation (remove on save)
  order: 1                     // Use the category's order
}

// Individual Equipment
{
  id: "m_eq_gun_12cm",
  name: "12cm Single Gun",
  categoryId: "m_cat_gun_s",
  isMaster: true,              // Automatically assigned during data retrieval (remove on save)
  type: "Item",                // Assigned during dynamic generation (remove on save)
  order: 100
}

Important: The type field will be treated similarly to isMaster.

  • Automatically assigned during data loading (category representatives get "Category", equipment gets "Item").
  • Removed during saving (not included in JSON files/LocalStorage). This ensures that the type is always generated dynamically and not stored persistently.

3. UI Changes in the Equipment Management Modal

The user interface for managing equipment will be updated to reflect the changes in the data schema and the dynamic generation of category representatives.

New Registration Form

The new registration form will provide a clear separation between adding equipment and adding categories:

┌─ New Registration ────────────────────┐
│ ● Add Equipment ○ Add Category │  ← Radio buttons to switch modes
│                                 │
│ ── Add Equipment Mode (Default) ──
│ Equipment Name *                        │
│ [12.7cm Twin Gun B Mod. 2]           │
│                                 │
│ Category *                      │
│ [Small Caliber Main Gun â–ŧ]                  │
│                                 │
│ [Add to List]                  │
│                                 │
│ ── Add Category Mode ──      │
│ Category Name *                    │
│ [Custom Category A]             │
│                                 │
│ â„šī¸ Category representative equipment will be │
│   created automatically                  │
│                                 │
│ [Add Category]                │
└─────────────────────────────────┘

The form will use radio buttons to switch between adding equipment and adding categories. When adding a category, the system will automatically create the corresponding category representative equipment.

Registered List (Maintain Current Behavior)

The display of the registered list will remain the same (showing Category and Item based on eq.type). However, it will internally include the dynamically generated category representatives.

4. Category Deletion Behavior

When a user attempts to delete a category, the system will display a confirmation dialog to ensure they understand the consequences.

Deletion Confirmation Dialog

The dialog will provide clear information about the items and missions affected by the deletion:

┌─────────────────────────────────────┐
│ Delete Category                [×]   │
├─────────────────────────────────────┤
│                                     │
│ Delete "Custom Category A"? │
│                                     │
│ âš ī¸ Equipment in this category:     │
│   â€ĸ Custom Equipment A                   │
│   â€ĸ Custom Equipment B                   │
│   2 items will be deleted               │
│                                     │
│ âš ī¸ Missions referencing this category:     │
│   â€ĸ Custom Mission 1                   │
│   â€ĸ Custom Mission 2                   │
│   This will affect 2 missions       │
│   (missions will not be deleted)          │
│                                     │
│ This operation cannot be undone.           │
│                                     │
├─────────────────────────────────────┤
│          [Cancel] [Delete]       │
└─────────────────────────────────────┘

The dialog will list the equipment and missions that will be affected by the deletion. It will also warn the user that the operation cannot be undone.

Deletion Process

The deletion process will consist of the following steps:

  1. Delete the category (from LocalStorage).
  2. Delete all equipment belonging to that category.
  3. Do not clean up mission references (maintain current behavior, only display a warning).

5. Startup Validation

To ensure data integrity and compatibility, the system will perform validation checks at startup.

// utils/validation.js
export function validateAndCleanEquipments(equipments) {
  const warnings = []
  
  // Detect and remove equipment with the type field
  const cleaned = equipments.filter(eq => {
    if (eq.hasOwnProperty('type')) {
      warnings.push({
        type: 'warning',
        message: `Equipment "${eq.name}" has an old format and was deleted`
      })
      return false
    }
    return true
  })
  
  return { cleaned, warnings }
}

export function validateAndCleanMissions(missions, categoryIds) {
  // Auto-complete targetType (for backward compatibility)
  const cleaned = missions.map(mission => ({
    ...mission,
    reqs: mission.reqs.map(req => {
      if (!req.targetType) {
        const isCategory = categoryIds.includes(req.targetId)
        return {
          ...req,
          targetType: isCategory ? 'category' : 'item'
        }
      }
      return req
    })
  }))
  
  return { cleaned, warnings: [] }
}

The validation will perform the following actions:

  • Remove any equipment with the old type field. This ensures that the system starts with a clean slate and avoids issues with outdated data.
  • Automatically complete the targetType field in missions for backward compatibility.

6. Migration

Since this is a beta version and the release is recent, a full migration process will not be implemented. Instead, the startup validation will delete old data. This simplifies the process and avoids the complexity of migrating existing data.

Scope of Impact

The changes will affect several files and modules within the application.

File Change Difficulty
src/data/equipments.json Remove 11 category representatives Low
src/data/missions.json Add targetType, modify targetId to category ID Medium
src/hooks/useEquipments.js Dynamically generate category representatives, add type assignment logic Medium
src/hooks/useCategories.js User category support (if necessary) Low
src/utils/localStorage.js Remove type on save Low
src/utils/validation.js Add startup validation Medium
src/utils/calculateScrapList.js targetType determination logic Medium
src/components/EquipmentModal.jsx Add mode switching UI Medium
src/App.jsx Add category deletion processing Medium
src/types/schema.js Update schema definition Low
docs/schema.md Update schema specification Low

Implementation Tasks

To implement the proposed solution, we have identified the following tasks:

  • [ ] Data Schema Updates
    • [ ] equipments.json: Remove 11 category representatives
    • [ ] missions.json: Add targetType, modify targetId
  • [ ] Runtime Logic
    • [ ] useEquipments: Dynamically generate category representatives, assign type
    • [ ] localStorage: Remove type on save
    • [ ] validation: Add startup validation
    • [ ] calculateScrapList: targetType determination logic
  • [ ] UI Changes
    • [ ] EquipmentModal: Add mode switching UI (Add Equipment/Add Category)
    • [ ] App.jsx: Add category deletion processing
  • [ ] Documentation Updates
    • [ ] schema.md: Update schema specification
    • [ ] types/schema.js: Update TypeScript type definitions
  • [ ] Testing
    • [ ] Verify dynamic generation functionality
    • [ ] Verify startup validation
    • [ ] Verify category deletion functionality
    • [ ] Verify mission selection and calculation logic

Benefits

This refactoring effort will yield several benefits:

  1. Data Consistency: Structural impossibility of category representative duplication.
  2. Improved Maintainability: Clearer relationship between categories and items.
  3. UI Improvement: No need to select type when adding equipment.
  4. Natural Deletion: Natural behavior of deleting all Items in a category when the category is deleted.

Remarks

  • Due to the beta status, a migration process will not be implemented, and startup validation will delete old data.
  • Schema versions will not be managed individually, but rather through overall code version upgrades.

In conclusion, by dynamically generating category representative equipment and refactoring the data schema, we can significantly improve data consistency, maintainability, and the user experience. This effort will ensure a more robust and reliable application for the future.

For more information on data consistency and refactoring best practices, you can visit a trusted website on software development. (Replace with an actual link to a relevant website).