Hatalar düzeltildi.
This commit is contained in:
parent
8daf982119
commit
c84b760a6b
7 changed files with 13 additions and 971 deletions
|
|
@ -18078,7 +18078,7 @@ public class ListFormSeeder : IDataSeedContributor, ITransientDependency
|
||||||
new EditingFormItemDto { Order = 1, DataField = "Code", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 1, DataField = "Code", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order = 2, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 2, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order = 3, DataField = "GroupName", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
new EditingFormItemDto { Order = 3, DataField = "GroupName", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||||
new EditingFormItemDto { Order = 4, DataField = "CurrencyCode", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 4, DataField = "CurrencyId", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxSelectBox },
|
||||||
new EditingFormItemDto { Order = 5, DataField = "PhoneCode", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
|
new EditingFormItemDto { Order = 5, DataField = "PhoneCode", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
|
||||||
new EditingFormItemDto { Order = 6, DataField = "TaxLabel", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 6, DataField = "TaxLabel", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order = 7, DataField = "ZipRequired", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxCheckBox },
|
new EditingFormItemDto { Order = 7, DataField = "ZipRequired", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxCheckBox },
|
||||||
|
|
@ -18230,13 +18230,20 @@ public class ListFormSeeder : IDataSeedContributor, ITransientDependency
|
||||||
ListFormCode = listFormCountry.ListFormCode,
|
ListFormCode = listFormCountry.ListFormCode,
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "CurrencyCode",
|
FieldName = "CurrencyId",
|
||||||
Width = 100,
|
Width = 100,
|
||||||
ListOrderNo = 5,
|
ListOrderNo = 5,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
IsDeleted = false,
|
IsDeleted = false,
|
||||||
AllowSearch = false,
|
AllowSearch = false,
|
||||||
|
LookupJson = JsonSerializer.Serialize(new LookupDto
|
||||||
|
{
|
||||||
|
DataSourceType = UiLookupDataSourceTypeEnum.Query,
|
||||||
|
DisplayExpr = "name",
|
||||||
|
ValueExpr = "key",
|
||||||
|
LookupQuery = LookupQueryValues.CurrencyValues,
|
||||||
|
}),
|
||||||
ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto
|
ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto
|
||||||
{
|
{
|
||||||
AllowReordering = true,
|
AllowReordering = true,
|
||||||
|
|
|
||||||
|
|
@ -503,20 +503,6 @@
|
||||||
"App.Coordinator.Tests"
|
"App.Coordinator.Tests"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"key": "admin.supplychain.materialTypes",
|
|
||||||
"path": "/admin/supplychain/materials/types",
|
|
||||||
"componentPath": "@/views/supplychain/components/MaterialTypes",
|
|
||||||
"routeType": "protected",
|
|
||||||
"authority": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "admin.supplychain.materialGroups",
|
|
||||||
"path": "/admin/supplychain/materials/groups",
|
|
||||||
"componentPath": "@/views/supplychain/components/MaterialGroups",
|
|
||||||
"routeType": "protected",
|
|
||||||
"authority": null
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "admin.supplychain.materials",
|
"key": "admin.supplychain.materials",
|
||||||
"path": "/admin/supplychain/materials",
|
"path": "/admin/supplychain/materials",
|
||||||
|
|
|
||||||
|
|
@ -34,16 +34,6 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
||||||
const [tagInput, setTagInput] = useState('')
|
const [tagInput, setTagInput] = useState('')
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
|
|
||||||
// Debug kategorileri
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('Categories received:', categories)
|
|
||||||
}, [categories])
|
|
||||||
|
|
||||||
// Debug form data değişimini takip et
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('FormData changed:', formData)
|
|
||||||
}, [formData])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (template) {
|
if (template) {
|
||||||
setFormData({
|
setFormData({
|
||||||
|
|
|
||||||
|
|
@ -8,20 +8,22 @@ import { runBranchSeed } from '@/services/branch.service'
|
||||||
function BranchSeed({
|
function BranchSeed({
|
||||||
open,
|
open,
|
||||||
onDialogClose,
|
onDialogClose,
|
||||||
|
id,
|
||||||
name,
|
name,
|
||||||
}: {
|
}: {
|
||||||
open: boolean
|
open: boolean
|
||||||
onDialogClose: () => void
|
onDialogClose: () => void
|
||||||
|
id: string
|
||||||
name: string
|
name: string
|
||||||
}) {
|
}) {
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [result, setResult] = useState<BranchSeedResultDto | null>(null)
|
const [result, setResult] = useState<BranchSeedResultDto | null>(null)
|
||||||
|
|
||||||
const handleRunSeed = async () => {
|
const handleRunSeed = async () => {
|
||||||
if (!name) return
|
if (!id) return
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
try {
|
try {
|
||||||
const data = await runBranchSeed(name)
|
const data = await runBranchSeed(id)
|
||||||
setResult(data)
|
setResult(data)
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
|
|
|
||||||
|
|
@ -1,564 +0,0 @@
|
||||||
import React, { useState, useMemo } from 'react'
|
|
||||||
import {
|
|
||||||
FaPlus,
|
|
||||||
FaEdit,
|
|
||||||
FaTrash,
|
|
||||||
FaSearch,
|
|
||||||
FaTh,
|
|
||||||
FaList,
|
|
||||||
FaSitemap,
|
|
||||||
FaChevronDown,
|
|
||||||
FaChevronRight,
|
|
||||||
FaAngleRight,
|
|
||||||
} from 'react-icons/fa'
|
|
||||||
import { mockMaterialGroups, populateParentGroups } from '../../../mocks/mockMaterialGroups'
|
|
||||||
import { MmMaterialGroup } from '../../../types/mm'
|
|
||||||
import { Container } from '@/components/shared'
|
|
||||||
|
|
||||||
interface MaterialGroupNode extends MmMaterialGroup {
|
|
||||||
children: MaterialGroupNode[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const MaterialGroups: React.FC = () => {
|
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
|
||||||
const [editingGroup, setEditingGroup] = useState<MmMaterialGroup | null>(null)
|
|
||||||
const [viewMode, setViewMode] = useState<'list' | 'card' | 'tree'>('tree')
|
|
||||||
|
|
||||||
// Mock data - gerçek uygulamada API'den gelecek
|
|
||||||
const [materialGroups, setMaterialGroups] = useState<MmMaterialGroup[]>(mockMaterialGroups)
|
|
||||||
|
|
||||||
const filteredGroups = materialGroups.filter(
|
|
||||||
(group) =>
|
|
||||||
group.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
group.code.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
(group.description && group.description.toLowerCase().includes(searchTerm.toLowerCase())),
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleAdd = () => {
|
|
||||||
setEditingGroup(null)
|
|
||||||
setIsModalOpen(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEdit = (group: MmMaterialGroup) => {
|
|
||||||
setEditingGroup(group)
|
|
||||||
setIsModalOpen(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDelete = (group: MmMaterialGroup) => {
|
|
||||||
if (window.confirm(`${group.name} grubunu silmek istediğinizden emin misiniz?`)) {
|
|
||||||
setMaterialGroups((prev) => prev.filter((g) => g.id !== group.id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSave = (formData: Partial<MmMaterialGroup>) => {
|
|
||||||
if (editingGroup) {
|
|
||||||
// Mevcut grubu güncelle
|
|
||||||
setMaterialGroups((prev) =>
|
|
||||||
prev.map((g) => (g.id === editingGroup.id ? { ...g, ...formData } : g)),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Yeni grup ekle
|
|
||||||
const newGroup: MmMaterialGroup = {
|
|
||||||
id: Date.now().toString(),
|
|
||||||
code: formData.code!,
|
|
||||||
name: formData.name!,
|
|
||||||
parentGroupId: formData.parentGroupId || undefined,
|
|
||||||
description: formData.description,
|
|
||||||
isActive: formData.isActive ?? true,
|
|
||||||
}
|
|
||||||
setMaterialGroups((prev) => [...prev, newGroup])
|
|
||||||
}
|
|
||||||
setIsModalOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ağaç yapısını oluşturmak için useMemo kullanıyoruz
|
|
||||||
const groupTree = useMemo(() => {
|
|
||||||
const buildTree = (groups: MmMaterialGroup[]): MaterialGroupNode[] => {
|
|
||||||
const groupMap = new Map<string, MaterialGroupNode>()
|
|
||||||
const roots: MaterialGroupNode[] = []
|
|
||||||
|
|
||||||
// Her grubu bir düğüme dönüştür ve haritaya ekle
|
|
||||||
groups.forEach((group) => {
|
|
||||||
groupMap.set(group.id, { ...group, children: [] })
|
|
||||||
})
|
|
||||||
|
|
||||||
// Üst-alt ilişkilerini kur
|
|
||||||
groups.forEach((group) => {
|
|
||||||
if (group.parentGroupId && groupMap.has(group.parentGroupId)) {
|
|
||||||
const parent = groupMap.get(group.parentGroupId)!
|
|
||||||
parent.children.push(groupMap.get(group.id)!)
|
|
||||||
} else {
|
|
||||||
roots.push(groupMap.get(group.id)!)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return roots
|
|
||||||
}
|
|
||||||
return buildTree(filteredGroups)
|
|
||||||
}, [filteredGroups])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
|
|
||||||
{/* Title & Description */}
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Malzeme Grupları</h2>
|
|
||||||
<p className="text-gray-600">Malzeme gruplarını yönetin</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Header Actions */}
|
|
||||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-1.5">
|
|
||||||
{/* View Mode Toggle */}
|
|
||||||
<div className="flex bg-gray-100 rounded-lg p-1">
|
|
||||||
<button
|
|
||||||
onClick={() => setViewMode('list')}
|
|
||||||
className={`p-1.5 rounded-md transition-colors ${
|
|
||||||
viewMode === 'list'
|
|
||||||
? 'bg-white text-blue-600 shadow-sm'
|
|
||||||
: 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
title="Liste Görünümü"
|
|
||||||
>
|
|
||||||
<FaList className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setViewMode('card')}
|
|
||||||
className={`p-1.5 rounded-md transition-colors ${
|
|
||||||
viewMode === 'card'
|
|
||||||
? 'bg-white text-blue-600 shadow-sm'
|
|
||||||
: 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
title="Kart Görünümü"
|
|
||||||
>
|
|
||||||
<FaTh className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setViewMode('tree')}
|
|
||||||
className={`p-1.5 rounded-md transition-colors ${
|
|
||||||
viewMode === 'tree'
|
|
||||||
? 'bg-white text-blue-600 shadow-sm'
|
|
||||||
: 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
title="Ağaç Görünümü"
|
|
||||||
>
|
|
||||||
<FaSitemap className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search Box */}
|
|
||||||
<div className="relative">
|
|
||||||
<FaSearch
|
|
||||||
size={16}
|
|
||||||
className="absolute left-2.5 top-1/2 transform -translate-y-1/2 text-gray-400"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Malzeme grubu ara..."
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
className="pl-8 pr-3 py-1 w-full sm:w-56 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Add New Group */}
|
|
||||||
<button
|
|
||||||
onClick={handleAdd}
|
|
||||||
className="flex items-center px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
|
||||||
>
|
|
||||||
<FaPlus size={14} className="mr-2" />
|
|
||||||
Yeni Grup
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{viewMode === 'list' && (
|
|
||||||
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
|
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Grup Kodu
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Grup Adı
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Üst Grup
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Açıklama
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Durum
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
İşlemler
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{filteredGroups.map((group) => (
|
|
||||||
<tr key={group.id} className="hover:bg-gray-50 text-xs">
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
|
||||||
<span className="text-sm font-mono font-medium text-gray-900">
|
|
||||||
{group.code}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
|
||||||
<div className="text-sm font-medium text-gray-900">{group.name}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5">
|
|
||||||
<div className="text-sm text-gray-900">
|
|
||||||
{group.parentGroup ? group.parentGroup.name : '-'}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5">
|
|
||||||
<div className="text-sm text-gray-900">{group.description}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded-full ${
|
|
||||||
group.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{group.isActive ? 'Aktif' : 'Pasif'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap text-right text-sm font-medium">
|
|
||||||
<div className="flex items-center justify-end space-x-2">
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(group)}
|
|
||||||
className="text-blue-600 hover:text-blue-900"
|
|
||||||
>
|
|
||||||
<FaEdit className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleDelete(group)}
|
|
||||||
className="text-red-600 hover:text-red-900"
|
|
||||||
>
|
|
||||||
<FaTrash className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{filteredGroups.length === 0 && (
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<p className="text-gray-500">Malzeme grubu bulunamadı</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{viewMode === 'card' && (
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-2">
|
|
||||||
{filteredGroups.map((group) => (
|
|
||||||
<div
|
|
||||||
key={group.id}
|
|
||||||
className="bg-white shadow-sm rounded-lg p-2.5 hover:shadow-md transition-shadow"
|
|
||||||
>
|
|
||||||
<div className="flex items-start justify-between mb-3">
|
|
||||||
<div className="flex-1">
|
|
||||||
<span className="text-xs font-mono font-medium text-blue-600 bg-blue-50 px-2 py-1 rounded">
|
|
||||||
{group.code}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-1">
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(group)}
|
|
||||||
className="text-blue-600 hover:text-blue-900 p-1"
|
|
||||||
title="Düzenle"
|
|
||||||
>
|
|
||||||
<FaEdit className="h-3 w-3" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleDelete(group)}
|
|
||||||
className="text-red-600 hover:text-red-900 p-1"
|
|
||||||
title="Sil"
|
|
||||||
>
|
|
||||||
<FaTrash className="h-3 w-3" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 className="font-medium text-gray-900 mb-1.5 text-sm">{group.name}</h3>
|
|
||||||
|
|
||||||
{group.description && (
|
|
||||||
<p className="text-sm text-gray-600 mb-3 line-clamp-2">{group.description}</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<h3 className="text-gray-900 mb-3 text-sm">
|
|
||||||
{group.parentGroupId && (
|
|
||||||
<span className="text-sm text-gray-600">
|
|
||||||
<FaAngleRight className="inline text-gray-600 mb-1" />
|
|
||||||
{group.parentGroup?.name}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded-full ${
|
|
||||||
group.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{group.isActive ? 'Aktif' : 'Pasif'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{filteredGroups.length === 0 && (
|
|
||||||
<div className="col-span-full text-center py-12">
|
|
||||||
<p className="text-gray-500">Malzeme grubu bulunamadı</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{viewMode === 'tree' && (
|
|
||||||
<div className="bg-white shadow-sm rounded-lg p-3">
|
|
||||||
{groupTree.map((node) => (
|
|
||||||
<GroupTreeNode
|
|
||||||
key={node.id}
|
|
||||||
node={node}
|
|
||||||
onEdit={handleEdit}
|
|
||||||
onDelete={handleDelete}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{groupTree.length === 0 && (
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<p className="text-gray-500">Ağaç görünümü için uygun malzeme grubu bulunamadı.</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Modal */}
|
|
||||||
{isModalOpen && (
|
|
||||||
<MaterialGroupModal
|
|
||||||
group={editingGroup}
|
|
||||||
onSave={handleSave}
|
|
||||||
onClose={() => setIsModalOpen(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ağaç Düğümü Komponenti
|
|
||||||
const GroupTreeNode: React.FC<{
|
|
||||||
node: MaterialGroupNode
|
|
||||||
level?: number
|
|
||||||
onEdit: (group: MmMaterialGroup) => void
|
|
||||||
onDelete: (group: MmMaterialGroup) => void
|
|
||||||
}> = ({ node, level = 0, onEdit, onDelete }) => {
|
|
||||||
const [isExpanded, setIsExpanded] = useState(level < 1) // İlk seviyeyi açık başlat
|
|
||||||
const hasChildren = node.children && node.children.length > 0
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
{/* Bağlantı çizgisi */}
|
|
||||||
{level > 0 && (
|
|
||||||
<span
|
|
||||||
className="absolute border-l border-gray-300"
|
|
||||||
style={{ left: `${level * 1.25 - 0.7}rem`, top: 0, height: '100%' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="flex items-center space-x-2 py-1.5 rounded-md hover:bg-gray-50"
|
|
||||||
style={{ paddingLeft: `${level * 1.25}rem` }}
|
|
||||||
>
|
|
||||||
{/* Girinti ve bağlantı çizgisi */}
|
|
||||||
{level > 0 && (
|
|
||||||
<span
|
|
||||||
className="absolute border-t border-gray-300 w-3"
|
|
||||||
style={{ left: `${level * 1.25 - 0.7}rem`, top: '1.1rem' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Genişletme ikonu */}
|
|
||||||
<div className="w-5 flex-shrink-0 text-center">
|
|
||||||
{hasChildren && (
|
|
||||||
<button
|
|
||||||
onClick={() => setIsExpanded(!isExpanded)}
|
|
||||||
className="p-1 text-gray-500 hover:text-gray-800 rounded-full hover:bg-gray-200"
|
|
||||||
>
|
|
||||||
{isExpanded ? <FaChevronDown size={10} /> : <FaChevronRight size={10} />}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Grup Bilgisi */}
|
|
||||||
<div className="flex-grow flex items-center space-x-3">
|
|
||||||
<span className="font-mono text-xs bg-blue-50 text-blue-700 px-2 py-0.5 rounded-md">
|
|
||||||
{node.code}
|
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium text-gray-800">{node.name}</span>
|
|
||||||
<span
|
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded-full ${
|
|
||||||
node.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{node.isActive ? 'Aktif' : 'Pasif'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* İşlemler */}
|
|
||||||
<div className="flex items-center space-x-2 pr-2">
|
|
||||||
<button
|
|
||||||
onClick={() => onEdit(node)}
|
|
||||||
className="text-blue-600 hover:text-blue-900"
|
|
||||||
title="Düzenle"
|
|
||||||
>
|
|
||||||
<FaEdit className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => onDelete(node)}
|
|
||||||
className="text-red-600 hover:text-red-900"
|
|
||||||
title="Sil"
|
|
||||||
>
|
|
||||||
<FaTrash className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isExpanded && hasChildren && (
|
|
||||||
<div>
|
|
||||||
{node.children.map((child) => (
|
|
||||||
<GroupTreeNode
|
|
||||||
key={child.id}
|
|
||||||
node={child}
|
|
||||||
level={level + 1}
|
|
||||||
onEdit={onEdit}
|
|
||||||
onDelete={onDelete}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MaterialGroupModalProps {
|
|
||||||
group?: MmMaterialGroup | null
|
|
||||||
onSave: (data: Partial<MmMaterialGroup>) => void
|
|
||||||
onClose: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const MaterialGroupModal: React.FC<MaterialGroupModalProps> = ({ group, onSave, onClose }) => {
|
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
code: group?.code || '',
|
|
||||||
name: group?.name || '',
|
|
||||||
parentGroupId: group?.parentGroupId || '',
|
|
||||||
description: group?.description || '',
|
|
||||||
isActive: group?.isActive ?? true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
onSave(formData)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center p-4 z-50">
|
|
||||||
<div className="bg-white rounded-lg w-full max-w-xs">
|
|
||||||
<div className="px-3 py-2 border-b border-gray-200">
|
|
||||||
<h3 className="text-sm font-medium text-gray-900">
|
|
||||||
{group ? 'Malzeme Grubunu Düzenle' : 'Yeni Malzeme Grubu'}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="p-3 space-y-2">
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">Grup Kodu</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={formData.code}
|
|
||||||
onChange={(e) => setFormData({ ...formData, code: e.target.value.toUpperCase() })}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">Grup Adı</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={formData.name}
|
|
||||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">Üst Grup</label>
|
|
||||||
<select
|
|
||||||
value={formData.parentGroupId}
|
|
||||||
onChange={(e) => setFormData({ ...formData, parentGroupId: e.target.value })}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
>
|
|
||||||
<option value="">Grup Seçiniz...</option>
|
|
||||||
{populateParentGroups(mockMaterialGroups)
|
|
||||||
.filter((g) => g.id !== group?.id) // Kendisini üst grup olarak seçemesin
|
|
||||||
.map((g) => (
|
|
||||||
<option key={g.id} value={g.id}>
|
|
||||||
{g.name} ({g.code})
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">Açıklama</label>
|
|
||||||
<textarea
|
|
||||||
value={formData.description}
|
|
||||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
rows={3}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="isActive"
|
|
||||||
checked={formData.isActive}
|
|
||||||
onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })}
|
|
||||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
|
||||||
/>
|
|
||||||
<label htmlFor="isActive" className="ml-2 text-sm text-gray-700">
|
|
||||||
Aktif
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end space-x-2 pt-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onClose}
|
|
||||||
className="px-2.5 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
|
||||||
>
|
|
||||||
İptal
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="px-2.5 py-1 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700"
|
|
||||||
>
|
|
||||||
Kaydet
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MaterialGroups
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
import { MaterialTypeEnum, MmMaterialType } from '@/types/mm'
|
|
||||||
import { getMaterialTypeText } from '@/utils/erp'
|
|
||||||
import { useState } from 'react'
|
|
||||||
|
|
||||||
interface MaterialTypeModalProps {
|
|
||||||
type?: MmMaterialType | null
|
|
||||||
onSave: (data: Partial<MmMaterialType>) => void
|
|
||||||
onClose: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const MaterialTypeModal: React.FC<MaterialTypeModalProps> = ({ type, onSave, onClose }) => {
|
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
code: type?.code || MaterialTypeEnum.RawMaterial,
|
|
||||||
name: type?.name || '',
|
|
||||||
description: type?.description || '',
|
|
||||||
isActive: type?.isActive ?? true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
onSave(formData)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
|
|
||||||
<div className="bg-white rounded-lg w-full max-w-xs">
|
|
||||||
<div className="px-3 py-2 border-b border-gray-200">
|
|
||||||
<h3 className="text-sm font-medium text-gray-900">
|
|
||||||
{type ? 'Malzeme Türünü Düzenle' : 'Yeni Malzeme Türü'}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="p-3 space-y-2">
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">Tür Kodu</label>
|
|
||||||
<select
|
|
||||||
value={formData.code}
|
|
||||||
onChange={(e) =>
|
|
||||||
setFormData({
|
|
||||||
...formData,
|
|
||||||
code: e.target.value as MaterialTypeEnum,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
{Object.values(MaterialTypeEnum).map((value) => (
|
|
||||||
<option key={value} value={value}>
|
|
||||||
{getMaterialTypeText(value)}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">Ad</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={formData.name}
|
|
||||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-700 mb-1">Açıklama</label>
|
|
||||||
<textarea
|
|
||||||
value={formData.description}
|
|
||||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
|
||||||
className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
||||||
rows={3}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="isActive"
|
|
||||||
checked={formData.isActive}
|
|
||||||
onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })}
|
|
||||||
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
|
||||||
/>
|
|
||||||
<label htmlFor="isActive" className="ml-2 text-sm text-gray-700">
|
|
||||||
Aktif
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end space-x-2 pt-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onClose}
|
|
||||||
className="px-2.5 py-1 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
|
|
||||||
>
|
|
||||||
İptal
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="px-2.5 py-1 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700"
|
|
||||||
>
|
|
||||||
Kaydet
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MaterialTypeModal
|
|
||||||
|
|
@ -1,269 +0,0 @@
|
||||||
import React, { useState } from 'react'
|
|
||||||
import { FaPlus, FaEdit, FaTrash, FaSearch, FaTh, FaList } from 'react-icons/fa'
|
|
||||||
import { MmMaterialType, MaterialTypeEnum } from '../../../types/mm'
|
|
||||||
import { mockMaterialTypes } from '../../../mocks/mockMaterialTypes'
|
|
||||||
import { getMaterialTypeText } from '../../../utils/erp'
|
|
||||||
import { Container } from '@/components/shared'
|
|
||||||
import MaterialTypeModal from './MaterialTypeModal'
|
|
||||||
|
|
||||||
const MaterialTypes: React.FC = () => {
|
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
|
||||||
const [editingType, setEditingType] = useState<MmMaterialType | null>(null)
|
|
||||||
const [viewMode, setViewMode] = useState<'list' | 'card'>('list')
|
|
||||||
|
|
||||||
// Mock data - gerçek uygulamada API'den gelecek
|
|
||||||
const [materialTypes, setMaterialTypes] = useState<MmMaterialType[]>(mockMaterialTypes)
|
|
||||||
|
|
||||||
const filteredTypes = materialTypes.filter(
|
|
||||||
(type) =>
|
|
||||||
type.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
type.description?.toLowerCase().includes(searchTerm.toLowerCase()),
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleAdd = () => {
|
|
||||||
setEditingType(null)
|
|
||||||
setIsModalOpen(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEdit = (type: MmMaterialType) => {
|
|
||||||
setEditingType(type)
|
|
||||||
setIsModalOpen(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDelete = (type: MmMaterialType) => {
|
|
||||||
if (window.confirm(`${type.name} türünü silmek istediğinizden emin misiniz?`)) {
|
|
||||||
setMaterialTypes((prev) => prev.filter((t) => t.id !== type.id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSave = (formData: Partial<MmMaterialType>) => {
|
|
||||||
if (editingType) {
|
|
||||||
// Update existing type
|
|
||||||
setMaterialTypes((prev) =>
|
|
||||||
prev.map((t) => (t.id === editingType.id ? { ...t, ...formData } : t)),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Add new type
|
|
||||||
const newType: MmMaterialType = {
|
|
||||||
id: Date.now().toString(),
|
|
||||||
code: formData.code as MaterialTypeEnum,
|
|
||||||
name: formData.name!,
|
|
||||||
description: formData.description,
|
|
||||||
isActive: formData.isActive ?? true,
|
|
||||||
className: '',
|
|
||||||
}
|
|
||||||
setMaterialTypes((prev) => [...prev, newType])
|
|
||||||
}
|
|
||||||
setIsModalOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
|
|
||||||
{/* Title & Description */}
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900">Malzeme Türleri</h2>
|
|
||||||
<p className="text-gray-600">Malzeme türlerini yönetin</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Header Actions */}
|
|
||||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-1.5">
|
|
||||||
{/* View Mode Toggle */}
|
|
||||||
<div className="flex bg-gray-100 rounded-lg p-1">
|
|
||||||
<button
|
|
||||||
onClick={() => setViewMode('list')}
|
|
||||||
className={`p-1.5 rounded-md transition-colors ${
|
|
||||||
viewMode === 'list'
|
|
||||||
? 'bg-white text-blue-600 shadow-sm'
|
|
||||||
: 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
title="Liste Görünümü"
|
|
||||||
>
|
|
||||||
<FaList className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setViewMode('card')}
|
|
||||||
className={`p-1.5 rounded-md transition-colors ${
|
|
||||||
viewMode === 'card'
|
|
||||||
? 'bg-white text-blue-600 shadow-sm'
|
|
||||||
: 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
title="Kart Görünümü"
|
|
||||||
>
|
|
||||||
<FaTh className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search Box */}
|
|
||||||
<div className="relative">
|
|
||||||
<FaSearch
|
|
||||||
size={16}
|
|
||||||
className="absolute left-2.5 top-1/2 transform -translate-y-1/2 text-gray-400"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Malzeme türü ara..."
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
className="pl-8 pr-3 py-1 w-full sm:w-56 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Add New Type */}
|
|
||||||
<button
|
|
||||||
onClick={handleAdd}
|
|
||||||
className="flex items-center px-2.5 py-1 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
|
||||||
>
|
|
||||||
<FaPlus size={14} className="mr-2" />
|
|
||||||
Yeni Tür
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Types Display */}
|
|
||||||
{viewMode === 'list' ? (
|
|
||||||
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
|
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead className="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Kod
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Ad
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Açıklama
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
Durum
|
|
||||||
</th>
|
|
||||||
<th className="px-3 py-1.5 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
İşlemler
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
|
||||||
{filteredTypes.map((type) => (
|
|
||||||
<tr key={type.id} className="hover:bg-gray-50 text-xs">
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
|
||||||
<span className="text-sm font-mono font-medium text-gray-900">
|
|
||||||
{getMaterialTypeText(type.code)}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
|
||||||
<div className="text-sm font-medium text-gray-900">{type.name}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5">
|
|
||||||
<div className="text-sm text-gray-900">{type.description}</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded-full ${
|
|
||||||
type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.isActive ? 'Aktif' : 'Pasif'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-3 py-1.5 whitespace-nowrap text-right text-sm font-medium">
|
|
||||||
<div className="flex items-center justify-end space-x-2">
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(type)}
|
|
||||||
className="text-blue-600 hover:text-blue-900"
|
|
||||||
>
|
|
||||||
<FaEdit className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleDelete(type)}
|
|
||||||
className="text-red-600 hover:text-red-900"
|
|
||||||
>
|
|
||||||
<FaTrash className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{filteredTypes.length === 0 && (
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<p className="text-gray-500">Malzeme türü bulunamadı</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-2">
|
|
||||||
{filteredTypes.map((type) => (
|
|
||||||
<div
|
|
||||||
key={type.id}
|
|
||||||
className="bg-white shadow-sm rounded-lg p-2.5 hover:shadow-md transition-shadow"
|
|
||||||
>
|
|
||||||
<div className="flex items-start justify-between mb-3">
|
|
||||||
<div className="flex-1">
|
|
||||||
<span className="text-xs font-mono font-medium text-blue-600 bg-blue-50 px-2 py-1 rounded">
|
|
||||||
{getMaterialTypeText(type.code)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-1">
|
|
||||||
<button
|
|
||||||
onClick={() => handleEdit(type)}
|
|
||||||
className="text-blue-600 hover:text-blue-900 p-1"
|
|
||||||
title="Düzenle"
|
|
||||||
>
|
|
||||||
<FaEdit className="h-3 w-3" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleDelete(type)}
|
|
||||||
className="text-red-600 hover:text-red-900 p-1"
|
|
||||||
title="Sil"
|
|
||||||
>
|
|
||||||
<FaTrash className="h-3 w-3" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 className="font-medium text-gray-900 mb-1.5 text-sm">{type.name}</h3>
|
|
||||||
|
|
||||||
{type.description && (
|
|
||||||
<p className="text-sm text-gray-600 mb-3 line-clamp-2">{type.description}</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-0.5 text-xs font-medium rounded-full ${
|
|
||||||
type.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{type.isActive ? 'Aktif' : 'Pasif'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{filteredTypes.length === 0 && (
|
|
||||||
<div className="col-span-full text-center py-12">
|
|
||||||
<p className="text-gray-500">Malzeme türü bulunamadı</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Modal */}
|
|
||||||
{isModalOpen && (
|
|
||||||
<MaterialTypeModal
|
|
||||||
type={editingType}
|
|
||||||
onSave={handleSave}
|
|
||||||
onClose={() => setIsModalOpen(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MaterialTypes
|
|
||||||
Loading…
Reference in a new issue