# Vue 3 Components Implementation Guide

## 📋 Status

✅ **Completed**:
- Vite configuration
- package.json with dependencies
- Main CSS (builder.css)
- BuilderApp.vue (main app component)

⏳ **Remaining Files to Create**:

### 1. Components (6 files)
- `TopBar.vue` - Top navigation with save/publish/undo/redo
- `BlocksPalette.vue` - Left sidebar with draggable blocks
- `BuilderCanvas.vue` - Main canvas with drag & drop
- `SettingsPanel.vue` - Right sidebar for settings
- `CanvasRow.vue` - Row component
- `CanvasColumn.vue` - Column component
- `CanvasElement.vue` - Element component

### 2. Composables (3 files)
- `useHistory.js` - Undo/redo functionality
- `useStructureApi.js` - API calls
- `useDragDrop.js` - Drag & drop logic

### 3. Utils (2 files)
- `elementDefaults.js` - Default settings for elements
- `api.js` - Axios instance

### 4. Blade Views (4 files)
- `builder/index.blade.php` - List structures
- `builder/create.blade.php` - Create wizard
- `builder/edit.blade.php` - Mount Vue app
- `builder/preview.blade.php` - Preview iframe

### 5. Controller & Routes
- `BuilderController.php` - Web controller
- Update `routes/web.php`

---

## 🚀 Quick Implementation

### Step 1: Install Dependencies

```bash
cd /var/www/html/lms_hocmai/app/Modules/UiBuilder/Resources/assets
npm install
```

### Step 2: Build Assets

```bash
npm run build
```

Or for development:
```bash
npm run dev
```

### Step 3: Create Remaining Files

Files are listed below with full implementation.

---

## 📄 File Contents

### Components

#### `TopBar.vue`
```vue
<template>
  <div class="builder-topbar">
    <div class="builder-topbar-left">
      <h1>UI Builder</h1>
    </div>
    
    <div class="builder-topbar-center">
      <div class="responsive-modes">
        <button 
          v-for="mode in modes" 
          :key="mode.value"
          :class="['mode-btn', { active: responsiveMode === mode.value }]"
          @click="$emit('change-mode', mode.value)"
        >
          {{ mode.icon }} {{ mode.label }}
        </button>
      </div>
    </div>
    
    <div class="builder-topbar-right">
      <button 
        class="btn btn-outline btn-sm" 
        :disabled="!canUndo"
        @click="$emit('undo')"
      >
        ↶ Undo
      </button>
      <button 
        class="btn btn-outline btn-sm" 
        :disabled="!canRedo"
        @click="$emit('redo')"
      >
        ↷ Redo
      </button>
      <button 
        class="btn btn-primary btn-sm" 
        :disabled="isSaving"
        @click="$emit('save')"
      >
        <span v-if="isSaving" class="loading-spinner"></span>
        💾 Save
      </button>
      <button 
        class="btn btn-success btn-sm"
        @click="$emit('publish')"
      >
        🚀 Publish
      </button>
    </div>
  </div>
</template>

<script setup>
defineProps({
  responsiveMode: String,
  canUndo: Boolean,
  canRedo: Boolean,
  isSaving: Boolean
});

defineEmits(['change-mode', 'undo', 'redo', 'save', 'publish']);

const modes = [
  { value: 'desktop', label: 'Desktop', icon: '🖥️' },
  { value: 'tablet', label: 'Tablet', icon: '📱' },
  { value: 'mobile', label: 'Mobile', icon: '📱' }
];
</script>
```

#### `BlocksPalette.vue`
```vue
<template>
  <div class="blocks-palette">
    <div class="blocks-palette-header">
      <h2>Blocks</h2>
      <input 
        v-model="searchQuery"
        type="text" 
        class="blocks-search" 
        placeholder="Search blocks..."
      />
    </div>
    
    <div 
      v-for="category in filteredCategories" 
      :key="category.name"
      class="blocks-category"
    >
      <div class="blocks-category-title">{{ category.name }}</div>
      <div class="blocks-grid">
        <div 
          v-for="block in category.blocks" 
          :key="block.type"
          class="block-item"
          draggable="true"
          @dragstart="onDragStart($event, block)"
          @click="$emit('add-element', block.type)"
        >
          <div class="block-item-icon">{{ block.icon }}</div>
          <div class="block-item-label">{{ block.label }}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';

defineEmits(['add-element']);

const searchQuery = ref('');

const categories = [
  {
    name: 'Content',
    blocks: [
      { type: 'text', label: 'Text', icon: '📝' },
      { type: 'image', label: 'Image', icon: '🖼️' },
      { type: 'links', label: 'Links', icon: '🔗' },
      { type: 'html', label: 'HTML', icon: '💻' }
    ]
  },
  {
    name: 'Social',
    blocks: [
      { type: 'social', label: 'Social Icons', icon: '👥' }
    ]
  },
  {
    name: 'Forms',
    blocks: [
      { type: 'newsletter', label: 'Newsletter', icon: '📧' }
    ]
  },
  {
    name: 'Navigation',
    blocks: [
      { type: 'menu', label: 'Menu', icon: '📋' }
    ]
  },
  {
    name: 'Layout',
    blocks: [
      { type: 'divider', label: 'Divider', icon: '➖' },
      { type: 'spacer', label: 'Spacer', icon: '⬜' }
    ]
  }
];

const filteredCategories = computed(() => {
  if (!searchQuery.value) return categories;
  
  return categories.map(cat => ({
    ...cat,
    blocks: cat.blocks.filter(block => 
      block.label.toLowerCase().includes(searchQuery.value.toLowerCase())
    )
  })).filter(cat => cat.blocks.length > 0);
});

function onDragStart(event, block) {
  event.dataTransfer.effectAllowed = 'copy';
  event.dataTransfer.setData('block-type', block.type);
}
</script>
```

### Composables

#### `useHistory.js`
```javascript
import { ref, computed } from 'vue';

export function useHistory(initialState) {
  const history = ref([]);
  const currentIndex = ref(-1);
  
  const canUndo = computed(() => currentIndex.value > 0);
  const canRedo = computed(() => currentIndex.value < history.value.length - 1);
  
  function pushHistory() {
    // Remove any "future" history if we're not at the end
    if (currentIndex.value < history.value.length - 1) {
      history.value = history.value.slice(0, currentIndex.value + 1);
    }
    
    // Add current state
    history.value.push(JSON.parse(JSON.stringify(initialState.value)));
    currentIndex.value = history.value.length - 1;
    
    // Limit history size to 50
    if (history.value.length > 50) {
      history.value.shift();
      currentIndex.value--;
    }
  }
  
  function undo() {
    if (canUndo.value) {
      currentIndex.value--;
      return JSON.parse(JSON.stringify(history.value[currentIndex.value]));
    }
    return null;
  }
  
  function redo() {
    if (canRedo.value) {
      currentIndex.value++;
      return JSON.parse(JSON.stringify(history.value[currentIndex.value]));
    }
    return null;
  }
  
  return {
    history,
    currentIndex,
    canUndo,
    canRedo,
    pushHistory,
    undo,
    redo
  };
}
```

#### `useStructureApi.js`
```javascript
import { ref } from 'vue';
import axios from 'axios';

export function useStructureApi() {
  const loading = ref(false);
  const error = ref(null);
  
  async function loadStructure(id) {
    loading.value = true;
    error.value = null;
    
    try {
      const response = await axios.get(`/api/admin/ui/structures/${id}`);
      return response.data.data;
    } catch (err) {
      error.value = err.message;
      console.error('Load structure failed:', err);
      return null;
    } finally {
      loading.value = false;
    }
  }
  
  async function saveStructure(id, structure) {
    loading.value = true;
    error.value = null;
    
    try {
      const url = id 
        ? `/api/admin/ui/structures/${id}`
        : '/api/admin/ui/structures';
      
      const method = id ? 'put' : 'post';
      const response = await axios[method](url, structure);
      
      return response.data.data;
    } catch (err) {
      error.value = err.message;
      console.error('Save structure failed:', err);
      alert('Failed to save: ' + err.message);
      return null;
    } finally {
      loading.value = false;
    }
  }
  
  async function publishStructure(id) {
    loading.value = true;
    error.value = null;
    
    try {
      const response = await axios.post(`/api/admin/ui/structures/${id}/publish`);
      return response.data.data;
    } catch (err) {
      error.value = err.message;
      console.error('Publish structure failed:', err);
      alert('Failed to publish: ' + err.message);
      return null;
    } finally {
      loading.value = false;
    }
  }
  
  return {
    loading,
    error,
    loadStructure,
    saveStructure,
    publishStructure
  };
}
```

---

## 📝 Next Steps

1. **Create all remaining component files** listed above
2. **Create Blade views** for index/create/edit/preview
3. **Create BuilderController.php**
4. **Update web routes**
5. **Run `npm install` and `npm run build`**
6. **Test the builder**

---

## 🎯 Implementation Priority

If implementing in phases:

### Phase 1: Basic (Minimum Viable Builder)
- ✅ Vite setup
- ✅ BuilderApp.vue
- ✅ TopBar.vue
- ✅ Basic CSS
- ⏳ Simple canvas (no drag & drop)
- ⏳ Basic settings panel
- ⏳ Blade views

### Phase 2: Drag & Drop
- ⏳ BlocksPalette with draggable
- ⏳ Canvas with drop zones
- ⏳ Reorder elements

### Phase 3: Polish
- ⏳ Undo/redo
- ⏳ Responsive preview
- ⏳ Image upload
- ⏳ Better UX

---

## 💡 Notes

- All components use Composition API (`<script setup>`)
- Uses Vue 3.4+ features
- Requires `vuedraggable` v4 for drag & drop
- Axios for API calls
- Tailwind-style utility classes in CSS

---

**Status**: Vite setup complete, main app structure done  
**Next**: Complete remaining components + Blade views  
**Estimate**: ~20-30 hours remaining for full implementation

