Feature: Russian language support & Vehicle Requests improvements
- Add Russian language support (title_ru, subtitle_ru) for hero banners - Add fuel/transmission translations for Mongolian (경유→Дизель, 오토→Автомат) - Improve Vehicle Requests admin page: - Display real request ID and user email - Show detailed request info (maker, grade, year, fuel, mileage) - Replace modal search with Cars page integration - Add "Add to Request" flow in Cars page for vehicle recommendations - Fix image URL handling in FilmStripSlider and car detail page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useSearchParams, useRouter } from 'next/navigation';
|
||||
import Image from 'next/image';
|
||||
import api, { heroBannersApi, carmodooApi } from '@/lib/api';
|
||||
import api, { heroBannersApi, carmodooApi, vehicleRequestsApi } from '@/lib/api';
|
||||
|
||||
interface CarmodooMaker {
|
||||
code: string;
|
||||
@@ -193,6 +194,16 @@ export default function CarsAdminPage() {
|
||||
}>;
|
||||
} | null>(null);
|
||||
|
||||
// Vehicle Request mode (when coming from Vehicle Requests page)
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const requestId = searchParams.get('requestId');
|
||||
const [addingToRequest, setAddingToRequest] = useState(false);
|
||||
const [requestAddResult, setRequestAddResult] = useState<{
|
||||
added: number;
|
||||
errors: number;
|
||||
} | null>(null);
|
||||
|
||||
// Dealer description editing state
|
||||
const [showDescEditModal, setShowDescEditModal] = useState(false);
|
||||
const [editingCar, setEditingCar] = useState<CarmodooCarItem | null>(null);
|
||||
@@ -249,6 +260,33 @@ export default function CarsAdminPage() {
|
||||
}
|
||||
}, [filters.model_code]);
|
||||
|
||||
// Pre-fill filters from URL params (when coming from Vehicle Requests page)
|
||||
useEffect(() => {
|
||||
if (requestId) {
|
||||
const makerCode = searchParams.get('maker_code') || '';
|
||||
const modelCode = searchParams.get('model_code') || '';
|
||||
const grade = searchParams.get('grade') || '';
|
||||
const yearMin = searchParams.get('year_min') || '';
|
||||
const yearMax = searchParams.get('year_max') || '';
|
||||
const mileageMax = searchParams.get('mileage_max') || '';
|
||||
const fuel = searchParams.get('fuel') || '';
|
||||
|
||||
setFilters(prev => ({
|
||||
...prev,
|
||||
maker_code: makerCode,
|
||||
model_code: modelCode,
|
||||
grade: grade,
|
||||
year_min: yearMin,
|
||||
year_max: yearMax,
|
||||
mileage_max: mileageMax,
|
||||
fuel: fuel,
|
||||
}));
|
||||
|
||||
// Switch to Carmodoo tab
|
||||
setActiveTab('carmodoo');
|
||||
}
|
||||
}, [requestId]);
|
||||
|
||||
const loadLocalCars = async (page = 1) => {
|
||||
setLocalLoading(true);
|
||||
try {
|
||||
@@ -714,6 +752,76 @@ export default function CarsAdminPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 차량 추천 목록에 추가 함수 (Vehicle Request용)
|
||||
const handleAddToRequest = async () => {
|
||||
if (!requestId) return;
|
||||
if (selectedCars.size === 0) {
|
||||
alert('Please select at least one car to add to the request.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`${selectedCars.size}개의 차량을 추천 목록에 추가하시겠습니까?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAddingToRequest(true);
|
||||
setRequestAddResult(null);
|
||||
|
||||
try {
|
||||
const selectedCarsList = cars.filter(car => selectedCars.has(car.id));
|
||||
let addedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (const car of selectedCarsList) {
|
||||
try {
|
||||
// Add vehicle to request
|
||||
await vehicleRequestsApi.adminAddVehicle(parseInt(requestId), {
|
||||
request_id: parseInt(requestId),
|
||||
car_data: {
|
||||
id: car.id,
|
||||
car_name: car.car_name,
|
||||
maker_name: car.maker_name,
|
||||
model_name: car.model_name,
|
||||
year: car.year,
|
||||
mileage: car.mileage,
|
||||
final_price: car.price,
|
||||
fuel: car.fuel,
|
||||
transmission: car.transmission,
|
||||
color: car.color,
|
||||
main_image: car.main_image,
|
||||
},
|
||||
is_approved: true,
|
||||
});
|
||||
addedCount++;
|
||||
} catch (err) {
|
||||
console.error(`Failed to add car ${car.id}:`, err);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
setRequestAddResult({
|
||||
added: addedCount,
|
||||
errors: errorCount,
|
||||
});
|
||||
|
||||
setSelectedCars(new Set());
|
||||
|
||||
if (errorCount > 0) {
|
||||
alert(`${addedCount}개의 차량이 추천 목록에 추가되었습니다.\n(실패: ${errorCount}개)`);
|
||||
} else {
|
||||
alert(`${addedCount}개의 차량이 추천 목록에 추가되었습니다.`);
|
||||
}
|
||||
|
||||
// Redirect back to vehicle requests page
|
||||
router.push('/admin/vehicle-requests');
|
||||
} catch (err) {
|
||||
console.error('Add to request failed:', err);
|
||||
alert('추천 목록 추가에 실패했습니다.');
|
||||
} finally {
|
||||
setAddingToRequest(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteCar = async (carId: number) => {
|
||||
if (!confirm('Are you sure you want to delete this car?')) {
|
||||
return;
|
||||
@@ -858,6 +966,29 @@ export default function CarsAdminPage() {
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-800 mb-6">Cars Management</h1>
|
||||
|
||||
{/* Request Mode Banner */}
|
||||
{requestId && (
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-blue-100 rounded-full p-2">
|
||||
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-blue-800">Adding vehicles to Request #{requestId}</p>
|
||||
<p className="text-sm text-blue-600">Search and select vehicles, then click "Add to Request" button.</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => router.push('/admin/vehicle-requests')}
|
||||
className="text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1"
|
||||
>
|
||||
← Back to Requests
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b border-gray-200 mb-6">
|
||||
<button
|
||||
@@ -1605,7 +1736,7 @@ export default function CarsAdminPage() {
|
||||
</span>
|
||||
<button
|
||||
onClick={handleImport}
|
||||
disabled={importing || registeringBanners || selectedCars.size === 0}
|
||||
disabled={importing || registeringBanners || addingToRequest || selectedCars.size === 0}
|
||||
className="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center gap-2"
|
||||
>
|
||||
{importing ? (
|
||||
@@ -1624,7 +1755,7 @@ export default function CarsAdminPage() {
|
||||
</button>
|
||||
<button
|
||||
onClick={handleRegisterAsBanner}
|
||||
disabled={importing || registeringBanners || selectedCars.size === 0}
|
||||
disabled={importing || registeringBanners || addingToRequest || selectedCars.size === 0}
|
||||
className="bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition-colors disabled:opacity-50 flex items-center gap-2"
|
||||
>
|
||||
{registeringBanners ? (
|
||||
@@ -1641,6 +1772,27 @@ export default function CarsAdminPage() {
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
{requestId && (
|
||||
<button
|
||||
onClick={handleAddToRequest}
|
||||
disabled={importing || registeringBanners || addingToRequest || selectedCars.size === 0}
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
|
||||
>
|
||||
{addingToRequest ? (
|
||||
<>
|
||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
|
||||
<span>Adding...</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
<span>Add to Request #{requestId}</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user