Implement Site Translations API and DataGrid View: Introduced a new API controller for managing site translations, including methods for listing, storing, updating, and deleting translations. Added a DataGrid view for the site translations page, allowing for enhanced user interaction and management of translations. Implemented session-based view toggling between traditional table and DataGrid formats, improving usability. Localization support has been integrated for all text elements, ensuring a seamless experience for users across different languages.
This commit is contained in:
@@ -5,17 +5,54 @@ namespace App\Filament\Admin\Resources\SiteTranslations\Pages;
|
|||||||
use App\Filament\Admin\Resources\SiteTranslations\SiteTranslationResource;
|
use App\Filament\Admin\Resources\SiteTranslations\SiteTranslationResource;
|
||||||
use Filament\Actions\CreateAction;
|
use Filament\Actions\CreateAction;
|
||||||
use Filament\Resources\Pages\ListRecords;
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
class ListSiteTranslations extends ListRecords
|
class ListSiteTranslations extends ListRecords
|
||||||
{
|
{
|
||||||
protected static string $resource = SiteTranslationResource::class;
|
protected static string $resource = SiteTranslationResource::class;
|
||||||
|
|
||||||
|
public bool $useDataGrid = false;
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
parent::mount();
|
||||||
|
|
||||||
|
// Session'dan görünüm tercihini al, yoksa varsayılan olarak false (eski görünüm)
|
||||||
|
$this->useDataGrid = session('site_translations_use_datagrid', false);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getHeaderActions(): array
|
protected function getHeaderActions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
CreateAction::make(),
|
Action::make('toggleView')
|
||||||
|
->label(fn() => $this->useDataGrid ? 'Tablo Görünümü' : 'DataGrid Görünümü')
|
||||||
|
->icon(fn() => $this->useDataGrid ? 'heroicon-o-table-cells' : 'heroicon-o-squares-2x2')
|
||||||
|
->color('gray')
|
||||||
|
->action(function () {
|
||||||
|
$this->useDataGrid = !$this->useDataGrid;
|
||||||
|
session(['site_translations_use_datagrid' => $this->useDataGrid]);
|
||||||
|
|
||||||
|
// Sayfayı yenile ki getContent() yeniden çağrılsın
|
||||||
|
return redirect()->to($this->getResource()::getUrl('index'));
|
||||||
|
}),
|
||||||
|
CreateAction::make()
|
||||||
|
->visible(fn() => !$this->useDataGrid), // Sadece eski görünümde göster
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getContent(): View
|
||||||
|
{
|
||||||
|
// Session'dan direkt oku ki her render'da güncel değeri alsın
|
||||||
|
$useDataGrid = session('site_translations_use_datagrid', false);
|
||||||
|
|
||||||
|
if ($useDataGrid) {
|
||||||
|
return view('filament.admin.resources.site-translations.datagrid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eski Filament table görünümü
|
||||||
|
return parent::getContent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,292 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\SiteTranslation;
|
||||||
|
use App\Models\Language;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
class SiteTranslationController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$query = SiteTranslation::query();
|
||||||
|
|
||||||
|
// Search
|
||||||
|
if ($request->has('search')) {
|
||||||
|
$search = $request->get('search');
|
||||||
|
$query->where(function ($q) use ($search) {
|
||||||
|
$q->where('key', 'like', "%{$search}%")
|
||||||
|
->orWhereRaw("JSON_SEARCH(translations, 'one', ?, NULL, '$[*]') IS NOT NULL", ["%{$search}%"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting
|
||||||
|
$sortField = $request->get('sort', 'id');
|
||||||
|
$sortOrder = $request->get('order', 'desc');
|
||||||
|
$query->orderBy($sortField, $sortOrder);
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
$page = $request->get('page', 1);
|
||||||
|
$pageSize = $request->get('pageSize', 20);
|
||||||
|
|
||||||
|
$totalCount = $query->count();
|
||||||
|
$items = $query->skip(($page - 1) * $pageSize)->take($pageSize)->get();
|
||||||
|
|
||||||
|
// Format data for DataGrid
|
||||||
|
$data = $items->map(function ($item) {
|
||||||
|
$formatted = [
|
||||||
|
'id' => $item->id,
|
||||||
|
'key' => $item->key,
|
||||||
|
'hash' => $item->hash,
|
||||||
|
'created_at' => $item->created_at?->toDateTimeString(),
|
||||||
|
'updated_at' => $item->updated_at?->toDateTimeString(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add translation fields for each active language
|
||||||
|
$languages = Language::where('is_active', true)->get();
|
||||||
|
$translations = $item->translations ?? [];
|
||||||
|
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
$formatted[$language->code] = $translations[$language->code] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formatted;
|
||||||
|
});
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'data' => $data,
|
||||||
|
'totalCount' => $totalCount,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*/
|
||||||
|
public function store(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$validator = Validator::make($request->all(), [
|
||||||
|
'key' => 'required|string|max:500',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'errors' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = md5($request->key);
|
||||||
|
|
||||||
|
// Check if translation already exists
|
||||||
|
$exists = SiteTranslation::where('hash', $hash)->first();
|
||||||
|
if ($exists) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Bu key zaten mevcut.',
|
||||||
|
], 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
$languages = Language::where('is_active', true)->get();
|
||||||
|
$translations = [];
|
||||||
|
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
if ($request->has($language->code)) {
|
||||||
|
$translations[$language->code] = $request->get($language->code, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$translation = SiteTranslation::create([
|
||||||
|
'hash' => $hash,
|
||||||
|
'key' => $request->key,
|
||||||
|
'translations' => $translations,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Cache::forget("site_translation_{$hash}");
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $translation->load('translations'),
|
||||||
|
'message' => 'Çeviri başarıyla oluşturuldu.',
|
||||||
|
], 201);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(Request $request, $id): JsonResponse
|
||||||
|
{
|
||||||
|
$translation = SiteTranslation::findOrFail($id);
|
||||||
|
|
||||||
|
$validator = Validator::make($request->all(), [
|
||||||
|
'key' => 'sometimes|required|string|max:500',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'errors' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$languages = Language::where('is_active', true)->get();
|
||||||
|
$translations = $translation->translations ?? [];
|
||||||
|
|
||||||
|
// Update translations
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
if ($request->has($language->code)) {
|
||||||
|
$translations[$language->code] = $request->get($language->code, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update key if provided
|
||||||
|
if ($request->has('key') && $request->key !== $translation->key) {
|
||||||
|
$translation->key = $request->key;
|
||||||
|
$translation->hash = md5($request->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
$translation->translations = $translations;
|
||||||
|
$translation->save();
|
||||||
|
|
||||||
|
Cache::forget("site_translation_{$translation->hash}");
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $translation->fresh(),
|
||||||
|
'message' => 'Çeviri başarıyla güncellendi.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch update multiple resources.
|
||||||
|
*/
|
||||||
|
public function batchUpdate(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$validator = Validator::make($request->all(), [
|
||||||
|
'changes' => 'required|array',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'errors' => $validator->fails(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$changes = $request->get('changes', []);
|
||||||
|
$updated = [];
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
foreach ($changes as $change) {
|
||||||
|
try {
|
||||||
|
// ID can be in 'key' field (integer) or directly in 'key' field
|
||||||
|
$id = is_numeric($change['key'] ?? null)
|
||||||
|
? (int)$change['key']
|
||||||
|
: ($change['key']['id'] ?? null);
|
||||||
|
|
||||||
|
if (!$id) {
|
||||||
|
$errors[] = 'ID bulunamadı.';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$translation = SiteTranslation::find($id);
|
||||||
|
if (!$translation) {
|
||||||
|
$errors[] = "ID {$id} için kayıt bulunamadı.";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $change['data'] ?? [];
|
||||||
|
$languages = Language::where('is_active', true)->get();
|
||||||
|
$translations = $translation->translations ?? [];
|
||||||
|
|
||||||
|
// Update translations
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
if (isset($data[$language->code])) {
|
||||||
|
$translations[$language->code] = $data[$language->code];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update key if provided
|
||||||
|
if (isset($data['key']) && $data['key'] !== $translation->key) {
|
||||||
|
$translation->key = $data['key'];
|
||||||
|
$translation->hash = md5($data['key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$translation->translations = $translations;
|
||||||
|
$translation->save();
|
||||||
|
|
||||||
|
Cache::forget("site_translation_{$translation->hash}");
|
||||||
|
|
||||||
|
$updated[] = $translation->id;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$id = is_numeric($change['key'] ?? null)
|
||||||
|
? (int)$change['key']
|
||||||
|
: ($change['key']['id'] ?? 'unknown');
|
||||||
|
$errors[] = "ID {$id} için hata: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => count($errors) === 0,
|
||||||
|
'updated' => $updated,
|
||||||
|
'errors' => $errors,
|
||||||
|
'message' => count($updated) . ' kayıt güncellendi.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy($id): JsonResponse
|
||||||
|
{
|
||||||
|
$translation = SiteTranslation::findOrFail($id);
|
||||||
|
$hash = $translation->hash;
|
||||||
|
|
||||||
|
$translation->delete();
|
||||||
|
Cache::forget("site_translation_{$hash}");
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Çeviri başarıyla silindi.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch delete multiple resources.
|
||||||
|
*/
|
||||||
|
public function batchDestroy(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$validator = Validator::make($request->all(), [
|
||||||
|
'ids' => 'required|array',
|
||||||
|
'ids.*' => 'required|integer|exists:site_translations,id',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'errors' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $request->get('ids', []);
|
||||||
|
$translations = SiteTranslation::whereIn('id', $ids)->get();
|
||||||
|
|
||||||
|
foreach ($translations as $translation) {
|
||||||
|
Cache::forget("site_translation_{$translation->hash}");
|
||||||
|
}
|
||||||
|
|
||||||
|
SiteTranslation::whereIn('id', $ids)->delete();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => count($ids) . ' kayıt başarıyla silindi.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
<x-filament-panels::page>
|
||||||
|
@php
|
||||||
|
$languages = \App\Models\Language::where('is_active', true)->get();
|
||||||
|
$apiUrl = route('api.site-translations.index');
|
||||||
|
$apiStoreUrl = route('api.site-translations.store');
|
||||||
|
$apiUpdateUrl = route('api.site-translations.update', ['id' => '__ID__']);
|
||||||
|
$apiDeleteUrl = route('api.site-translations.destroy', ['id' => '__ID__']);
|
||||||
|
$apiBatchUpdateUrl = route('api.site-translations.batch-update');
|
||||||
|
$apiBatchDeleteUrl = route('api.site-translations.batch-destroy');
|
||||||
|
|
||||||
|
// CSRF token
|
||||||
|
$csrfToken = csrf_token();
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div id="site-translations-datagrid" style="margin-top: 1rem;">
|
||||||
|
<div style="padding: 20px; text-align: center; color: #666;">
|
||||||
|
<p>DataGrid yükleniyor...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#site-translations-datagrid {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 250px);
|
||||||
|
min-height: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dx-datagrid-headers {
|
||||||
|
background-color: var(--fi-primary-50, #f0f9ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dx-state-focused {
|
||||||
|
border-color: var(--fi-primary-600, #0284c7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dx-button.dx-button-success {
|
||||||
|
background-color: var(--fi-success-600, #16a34a);
|
||||||
|
border-color: var(--fi-success-600, #16a34a);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.1.5/css/dx.light.css" />
|
||||||
|
<script src="https://cdn3.devexpress.com/jslib/23.1.5/js/dx.all.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
function initializeDataGrid() {
|
||||||
|
// DOM element'inin hazır olduğunu kontrol et
|
||||||
|
const gridElement = document.getElementById('site-translations-datagrid');
|
||||||
|
if (!gridElement) {
|
||||||
|
console.error('DataGrid container bulunamadı!');
|
||||||
|
setTimeout(initializeDataGrid, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DevExtreme'in yüklendiğini kontrol et
|
||||||
|
if (typeof DevExpress === 'undefined') {
|
||||||
|
console.error('DevExtreme yüklenemedi!');
|
||||||
|
gridElement.innerHTML =
|
||||||
|
'<div style="padding: 20px; color: red;">DevExtreme kütüphanesi yüklenemedi. Lütfen sayfayı yenileyin.</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('DevExtreme yüklendi, DataGrid başlatılıyor...');
|
||||||
|
|
||||||
|
const languages = @json($languages->map(fn($lang) => ['code' => $lang->code, 'name' => $lang->name]));
|
||||||
|
const apiUrl = @json($apiUrl);
|
||||||
|
const apiStoreUrl = @json($apiStoreUrl);
|
||||||
|
const apiUpdateUrl = @json($apiUpdateUrl);
|
||||||
|
const apiDeleteUrl = @json($apiDeleteUrl);
|
||||||
|
const apiBatchUpdateUrl = @json($apiBatchUpdateUrl);
|
||||||
|
const apiBatchDeleteUrl = @json($apiBatchDeleteUrl);
|
||||||
|
const csrfToken = @json($csrfToken);
|
||||||
|
|
||||||
|
// Column definitions
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
dataField: 'key',
|
||||||
|
caption: 'Key / Orijinal Metin',
|
||||||
|
width: 300,
|
||||||
|
allowEditing: true,
|
||||||
|
editorType: 'dxTextArea',
|
||||||
|
editorOptions: {
|
||||||
|
height: 60
|
||||||
|
},
|
||||||
|
validationRules: [{ type: 'required', message: 'Key gereklidir' }]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add language columns
|
||||||
|
languages.forEach(lang => {
|
||||||
|
columns.push({
|
||||||
|
dataField: lang.code,
|
||||||
|
caption: `${lang.name} (${lang.code})`,
|
||||||
|
allowEditing: true,
|
||||||
|
width: 250,
|
||||||
|
editorType: 'dxTextArea',
|
||||||
|
editorOptions: {
|
||||||
|
height: 80,
|
||||||
|
maxHeight: 200
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
columns.push({
|
||||||
|
dataField: 'created_at',
|
||||||
|
caption: 'Oluşturulma',
|
||||||
|
dataType: 'datetime',
|
||||||
|
allowEditing: false,
|
||||||
|
width: 150,
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
|
||||||
|
columns.push({
|
||||||
|
dataField: 'updated_at',
|
||||||
|
caption: 'Güncellenme',
|
||||||
|
dataType: 'datetime',
|
||||||
|
allowEditing: false,
|
||||||
|
width: 150,
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function for API calls
|
||||||
|
function apiCall(url, method, data) {
|
||||||
|
const options = {
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
options.body = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(url, options)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('API Error:', error);
|
||||||
|
return { success: false, message: error.message };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataGrid configuration
|
||||||
|
const dataGrid = new DevExpress.data.DataSource({
|
||||||
|
load: function(loadOptions) {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
if (loadOptions.skip !== undefined && loadOptions.take) {
|
||||||
|
params.append('page', Math.floor(loadOptions.skip / loadOptions.take) + 1);
|
||||||
|
params.append('pageSize', loadOptions.take);
|
||||||
|
} else {
|
||||||
|
params.append('page', '1');
|
||||||
|
params.append('pageSize', '20');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting
|
||||||
|
if (loadOptions.sort) {
|
||||||
|
loadOptions.sort.forEach(sort => {
|
||||||
|
params.append('sort', sort.selector || sort);
|
||||||
|
params.append('order', sort.desc ? 'desc' : 'asc');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search
|
||||||
|
if (loadOptions.searchValue) {
|
||||||
|
params.append('search', loadOptions.searchValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestUrl = apiUrl + '?' + params.toString();
|
||||||
|
console.log('DataGrid loading data from:', requestUrl);
|
||||||
|
|
||||||
|
return apiCall(requestUrl, 'GET')
|
||||||
|
.then(function(response) {
|
||||||
|
console.log('DataGrid API response:', response);
|
||||||
|
if (!response || !response.data) {
|
||||||
|
console.error('Invalid API response:', response);
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
totalCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
data: response.data || [],
|
||||||
|
totalCount: response.totalCount || 0
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('DataGrid load error:', error);
|
||||||
|
DevExpress.ui.notify('Veri yüklenirken hata oluştu: ' + (error.message || 'Bilinmeyen hata'), 'error', 5000);
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
totalCount: 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const dataGridInstance = new DevExpress.ui.dxDataGrid(gridElement, {
|
||||||
|
dataSource: dataGrid,
|
||||||
|
columns: columns,
|
||||||
|
editing: {
|
||||||
|
mode: 'batch',
|
||||||
|
allowAdding: true,
|
||||||
|
allowUpdating: true,
|
||||||
|
allowDeleting: true,
|
||||||
|
useIcons: true,
|
||||||
|
texts: {
|
||||||
|
addRow: 'Yeni Ekle',
|
||||||
|
saveAllChanges: 'Tüm Değişiklikleri Kaydet',
|
||||||
|
cancelAllChanges: 'İptal',
|
||||||
|
confirmDeleteMessage: 'Bu kaydı silmek istediğinizden emin misiniz?'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchPanel: {
|
||||||
|
visible: true,
|
||||||
|
width: 300,
|
||||||
|
placeholder: 'Ara...'
|
||||||
|
},
|
||||||
|
headerFilter: {
|
||||||
|
visible: true
|
||||||
|
},
|
||||||
|
filterRow: {
|
||||||
|
visible: true
|
||||||
|
},
|
||||||
|
paging: {
|
||||||
|
pageSize: 20,
|
||||||
|
pageIndex: 0
|
||||||
|
},
|
||||||
|
pager: {
|
||||||
|
showPageSizeSelector: true,
|
||||||
|
allowedPageSizes: [10, 20, 50, 100],
|
||||||
|
showInfo: true,
|
||||||
|
showNavigationButtons: true
|
||||||
|
},
|
||||||
|
sorting: {
|
||||||
|
mode: 'multiple'
|
||||||
|
},
|
||||||
|
showBorders: true,
|
||||||
|
rowAlternationEnabled: true,
|
||||||
|
hoverStateEnabled: true,
|
||||||
|
columnAutoWidth: true,
|
||||||
|
allowColumnResizing: true,
|
||||||
|
columnResizingMode: 'widget',
|
||||||
|
wordWrapEnabled: true,
|
||||||
|
onSaving: function(e) {
|
||||||
|
e.cancel = true;
|
||||||
|
|
||||||
|
const changes = e.changes || [];
|
||||||
|
const insertPromises = [];
|
||||||
|
const updateChanges = [];
|
||||||
|
const deletePromises = [];
|
||||||
|
|
||||||
|
// Separate changes by type
|
||||||
|
changes.forEach(function(change) {
|
||||||
|
if (change.type === 'insert') {
|
||||||
|
const requestData = {
|
||||||
|
key: change.data.key || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
languages.forEach(lang => {
|
||||||
|
if (change.data[lang.code] !== undefined) {
|
||||||
|
requestData[lang.code] = change.data[lang.code] || '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
insertPromises.push(apiCall(apiStoreUrl, 'POST', requestData));
|
||||||
|
} else if (change.type === 'update') {
|
||||||
|
const oldData = change.key;
|
||||||
|
const newData = change.data;
|
||||||
|
const requestData = {
|
||||||
|
key: newData.key !== undefined ? newData.key : oldData.key
|
||||||
|
};
|
||||||
|
|
||||||
|
languages.forEach(lang => {
|
||||||
|
if (newData[lang.code] !== undefined) {
|
||||||
|
requestData[lang.code] = newData[lang.code] || '';
|
||||||
|
} else if (oldData[lang.code] !== undefined) {
|
||||||
|
requestData[lang.code] = oldData[lang.code] || '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateChanges.push({
|
||||||
|
key: oldData.id,
|
||||||
|
data: requestData
|
||||||
|
});
|
||||||
|
} else if (change.type === 'remove') {
|
||||||
|
deletePromises.push(
|
||||||
|
apiCall(apiDeleteUrl.replace('__ID__', change.key.id), 'DELETE')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle batch updates
|
||||||
|
let updatePromise = Promise.resolve({ success: true, updated: [] });
|
||||||
|
if (updateChanges.length > 0) {
|
||||||
|
updatePromise = apiCall(apiBatchUpdateUrl, 'POST', {
|
||||||
|
changes: updateChanges
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute all promises
|
||||||
|
Promise.all([
|
||||||
|
Promise.all(insertPromises),
|
||||||
|
updatePromise,
|
||||||
|
Promise.all(deletePromises)
|
||||||
|
]).then(function(results) {
|
||||||
|
const [insertResults, updateResult, deleteResults] = results;
|
||||||
|
let successCount = 0;
|
||||||
|
let errorCount = 0;
|
||||||
|
let errorMessages = [];
|
||||||
|
|
||||||
|
insertResults.forEach(function(result) {
|
||||||
|
if (result.success) {
|
||||||
|
successCount++;
|
||||||
|
} else {
|
||||||
|
errorCount++;
|
||||||
|
if (result.message) errorMessages.push(result.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updateResult.success) {
|
||||||
|
successCount += updateResult.updated ? updateResult.updated.length : 0;
|
||||||
|
if (updateResult.errors && updateResult.errors.length > 0) {
|
||||||
|
errorCount += updateResult.errors.length;
|
||||||
|
errorMessages = errorMessages.concat(updateResult.errors);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorCount++;
|
||||||
|
if (updateResult.message) errorMessages.push(updateResult.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteResults.forEach(function(result) {
|
||||||
|
if (result.success) {
|
||||||
|
successCount++;
|
||||||
|
} else {
|
||||||
|
errorCount++;
|
||||||
|
if (result.message) errorMessages.push(result.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errorCount === 0) {
|
||||||
|
DevExpress.ui.notify(
|
||||||
|
`${successCount} işlem başarıyla tamamlandı`,
|
||||||
|
'success',
|
||||||
|
3000
|
||||||
|
);
|
||||||
|
e.component.refresh();
|
||||||
|
} else {
|
||||||
|
const message = errorMessages.length > 0
|
||||||
|
? errorMessages.join('; ')
|
||||||
|
: `${errorCount} işlem başarısız oldu`;
|
||||||
|
DevExpress.ui.notify(message, 'error', 5000);
|
||||||
|
e.component.refresh();
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
DevExpress.ui.notify(
|
||||||
|
'İşlem sırasında hata oluştu: ' + (error.message || 'Bilinmeyen hata'),
|
||||||
|
'error',
|
||||||
|
5000
|
||||||
|
);
|
||||||
|
e.component.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('DataGrid başlatıldı:', dataGridInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sayfa yüklendiğinde başlat
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
setTimeout(initializeDataGrid, 500);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// DOM zaten hazır, DevExtreme yüklenene kadar bekle
|
||||||
|
setTimeout(initializeDataGrid, 500);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</x-filament-panels::page>
|
||||||
@@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Route;
|
|||||||
use App\Http\Controllers\PageController;
|
use App\Http\Controllers\PageController;
|
||||||
use App\Http\Controllers\BlogController;
|
use App\Http\Controllers\BlogController;
|
||||||
use App\Http\Controllers\TemplatePreviewController;
|
use App\Http\Controllers\TemplatePreviewController;
|
||||||
|
use App\Http\Controllers\Api\SiteTranslationController;
|
||||||
use App\Models\Page;
|
use App\Models\Page;
|
||||||
|
|
||||||
// Debug Route - Veritabanı kontrolü
|
// Debug Route - Veritabanı kontrolü
|
||||||
@@ -85,6 +86,16 @@ Route::any('/admin/template-preview', [TemplatePreviewController::class, 'previe
|
|||||||
->middleware(['auth'])
|
->middleware(['auth'])
|
||||||
->name('template.preview');
|
->name('template.preview');
|
||||||
|
|
||||||
|
// Site Translations API (DevExtreme DataGrid için)
|
||||||
|
Route::prefix('admin/api')->middleware(['auth'])->group(function () {
|
||||||
|
Route::get('/site-translations', [SiteTranslationController::class, 'index'])->name('api.site-translations.index');
|
||||||
|
Route::post('/site-translations', [SiteTranslationController::class, 'store'])->name('api.site-translations.store');
|
||||||
|
Route::put('/site-translations/{id}', [SiteTranslationController::class, 'update'])->name('api.site-translations.update');
|
||||||
|
Route::delete('/site-translations/{id}', [SiteTranslationController::class, 'destroy'])->name('api.site-translations.destroy');
|
||||||
|
Route::post('/site-translations/batch-update', [SiteTranslationController::class, 'batchUpdate'])->name('api.site-translations.batch-update');
|
||||||
|
Route::post('/site-translations/batch-destroy', [SiteTranslationController::class, 'batchDestroy'])->name('api.site-translations.batch-destroy');
|
||||||
|
});
|
||||||
|
|
||||||
// Products & Services
|
// Products & Services
|
||||||
Route::get('/urun-hizmet/{slug}', [\App\Http\Controllers\ProductController::class, 'show'])->name('products.show');
|
Route::get('/urun-hizmet/{slug}', [\App\Http\Controllers\ProductController::class, 'show'])->name('products.show');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user