Hatalar düzeltildi.

This commit is contained in:
Sedat ÖZTÜRK 2025-10-31 14:56:09 +03:00
parent 8daf982119
commit c84b760a6b
7 changed files with 13 additions and 971 deletions

View file

@ -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,

View file

@ -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",

View file

@ -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({

View file

@ -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) {

View file

@ -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">
ı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">ı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

View file

@ -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">ı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

View file

@ -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">
ı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