# Collection Icon Customization Plan

## Overview

Add the ability to customize collection (folder) icons with emojis or predefined icons. This feature enhances the user experience by allowing visual differentiation between collections.

## Requirements

1. **Customization Options**:
   - Custom emoji (e.g., 📁, 🎵, 📷, 💼, 🏠)
   - Predefined cross-browser icons from a curated set
   - Default folder icon when no customization is set

2. **Behavior**:
   - Default to standard folder icon if no customization
   - Persist across browser sessions (stored in database)
   - Easy to change or revert to default
   - Intuitive, user-friendly UI

3. **Security Consideration**:
   - Icon data should be encrypted (E2EE) like collection name
   - Only emoji characters or predefined icon identifiers allowed

---

## Data Model Design

### New Field: `custom_icon`

Add a new encrypted field to the Collection model that stores icon customization data.

```go
// CustomIcon stores the collection's custom icon configuration
type CustomIcon struct {
    Type  string `json:"type"`  // "emoji", "icon", or "" (empty = default)
    Value string `json:"value"` // Emoji character or icon identifier
}
```

**Field Storage Options**:

| Option | Pros | Cons |
|--------|------|------|
| A) Single encrypted JSON field | Simple, one field | Requires parsing |
| B) Two fields (type + value) | Clear structure | More columns |
| C) Single string field | Simplest | Limited validation |

**Recommended: Option C** - Single `encrypted_custom_icon` field storing either:
- Empty string `""` → Default folder icon
- Emoji character (e.g., `"📷"`) → Display as emoji
- Icon identifier (e.g., `"icon:briefcase"`) → Predefined icon

This keeps the schema simple and the client handles interpretation.

---

## Implementation Plan

### Phase 1: Backend (cloud/maplefile-backend)

#### 1.1 Database Schema Update

**Note:** No new migration files needed - updating existing migration `012_create_collections_by_id.up.cql` directly (assumes full database wipe).

Add to `collections_by_id` table:
```sql
encrypted_custom_icon TEXT,
```

#### 1.2 Update Domain Model

File: `internal/domain/collection/model.go`

```go
type Collection struct {
    // ... existing fields ...

    // EncryptedCustomIcon stores the custom icon for this collection.
    // Empty string means use default folder icon.
    // Contains either an emoji character or "icon:<identifier>" for predefined icons.
    // Encrypted with the collection key for E2EE.
    EncryptedCustomIcon string `bson:"encrypted_custom_icon" json:"encrypted_custom_icon"`
}
```

Also add to `CollectionSyncItem` for sync operations:
```go
type CollectionSyncItem struct {
    // ... existing fields ...
    EncryptedCustomIcon string `json:"encrypted_custom_icon,omitempty" bson:"encrypted_custom_icon,omitempty"`
}
```

**Note:** The sync query in `collectionsync.go:getCollectionSyncItem()` fetches minimal data from `collections_by_id`. This query will need to include `encrypted_custom_icon` so clients can display the correct icon during sync.

#### 1.3 Update Repository Layer

Files to modify:
- `internal/repo/collection/create.go` - Include new field in INSERT
- `internal/repo/collection/update.go` - Include new field in UPDATE
- `internal/repo/collection/get.go` - Include new field in SELECT
- `internal/repo/collection/sync.go` - Include new field in sync queries

#### 1.4 Update HTTP Handlers (if needed)

The existing create/update endpoints should automatically handle the new field since they accept the full Collection struct.

---

### Phase 2: Frontend (web/maplefile-frontend)

#### 2.1 Create Icon Picker Component

New file: `src/components/IconPicker/IconPicker.jsx`

Features:
- Emoji picker tab with common categories (objects, activities, symbols)
- Predefined icons tab (Heroicons subset)
- "Default" option to revert to folder icon
- Search/filter functionality
- Recently used icons

```jsx
// Example structure
const IconPicker = ({ value, onChange, onClose }) => {
  const [activeTab, setActiveTab] = useState('emoji'); // 'emoji' | 'icons'

  const predefinedIcons = [
    { id: 'briefcase', icon: BriefcaseIcon, label: 'Work' },
    { id: 'photo', icon: PhotoIcon, label: 'Photos' },
    { id: 'music', icon: MusicalNoteIcon, label: 'Music' },
    { id: 'document', icon: DocumentIcon, label: 'Documents' },
    { id: 'archive', icon: ArchiveBoxIcon, label: 'Archive' },
    // ... more icons
  ];

  const popularEmojis = ['📁', '📷', '🎵', '💼', '🏠', '❤️', '⭐', '🎮', '📚', '🎨'];

  return (
    // ... picker UI
  );
};
```

