Wizard File Manager liste düzenlendi
This commit is contained in:
parent
8dda4498ef
commit
10bf95da66
6 changed files with 104 additions and 100 deletions
|
|
@ -52,6 +52,8 @@ public class WizardFileInfoDto
|
|||
public string FileName { get; set; }
|
||||
public string WizardName { get; set; }
|
||||
public string ListFormCode { get; set; }
|
||||
public string DefaultLayout { get; set; }
|
||||
public string MenuIcon { get; set; }
|
||||
public string CreatedAt { get; set; }
|
||||
public bool HasInsertedRecords { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -407,6 +407,8 @@ public class ListFormWizardAppService(
|
|||
{
|
||||
FileName = fileName,
|
||||
WizardName = seed?.Wizard?.WizardName ?? fileName,
|
||||
DefaultLayout = seed?.Wizard?.DefaultLayout ?? string.Empty,
|
||||
MenuIcon = seed?.Wizard?.MenuIcon ?? string.Empty,
|
||||
ListFormCode = seed?.Wizard?.ListFormCode ?? string.Empty,
|
||||
CreatedAt = fileName.Length >= 12 ? fileName[..12] : fileName,
|
||||
HasInsertedRecords = seed?.InsertedRecords != null &&
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ export interface WizardFileInfoDto {
|
|||
fileName: string
|
||||
wizardName: string
|
||||
listFormCode: string
|
||||
defaultLayout: string
|
||||
menuIcon: string
|
||||
createdAt: string
|
||||
hasInsertedRecords: boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useEffect, useCallback, useMemo } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import classNames from 'classnames'
|
||||
import { Button, Input, Notification, toast } from '@/components/ui'
|
||||
import { Badge, Button, Input, Notification, toast } from '@/components/ui'
|
||||
import Container from '@/components/shared/Container'
|
||||
import ConfirmDialog from '@/components/shared/ConfirmDialog'
|
||||
import {
|
||||
|
|
@ -12,6 +12,7 @@ import {
|
|||
FaExclamationTriangle,
|
||||
FaSearch,
|
||||
FaEdit,
|
||||
FaUser,
|
||||
} from 'react-icons/fa'
|
||||
import { FcAcceptDatabase } from 'react-icons/fc'
|
||||
import { deleteWizardFile, getWizardFiles } from '@/services/wizard.service'
|
||||
|
|
@ -21,17 +22,7 @@ import { useStoreState } from '@/store/store'
|
|||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { WizardFileInfoDto } from '@/proxy/admin/wizard/models'
|
||||
import { UiEvalService } from '@/services/UiEvalService'
|
||||
|
||||
// Timestamp formatı: "202605021730" → "2026-05-02 17:30"
|
||||
const formatTimestamp = (raw: string): string => {
|
||||
if (raw.length < 12) return raw
|
||||
const y = raw.slice(0, 4)
|
||||
const mo = raw.slice(4, 6)
|
||||
const d = raw.slice(6, 8)
|
||||
const h = raw.slice(8, 10)
|
||||
const mi = raw.slice(10, 12)
|
||||
return `${y}-${mo}-${d} ${h}:${mi}`
|
||||
}
|
||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
||||
|
||||
interface ConfirmState {
|
||||
fileName: string
|
||||
|
|
@ -50,6 +41,9 @@ const WizardFileManager = () => {
|
|||
const [confirm, setConfirm] = useState<ConfirmState | null>(null)
|
||||
const [search, setSearch] = useState('')
|
||||
const [showDbMigrateDialog, setShowDbMigrateDialog] = useState(false)
|
||||
const IconComponent = (icon: string) => {
|
||||
return navigationIcon[icon] || FaDatabase // default icon
|
||||
}
|
||||
|
||||
const filteredFiles = useMemo(() => {
|
||||
const q = search.trim().toLowerCase()
|
||||
|
|
@ -179,64 +173,66 @@ const WizardFileManager = () => {
|
|||
)}
|
||||
|
||||
<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>
|
||||
{filteredFiles.map((f) => {
|
||||
const Icon = IconComponent(f.menuIcon)
|
||||
return (
|
||||
<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">
|
||||
<Icon className="text-indigo-400 shrink-0 text-3xl" />
|
||||
<div className="min-w-0">
|
||||
<div className="font-medium text-sm text-gray-800 dark:text-gray-200 truncate">
|
||||
{f.wizardName || f.fileName} <Badge content={f.defaultLayout} />
|
||||
</div>
|
||||
<div className="text-xs text-gray-400 flex gap-3 mt-0.5">
|
||||
<span className="truncate">{f.fileName} - {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-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/20"
|
||||
type="button"
|
||||
title={translate('::App.Platform.Edit') || 'Edit'}
|
||||
onClick={() =>
|
||||
navigate(ROUTES_ENUM.protected.saas.listFormManagement.wizard, {
|
||||
state: { editFileName: f.fileName },
|
||||
})
|
||||
}
|
||||
>
|
||||
<FaExclamationTriangle />
|
||||
</span>
|
||||
)}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="plain"
|
||||
className="text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/20"
|
||||
type="button"
|
||||
title={translate('::App.Platform.Edit') || 'Edit'}
|
||||
onClick={() =>
|
||||
navigate(ROUTES_ENUM.protected.saas.listFormManagement.wizard, {
|
||||
state: { editFileName: f.fileName },
|
||||
})
|
||||
}
|
||||
>
|
||||
<FaEdit />
|
||||
</Button>
|
||||
<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>
|
||||
<FaEdit />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="plain"
|
||||
className="text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20"
|
||||
type="button"
|
||||
title={translate('::App.Platform.Delete') || 'Delete'}
|
||||
loading={deletingFile === f.fileName}
|
||||
onClick={() =>
|
||||
setConfirm({ fileName: f.fileName, wizardName: f.wizardName || f.fileName })
|
||||
}
|
||||
>
|
||||
<FaTrash />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* DB Migrate Confirmation Dialog */}
|
||||
|
|
@ -261,34 +257,36 @@ const WizardFileManager = () => {
|
|||
|
||||
{/* Delete 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 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>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -882,11 +882,11 @@ GO`,
|
|||
))}
|
||||
</select>
|
||||
<Button
|
||||
size="sm"
|
||||
size="xs"
|
||||
variant="default"
|
||||
icon={<FcAcceptDatabase />}
|
||||
onClick={() => setShowDbMigrateConfirmDialog(true)}
|
||||
className="shadow-sm"
|
||||
className="shadow-sm px-2 py-1"
|
||||
title={translate('::App.DbMigrate.StartMessage') || 'Run DB Migration'}
|
||||
>
|
||||
{translate('::ListForms.ListForm.DbMigrate') || 'DB Migrate'}
|
||||
|
|
@ -895,12 +895,12 @@ GO`,
|
|||
|
||||
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||
<Button
|
||||
size="sm"
|
||||
size="xs"
|
||||
variant="default"
|
||||
icon={<FaCopy />}
|
||||
onClick={handleOpenCopyDialog}
|
||||
disabled={!state.selectedDataSource}
|
||||
className="shadow-sm"
|
||||
className="shadow-sm px-2 py-1"
|
||||
title={
|
||||
translate('::App.Platform.CopyOrExecuteSql') ||
|
||||
'Seçili nesneleri kopyala veya SQL script calistir'
|
||||
|
|
@ -909,23 +909,23 @@ GO`,
|
|||
{translate('::App.Platform.CopySelectedObjects') || 'Copy Selected Objects'}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
size="xs"
|
||||
variant="default"
|
||||
icon={<FaFileAlt />}
|
||||
onClick={handleNewQuery}
|
||||
className="shadow-sm"
|
||||
className="shadow-sm px-2 py-1"
|
||||
>
|
||||
{translate('::App.Platform.NewQuery') || 'New Query'}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
size="xs"
|
||||
variant="solid"
|
||||
color="blue-600"
|
||||
icon={<FaPlay />}
|
||||
onClick={handleExecute}
|
||||
loading={state.isExecuting}
|
||||
disabled={!state.selectedDataSource}
|
||||
className="shadow-sm"
|
||||
className="shadow-sm px-2 py-1"
|
||||
>
|
||||
{translate('::App.Platform.Execute')}
|
||||
<span className="ml-1 text-xs opacity-75">(F5)</span>
|
||||
|
|
|
|||
|
|
@ -127,19 +127,19 @@ export const MenuManager = () => {
|
|||
onClick={handleSave}
|
||||
disabled={!isDesignMode || isSaving}
|
||||
className={`
|
||||
flex items-center gap-2 px-4 py-2 rounded-lg transition-colors
|
||||
flex items-center gap-2 px-2 py-1 rounded-lg transition-colors
|
||||
${isDesignMode ? 'bg-green-600 hover:bg-green-700 text-white' : 'bg-gray-300 text-gray-500 cursor-not-allowed'}
|
||||
${isSaving ? 'opacity-50' : ''}
|
||||
`}
|
||||
>
|
||||
{isSaving ? (
|
||||
<>
|
||||
<FaSpinner size={16} className="animate-spin" />
|
||||
<FaSpinner size={10} className="animate-spin" />
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FaRegSave size={16} />
|
||||
<FaRegSave size={10} />
|
||||
Save Changes
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue