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:
@@ -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>
|
||||
Reference in New Issue
Block a user