updated more items
This commit is contained in:
parent
6afae19013
commit
659b3705fc
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\MagentoCategoryMigrationService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CustomersController extends Controller
|
||||
{
|
||||
protected $migrationService;
|
||||
|
||||
public function __construct(MagentoCategoryMigrationService $migrationService)
|
||||
{
|
||||
$this->migrationService = $migrationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the customers page
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$m1Customers = $this->migrationService->getMagento1Customers();
|
||||
$m2Customers = $this->migrationService->getMagento2Customers();
|
||||
$m1CustomersNotInM2 = $this->migrationService->getM1CustomersNotInM2();
|
||||
$m2CustomersNotInM1 = $this->migrationService->getM2CustomersNotInM1();
|
||||
|
||||
return view('customers.index', [
|
||||
'm1Customers' => $m1Customers,
|
||||
'm2Customers' => $m2Customers,
|
||||
'm1CustomersNotInM2' => $m1CustomersNotInM2,
|
||||
'm2CustomersNotInM1' => $m2CustomersNotInM1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all customers from Magento 1 to Magento 2
|
||||
*/
|
||||
public function migrateCustomers(Request $request)
|
||||
{
|
||||
try {
|
||||
$dryRun = $request->input('dry_run', false);
|
||||
$result = $this->migrationService->migrateCustomers($dryRun);
|
||||
|
||||
return response()->json($result, $result['success'] ? 200 : 400);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Customer migration error: ' . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Migration failed: ' . $e->getMessage(),
|
||||
'added' => 0,
|
||||
'updated' => 0,
|
||||
'errors' => 0,
|
||||
'log' => []
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single customer from Magento 2
|
||||
*/
|
||||
public function deleteM2Customer(Request $request, $customerId)
|
||||
{
|
||||
try {
|
||||
$result = $this->migrationService->deleteM2Customer($customerId);
|
||||
|
||||
return response()->json($result, $result['success'] ? 200 : 400);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Delete M2 customer error: ' . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Deletion failed: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -123,5 +123,29 @@ public function deleteM2ProductsAboveM1Max(Request $request)
|
|||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix category products - ensure products are added to categories if missing from catalog_category_product table
|
||||
*/
|
||||
public function fixCategoryProducts(Request $request)
|
||||
{
|
||||
try {
|
||||
$result = $this->migrationService->fixCategoryProducts();
|
||||
|
||||
return response()->json($result, $result['success'] ? 200 : 400);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Fix category products error: ' . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Fix failed: ' . $e->getMessage(),
|
||||
'added' => 0,
|
||||
'skipped' => 0,
|
||||
'errors' => 0,
|
||||
'log' => []
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,115 @@
|
|||
// Customers page JavaScript
|
||||
|
||||
let routes = {};
|
||||
let csrfToken = '';
|
||||
|
||||
// Initialize on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (window.customerRoutes) {
|
||||
routes = window.customerRoutes;
|
||||
csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||
}
|
||||
});
|
||||
|
||||
function startCustomerMigration(dryRun) {
|
||||
const button = dryRun ? document.getElementById('dryRunCustomerMigrationBtn') : document.getElementById('startCustomerMigrationBtn');
|
||||
const otherButton = dryRun ? document.getElementById('startCustomerMigrationBtn') : document.getElementById('dryRunCustomerMigrationBtn');
|
||||
const originalText = button.textContent;
|
||||
button.disabled = true;
|
||||
otherButton.disabled = true;
|
||||
button.textContent = dryRun ? 'Running Dry Run...' : 'Migrating...';
|
||||
button.style.cursor = 'not-allowed';
|
||||
|
||||
const logContent = document.getElementById('customerMigrationLogContent');
|
||||
logContent.innerHTML = '<div class="log-entry">' + (dryRun ? 'Running dry run...' : 'Starting migration...') + '</div>';
|
||||
|
||||
fetch(routes.migrateCustomers, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken
|
||||
},
|
||||
body: JSON.stringify({ dry_run: dryRun })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
button.disabled = false;
|
||||
otherButton.disabled = false;
|
||||
button.textContent = originalText;
|
||||
button.style.cursor = 'pointer';
|
||||
|
||||
if (data.success) {
|
||||
const successEntry = document.createElement('div');
|
||||
successEntry.className = 'log-entry success';
|
||||
successEntry.textContent = `✓ ${dryRun ? 'Dry run' : 'Migration'} completed! Added: ${data.added || 0}, Updated: ${data.updated || 0}, Errors: ${data.errors || 0}`;
|
||||
logContent.appendChild(successEntry);
|
||||
|
||||
if (data.log && data.log.length > 0) {
|
||||
data.log.forEach(log => {
|
||||
const entry = document.createElement('div');
|
||||
entry.className = 'log-entry ' + (log.includes('ERROR') ? 'error' : 'success');
|
||||
entry.textContent = log;
|
||||
logContent.appendChild(entry);
|
||||
});
|
||||
}
|
||||
|
||||
const logContainer = document.getElementById('customerMigrationLogContainer');
|
||||
logContainer.scrollTop = logContainer.scrollHeight;
|
||||
} else {
|
||||
const errorEntry = document.createElement('div');
|
||||
errorEntry.className = 'log-entry error';
|
||||
errorEntry.textContent = '✗ ' + (dryRun ? 'Dry run' : 'Migration') + ' failed: ' + (data.message || 'Unknown error');
|
||||
logContent.appendChild(errorEntry);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
button.disabled = false;
|
||||
otherButton.disabled = false;
|
||||
button.textContent = originalText;
|
||||
button.style.cursor = 'pointer';
|
||||
|
||||
const errorEntry = document.createElement('div');
|
||||
errorEntry.className = 'log-entry error';
|
||||
errorEntry.textContent = '✗ Error: ' + error.message;
|
||||
logContent.appendChild(errorEntry);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteM2Customer(customerId, email, firstname, lastname) {
|
||||
const customerName = firstname !== 'N/A' && lastname !== 'N/A'
|
||||
? `${firstname} ${lastname}`
|
||||
: email !== 'N/A'
|
||||
? email
|
||||
: `ID ${customerId}`;
|
||||
|
||||
if (!confirm(`Are you sure you want to delete customer "${customerName}" (Email: ${email})?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = routes.deleteCustomer.replace(':id', customerId);
|
||||
|
||||
fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('Customer deleted successfully!');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error: ' + (data.message || 'Failed to delete customer'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Error: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
// Make functions available globally
|
||||
window.startCustomerMigration = startCustomerMigration;
|
||||
window.deleteM2Customer = deleteM2Customer;
|
||||
|
||||
|
|
@ -9,6 +9,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
routes = window.productRoutes;
|
||||
csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||
}
|
||||
|
||||
// Attach event listeners to buttons
|
||||
const fixCategoryProductsBtn = document.getElementById('fixCategoryProductsBtn');
|
||||
if (fixCategoryProductsBtn) {
|
||||
fixCategoryProductsBtn.addEventListener('click', fixCategoryProducts);
|
||||
}
|
||||
});
|
||||
|
||||
function startProductMigration(dryRun) {
|
||||
|
|
@ -190,6 +196,78 @@ function syncProductCategories() {
|
|||
});
|
||||
}
|
||||
|
||||
function fixCategoryProducts() {
|
||||
if (!confirm('Are you sure you want to fix category products? This will add missing products to the catalog_category_product table based on their category_ids attribute in Magento 2.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const button = document.getElementById('fixCategoryProductsBtn');
|
||||
const logContainer = document.getElementById('fixCategoryProductsLogContainer');
|
||||
const logContent = document.getElementById('fixCategoryProductsLogContent');
|
||||
|
||||
if (!button) {
|
||||
console.error('fixCategoryProductsBtn not found');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!routes.fixCategoryProducts) {
|
||||
console.error('routes.fixCategoryProducts not found', routes);
|
||||
alert('Error: Route not configured. Please refresh the page.');
|
||||
return;
|
||||
}
|
||||
|
||||
const originalText = button.textContent;
|
||||
button.disabled = true;
|
||||
button.textContent = 'Fixing...';
|
||||
logContainer.style.display = 'block';
|
||||
logContent.innerHTML = '<div class="log-entry">Starting fix process...</div>';
|
||||
|
||||
fetch(routes.fixCategoryProducts, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
button.disabled = false;
|
||||
button.textContent = originalText;
|
||||
|
||||
if (data.success) {
|
||||
const successEntry = document.createElement('div');
|
||||
successEntry.className = 'log-entry success';
|
||||
successEntry.textContent = `✓ Fix completed! Added: ${data.added || 0}, Skipped: ${data.skipped || 0}, Errors: ${data.errors || 0}`;
|
||||
logContent.appendChild(successEntry);
|
||||
|
||||
if (data.log && data.log.length > 0) {
|
||||
data.log.forEach(log => {
|
||||
const entry = document.createElement('div');
|
||||
entry.className = 'log-entry ' + (log.includes('ERROR') ? 'error' : (log.includes('ADDED') ? 'success' : 'info'));
|
||||
entry.textContent = log;
|
||||
logContent.appendChild(entry);
|
||||
});
|
||||
}
|
||||
|
||||
logContent.scrollTop = logContent.scrollHeight;
|
||||
} else {
|
||||
const errorEntry = document.createElement('div');
|
||||
errorEntry.className = 'log-entry error';
|
||||
errorEntry.textContent = '✗ Fix failed: ' + (data.message || 'Unknown error');
|
||||
logContent.appendChild(errorEntry);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
button.disabled = false;
|
||||
button.textContent = originalText;
|
||||
|
||||
const errorEntry = document.createElement('div');
|
||||
errorEntry.className = 'log-entry error';
|
||||
errorEntry.textContent = '✗ Error: ' + error.message;
|
||||
logContent.appendChild(errorEntry);
|
||||
});
|
||||
}
|
||||
|
||||
function loadM1CategoryTreeWithProducts() {
|
||||
const container = document.getElementById('m1-category-products-tree-container');
|
||||
if (!container) return;
|
||||
|
|
@ -353,6 +431,7 @@ window.startProductMigration = startProductMigration;
|
|||
window.deleteM2Product = deleteM2Product;
|
||||
window.deleteProductsAboveM1Max = deleteProductsAboveM1Max;
|
||||
window.syncProductCategories = syncProductCategories;
|
||||
window.fixCategoryProducts = fixCategoryProducts;
|
||||
window.loadM1CategoryTreeWithProducts = loadM1CategoryTreeWithProducts;
|
||||
window.loadM2CategoryTreeWithProducts = loadM2CategoryTreeWithProducts;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<!-- Customer Migration Section -->
|
||||
<div class="section">
|
||||
<h2>👥 Customer Migration</h2>
|
||||
<div class="info-box" style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 10px; color: #1976D2; font-size: 1.1em;">What happens when you click "Start Customer Migration"?</h3>
|
||||
<p style="margin: 5px 0; color: #1976D2;">The customer migration process will:</p>
|
||||
<ul style="margin: 10px 0 0 20px; color: #1976D2; line-height: 1.8;">
|
||||
<li><strong>Create new customers:</strong> If a customer with the same email doesn't exist in Magento 2, it will be created with all its attributes</li>
|
||||
<li><strong>Update existing customers:</strong> If a customer with the same email already exists in Magento 2, it will be updated with the latest data from Magento 1</li>
|
||||
<li><strong>Migrate customer attributes:</strong> All customer attributes including email, firstname, lastname, and other custom attributes will be migrated</li>
|
||||
<li><strong>Preserve customer data:</strong> Website ID, group ID, and timestamps will be preserved</li>
|
||||
<li><strong>Generate logs:</strong> Detailed migration logs showing which customers were added, updated, or encountered errors</li>
|
||||
</ul>
|
||||
<p style="margin: 10px 0 0 0; color: #d32f2f; font-weight: 600;">⚠️ <strong>Warning:</strong> This will modify your Magento 2 database. Make sure you have a backup before proceeding.</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px; display: flex; gap: 15px; flex-wrap: wrap;">
|
||||
<button id="dryRunCustomerMigrationBtn" class="btn btn-secondary" onclick="startCustomerMigration(true)">
|
||||
Run Dry Run (Check for Errors)
|
||||
</button>
|
||||
<button id="startCustomerMigrationBtn" class="btn btn-primary" onclick="startCustomerMigration(false)">
|
||||
Start Customer Migration
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Migration Logs Section -->
|
||||
<div class="section" style="margin-top: 30px;">
|
||||
<h2>📋 Customer Migration Logs</h2>
|
||||
<p style="margin-bottom: 15px; color: #666;">Detailed logs showing which customers were added, updated, or encountered errors during migration:</p>
|
||||
<div class="log-container" id="customerMigrationLogContainer" style="display: block;">
|
||||
<div id="customerMigrationLogContent">
|
||||
<div class="log-entry" style="color: #999; font-style: italic;">
|
||||
No customer migration logs yet. Click "Run Dry Run" or "Start Customer Migration" to begin.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customer Statistics -->
|
||||
<div class="section" style="margin-top: 30px;">
|
||||
<h2>📊 Customer Statistics</h2>
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<div class="number">{{ $m1Customers->count() }}</div>
|
||||
<div class="label">Magento 1 Customers</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="number">{{ $m2Customers->count() }}</div>
|
||||
<div class="label">Magento 2 Customers</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="number">{{ $m1CustomersNotInM2->count() }}</div>
|
||||
<div class="label">M1 Customers Not in M2</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="number">{{ $m2CustomersNotInM1->count() }}</div>
|
||||
<div class="label">M2 Customers Not in M1</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customers Not in M2 Section -->
|
||||
@if($m1CustomersNotInM2->count() > 0)
|
||||
<div class="section" style="margin-top: 30px;">
|
||||
<h2>⚠️ Magento 1 Customers Not in Magento 2</h2>
|
||||
<p>These customers exist in Magento 1 but are missing in Magento 2:</p>
|
||||
<div style="background: white; border: 1px solid #ddd; border-radius: 6px; padding: 20px; max-height: 400px; overflow-y: auto; margin-top: 15px;">
|
||||
<table class="products-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Email</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Website ID</th>
|
||||
<th>Group ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($m1CustomersNotInM2->take(50) as $customer)
|
||||
<tr>
|
||||
<td>{{ $customer->entity_id }}</td>
|
||||
<td>{{ $customer->email ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->firstname ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->lastname ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->website_id ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->group_id ?? 'N/A' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@if($m1CustomersNotInM2->count() > 50)
|
||||
<p style="margin-top: 15px; color: #666;">Showing first 50 of {{ $m1CustomersNotInM2->count() }} customers.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Customers Not in M1 Section -->
|
||||
@if($m2CustomersNotInM1->count() > 0)
|
||||
<div class="section" style="margin-top: 30px;">
|
||||
<h2>⚠️ Magento 2 Customers Not in Magento 1</h2>
|
||||
<p>These customers exist in Magento 2 but are missing in Magento 1:</p>
|
||||
<div style="background: white; border: 1px solid #ddd; border-radius: 6px; padding: 20px; max-height: 400px; overflow-y: auto; margin-top: 15px;">
|
||||
<table class="products-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Email</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Website ID</th>
|
||||
<th>Group ID</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($m2CustomersNotInM1->take(50) as $customer)
|
||||
<tr>
|
||||
<td>{{ $customer->entity_id }}</td>
|
||||
<td>{{ $customer->email ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->firstname ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->lastname ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->website_id ?? 'N/A' }}</td>
|
||||
<td>{{ $customer->group_id ?? 'N/A' }}</td>
|
||||
<td>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
onclick="deleteM2Customer({{ $customer->entity_id }}, '{{ addslashes($customer->email ?? 'N/A') }}', '{{ addslashes($customer->firstname ?? 'N/A') }}', '{{ addslashes($customer->lastname ?? 'N/A') }}')"
|
||||
style="padding: 6px 12px; font-size: 0.85em;">
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@if($m2CustomersNotInM1->count() > 50)
|
||||
<p style="margin-top: 15px; color: #666;">Showing first 50 of {{ $m2CustomersNotInM1->count() }} customers.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
{{-- CSS is loaded globally via app.css --}}
|
||||
|
||||
@push('scripts')
|
||||
@vite(['resources/js/customers.js'])
|
||||
<script>
|
||||
window.customerRoutes = {
|
||||
migrateCustomers: '{{ route("customers.migrate-customers") }}',
|
||||
deleteCustomer: '{{ route("customers.delete-customer", ["customerId" => ":id"]) }}'
|
||||
};
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
|
|
@ -14,5 +14,8 @@
|
|||
<a href="{{ route('products.index') }}" class="nav-link {{ request()->routeIs('products.*') ? 'active' : '' }}">
|
||||
Products
|
||||
</a>
|
||||
<a href="{{ route('customers.index') }}" class="nav-link {{ request()->routeIs('customers.*') ? 'active' : '' }}">
|
||||
Customers
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,30 @@ class="btn btn-danger"
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Fix Category Products Section -->
|
||||
<div class="section" style="margin-top: 30px;">
|
||||
<h2>🔧 Fix Category Products</h2>
|
||||
<div class="info-box" style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 10px; color: #1976D2; font-size: 1.1em;">What does this do?</h3>
|
||||
<p style="margin: 5px 0; color: #1976D2;">This tool checks the Magento 2 <code>catalog_category_product</code> table and ensures products are added to categories if they are missing:</p>
|
||||
<ul style="margin: 10px 0 0 20px; color: #1976D2; line-height: 1.8;">
|
||||
<li><strong>Finds missing products:</strong> Identifies products in Magento 2 that are not in the <code>catalog_category_product</code> table</li>
|
||||
<li><strong>Checks category_ids attribute:</strong> For each missing product, reads the <code>category_ids</code> attribute value</li>
|
||||
<li><strong>Adds to categories:</strong> Adds the product to the categories specified in its <code>category_ids</code> attribute</li>
|
||||
<li><strong>Validates categories:</strong> Only adds products to categories that exist in Magento 2</li>
|
||||
</ul>
|
||||
<p style="margin: 10px 0 0 0; color: #d32f2f; font-weight: 600;">⚠️ <strong>Warning:</strong> This will modify your Magento 2 database. Make sure you have a backup before proceeding.</p>
|
||||
</div>
|
||||
<button id="fixCategoryProductsBtn" class="btn btn-primary" style="margin-top: 15px;">
|
||||
Fix Category Products
|
||||
</button>
|
||||
<div class="log-container" id="fixCategoryProductsLogContainer" style="display: none; margin-top: 20px; overflow: visible; max-height: none;">
|
||||
<h3 style="margin-bottom: 10px; color: white;">Fix Logs</h3>
|
||||
<div id="fixCategoryProductsLogContent" style="max-height: 400px; overflow-y: auto; background: #000000; color: #ffffff; padding: 15px; border-radius: 4px; font-family: monospace; font-size: 0.9em;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Category Tree with Products Section -->
|
||||
<div class="section" style="margin-top: 30px;">
|
||||
<h2>🌳 Category Tree with Products</h2>
|
||||
|
|
@ -226,6 +250,7 @@ class="btn btn-danger"
|
|||
deleteProduct: '{{ route("products.delete-product", ["productId" => ":id"]) }}',
|
||||
deleteProductsAboveM1Max: '{{ route("products.delete-products-above-m1-max") }}',
|
||||
syncProductCategories: '{{ route("products.sync-product-categories") }}',
|
||||
fixCategoryProducts: '{{ route("products.fix-category-products") }}',
|
||||
magento1CategoryTreeWithProducts: '{{ route("categories.magento1-category-tree-with-products") }}',
|
||||
magento2CategoryTreeWithProducts: '{{ route("categories.magento2-category-tree-with-products") }}'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
use App\Http\Controllers\MigrationController;
|
||||
use App\Http\Controllers\AttributesController;
|
||||
use App\Http\Controllers\ProductsController;
|
||||
use App\Http\Controllers\CustomersController;
|
||||
|
||||
Route::get('/', function () {
|
||||
return redirect('/connections');
|
||||
|
|
@ -50,6 +51,14 @@
|
|||
Route::get('/', [ProductsController::class, 'index'])->name('index');
|
||||
Route::post('/migrate', [ProductsController::class, 'migrateProducts'])->name('migrate-products');
|
||||
Route::post('/sync-categories', [ProductsController::class, 'syncProductCategories'])->name('sync-product-categories');
|
||||
Route::post('/fix-category-products', [ProductsController::class, 'fixCategoryProducts'])->name('fix-category-products');
|
||||
Route::delete('/{productId}', [ProductsController::class, 'deleteM2Product'])->name('delete-product');
|
||||
Route::delete('/above-m1-max', [ProductsController::class, 'deleteM2ProductsAboveM1Max'])->name('delete-products-above-m1-max');
|
||||
});
|
||||
|
||||
// Customers routes
|
||||
Route::prefix('customers')->name('customers.')->group(function () {
|
||||
Route::get('/', [CustomersController::class, 'index'])->name('index');
|
||||
Route::post('/migrate', [CustomersController::class, 'migrateCustomers'])->name('migrate-customers');
|
||||
Route::delete('/{customerId}', [CustomersController::class, 'deleteM2Customer'])->name('delete-customer');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export default defineConfig({
|
|||
'resources/js/migration.js',
|
||||
'resources/js/attributes.js',
|
||||
'resources/js/products.js',
|
||||
'resources/js/customers.js',
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
|
|
|
|||
Loading…
Reference in New Issue