erp-platform/ui/src/views/crm/components/SalesTeamCreate.tsx
2025-09-15 22:22:43 +03:00

427 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react'
import {
FaUsers,
FaArrowLeft,
FaSave,
FaTimes,
FaPlus,
FaTrash,
FaMapMarkerAlt,
} from 'react-icons/fa'
import { useNavigate } from 'react-router-dom'
import { CrmTerritory } from '../../../types/crm'
import { mockEmployees } from '../../../mocks/mockEmployees'
import { Team, TeamMember, TeamRoleEnum } from '../../../types/common'
import { Container } from '@/components/shared'
const SalesTeamCreate: React.FC = () => {
const navigate = useNavigate()
const [formData, setFormData] = useState({
teamCode: '',
name: '',
description: '',
managerId: '',
isActive: true,
})
const [members, setMembers] = useState<Partial<TeamMember>[]>([])
const [territories, setTerritories] = useState<Partial<CrmTerritory>[]>([])
const [errors, setErrors] = useState<Record<string, string>>({})
const handleInputChange = (field: string, value: string | boolean) => {
setFormData((prev) => ({ ...prev, [field]: value }))
if (errors[field]) {
setErrors((prev) => ({ ...prev, [field]: '' }))
}
}
const addMember = () => {
setMembers((prev) => [
...prev,
{
employeeId: '',
role: TeamRoleEnum.Member,
isActive: true,
},
])
}
const removeMember = (index: number) => {
setMembers((prev) => prev.filter((_, i) => i !== index))
}
const updateMember = (index: number, field: string, value: string | TeamRoleEnum | boolean) => {
setMembers((prev) =>
prev.map((member, i) => (i === index ? { ...member, [field]: value } : member)),
)
}
const addTerritory = () => {
setTerritories((prev) => [
...prev,
{
territoryCode: '',
name: '',
description: '',
region: '',
countries: [],
cities: [],
isActive: true,
},
])
}
const removeTerritory = (index: number) => {
setTerritories((prev) => prev.filter((_, i) => i !== index))
}
const updateTerritory = (index: number, field: string, value: string | string[] | boolean) => {
setTerritories((prev) =>
prev.map((territory, i) => (i === index ? { ...territory, [field]: value } : territory)),
)
}
const validateForm = () => {
const newErrors: Record<string, string> = {}
if (!formData.teamCode.trim()) {
newErrors.teamCode = 'Takım kodu zorunludur'
}
if (!formData.name.trim()) {
newErrors.name = 'Takım adı zorunludur'
}
if (!formData.managerId.trim()) {
newErrors.managerId = 'Takım lideri seçimi zorunludur'
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
const handleSave = () => {
if (!validateForm()) return
const newTeam: Partial<Team> = {
...formData,
members: members.map((member, index) => ({
...member,
id: `member-new-${index}`,
teamId: 'new-team',
joinDate: new Date(),
})) as TeamMember[],
territories: territories.map((territory, index) => ({
...territory,
id: `territory-new-${index}`,
assignedTeamId: 'new-team',
})) as CrmTerritory[],
targets: [],
creationTime: new Date(),
lastModificationTime: new Date(),
}
console.log('Creating new sales team:', newTeam)
alert('Satış ekibi başarıyla oluşturuldu!')
navigate('/admin/crm/sales-teams')
}
const handleCancel = () => {
if (confirm('Değişiklikler kaydedilmedi. Çıkmak istediğinizden emin misiniz?')) {
navigate('/admin/crm/sales-teams')
}
}
return (
<Container>
<div className="space-y-2">
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<button
onClick={() => navigate('/admin/crm/sales-teams')}
className="p-1.5 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"
>
<FaArrowLeft className="w-5 h-5" />
</button>
<div>
<h2 className="text-xl font-bold text-gray-900">Yeni Satış Ekibi</h2>
<p className="text-sm text-gray-600 mt-1">
Yeni satış ekibi oluşturun ve ekip üyelerini atayın
</p>
</div>
</div>
<div className="flex gap-2">
<button
onClick={handleCancel}
className="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors"
>
<FaTimes className="w-4 h-4" />
İptal
</button>
<button
onClick={handleSave}
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
>
<FaSave className="w-4 h-4" />
Kaydet
</button>
</div>
</div>
{/* Form */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
{/* Basic Information */}
<div className="lg:col-span-2">
<div className="bg-white rounded-lg shadow-sm border p-4">
<h3 className="text-base font-semibold text-gray-900 mb-4 flex items-center">
<FaUsers className="w-5 h-5 mr-2" />
Temel Bilgiler
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">
Takım Kodu *
</label>
<input
type="text"
value={formData.teamCode}
onChange={(e) => handleInputChange('teamCode', e.target.value)}
className={`w-full px-3 py-1.5 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
errors.teamCode ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="ST-001"
/>
{errors.teamCode && (
<p className="text-red-500 text-sm mt-1">{errors.teamCode}</p>
)}
</div>
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">
Takım Lideri *
</label>
<select
value={formData.managerId}
onChange={(e) => handleInputChange('managerId', e.target.value)}
className={`w-full px-3 py-1.5 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
errors.managerId ? 'border-red-500' : 'border-gray-300'
}`}
>
<option value="">Çalışan seçin</option>
{mockEmployees.map((employee) => (
<option key={employee.id} value={employee.id}>
{employee.fullName}
</option>
))}
</select>
{errors.managerId && (
<p className="text-red-500 text-sm mt-1">{errors.managerId}</p>
)}
</div>
</div>
<div className="mt-4">
<label className="block text-xs font-medium text-gray-700 mb-1">Takım Adı *</label>
<input
type="text"
value={formData.name}
onChange={(e) => handleInputChange('name', e.target.value)}
className={`w-full px-3 py-1.5 text-sm border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
errors.name ? 'border-red-500' : 'border-gray-300'
}`}
placeholder="Kurumsal Satış Ekibi"
/>
{errors.name && <p className="text-red-500 text-sm mt-1">{errors.name}</p>}
</div>
<div className="mt-4">
<label className="block text-xs font-medium text-gray-700 mb-1">ıklama</label>
<textarea
value={formData.description}
onChange={(e) => handleInputChange('description', e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
rows={3}
placeholder="Ekip açıklaması..."
/>
</div>
<div className="mt-4">
<label className="flex items-center">
<input
type="checkbox"
checked={formData.isActive}
onChange={(e) => handleInputChange('isActive', e.target.checked)}
className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
/>
<span className="ml-2 text-sm text-gray-700">Aktif</span>
</label>
</div>
</div>
{/* Team Members */}
<div className="bg-white rounded-lg shadow-sm border p-4 mt-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-base font-semibold text-gray-900 flex items-center">
<FaUsers className="w-5 h-5 mr-2" />
Ekip Üyeleri
</h3>
<button
onClick={addMember}
className="flex items-center gap-2 px-3 py-1.5 text-sm text-blue-600 border border-blue-200 rounded-md hover:bg-blue-50 transition-colors"
>
<FaPlus className="w-4 h-4" />
Üye Ekle
</button>
</div>
<div className="space-y-3">
{members.map((member, index) => (
<div
key={index}
className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg"
>
<div className="flex-1 grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">
Çalışan
</label>
<select
value={member.employeeId || ''}
onChange={(e) => updateMember(index, 'employeeId', e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Çalışan seçin</option>
{mockEmployees.map((employee) => (
<option key={employee.id} value={employee.id}>
{employee.fullName}
</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">Rol</label>
<select
value={member.role || TeamRoleEnum.Member}
onChange={(e) => updateMember(index, 'role', e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value={TeamRoleEnum.Member}>Üye</option>
<option value={TeamRoleEnum.Lead}>Takım Lideri</option>
<option value={TeamRoleEnum.Manager}>Yönetici</option>
</select>
</div>
</div>
<button
onClick={() => removeMember(index)}
className="p-1.5 text-red-600 hover:bg-red-50 rounded-lg transition-colors"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
))}
{members.length === 0 && (
<div className="text-center py-8 text-gray-500">
<FaUsers className="w-8 h-8 mx-auto mb-2 text-gray-400" />
<p>Henüz ekip üyesi eklenmedi</p>
<p className="text-sm">Üye eklemek için yukarıdaki butona tıklayın</p>
</div>
)}
</div>
</div>
</div>
{/* Territories */}
<div>
<div className="bg-white rounded-lg shadow-sm border p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-base font-semibold text-gray-900 flex items-center">
<FaMapMarkerAlt className="w-5 h-5 mr-2" />
Bölgeler
</h3>
<button
onClick={addTerritory}
className="flex items-center gap-2 px-3 py-1.5 text-sm text-blue-600 border border-blue-200 rounded-md hover:bg-blue-50 transition-colors"
>
<FaPlus className="w-4 h-4" />
Bölge Ekle
</button>
</div>
<div className="space-y-3">
{territories.map((territory, index) => (
<div key={index} className="p-3 border border-gray-200 rounded-lg">
<div className="flex justify-between items-start mb-3">
<h4 className="font-medium text-gray-900">Bölge {index + 1}</h4>
<button
onClick={() => removeTerritory(index)}
className="p-1.5 text-red-600 hover:bg-red-50 rounded"
>
<FaTrash className="w-4 h-4" />
</button>
</div>
<div className="space-y-2">
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">
Bölge Kodu
</label>
<input
type="text"
value={territory.territoryCode || ''}
onChange={(e) => updateTerritory(index, 'territoryCode', e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="TR-IST"
/>
</div>
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">
Bölge Adı
</label>
<input
type="text"
value={territory.name || ''}
onChange={(e) => updateTerritory(index, 'name', e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="İstanbul Anadolu"
/>
</div>
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">
Bölge
</label>
<input
type="text"
value={territory.region || ''}
onChange={(e) => updateTerritory(index, 'region', e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Marmara"
/>
</div>
</div>
</div>
))}
{territories.length === 0 && (
<div className="text-center py-8 text-gray-500">
<FaMapMarkerAlt className="w-8 h-8 mx-auto mb-2 text-gray-400" />
<p>Henüz bölge eklenmedi</p>
<p className="text-sm">Bölge eklemek için yukarıdaki butona tıklayın</p>
</div>
)}
</div>
</div>
</div>
</div>
</div>
</Container>
)
}
export default SalesTeamCreate