ActivityPanel entity düzenlemesi yapıldı

This commit is contained in:
Sedat ÖZTÜRK 2025-10-13 16:25:28 +03:00
parent d77675108e
commit f4106ab714
5 changed files with 88 additions and 86 deletions

View file

@ -20,10 +20,10 @@ export interface ActivityItem {
content: string content: string
creatorId?: string creatorId?: string
creationTime?: Date creationTime?: Date
attachedFiles?: FileData[] attachedFiles?: ActivityFile[]
} }
export interface FileData { export interface ActivityFile {
id?: string id?: string
entityName: string entityName: string
entityId: string entityId: string

View file

@ -13,17 +13,15 @@ import { Activity } from '@/proxy/formActivity/models'
interface ActivityListProps { interface ActivityListProps {
activities: Activity[] activities: Activity[]
onDeleteNote?: (noteId: string) => void onDeleteActivity?: (activityId: string) => void
onDeleteFile?: (fileId: string) => void onDeleteFile?: (fileId: string) => void
onDeleteMessage?: (messageId: string) => void
onDownloadFile?: (fileData: any) => void onDownloadFile?: (fileData: any) => void
} }
export const ActivityList: React.FC<ActivityListProps> = ({ export const ActivityList: React.FC<ActivityListProps> = ({
activities, activities,
onDeleteNote, onDeleteActivity,
onDeleteFile, onDeleteFile,
onDeleteMessage,
onDownloadFile, onDownloadFile,
}) => { }) => {
const formatDate = (date: Date) => { const formatDate = (date: Date) => {
@ -48,14 +46,7 @@ export const ActivityList: React.FC<ActivityListProps> = ({
} }
const handleDelete = (activity: Activity) => { const handleDelete = (activity: Activity) => {
switch (activity.type) { onDeleteActivity?.(activity.id)
case 'note':
onDeleteNote?.(activity.id)
break
case 'message':
onDeleteMessage?.(activity.id)
break
}
} }
const handleDownloadFile = (fileData: any) => { const handleDownloadFile = (fileData: any) => {

View file

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { Button, Input, Dialog, Select } from '@/components/ui' import { Button, Input, Dialog, Select } from '@/components/ui'
import { FaFileUpload, FaEnvelope, FaStickyNote, FaUsers, FaTimes, FaPlus, FaTrash, FaPaperclip } from 'react-icons/fa' import { FaFileUpload, FaEnvelope, FaStickyNote, FaUsers, FaTimes, FaPlus, FaTrash, FaPaperclip } from 'react-icons/fa'
import { FileData } from '@/proxy/formActivity/models' import { ActivityFile } from '@/proxy/formActivity/models'
import { getUsers } from '@/services/identity.service' import { getUsers } from '@/services/identity.service'
import { IdentityUserDto } from '@/proxy/admin/models' import { IdentityUserDto } from '@/proxy/admin/models'
@ -234,7 +234,7 @@ export const AddNoteModal: React.FC<AddNoteModalProps> = ({ isOpen, onClose, onS
interface AddFileModalProps { interface AddFileModalProps {
isOpen: boolean isOpen: boolean
onClose: () => void onClose: () => void
onUpload: (file: File) => Promise<FileData> onUpload: (file: File) => Promise<ActivityFile>
} }
export const AddFileModal: React.FC<AddFileModalProps> = ({ isOpen, onClose, onUpload }) => { export const AddFileModal: React.FC<AddFileModalProps> = ({ isOpen, onClose, onUpload }) => {

View file

@ -9,7 +9,6 @@ import {
FaPlus, FaPlus,
FaEnvelope, FaEnvelope,
FaTimes, FaTimes,
FaHistory,
FaGripVertical, FaGripVertical,
} from 'react-icons/fa' } from 'react-icons/fa'
@ -37,14 +36,11 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
const buttonRef = useRef<HTMLDivElement>(null) const buttonRef = useRef<HTMLDivElement>(null)
const { const {
notes,
messages,
activities, activities,
addContent, deleteActivity,
sendMessage,
deleteNote,
deleteFile, deleteFile,
deleteMessage, addNote,
sendMessage,
} = useFormActivity(entityName, entityId) } = useFormActivity(entityName, entityId)
const handleDownloadFile = (fileData: any) => { const handleDownloadFile = (fileData: any) => {
@ -55,7 +51,9 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
link.click() link.click()
} }
const getTotalCount = () => notes.length + messages.length const getTotalCount = () => activities.length
const getNoteCount = () => activities.filter(a => a.type === 'note').length
const getMessageCount = () => activities.filter(a => a.type === 'message').length
// Mouse drag handlers // Mouse drag handlers
const handleMouseDown = (e: React.MouseEvent) => { const handleMouseDown = (e: React.MouseEvent) => {
@ -181,19 +179,27 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{isVisible ? <FaChevronRight /> : <FaChevronLeft />} {isVisible ? <FaChevronRight /> : <FaChevronLeft />}
<FaHistory />
{getTotalCount() > 0 && <Badge content={getTotalCount()} />} {getTotalCount() > 0 && <Badge content={getTotalCount()} />}
</div> </div>
</Button> </Button>
</div> </div>
</div> </div>
{/* Overlay - Click outside to close */}
{isVisible && (
<div
className="fixed inset-0 bg-black bg-opacity-25 z-20"
onClick={onToggle}
/>
)}
{/* Activity Panel */} {/* Activity Panel */}
<div <div
className={`fixed right-0 top-0 h-full bg-white border-l border-gray-300 shadow-xl transform transition-transform duration-300 ease-in-out z-30 ${ className={`fixed right-0 top-0 h-full bg-white border-l border-gray-300 shadow-xl transform transition-transform duration-300 ease-in-out z-30 ${
isVisible ? 'translate-x-0' : 'translate-x-full' isVisible ? 'translate-x-0' : 'translate-x-full'
}`} }`}
style={{ width: '400px' }} style={{ width: '400px' }}
onClick={(e) => e.stopPropagation()}
> >
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
{/* Header */} {/* Header */}
@ -254,7 +260,7 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
: 'border-transparent text-gray-500 hover:text-gray-700' : 'border-transparent text-gray-500 hover:text-gray-700'
}`} }`}
> >
Notlar ({notes.length}) Notlar ({getNoteCount()})
</button> </button>
<button <button
@ -265,7 +271,7 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
: 'border-transparent text-gray-500 hover:text-gray-700' : 'border-transparent text-gray-500 hover:text-gray-700'
}`} }`}
> >
Mesajlar ({messages.length}) Mesajlar ({getMessageCount()})
</button> </button>
</div> </div>
</div> </div>
@ -275,9 +281,8 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
{activeTab === 'activities' && ( {activeTab === 'activities' && (
<ActivityList <ActivityList
activities={activities} activities={activities}
onDeleteNote={deleteNote} onDeleteActivity={deleteActivity}
onDeleteFile={deleteFile} onDeleteFile={deleteFile}
onDeleteMessage={deleteMessage}
onDownloadFile={handleDownloadFile} onDownloadFile={handleDownloadFile}
/> />
)} )}
@ -285,9 +290,8 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
{activeTab === 'notes' && ( {activeTab === 'notes' && (
<ActivityList <ActivityList
activities={activities.filter((a) => a.type === 'note')} activities={activities.filter((a) => a.type === 'note')}
onDeleteNote={deleteNote} onDeleteActivity={deleteActivity}
onDeleteFile={deleteFile} onDeleteFile={deleteFile}
onDeleteMessage={deleteMessage}
onDownloadFile={handleDownloadFile} onDownloadFile={handleDownloadFile}
/> />
)} )}
@ -295,9 +299,8 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
{activeTab === 'messages' && ( {activeTab === 'messages' && (
<ActivityList <ActivityList
activities={activities.filter((a) => a.type === 'message')} activities={activities.filter((a) => a.type === 'message')}
onDeleteNote={deleteNote} onDeleteActivity={deleteActivity}
onDeleteFile={deleteFile} onDeleteFile={deleteFile}
onDeleteMessage={deleteMessage}
onDownloadFile={handleDownloadFile} onDownloadFile={handleDownloadFile}
/> />
)} )}
@ -309,7 +312,7 @@ export const FormActivityPanel: React.FC<ActivityPanelProps> = ({
<AddContentModal <AddContentModal
isOpen={showAddContent} isOpen={showAddContent}
onClose={() => setShowAddContent(false)} onClose={() => setShowAddContent(false)}
onSaveContent={addContent} onSaveContent={addNote}
/> />
<SendMessageModal <SendMessageModal

View file

@ -1,12 +1,14 @@
import { ActivityItem, FileData, Activity } from '@/proxy/formActivity/models' import { ActivityItem, ActivityFile, Activity } from '@/proxy/formActivity/models'
import { useStoreState } from '@/store/store'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
const STORAGE_PREFIX = 'form_activity_' const STORAGE_PREFIX = 'form_activity_'
export const useFormActivity = (entityName: string, entityId: string) => { export const useFormActivity = (entityName: string, entityId: string) => {
const [activityItems, setActivityItems] = useState<ActivityItem[]>([]) const [activityItems, setActivityItems] = useState<ActivityItem[]>([])
const [files, setFiles] = useState<FileData[]>([]) const [files, setFiles] = useState<ActivityFile[]>([])
const [activities, setActivities] = useState<Activity[]>([]) const [activities, setActivities] = useState<Activity[]>([])
const { user } = useStoreState((state) => state.auth)
const storageKey = `${STORAGE_PREFIX}${entityName}_${entityId}` const storageKey = `${STORAGE_PREFIX}${entityName}_${entityId}`
@ -23,7 +25,7 @@ export const useFormActivity = (entityName: string, entityId: string) => {
const oldMessages = parsed.messages || [] const oldMessages = parsed.messages || []
const combinedActivityItems: ActivityItem[] = [ const combinedActivityItems: ActivityItem[] = [
...oldNotes.map((note: any) => ({ ...note, type: 'note' as const })), ...oldNotes.map((note: any) => ({ ...note, type: 'note' as const })),
...oldMessages.map((message: any) => ({ ...message, type: 'message' as const })) ...oldMessages.map((message: any) => ({ ...message, type: 'message' as const })),
] ]
setActivityItems(parsed.activityItems || combinedActivityItems || []) setActivityItems(parsed.activityItems || combinedActivityItems || [])
setFiles(parsed.files || []) setFiles(parsed.files || [])
@ -38,7 +40,7 @@ export const useFormActivity = (entityName: string, entityId: string) => {
const allActivities: Activity[] = [] const allActivities: Activity[] = []
// Convert all activity items to activities // Convert all activity items to activities
activityItems.forEach(item => { activityItems.forEach((item) => {
allActivities.push({ allActivities.push({
id: item.id || `${item.type}_${Date.now()}`, id: item.id || `${item.type}_${Date.now()}`,
type: item.type, type: item.type,
@ -47,12 +49,14 @@ export const useFormActivity = (entityName: string, entityId: string) => {
recipientUserName: item.recipientUserName, recipientUserName: item.recipientUserName,
creationTime: item.creationTime || new Date(), creationTime: item.creationTime || new Date(),
creatorId: item.creatorId || 'Bilinmeyen', creatorId: item.creatorId || 'Bilinmeyen',
data: item data: item,
}) })
}) })
// Sort by timestamp (newest first) // Sort by timestamp (newest first)
allActivities.sort((a, b) => new Date(b.creationTime).getTime() - new Date(a.creationTime).getTime()) allActivities.sort(
(a, b) => new Date(b.creationTime).getTime() - new Date(a.creationTime).getTime(),
)
setActivities(allActivities) setActivities(allActivities)
}, [activityItems, files]) }, [activityItems, files])
@ -62,27 +66,27 @@ export const useFormActivity = (entityName: string, entityId: string) => {
const dataToSave = { const dataToSave = {
activityItems, activityItems,
files files,
} }
localStorage.setItem(storageKey, JSON.stringify(dataToSave)) localStorage.setItem(storageKey, JSON.stringify(dataToSave))
}, [activityItems, files, storageKey]) }, [activityItems, files, storageKey])
const addNote = (subject: string, content: string) => { const addActivity = (subject: string, content: string, type: 'note' | 'message' = 'note') => {
const newActivityItem: ActivityItem = { const newActivityItem: ActivityItem = {
id: `note_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, id: `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
type: 'note', type,
entityName, entityName,
entityId, entityId,
subject, subject,
content, content,
creatorId: 'Mevcut Kullanıcı', // Bu gerçek implementasyonda authentication'dan gelecek creatorId: user.id,
creationTime: new Date() creationTime: new Date(),
} }
setActivityItems(prev => [...prev, newActivityItem]) setActivityItems((prev) => [...prev, newActivityItem])
return newActivityItem return newActivityItem
} }
const addContent = async (subject: string, content: string, files: File[]) => { const addNote = async (subject: string, content: string, files: File[]) => {
const timestamp = new Date() const timestamp = new Date()
const baseId = `content_${timestamp.getTime()}_${Math.random().toString(36).substr(2, 9)}` const baseId = `content_${timestamp.getTime()}_${Math.random().toString(36).substr(2, 9)}`
@ -94,19 +98,19 @@ export const useFormActivity = (entityName: string, entityId: string) => {
entityId, entityId,
subject, subject,
content: content || (files.length > 0 ? `${files.length} dosya eklendi` : ''), content: content || (files.length > 0 ? `${files.length} dosya eklendi` : ''),
creatorId: 'Mevcut Kullanıcı', creatorId: user.id,
creationTime: timestamp, creationTime: timestamp,
attachedFiles: [] // Dosyaları buraya ekleyeceğiz attachedFiles: [], // Dosyaları buraya ekleyeceğiz
} }
// Dosyaları yükle ve note'a attach et // Dosyaları yükle ve note'a attach et
const uploadedFiles: FileData[] = [] const uploadedFiles: ActivityFile[] = []
for (const file of files) { for (const file of files) {
try { try {
const uploadedFile = await new Promise<FileData>((resolve, reject) => { const uploadedFile = await new Promise<ActivityFile>((resolve, reject) => {
const reader = new FileReader() const reader = new FileReader()
reader.onload = () => { reader.onload = () => {
const newFile: FileData = { const newFile: ActivityFile = {
id: `${baseId}_file_${Math.random().toString(36).substr(2, 9)}`, id: `${baseId}_file_${Math.random().toString(36).substr(2, 9)}`,
entityName, entityName,
entityId, entityId,
@ -114,8 +118,8 @@ export const useFormActivity = (entityName: string, entityId: string) => {
fileSize: file.size, fileSize: file.size,
fileType: file.type, fileType: file.type,
filePath: `uploads/${entityName}/${entityId}/${file.name}`, filePath: `uploads/${entityName}/${entityId}/${file.name}`,
creatorId: 'Mevcut Kullanıcı', creatorId: user.id,
creationTime: timestamp creationTime: timestamp,
} }
resolve(newFile) resolve(newFile)
} }
@ -132,22 +136,22 @@ export const useFormActivity = (entityName: string, entityId: string) => {
newActivityItem.attachedFiles = uploadedFiles newActivityItem.attachedFiles = uploadedFiles
// Activity Item'ı kaydet // Activity Item'ı kaydet
setActivityItems(prev => [...prev, newActivityItem]) setActivityItems((prev) => [...prev, newActivityItem])
// Dosyaları ayrı ayrı da kaydet (eski sistem uyumluluğu için) // Dosyaları ayrı ayrı da kaydet (eski sistem uyumluluğu için)
if (uploadedFiles.length > 0) { if (uploadedFiles.length > 0) {
setFiles(prev => [...prev, ...uploadedFiles]) setFiles((prev) => [...prev, ...uploadedFiles])
} }
return newActivityItem return newActivityItem
} }
const addFile = (file: File) => { const addFile = (file: File) => {
return new Promise<FileData>((resolve, reject) => { return new Promise<ActivityFile>((resolve, reject) => {
// Simulate file upload - gerçek implementasyonda API call yapılacak // Simulate file upload - gerçek implementasyonda API call yapılacak
const reader = new FileReader() const reader = new FileReader()
reader.onload = () => { reader.onload = () => {
const newFile: FileData = { const newFile: ActivityFile = {
id: `file_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, id: `file_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
entityName, entityName,
entityId, entityId,
@ -155,10 +159,10 @@ export const useFormActivity = (entityName: string, entityId: string) => {
fileSize: file.size, fileSize: file.size,
fileType: file.type, fileType: file.type,
filePath: `uploads/${entityName}/${entityId}/${file.name}`, // Simulated path filePath: `uploads/${entityName}/${entityId}/${file.name}`, // Simulated path
creatorId: 'Mevcut Kullanıcı', creatorId: user.id,
creationTime: new Date() creationTime: new Date(),
} }
setFiles(prev => [...prev, newFile]) setFiles((prev) => [...prev, newFile])
resolve(newFile) resolve(newFile)
} }
reader.onerror = () => reject(reader.error) reader.onerror = () => reject(reader.error)
@ -166,8 +170,23 @@ export const useFormActivity = (entityName: string, entityId: string) => {
}) })
} }
const sendMessage = (recipients: Array<{ value: string, label: string, email?: string }>, subject: string, content: string) => {
const newMessageItems: ActivityItem[] = recipients.map(recipient => ({
const deleteActivity = (itemId: string) => {
setActivityItems((prev) => prev.filter((item) => item.id !== itemId))
}
const deleteFile = (fileId: string) => {
setFiles((prev) => prev.filter((file) => file.id !== fileId))
}
const sendMessage = (
recipients: Array<{ value: string; label: string; email?: string }>,
subject: string,
content: string,
) => {
// Her recipient için ayrı mesaj aktivitesi oluştur
const newMessageItems: ActivityItem[] = recipients.map((recipient) => ({
id: `message_${Date.now()}_${Math.random().toString(36).substr(2, 9)}_${recipient.value}`, id: `message_${Date.now()}_${Math.random().toString(36).substr(2, 9)}_${recipient.value}`,
type: 'message' as const, type: 'message' as const,
entityName, entityName,
@ -176,37 +195,26 @@ export const useFormActivity = (entityName: string, entityId: string) => {
recipientUserName: recipient.label, recipientUserName: recipient.label,
subject, subject,
content, content,
creatorId: 'Mevcut Kullanıcı', creatorId: user.id,
creationTime: new Date() creationTime: new Date(),
})) }))
setActivityItems(prev => [...prev, ...newMessageItems])
// Tüm mesajları activityItems'a ekle
setActivityItems((prev) => [...prev, ...newMessageItems])
return newMessageItems return newMessageItems
} }
const deleteActivityItem = (itemId: string) => {
setActivityItems(prev => prev.filter(item => item.id !== itemId))
}
const deleteFile = (fileId: string) => {
setFiles(prev => prev.filter(file => file.id !== fileId))
}
// Geriye dönük uyumluluk için
const notes = activityItems.filter(item => item.type === 'note')
const messages = activityItems.filter(item => item.type === 'message')
return { return {
activityItems, activityItems,
notes,
files, files,
messages,
activities, activities,
// Ana aktivite fonksiyonları
addActivity,
deleteActivity,
addNote, addNote,
addFile,
addContent,
sendMessage, sendMessage,
deleteNote: deleteActivityItem, // Not silme için // Dosya işlemleri
addFile,
deleteFile, deleteFile,
deleteMessage: deleteActivityItem // Mesaj silme için
} }
} }