Files
smoothschedule/RESOURCE_TYPES_PLAN.md
poduck b10426fbdb feat: Add photo galleries to services, resource types management, and UI improvements
Major features:
- Add drag-and-drop photo gallery to Service create/edit modals
- Add Resource Types management section to Settings (CRUD for custom types)
- Add edit icon consistency to Resources table (pencil icon in actions)
- Improve Services page with drag-to-reorder and customer preview mockup

Backend changes:
- Add photos JSONField to Service model with migration
- Add ResourceType model with category (STAFF/OTHER), description fields
- Add ResourceTypeViewSet with CRUD operations
- Add service reorder endpoint for display order

Frontend changes:
- Services page: two-column layout, drag-reorder, photo upload
- Settings page: Resource Types tab with full CRUD modal
- Resources page: Edit icon in actions column instead of row click
- Sidebar: Payments link visibility based on role and paymentsEnabled
- Update types.ts with Service.photos and ResourceTypeDefinition

Note: Removed photos from ResourceType (kept only for Service)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 01:11:53 -05:00

7.6 KiB

Custom Resource Types & Logo Upload - Implementation Plan

Overview

Allow businesses to create custom resource types (e.g., "Stylist", "Massage Therapist", "Treatment Room") instead of hardcoded types. Also add logo upload to business branding.

Features

1. Custom Resource Types

  • Default Types: Always include one "Staff" type (cannot be deleted)
  • Custom Names: Users can name types whatever they want (e.g., "Hair Stylist", "Nail Technician", "Massage Room")
  • Categories: Each type has a category:
    • STAFF: Requires staff member assignment
    • OTHER: No staff assignment needed
  • Management: Add, edit, delete custom types in Business Settings

2. Logo Upload

  • Upload business logo in branding section
  • Support PNG, JPG, SVG
  • Preview before saving
  • Used in customer-facing pages

Database Changes

Backend Models

# smoothschedule/schedule/models.py

