Seeder ve WizardFileManager düzeltmesi

This commit is contained in:
Sedat Öztürk 2026-05-02 22:31:43 +03:00
parent d44555ad6a
commit 444261ba39
3 changed files with 147 additions and 108 deletions

View file

@ -15998,6 +15998,36 @@
"en": "Listform Wizard",
"tr": "Listform Sihirbazı"
},
{
"resourceName": "Platform",
"key": "App.Listforms.WizardFileLoadError",
"en": "Failed to load wizard files.",
"tr": "Wizard dosyaları yüklenemedi."
},
{
"resourceName": "Platform",
"key": "App.Listforms.WizardNoFiles",
"en": "No wizard file saved yet.",
"tr": "Henüz kaydedilmiş wizard dosyası yok."
},
{
"resourceName": "Platform",
"key": "App.Listforms.WizardFileDeleteConfirm",
"en": "Are you sure you want to delete this wizard file? All related database records (permission, menu, language, listform) will also be deleted. This action cannot be undone.",
"tr": "Bu wizard dosyasını silmek istediğinizden emin misiniz? ve buna ait tüm veritabanı kayıtları (izin, menü, dil, listform) silinecek. Bu işlem geri alınamaz."
},
{
"resourceName": "Platform",
"key": "App.Listforms.WizardFileDeleteError",
"en": "Failed to delete wizard file.",
"tr": "Wizard dosyası silinemedi."
},
{
"resourceName": "Platform",
"key": "App.Listforms.WizardFileDeleteSuccess",
"en": "Wizard file deleted successfully.",
"tr": "Wizard dosyası başarıyla silindi."
},
{
"resourceName": "Platform",
"key": "App.Listforms.WizardManager",

View file

@ -70,18 +70,18 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
var wizardDataPath = Path.Combine(Directory.GetCurrentDirectory(), "Seeds", "WizardData");
if (!Directory.Exists(wizardDataPath))
{
_logger.LogInformation("Seeds/WizardData dizini bulunamadı, atlanıyor.");
_logger.LogInformation("Seeds/WizardData directory not found, skipping.");
return;
}
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
if (jsonFiles.Length == 0)
{
_logger.LogInformation("Seeds/WizardData dizininde JSON dosyası bulunamadı, atlanıyor.");
_logger.LogInformation("No JSON files found in Seeds/WizardData directory, skipping.");
return;
}
_logger.LogInformation("WizardDataSeeder başladı. {Count} dosya işlenecek.", jsonFiles.Length);
_logger.LogInformation("WizardDataSeeder started. {Count} files to be processed.", jsonFiles.Length);
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
@ -94,14 +94,14 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
if (seedFile?.Wizard == null)
{
_logger.LogWarning("Geçersiz dosya atlandı: {FilePath}", filePath);
_logger.LogWarning("Invalid file skipped: {FilePath}", filePath);
continue;
}
var wizardName = seedFile.Wizard.WizardName?.Trim();
if (string.IsNullOrWhiteSpace(wizardName))
{
_logger.LogWarning("WizardName boş olduğu için atlandı: {FilePath}", filePath);
_logger.LogWarning("WizardName is empty, skipped: {FilePath}", filePath);
continue;
}
@ -110,13 +110,13 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
// Zaten seeded mi kontrol et (ListForm var mı?)
if (await _repoListForm.AnyAsync(a => a.ListFormCode == seedFile.Wizard.ListFormCode))
{
_logger.LogInformation("[{File}] '{WizardName}' zaten mevcut, atlandı.", fileName, wizardName);
_logger.LogInformation("[{File}] '{WizardName}' already exists, skipped.", fileName, wizardName);
continue;
}
_logger.LogInformation("[{File}] '{WizardName}' uygulanıyor...", fileName, wizardName);
_logger.LogInformation("[{File}] '{WizardName}' is being applied...", fileName, wizardName);
await ApplyWizardSeedAsync(seedFile);
_logger.LogInformation("[{File}] '{WizardName}' başarıyla uygulandı.", fileName, wizardName);
_logger.LogInformation("[{File}] '{WizardName}' has been successfully applied.", fileName, wizardName);
}
catch (Exception ex)
{
@ -190,23 +190,23 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
permNote = await _repoPerm.InsertAsync(new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, WizardConsts.PermNote(wizardName), permRead.Name, WizardConsts.LangKeyNote, true, MultiTenancySides.Both), autoSave: true);
// Permission Grants - Admin role için, sadece eksik olanları ekle
var existingGrants = await _permissionGrantRepository.GetListAsync("R", PlatformConsts.AbpIdentity.User.AdminRoleName);
var existingGrantNames = new HashSet<string>(existingGrants.Select(g => g.Name));
// // Permission Grants - Admin role için, sadece eksik olanları ekle
// var existingGrants = await _permissionGrantRepository.GetListAsync("R", PlatformConsts.AbpIdentity.User.AdminRoleName);
// var existingGrantNames = new HashSet<string>(existingGrants.Select(g => g.Name));
var grantsToInsert = new[]
{
permRead.Name, permCreate.Name, permUpdate.Name,
permDelete.Name, permExport.Name, permImport.Name, permNote.Name
}
.Where(name => !existingGrantNames.Contains(name))
.Select(name => new PermissionGrant(Guid.NewGuid(), name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName))
.ToList();
// var grantsToInsert = new[]
// {
// permRead.Name, permCreate.Name, permUpdate.Name,
// permDelete.Name, permExport.Name, permImport.Name, permNote.Name
// }
// .Where(name => !existingGrantNames.Contains(name))
// .Select(name => new PermissionGrant(Guid.NewGuid(), name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName))
// .ToList();
if (grantsToInsert.Count > 0)
{
await _permissionGrantRepository.InsertManyAsync(grantsToInsert, autoSave: true);
}
// if (grantsToInsert.Count > 0)
// {
// await _permissionGrantRepository.InsertManyAsync(grantsToInsert, autoSave: true);
// }
// Menu Parent
var menuQueryable = await _repoMenu.GetQueryableAsync();

