erp-platform/ui/src/views/intranet/SocialWall/MediaManager.tsx
2025-10-19 23:02:09 +03:00

243 lines
9.7 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 { 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