class ResourceType(models.Model):
    """Custom resource type definitions per business"""
    business = models.ForeignKey('tenants.Business', on_delete=models.CASCADE, related_name='resource_types')
    name = models.CharField(max_length=100)  # "Stylist", "Treatment Room", etc.
    category = models.CharField(max_length=10, choices=[
        ('STAFF', 'Staff'),
        ('OTHER', 'Other'),
    ])
    is_default = models.BooleanField(default=False)  # Cannot be deleted
    icon_name = models.CharField(max_length=50, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ['business', 'name']
        ordering = ['name']

    def __str__(self):
        return f"{self.business.name} - {self.name}"


class Resource(models.Model):
    """Update existing Resource model"""
    # ... existing fields ...

    # NEW FIELD - references custom resource type
    resource_type = models.ForeignKey(
        ResourceType,
        on_delete=models.PROTECT,  # Cannot delete type if resources use it
        related_name='resources',
        null=True,  # For migration
        blank=True
    )

    # DEPRECATED - keep for backwards compatibility during migration
    type = models.CharField(
        max_length=20,
        choices=[('STAFF', 'Staff'), ('ROOM', 'Room'), ('EQUIPMENT', 'Equipment')],
        default='STAFF'
    )


# smoothschedule/tenants/models.py

class Business(models.Model):
    # ... existing fields ...

    # NEW FIELD - logo upload
    logo = models.ImageField(upload_to='business_logos/', null=True, blank=True)

Migration Strategy

  1. Create ResourceType model
  2. For each business, create default resource types:
    • "Staff" (category=STAFF, is_default=True)
    • "Room" (category=OTHER, is_default=True)
    • "Equipment" (category=OTHER, is_default=True)
  3. Migrate existing resources to use default types based on their type field
  4. Eventually deprecate Resource.type field

API Endpoints

Resource Types

GET    /api/resource-types/          # List business resource types
POST   /api/resource-types/          # Create new type
PATCH  /api/resource-types/{id}/     # Update type
DELETE /api/resource-types/{id}/     # Delete type (if not in use & not default)

Logo Upload

POST   /api/business/logo/           # Upload logo
DELETE /api/business/logo/           # Remove logo

Frontend Implementation

1. Business Settings - Resource Types Tab

// Add new tab to Settings.tsx

<Tab name="Resource Types">
  <ResourceTypesManager />
</Tab>

ResourceTypesManager Component

interface ResourceTypesManagerProps {}

const ResourceTypesManager: React.FC = () => {
  const { data: types = [] } = useResourceTypes();
  const createMutation = useCreateResourceType();
  const updateMutation = useUpdateResourceType();
  const deleteMutation = useDeleteResourceType();

  return (
    <div>
      <h3>Resource Types</h3>
      <p>Customize how you categorize your bookable resources.</p>

      {/* List of types */}
      {types.map(type => (
        <TypeCard
          key={type.id}
          type={type}
          onEdit={handleEdit}
          onDelete={handleDelete}
          canDelete={!type.isDefault && !type.inUse}
        />
      ))}

      {/* Add new type button */}
      <button onClick={openCreateModal}>
        + Add Resource Type
      </button>

      {/* Create/Edit Modal */}
      <ResourceTypeModal
        type={editingType}
        onSave={handleSave}
        onClose={closeModal}
      />
    </div>
  );
};

2. Logo Upload in Branding Section

// In Settings.tsx, Branding section

<div className="space-y-4">
  <h4>Business Logo</h4>

  {/* Logo preview */}
  {business.logoUrl && (
    <div className="relative w-32 h-32">
      <img src={business.logoUrl} alt="Business logo" />
      <button onClick={removeLogo}>
        <X size={16} />
      </button>
    </div>
  )}

  {/* Upload button */}
  <input
    type="file"
    accept="image/png,image/jpeg,image/svg+xml"
    onChange={handleLogoUpload}
    className="hidden"
    id="logo-upload"
  />
  <label htmlFor="logo-upload">
    <Upload size={16} />
    Upload Logo
  </label>
</div>

3. Update Resources.tsx to use custom types

// Resources.tsx

const Resources: React.FC = () => {
  const { data: resourceTypes = [] } = useResourceTypes();

  return (
    <ResourceModal>
      <select name="resourceType">
        {resourceTypes.map(type => (
          <option key={type.id} value={type.id}>
            {type.name}
          </option>
        ))}
      </select>

      {/* Show staff selector if selected type is STAFF category */}
      {selectedType?.category === 'STAFF' && (
        <StaffSelector required />
      )}
    </ResourceModal>
  );
};

Implementation Steps

Phase 1: Backend Foundation

  1. Create TypeScript types (DONE)
  2. Create Django models (ResourceType, add Business.logo)
  3. Create migrations with default data
  4. Create serializers
  5. Create API views and URLs
  6. Add file upload handling for logos

Phase 2: Frontend Hooks & Types

  1. Create useResourceTypes hook (DONE)
  2. Create logo upload utilities
  3. Update Resource type to include typeId

Phase 3: UI Components

  1. Add "Resource Types" tab to Settings
  2. Create ResourceTypesManager component
  3. Create ResourceTypeModal component
  4. Add logo upload to Branding section
  5. Update Resources page to use custom types

Phase 4: Migration & Testing

  1. Test creating/editing/deleting types
  2. Test logo upload/removal
  3. Ensure backwards compatibility
  4. Test resource creation with custom types

User Experience

Creating a Custom Resource Type

  1. Navigate to Business Settings → Resource Types
  2. Click "+ Add Resource Type"
  3. Enter name (e.g., "Massage Therapist")
  4. Select category: Staff or Other
  5. Click Save
  6. New type appears in list and is available when creating resources
  1. Navigate to Business Settings → General → Branding
  2. Click "Upload Logo"
  3. Select image file (PNG, JPG, or SVG)
  4. Preview appears
  5. Click "Save Changes"
  6. Logo appears in header and customer-facing pages

Benefits

  1. Flexibility: Businesses can name types to match their industry
  2. Clarity: "Massage Therapist" is clearer than "Staff" for a spa
  3. Scalability: Add new types as business grows
  4. Branding: Logo upload improves professional appearance
  5. User-Friendly: Simple UI for non-technical users

Notes

  • Default "Staff" type cannot be deleted (ensures at least one staff type exists)
  • Cannot delete types that are in use by existing resources
  • Logo file size limit: 5MB
  • Logo recommended dimensions: 500x500px (square) or 500x200px (wide)