View file

@ -4,7 +4,14 @@ import classNames from 'classnames'
import { Button, Input, Notification, toast } from '@/components/ui'
import Container from '@/components/shared/Container'
import { WizardFileInfoDto } from '@/proxy/admin/list-form/models'
import { FaTrash, FaSync, FaDatabase, FaPlus, FaExclamationTriangle, FaSearch } from 'react-icons/fa'
import {
FaTrash,
FaSync,
FaDatabase,
FaPlus,
FaExclamationTriangle,
FaSearch,
} from 'react-icons/fa'
import { deleteWizardFile, getWizardFiles } from '@/services/wizard.service'
import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -56,7 +63,9 @@ const WizardFileManager = () => {
setFiles(res.data ?? [])
} catch {
toast.push(
<Notification type="danger">Wizard dosyaları yüklenemedi.</Notification>,
<Notification type="danger">
{translate('::App.Listforms.WizardFileLoadError') || 'Failed to load wizard files.'}
</Notification>,
{ placement: 'top-end' },
)
} finally {
@ -76,7 +85,9 @@ const WizardFileManager = () => {
await deleteWizardFile(confirm.fileName)
toast.push(
<Notification type="success" duration={3000}>
<strong>{confirm.wizardName}</strong> silindi.
<strong>{confirm.wizardName}</strong>{' '}
{translate('::App.Listforms.WizardFileDeleteSuccess') ||
'wizard file deleted successfully.'}
</Notification>,
{ placement: 'top-end' },
)
@ -84,7 +95,8 @@ const WizardFileManager = () => {
} catch (err: any) {
toast.push(
<Notification type="danger">
Silme başarısız: {err?.message ?? 'Bilinmeyen hata'}
{translate('::App.Listforms.WizardFileDeleteError') || 'Failed to delete wizard file.'}:{' '}
{err?.message ?? 'Unknown error'}
</Notification>,
{ placement: 'top-end' },
)
@ -134,102 +146,99 @@ const WizardFileManager = () => {
className="flex items-center"
>
<FaPlus className="mr-1" />
{translate('::ListForms.ListForm.AddNewRecord') || 'Add New Record'}
{translate('::ListForms.ListForm.AddNewRecord') || 'Add New Record'}
</Button>
</div>
</div>
<div className="mt-4">
{filteredFiles.length === 0 && !loading && (
<p className="text-xs text-gray-400 text-center py-4">
{translate('::App.Listforms.WizardNoFiles') || 'No wizard files found.'}
</p>
)}
{filteredFiles.length === 0 && !loading && (
<p className="text-xs text-gray-400 text-center py-4">
Henüz kaydedilmiş wizard dosyası yok.
</p>
)}
{loading && (
<p className="text-xs text-gray-400 text-center py-4 animate-pulse">
{translate('::App.Platform.Loading') || 'Loading...'}
</p>
)}
{loading && (
<p className="text-xs text-gray-400 text-center py-4 animate-pulse">Yükleniyor...</p>
)}
<div className="space-y-2">
{filteredFiles.map((f) => (
<div
key={f.fileName}
className="flex items-center justify-between p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800"
>
<div className="flex items-center gap-3 min-w-0">
<FaDatabase className="text-indigo-400 shrink-0 text-lg" />
<div className="min-w-0">
<div className="font-medium text-sm text-gray-800 dark:text-gray-200 truncate">
{f.wizardName || f.fileName}
</div>
<div className="text-xs text-gray-400 flex gap-3 mt-0.5">
<span>{formatTimestamp(f.createdAt)}</span>
<span className="truncate">{f.listFormCode}</span>
<div className="space-y-2">
{filteredFiles.map((f) => (
<div
key={f.fileName}
className="flex items-center justify-between p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800"
>
<div className="flex items-center gap-3 min-w-0">
<FaDatabase className="text-indigo-400 shrink-0 text-lg" />
<div className="min-w-0">
<div className="font-medium text-sm text-gray-800 dark:text-gray-200 truncate">
{f.wizardName || f.fileName}
</div>
<div className="text-xs text-gray-400 flex gap-3 mt-0.5">
<span>{formatTimestamp(f.createdAt)}</span>
<span className="truncate">{f.listFormCode}</span>
</div>
</div>
</div>
</div>
<div className="flex items-center gap-2 shrink-0 ml-3">
{!f.hasInsertedRecords && (
<span
title="Bu dosyada izlenen kayıt bilgisi yok. Eski format olabilir."
className="text-yellow-500 text-xs flex items-center gap-1"
<div className="flex items-center gap-2 shrink-0 ml-3">
{!f.hasInsertedRecords && (
<span
title="Bu dosyada izlenen kayıt bilgisi yok. Eski format olabilir."
className="text-yellow-500 text-xs flex items-center gap-1"
>
<FaExclamationTriangle />
</span>
)}
<Button
size="sm"
variant="plain"
className="text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20"
type="button"
loading={deletingFile === f.fileName}
onClick={() =>
setConfirm({ fileName: f.fileName, wizardName: f.wizardName || f.fileName })
}
>
<FaExclamationTriangle />
</span>
)}
<Button
size="sm"
variant="plain"
className="text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20"
type="button"
loading={deletingFile === f.fileName}
onClick={() => setConfirm({ fileName: f.fileName, wizardName: f.wizardName || f.fileName })}
>
<FaTrash />
</Button>
</div>
</div>
))}
</div>
{/* Confirm Dialog */}
{confirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-xl p-6 max-w-sm w-full mx-4">
<div className="flex items-center gap-3 mb-4">
<FaExclamationTriangle className="text-red-500 text-xl shrink-0" />
<div>
<p className="font-semibold text-gray-800 dark:text-gray-200">Wizard Sil</p>
<p className="text-sm text-gray-500 mt-1">
<strong>{confirm.wizardName}</strong> wizard'ı ve buna ait tüm veritabanı
kayıtları (izin, menü, dil, listform) silinecek. Bu işlem geri alınamaz.
</p>
<FaTrash />
</Button>
</div>
</div>
<div className="flex justify-end gap-2 mt-4">
<Button
size="sm"
variant="plain"
type="button"
onClick={() => setConfirm(null)}
>
İptal
</Button>
<Button
size="sm"
variant="solid"
color="red"
type="button"
onClick={handleDeleteConfirm}
>
Evet, Sil
</Button>
))}
</div>
{/* Confirm Dialog */}
{confirm && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-xl p-6 max-w-sm w-full mx-4">
<div className="flex items-center gap-3 mb-4">
<FaExclamationTriangle className="text-red-500 text-xl shrink-0" />
<div>
<p className="font-semibold text-gray-800 dark:text-gray-200">{translate('::App.Platform.DeleteAction')}</p>
<p className="text-sm text-gray-500 mt-1">
{translate('::App.Listforms.WizardFileDeleteConfirm')}
</p>
</div>
</div>
<div className="flex justify-end gap-2 mt-4">
<Button size="sm" variant="plain" type="button" onClick={() => setConfirm(null)}>
İptal
</Button>
<Button
size="sm"
variant="solid"
color="red"
type="button"
onClick={handleDeleteConfirm}
>
{translate('::App.Platform.Delete') || 'Yes, Delete'}
</Button>
</div>
</div>
</div>
</div>
)}
)}
</div>
</Container>
)