erp-platform/ui/src/views/intranet/SocialWall/CreatePost.tsx

465 lines
18 KiB
TypeScript
Raw Normal View History

2025-10-18 20:04:24 +00:00
import React, { useState, useRef } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import classNames from 'classnames'
import EmojiPicker, { EmojiClickData } from 'emoji-picker-react'
import {
2025-10-20 18:38:21 +00:00
FaChartBar,
FaSmile,
FaTimes,
FaImages,
FaMapMarkerAlt
} from 'react-icons/fa'
import MediaManager from './MediaManager'
import LocationPicker from './LocationPicker'
2025-10-28 10:57:32 +00:00
import { SocialMedia } from '@/types/intranet'
2025-10-18 20:04:24 +00:00
interface CreatePostProps {
onCreatePost: (post: {
content: string
2025-10-28 10:57:32 +00:00
location?: string
2025-10-18 20:04:24 +00:00
media?: {
type: 'mixed' | 'poll'
2025-10-27 21:08:36 +00:00
mediaItems?: SocialMedia[]
2025-10-18 20:04:24 +00:00
poll?: {
question: string
options: Array<{ text: string }>
}
}
}) => void
}
const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
const [content, setContent] = useState('')
const [mediaType, setMediaType] = useState<'media' | 'poll' | null>(null)
2025-10-27 21:08:36 +00:00
const [mediaItems, setMediaItems] = useState<SocialMedia[]>([])
2025-10-28 10:57:32 +00:00
const [location, setLocation] = useState<string | null>(null)
2025-10-18 20:04:24 +00:00
const [pollQuestion, setPollQuestion] = useState('')
const [pollOptions, setPollOptions] = useState(['', ''])
const [isExpanded, setIsExpanded] = useState(false)
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
const [showMediaManager, setShowMediaManager] = useState(false)
const [showLocationPicker, setShowLocationPicker] = useState(false)
const textareaRef = useRef<HTMLTextAreaElement>(null)
const emojiPickerRef = useRef<HTMLDivElement>(null)
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (!content.trim() && mediaItems.length === 0 && !mediaType) return
let media = undefined
if (mediaType === 'media' && mediaItems.length > 0) {
media = {
type: 'mixed' as const,
mediaItems
}
} else if (mediaType === 'poll' && pollQuestion && pollOptions.filter(o => o.trim()).length >= 2) {
media = {
type: 'poll' as const,
poll: {
question: pollQuestion,
options: pollOptions.filter(o => o.trim()).map(text => ({ text }))
}
}
}
onCreatePost({
content,
media,
location: location || undefined
})
// Reset form
setContent('')
setMediaType(null)
setMediaItems([])
setLocation(null)
setPollQuestion('')
setPollOptions(['', ''])
setIsExpanded(false)
setShowEmojiPicker(false)
}
const handleEmojiClick = (emojiData: EmojiClickData) => {
const emoji = emojiData.emoji
const textarea = textareaRef.current
if (!textarea) return
const start = textarea.selectionStart
const end = textarea.selectionEnd
const text = content
const before = text.substring(0, start)
const after = text.substring(end)
setContent(before + emoji + after)
// Set cursor position after emoji
setTimeout(() => {
textarea.selectionStart = textarea.selectionEnd = start + emoji.length
textarea.focus()
}, 0)
}
const addPollOption = () => {
if (pollOptions.length < 6) {
setPollOptions([...pollOptions, ''])
}
}
const removePollOption = (index: number) => {
if (pollOptions.length > 2) {
setPollOptions(pollOptions.filter((_, i) => i !== index))
}
}
const updatePollOption = (index: number, value: string) => {
const newOptions = [...pollOptions]
newOptions[index] = value
setPollOptions(newOptions)
}
const clearMedia = () => {
setMediaType(null)
setMediaItems([])
setPollQuestion('')
setPollOptions(['', ''])
}
2025-10-27 21:08:36 +00:00
const removeMediaItem = (id: string | undefined) => {
if (!id) return
2025-10-18 20:04:24 +00:00
setMediaItems(mediaItems.filter((m) => m.id !== id))
}
// Close emoji picker when clicking outside
React.useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (emojiPickerRef.current && !emojiPickerRef.current.contains(event.target as Node)) {
setShowEmojiPicker(false)
}
}
if (showEmojiPicker) {
document.addEventListener('mousedown', handleClickOutside)
}
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [showEmojiPicker])
return (
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 mb-6">
<form onSubmit={handleSubmit}>
{/* Text Input */}
<div className="flex gap-3 mb-4">
<img
src="https://i.pravatar.cc/150?img=1"
alt="Your avatar"
className="w-12 h-12 rounded-full object-cover"
/>
<div className="flex-1">
<textarea
ref={textareaRef}
value={content}
onChange={(e) => setContent(e.target.value)}
onFocus={() => setIsExpanded(true)}
placeholder="Ne düşünüyorsunuz?"
className={classNames(
'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none transition-all',
isExpanded ? 'min-h-[120px]' : 'min-h-[48px]'
)}
rows={isExpanded ? 4 : 1}
/>
</div>
</div>
{/* Media Preview */}
<AnimatePresence>
{mediaType === 'media' && mediaItems.length > 0 && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="mb-4"
>
<div className="flex items-center justify-between mb-2">
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">
Medyalar ({mediaItems.length})
</h4>
<button
type="button"
onClick={() => {
clearMedia()
}}
className="text-sm text-red-600 hover:text-red-700 font-medium"
title="Tüm medyaları kaldır"
>
Vazgeç
</button>
</div>
<div className="grid grid-cols-4 gap-2">
{mediaItems.map((item) => (
<div key={item.id} className="relative group">
{item.type === 'image' ? (
<img
2025-10-28 10:57:32 +00:00
src={item.urls?.[0]}
2025-10-18 20:04:24 +00:00
alt="Preview"
className="w-full h-24 object-cover rounded-lg"
/>
) : (
<div className="w-full h-24 bg-gray-900 rounded-lg relative">
2025-10-28 10:57:32 +00:00
<video src={item.urls?.[0]} className="w-full h-full object-cover rounded-lg" />
2025-10-18 20:04:24 +00:00
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-10 h-10 bg-black bg-opacity-50 rounded-full flex items-center justify-center">
<div className="w-0 h-0 border-t-8 border-t-transparent border-l-12 border-l-white border-b-8 border-b-transparent ml-1"></div>
</div>
</div>
</div>
)}
<button
type="button"
onClick={() => removeMediaItem(item.id)}
className="absolute -top-2 -right-2 p-1 bg-red-600 text-white rounded-full opacity-0 group-hover:opacity-100 transition-opacity"
>
2025-10-20 18:38:21 +00:00
<FaTimes className="w-4 h-4" />
2025-10-18 20:04:24 +00:00
</button>
<div className="absolute bottom-1 left-1 px-2 py-0.5 bg-black bg-opacity-70 text-white text-xs rounded">
{item.type === 'image' ? '📷' : '🎥'}
</div>
</div>
))}
<button
type="button"
onClick={() => setShowMediaManager(true)}
className="h-24 border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg flex items-center justify-center hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors"
>
2025-10-20 18:38:21 +00:00
<FaImages className="w-6 h-6 text-gray-400" />
2025-10-18 20:04:24 +00:00
</button>
</div>
</motion.div>
)}
{location && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="mb-4"
>
<div className="flex items-center justify-between mb-2">
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">Konum</h4>
<button
type="button"
onClick={() => setLocation(null)}
className="text-sm text-red-600 hover:text-red-700 font-medium"
title="Konumu kaldır"
>
Vazgeç
</button>
</div>
<div className="p-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700">
<div className="flex items-start gap-2">
2025-10-20 18:38:21 +00:00
<FaMapMarkerAlt className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" />
2025-10-18 20:04:24 +00:00
<div className="flex-1 min-w-0">
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-1">
2025-10-28 10:57:32 +00:00
{JSON.parse(location).name}
2025-10-18 20:04:24 +00:00
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
2025-10-28 10:57:32 +00:00
{JSON.parse(location).address}
2025-10-18 20:04:24 +00:00
</p>
</div>
</div>
</div>
</motion.div>
)}
{mediaType === 'poll' && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="mb-4"
>
<div className="flex items-center justify-between mb-2">
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">Anket</h4>
<button
type="button"
onClick={() => {
clearMedia()
}}
className="text-sm text-red-600 hover:text-red-700 font-medium"
title="Anketi kaldır"
>
Vazgeç
</button>
</div>
<input
type="text"
value={pollQuestion}
onChange={(e) => setPollQuestion(e.target.value)}
placeholder="Soru"
className="w-full px-4 py-2 mb-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<div className="space-y-2">
{pollOptions.map((option, index) => (
<div key={index} className="flex gap-2">
<input
type="text"
value={option}
onChange={(e) => updatePollOption(index, e.target.value)}
placeholder={`Seçenek ${index + 1}`}
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
{pollOptions.length > 2 && (
<button
type="button"
onClick={() => removePollOption(index)}
className="p-2 text-gray-500 hover:text-red-600"
>
2025-10-20 18:38:21 +00:00
<FaTimes className="w-5 h-5" />
2025-10-18 20:04:24 +00:00
</button>
)}
</div>
))}
</div>
{pollOptions.length < 6 && (
<button
type="button"
onClick={addPollOption}
className="mt-2 text-sm text-blue-600 hover:text-blue-700 font-medium"
>
+ Seçenek Ekle
</button>
)}
</motion.div>
)}
</AnimatePresence>
{/* Actions */}
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="flex items-center justify-between pt-3 border-t border-gray-200 dark:border-gray-700"
>
<div className="flex gap-2 relative">
<button
type="button"
onClick={() => {
if (mediaType === 'media' && mediaItems.length > 0) {
// Eğer zaten medya varsa, yöneticiyi aç
setShowMediaManager(true)
} else {
// Başka bir tip seçiliyse temizle ve medya modunu aç
clearMedia()
setMediaType('media')
setShowMediaManager(true)
}
}}
className={classNames(
'p-2 rounded-full transition-colors',
mediaType === 'media'
? 'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400'
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'
)}
title={mediaType === 'media' ? 'Medyaları düzenle' : 'Medya ekle'}
>
2025-10-20 18:38:21 +00:00
<FaImages className="w-5 h-5" />
2025-10-18 20:04:24 +00:00
{mediaType === 'media' && mediaItems.length > 0 && (
<span className="absolute -top-1 -right-1 w-4 h-4 bg-blue-600 text-white text-xs rounded-full flex items-center justify-center">
{mediaItems.length}
</span>
)}
</button>
<button
type="button"
onClick={() => {
// Başka bir tip seçiliyse temizle
if (mediaType !== 'poll') {
clearMedia()
}
setMediaType(mediaType === 'poll' ? null : 'poll')
}}
className={classNames(
'p-2 rounded-full transition-colors',
mediaType === 'poll'
? 'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400'
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'
)}
title={mediaType === 'poll' ? 'Anketi kaldır' : 'Anket ekle'}
>
2025-10-20 18:38:21 +00:00
<FaChartBar className="w-5 h-5" />
2025-10-18 20:04:24 +00:00
</button>
<button
type="button"
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
className="p-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors"
title="Emoji ekle"
>
2025-10-20 18:38:21 +00:00
<FaSmile className="w-5 h-5" />
2025-10-18 20:04:24 +00:00
</button>
<button
type="button"
onClick={() => setShowLocationPicker(true)}
className={classNames(
'p-2 rounded-full transition-colors',
location
? 'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400'
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'
)}
title={location ? 'Konumu değiştir' : 'Konum ekle'}
>
2025-10-20 18:38:21 +00:00
<FaMapMarkerAlt className="w-5 h-5" />
2025-10-18 20:04:24 +00:00
</button>
{/* Emoji Picker */}
{showEmojiPicker && (
<div ref={emojiPickerRef} className="absolute bottom-12 left-0 z-50">
<EmojiPicker
onEmojiClick={handleEmojiClick}
autoFocusSearch={false}
/>
</div>
)}
</div>
<button
type="submit"
disabled={!content.trim() && mediaItems.length === 0 && !mediaType}
className="px-6 py-2 bg-blue-600 text-white font-medium rounded-full hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
>
Paylaş
</button>
</motion.div>
)}
</AnimatePresence>
</form>
{/* Media Manager Modal */}
<AnimatePresence>
{showMediaManager && (
<MediaManager
media={mediaItems}
onChange={setMediaItems}
onClose={() => setShowMediaManager(false)}
/>
)}
</AnimatePresence>
{/* Location Picker Modal */}
<AnimatePresence>
{showLocationPicker && (
<LocationPicker
onSelect={setLocation}
onClose={() => setShowLocationPicker(false)}
/>
)}
</AnimatePresence>
</div>
)
}
export default CreatePost