#### 2.2 Update CollectionEdit Page

File: `src/pages/User/FileManager/Collections/CollectionEdit.jsx`

Add icon customization section:

```jsx
{/* Icon Customization Section */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
  <h2 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
    <SparklesIcon className="h-5 w-5 mr-2 text-gray-500" />
    Customize Icon
  </h2>

  <div className="flex items-center space-x-4">
    {/* Current Icon Preview */}
    <div className="flex items-center justify-center h-16 w-16 bg-gray-100 rounded-xl border-2 border-dashed border-gray-300">
      <CollectionIcon icon={formData.customIcon} size="lg" />
    </div>

    {/* Change/Reset Buttons */}
    <div className="space-y-2">
      <button onClick={() => setShowIconPicker(true)} className="...">
        Change Icon
      </button>
      {formData.customIcon && (
        <button onClick={() => setFormData({...formData, customIcon: ''})} className="...">
          Reset to Default
        </button>
      )}
    </div>
  </div>
</div>
```

#### 2.3 Create CollectionIcon Component

New file: `src/components/CollectionIcon/CollectionIcon.jsx`

Renders the appropriate icon based on the customIcon value:

```jsx
const CollectionIcon = ({ icon, collectionType = 'folder', size = 'md', className = '' }) => {
  const sizes = {
    sm: 'h-4 w-4',
    md: 'h-6 w-6',
    lg: 'h-10 w-10',
  };

  // Default folder/album icon
  if (!icon || icon === '') {
    const Icon = collectionType === 'album' ? PhotoIcon : FolderIcon;
    return <Icon className={`${sizes[size]} ${className}`} />;
  }

  // Predefined icon
  if (icon.startsWith('icon:')) {
    const iconId = icon.replace('icon:', '');
    const IconComponent = predefinedIconMap[iconId];
    return IconComponent ? <IconComponent className={`${sizes[size]} ${className}`} /> : <FolderIcon className={`${sizes[size]} ${className}`} />;
  }

  // Emoji
  return <span className={`${emojiSizes[size]} ${className}`}>{icon}</span>;
};
```

#### 2.4 Update Collection List/Grid Views

Update anywhere collections are displayed to use the new `CollectionIcon` component:
- `FileManagerIndex.jsx`
- `CollectionDetails.jsx`
- Sidebar navigation (if applicable)

#### 2.5 Update Encryption/Decryption

Update the collection encryption service to handle the new field:
- Encrypt `customIcon` when saving
- Decrypt `encrypted_custom_icon` when loading

---

### Phase 3: Native Desktop (native/desktop/maplefile)

#### 3.1 Update Domain Model

File: `internal/domain/collection/model.go`

```go
type Collection struct {
    // ... existing fields ...

    // CustomIcon stores the decrypted custom icon for this collection.
    // Empty string means use default folder icon.
    // Contains either an emoji character or "icon:<identifier>" for predefined icons.
    CustomIcon string `json:"custom_icon,omitempty"`

    // EncryptedCustomIcon is the encrypted version from cloud
    EncryptedCustomIcon string `json:"encrypted_custom_icon,omitempty"`
}
```

#### 3.2 Update Sync Service

Ensure the sync service handles the new field when syncing collections from the cloud.

#### 3.3 Update Frontend (Wails)

The desktop frontend uses the same React patterns, so the IconPicker and CollectionIcon components can be shared or adapted.

---

## Predefined Icon Set

A curated set of cross-browser compatible icons:

| ID | Icon | Use Case |
|----|------|----------|
| `briefcase` | BriefcaseIcon | Work |
| `photo` | PhotoIcon | Photos |
| `music` | MusicalNoteIcon | Music |
| `video` | VideoCameraIcon | Videos |
| `document` | DocumentTextIcon | Documents |
| `archive` | ArchiveBoxIcon | Archive |
| `star` | StarIcon | Favorites |
| `heart` | HeartIcon | Personal |
| `home` | HomeIcon | Home |
| `academic` | AcademicCapIcon | School |
| `code` | CodeBracketIcon | Code |
| `cloud` | CloudIcon | Cloud |
| `lock` | LockClosedIcon | Private |
| `gift` | GiftIcon | Gifts |
| `calendar` | CalendarIcon | Events |

