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

244 lines
9.7 KiB
TypeScript
Raw Normal View History

2025-10-18 20:04:24 +00:00
import React, { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { HiX, HiPlus, HiOutlineLink, HiOutlineUpload } from 'react-icons/hi'
import classNames from 'classnames'
export interface MediaItem {
id: string
type: 'image' | 'video'
url: string
file?: File
}
interface MediaManagerProps {
media: MediaItem[]
onChange: (media: MediaItem[]) => void
onClose: () => void
}
const MediaManager: React.FC<MediaManagerProps> = ({ media, onChange, onClose }) => {
const [activeTab, setActiveTab] = useState<'upload' | 'url'>('upload')
const [urlInput, setUrlInput] = useState('')
const [mediaType, setMediaType] = useState<'image' | 'video'>('image')
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files
if (!files) return
const newMedia: MediaItem[] = Array.from(files).map((file) => ({
id: Math.random().toString(36).substr(2, 9),
type: file.type.startsWith('video/') ? 'video' : 'image',
url: URL.createObjectURL(file),
file
}))
onChange([...media, ...newMedia])
e.target.value = ''
}
const handleUrlAdd = () => {
if (!urlInput.trim()) return
const newMedia: MediaItem = {
id: Math.random().toString(36).substr(2, 9),
type: mediaType,
url: urlInput
}
onChange([...media, newMedia])
setUrlInput('')
}
const removeMedia = (id: string) => {
onChange(media.filter((m) => m.id !== id))
}
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-hidden"
>
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Medya Ekle</h2>
<button
onClick={onClose}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors"
>
<HiX className="w-5 h-5 text-gray-500 dark:text-gray-400" />
</button>
</div>
{/* Tabs */}
<div className="flex border-b border-gray-200 dark:border-gray-700 px-4">
<button
onClick={() => setActiveTab('upload')}
className={classNames(
'px-4 py-3 font-medium border-b-2 transition-colors',
activeTab === 'upload'
? 'border-blue-600 text-blue-600'
: 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
)}
>
<div className="flex items-center gap-2">
<HiOutlineUpload className="w-5 h-5" />
<span>Bilgisayarımdan Seç</span>
</div>
</button>
<button
onClick={() => setActiveTab('url')}
className={classNames(
'px-4 py-3 font-medium border-b-2 transition-colors',
activeTab === 'url'
? 'border-blue-600 text-blue-600'
: 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
)}
>
<div className="flex items-center gap-2">
<HiOutlineLink className="w-5 h-5" />
<span>URL ile Ekle</span>
</div>
</button>
</div>
{/* Content */}
<div className="p-4 overflow-y-auto max-h-[calc(90vh-240px)]">
{activeTab === 'upload' ? (
<div>
<label className="block">
<div className="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-8 text-center hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors cursor-pointer">
<HiOutlineUpload className="w-12 h-12 mx-auto mb-4 text-gray-400" />
<p className="text-gray-700 dark:text-gray-300 font-medium mb-1">
Dosya seçmek için tıklayın
</p>
<p className="text-sm text-gray-500 dark:text-gray-400">
Resim veya Video (PNG, JPG, GIF, MP4, MOV)
</p>
</div>
<input
type="file"
accept="image/*,video/*"
multiple
onChange={handleFileSelect}
className="hidden"
/>
</label>
</div>
) : (
<div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Medya Tipi
</label>
<div className="flex gap-2">
<button
onClick={() => setMediaType('image')}
className={classNames(
'flex-1 py-2 px-4 rounded-lg font-medium transition-colors',
mediaType === 'image'
? 'bg-blue-600 text-white'
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
)}
>
Resim
</button>
<button
onClick={() => setMediaType('video')}
className={classNames(
'flex-1 py-2 px-4 rounded-lg font-medium transition-colors',
mediaType === 'video'
? 'bg-blue-600 text-white'
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
)}
>
Video
</button>
</div>
</div>
<div className="flex gap-2">
<input
type="url"
value={urlInput}
onChange={(e) => setUrlInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleUrlAdd()}
placeholder={`${mediaType === 'image' ? 'Resim' : 'Video'} URL'si girin`}
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"
/>
<button
onClick={handleUrlAdd}
disabled={!urlInput.trim()}
className="px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
>
Ekle
</button>
</div>
</div>
)}
{/* Media Preview */}
{media.length > 0 && (
<div className="mt-6">
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
Eklenen Medyalar ({media.length})
</h3>
<div className="grid grid-cols-4 gap-3">
{media.map((item) => (
<div key={item.id} className="relative group">
{item.type === 'image' ? (
<img
src={item.url}
alt="Media preview"
className="w-full h-24 object-cover rounded-lg"
/>
) : (
<div className="w-full h-24 bg-gray-900 rounded-lg flex items-center justify-center">
<video src={item.url} className="w-full h-full object-cover rounded-lg" />
<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
onClick={() => removeMedia(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"
>
<HiX className="w-4 h-4" />
</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>
))}
</div>
</div>
)}
</div>
{/* Footer */}
<div className="flex items-center justify-end gap-2 p-4 border-t border-gray-200 dark:border-gray-700">
<button
onClick={onClose}
className="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
>
İptal
</button>
<button
onClick={onClose}
disabled={media.length === 0}
className="px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
>
Tamam ({media.length})
</button>
</div>
</motion.div>
</div>
)
}
export default MediaManager