---

## Migration Strategy

1. **Backward Compatible**: Empty `encrypted_custom_icon` means default icon
2. **No Data Migration Needed**: New collections get the field, old collections have NULL/empty
3. **Clients Handle Missing Field**: Treat NULL/empty as default

---

## Testing Checklist

### Backend
- [ ] Migration runs successfully
- [ ] Create collection with custom icon
- [ ] Update collection custom icon
- [ ] Revert to default icon
- [ ] Sync includes custom icon field
- [ ] E2EE encryption/decryption works

### Frontend (Web)
- [ ] Icon picker opens and closes
- [ ] Emoji selection works
- [ ] Predefined icon selection works
- [ ] Reset to default works
- [ ] Icon persists after page reload
- [ ] Icon displays correctly in list/grid views
- [ ] Works across different browsers

### Native Desktop
- [ ] Sync downloads custom icon
- [ ] Icon displays correctly
- [ ] Edit icon works (if implemented)

---

## Security Considerations

1. **E2EE**: Custom icon is encrypted with collection key
2. **Input Validation**: Only allow valid emoji or predefined icon IDs
3. **XSS Prevention**: Sanitize icon display (emoji rendering, no HTML)
4. **Size Limits**: Max length for custom icon field (e.g., 50 chars)

---

## Future Enhancements

1. **Custom uploaded icons** (requires more complex storage)
2. **Icon color customization**
3. **Icon packs/themes**
4. **Bulk icon changes** (apply to multiple collections)

---

## Files to Modify

### Backend (cloud/maplefile-backend)

**Schema Update (modify existing migration):**
1. `migrations/012_create_collections_by_id.up.cql` - Add `encrypted_custom_icon TEXT` column

**Domain Layer:**
2. `internal/domain/collection/model.go` - Add `EncryptedCustomIcon` field to `Collection` and `CollectionSyncItem` structs

**Repository Layer:**
4. `internal/repo/collection/create.go` - Add `encrypted_custom_icon` to INSERT query (line ~57-66)
5. `internal/repo/collection/update.go` - Add `encrypted_custom_icon` to UPDATE query (line ~64-73)
6. `internal/repo/collection/get.go` - Add `encrypted_custom_icon` to SELECT query and scan (line ~44-52, ~72-90)
7. `internal/repo/collection/collectionsync.go` - Add field to sync queries

**Note:** Secondary tables (013-017) do NOT need modification - they are lookup/index tables that only store keys and minimal fields. The `encrypted_custom_icon` is stored only in `collections_by_id`.

### Frontend (web/maplefile-frontend)

**New Components:**
1. `src/components/IconPicker/IconPicker.jsx` - Modal with emoji grid + predefined icons
2. `src/components/CollectionIcon/CollectionIcon.jsx` - Renders appropriate icon based on value

**Modified Pages:**
3. `src/pages/User/FileManager/Collections/CollectionEdit.jsx` - Add icon customization section
4. `src/pages/User/FileManager/Collections/CollectionDetails.jsx` - Display custom icon
5. `src/pages/User/FileManager/Collections/CollectionCreate.jsx` - Optional icon selection on create
6. `src/pages/User/FileManager/FileManagerIndex.jsx` - Display custom icons in list/grid

**Services:**
7. Collection encryption service - Handle `customIcon` field encryption/decryption

### Native Desktop (native/desktop/maplefile)

**Domain:**
1. `internal/domain/collection/model.go` - Add `CustomIcon` and `EncryptedCustomIcon` fields

**Repository:**
2. `internal/repo/collection/repository.go` - Handle new field in CRUD operations

**Sync:**
3. `internal/service/sync/collection.go` - Include field in sync operations

**Frontend:**
4. `frontend/src/` - Adapt IconPicker and CollectionIcon components (if not shared with web)

---

## Estimated Effort

| Phase | Effort |
|-------|--------|
| Backend (migration + model) | 2-3 hours |
| Frontend components | 4-6 hours |
| Frontend integration | 2-3 hours |
| Native desktop | 2-3 hours |
| Testing | 2-3 hours |
| **Total** | **12-18 hours** |
