Hr SocialPost interface

This commit is contained in:
Sedat Öztürk 2025-10-28 00:08:36 +03:00
parent 181930905e
commit 0203ffb8b3
9 changed files with 126 additions and 195 deletions

View file

@ -14,19 +14,13 @@ import {
SocialPost,
} from '@/types/intranet'
// Mevcut çalışanları kullan - "Siz" kullanıcısı mockEmployees[0] (Ali Öztürk) olacak
const currentUser = { ...mockEmployees[0], fullName: 'Siz' } // Ali Öztürk'ü "Siz" olarak kullan
export const mockSocialPosts: SocialPost[] = [
{
id: '1',
author: {
id: 'user1',
name: 'Ahmet Yılmaz',
avatar: 'https://i.pravatar.cc/150?img=12',
title: 'Yazılım Geliştirici',
email: 'ahmet.yilmaz@sozsoft.com',
phone: '+90 532 123 45 67',
department: 'Yazılım Geliştirme',
location: 'İstanbul, Türkiye',
},
author: mockEmployees[2], // Mehmet Yılmaz
content:
'Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀',
creationTime: new Date('2024-10-15T10:30:00'),
@ -45,29 +39,18 @@ export const mockSocialPosts: SocialPost[] = [
likes: {
count: 24,
isLiked: true,
users: [
{ id: 'user2', name: 'Ayşe Demir', avatar: 'https://i.pravatar.cc/150?img=5' },
{ id: 'user3', name: 'Mehmet Kaya', avatar: 'https://i.pravatar.cc/150?img=8' },
],
users: [mockEmployees[1], mockEmployees[3]], // Ayşe Kaya, Selin Demir
},
comments: [
{
id: 'c1',
author: {
id: 'user2',
name: 'Ayşe Demir',
avatar: 'https://i.pravatar.cc/150?img=5',
},
author: mockEmployees[1], // Ayşe Kaya
content: 'Harika görünüyor! Başarılar 👏',
creationTime: new Date('2024-10-15T11:00:00'),
},
{
id: 'c2',
author: {
id: 'user3',
name: 'Mehmet Kaya',
avatar: 'https://i.pravatar.cc/150?img=8',
},
author: mockEmployees[3], // Selin Demir
content: 'TypeScript gerçekten fark yaratıyor!',
creationTime: new Date('2024-10-15T11:30:00'),
},
@ -76,12 +59,7 @@ export const mockSocialPosts: SocialPost[] = [
},
{
id: '2',
author: {
id: 'currentUser',
name: 'Siz',
avatar: 'https://i.pravatar.cc/150?img=1',
title: 'Proje Yöneticisi',
},
author: currentUser,
content:
'Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!',
creationTime: new Date('2024-10-16T09:00:00'),
@ -108,11 +86,7 @@ export const mockSocialPosts: SocialPost[] = [
comments: [
{
id: 'c3',
author: {
id: 'user4',
name: 'Fatma Şahin',
avatar: 'https://i.pravatar.cc/150?img=9',
},
author: mockEmployees[4], // Ahmet Çelik
content: 'Mesajlaşma özelliğine kesinlikle ihtiyacımız var!',
creationTime: new Date('2024-10-16T10:15:00'),
},
@ -121,12 +95,7 @@ export const mockSocialPosts: SocialPost[] = [
},
{
id: '3',
author: {
id: 'user5',
name: 'Zeynep Arslan',
avatar: 'https://i.pravatar.cc/150?img=10',
title: 'UI/UX Tasarımcı',
},
author: mockEmployees[5], // Zeynep Arslan
content:
'Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨',
creationTime: new Date('2024-10-17T14:20:00'),
@ -141,26 +110,18 @@ export const mockSocialPosts: SocialPost[] = [
likes: {
count: 42,
isLiked: true,
users: [{ id: 'user1', name: 'Ahmet Yılmaz', avatar: 'https://i.pravatar.cc/150?img=12' }],
users: [mockEmployees[2]], // Mehmet Yılmaz
},
comments: [
{
id: 'c4',
author: {
id: 'user6',
name: 'Can Öztürk',
avatar: 'https://i.pravatar.cc/150?img=11',
},
author: mockEmployees[6], // Burak Koç
content: 'Tasarımlar çok şık! Renk paleti özellikle güzel 😍',
creationTime: new Date('2024-10-17T15:00:00'),
},
{
id: 'c5',
author: {
id: 'user7',
name: 'Elif Yıldız',
avatar: 'https://i.pravatar.cc/150?img=20',
},
author: mockEmployees[7], // Elif Şahin
content: 'Dark mode opsiyonu da olacak mı?',
creationTime: new Date('2024-10-17T15:30:00'),
},
@ -169,12 +130,7 @@ export const mockSocialPosts: SocialPost[] = [
},
{
id: '4',
author: {
id: 'user8',
name: 'Burak Çelik',
avatar: 'https://i.pravatar.cc/150?img=13',
title: 'DevOps Mühendisi',
},
author: mockEmployees[6], // Burak Koç
content:
'CI/CD pipeline güncellememiz tamamlandı! Deployment süremiz %40 azaldı. Otomasyonun gücü 💪',
creationTime: new Date('2024-10-18T08:45:00'),
@ -190,11 +146,7 @@ export const mockSocialPosts: SocialPost[] = [
comments: [
{
id: 'c6',
author: {
id: 'user9',
name: 'Deniz Koç',
avatar: 'https://i.pravatar.cc/150?img=14',
},
author: mockEmployees[8], // Canan Öztürk
content: 'Harika iş! Detayları paylaşabilir misin?',
creationTime: new Date('2024-10-18T09:15:00'),
},
@ -203,12 +155,7 @@ export const mockSocialPosts: SocialPost[] = [
},
{
id: '5',
author: {
id: 'user10',
name: 'Selin Aydın',
avatar: 'https://i.pravatar.cc/150?img=15',
title: 'İK Müdürü',
},
author: mockEmployees[7], // Elif Şahin
content:
'Ekip üyelerimize yeni eğitim programımızı duyurmak istiyorum! 🎓 React, TypeScript ve Modern Web Geliştirme konularında kapsamlı bir program hazırladık.',
creationTime: new Date('2024-10-14T16:00:00'),
@ -220,21 +167,13 @@ export const mockSocialPosts: SocialPost[] = [
comments: [
{
id: 'c7',
author: {
id: 'user1',
name: 'Ahmet Yılmaz',
avatar: 'https://i.pravatar.cc/150?img=12',
},
author: mockEmployees[2], // Mehmet Yılmaz
content: 'Ne zaman başlıyor?',
creationTime: new Date('2024-10-14T16:30:00'),
},
{
id: 'c8',
author: {
id: 'user10',
name: 'Selin Aydın',
avatar: 'https://i.pravatar.cc/150?img=15',
},
author: mockEmployees[7], // Elif Şahin
content: 'Gelecek hafta başlıyoruz! Kayıt linki mail ile paylaşılacak.',
creationTime: new Date('2024-10-14T17:00:00'),
},
@ -243,12 +182,7 @@ export const mockSocialPosts: SocialPost[] = [
},
{
id: '6',
author: {
id: 'user11',
name: 'Deniz Öztürk',
avatar: 'https://i.pravatar.cc/150?img=20',
title: 'Proje Yöneticisi',
},
author: mockEmployees[9], // Murat Aydın
content: 'Bugün müşteri ile harika bir toplantı yaptık! Yeni projenin detaylarını konuştuk. 🎯',
creationTime: new Date('2024-10-17T14:00:00'),
location: {

View file

@ -232,77 +232,49 @@ export interface Visitor {
photo?: string
}
// Sosyal Duvar
export interface SocialPost {
// Sosyal Duvar - Comment Interface
export interface SocialComment {
id: string
author: {
id: string
name: string
avatar: string
title: string
email?: string
phone?: string
department?: string
location?: string
}
author: HrEmployee
content: string
creationTime: Date
location?: {
id: string
name: string
address: string
lat: number
lng: number
placeId?: string
}
media?: {
type: 'image' | 'video' | 'poll'
url?: string
urls?: string[]
poll?: {
question: string
options: Array<{
id: string
text: string
votes: number
}>
totalVotes: number
endsAt: Date
userVote?: string
}
}
likes: {
count: number
isLiked: boolean
users: Array<{ id: string; name: string; avatar: string }>
}
comments: Array<{
id: string
author: {
id: string
name: string
avatar: string
}
content: string
creationTime: Date
}>
isOwnPost: boolean
}
export interface MediaItem {
// Sosyal Duvar - Likes Interface
export interface SocialLikes {
count: number
isLiked: boolean
users: HrEmployee[]
}
// Sosyal Duvar - Poll Option Interface
export interface SocialPollOption {
id: string
type: 'image' | 'video'
url: string
file?: File
text: string
votes: number
}
export interface LightboxMedia {
type: 'image' | 'video'
// Sosyal Duvar - Poll Interface
export interface SocialPoll {
question: string
options: SocialPollOption[]
totalVotes: number
endsAt: Date
userVote?: string
}
// Unified Media interface for all media-related operations
export interface SocialMedia {
id?: string
type: 'image' | 'video' | 'poll'
url?: string
urls?: string[]
file?: File
poll?: SocialPoll
}
export interface Location {
// Sosyal Duvar - Social Location
export interface SocialLocation {
id: string
name: string
address: string
@ -310,3 +282,16 @@ export interface Location {
lng: number
placeId?: string
}
// Sosyal Duvar - Ana Interface
export interface SocialPost {
id: string
author: HrEmployee
content: string
creationTime: Date
location?: SocialLocation
media?: SocialMedia
likes: SocialLikes
comments: SocialComment[]
isOwnPost: boolean
}

View file

@ -11,15 +11,15 @@ import {
} from 'react-icons/fa'
import MediaManager from './MediaManager'
import LocationPicker from './LocationPicker'
import { Location, MediaItem } from '@/types/intranet'
import { SocialLocation, SocialMedia } from '@/types/intranet'
interface CreatePostProps {
onCreatePost: (post: {
content: string
location?: Location
location?: SocialLocation
media?: {
type: 'mixed' | 'poll'
mediaItems?: MediaItem[]
mediaItems?: SocialMedia[]
poll?: {
question: string
options: Array<{ text: string }>
@ -31,8 +31,8 @@ interface CreatePostProps {
const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
const [content, setContent] = useState('')
const [mediaType, setMediaType] = useState<'media' | 'poll' | null>(null)
const [mediaItems, setMediaItems] = useState<MediaItem[]>([])
const [location, setLocation] = useState<Location | null>(null)
const [mediaItems, setMediaItems] = useState<SocialMedia[]>([])
const [location, setLocation] = useState<SocialLocation | null>(null)
const [pollQuestion, setPollQuestion] = useState('')
const [pollOptions, setPollOptions] = useState(['', ''])
const [isExpanded, setIsExpanded] = useState(false)
@ -126,7 +126,8 @@ const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
setPollOptions(['', ''])
}
const removeMediaItem = (id: string) => {
const removeMediaItem = (id: string | undefined) => {
if (!id) return
setMediaItems(mediaItems.filter((m) => m.id !== id))
}

View file

@ -1,9 +1,9 @@
import React from 'react'
import { FaExternalLinkAlt, FaMapMarkerAlt } from 'react-icons/fa'
import { Location } from '@/types/intranet'
import { SocialLocation } from '@/types/intranet'
interface LocationMapProps {
location: Location
location: SocialLocation
className?: string
showDirections?: boolean
}

View file

@ -2,10 +2,10 @@ import React, { useState, useEffect, useRef } from 'react'
import { motion } from 'framer-motion'
import { FaTimes, FaSearch, FaMapMarkerAlt } from 'react-icons/fa'
import classNames from 'classnames'
import { Location } from '@/types/intranet'
import { SocialLocation } from '@/types/intranet'
interface LocationPickerProps {
onSelect: (location: Location) => void
onSelect: (location: SocialLocation) => void
onClose: () => void
}
@ -21,8 +21,8 @@ declare global {
const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) => {
const [searchQuery, setSearchQuery] = useState('')
const [locations, setLocations] = useState<Location[]>([])
const [selectedLocation, setSelectedLocation] = useState<Location | null>(null)
const [locations, setLocations] = useState<SocialLocation[]>([])
const [selectedLocation, setSelectedLocation] = useState<SocialLocation | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [isGoogleLoaded, setIsGoogleLoaded] = useState(false)
@ -146,7 +146,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
}
// Her bir prediction için detaylı bilgi al
const detailedLocations: Location[] = []
const detailedLocations: SocialLocation[] = []
let completed = 0
predictions.forEach((prediction: any) => {
@ -193,7 +193,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
}
}, [searchQuery, isGoogleLoaded])
const handleSelect = (location: Location) => {
const handleSelect = (location: SocialLocation) => {
setSelectedLocation(location)
}

View file

@ -5,12 +5,12 @@ import Video from 'yet-another-react-lightbox/plugins/video'
import Zoom from 'yet-another-react-lightbox/plugins/zoom'
import Counter from 'yet-another-react-lightbox/plugins/counter'
import 'yet-another-react-lightbox/plugins/counter.css'
import { LightboxMedia } from '@/types/intranet'
import { SocialMedia } from '@/types/intranet'
interface MediaLightboxProps {
isOpen: boolean
onClose: () => void
media: LightboxMedia
media: SocialMedia
startIndex?: number
}

View file

@ -2,11 +2,11 @@ import React, { useState } from 'react'
import { motion } from 'framer-motion'
import { FaTimes, FaLink, FaUpload } from 'react-icons/fa'
import classNames from 'classnames'
import { MediaItem } from '@/types/intranet'
import { SocialMedia } from '@/types/intranet'
interface MediaManagerProps {
media: MediaItem[]
onChange: (media: MediaItem[]) => void
media: SocialMedia[]
onChange: (media: SocialMedia[]) => void
onClose: () => void
}
@ -19,7 +19,7 @@ const MediaManager: React.FC<MediaManagerProps> = ({ media, onChange, onClose })
const files = e.target.files
if (!files) return
const newMedia: MediaItem[] = Array.from(files).map((file) => ({
const newMedia: SocialMedia[] = Array.from(files).map((file) => ({
id: Math.random().toString(36).substr(2, 9),
type: file.type.startsWith('video/') ? 'video' : 'image',
url: URL.createObjectURL(file),
@ -33,7 +33,7 @@ const MediaManager: React.FC<MediaManagerProps> = ({ media, onChange, onClose })
const handleUrlAdd = () => {
if (!urlInput.trim()) return
const newMedia: MediaItem = {
const newMedia: SocialMedia = {
id: Math.random().toString(36).substr(2, 9),
type: mediaType,
url: urlInput
@ -43,7 +43,8 @@ const MediaManager: React.FC<MediaManagerProps> = ({ media, onChange, onClose })
setUrlInput('')
}
const removeMedia = (id: string) => {
const removeMedia = (id: string | undefined) => {
if (!id) return
onChange(media.filter((m) => m.id !== id))
}

View file

@ -281,22 +281,34 @@ const PostItem: React.FC<PostItemProps> = ({ post, onLike, onComment, onDelete,
onMouseLeave={() => setShowUserCard(false)}
>
<img
src={post.author.avatar}
alt={post.author.name}
src={post.author.avatar || 'https://i.pravatar.cc/150?img=1'}
alt={post.author.fullName}
className="w-12 h-12 rounded-full object-cover cursor-pointer ring-2 ring-transparent hover:ring-blue-500 transition-all"
/>
<AnimatePresence>
{showUserCard && (
<UserProfileCard user={post.author} position="bottom" />
<UserProfileCard
user={{
id: post.author.id,
name: post.author.fullName,
avatar: post.author.avatar || 'https://i.pravatar.cc/150?img=1',
title: post.author.jobPosition?.name || 'Çalışan',
email: post.author.email,
phone: post.author.phone,
department: post.author.department?.name,
location: post.author.workLocation
}}
position="bottom"
/>
)}
</AnimatePresence>
</div>
<div>
<h3 className="font-semibold text-gray-900 dark:text-gray-100">
{post.author.name}
{post.author.fullName}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">
{post.author.title} {dayjs(post.creationTime).fromNow()}
{post.author.jobPosition?.name || 'Çalışan'} {dayjs(post.creationTime).fromNow()}
</p>
</div>
</div>
@ -391,8 +403,8 @@ const PostItem: React.FC<PostItemProps> = ({ post, onLike, onComment, onDelete,
onMouseLeave={() => setHoveredCommentAuthor(null)}
>
<img
src={comment.author.avatar}
alt={comment.author.name}
src={comment.author.avatar || 'https://i.pravatar.cc/150?img=1'}
alt={comment.author.fullName}
className="w-8 h-8 rounded-full object-cover cursor-pointer ring-2 ring-transparent hover:ring-blue-500 transition-all"
/>
<AnimatePresence>
@ -400,9 +412,9 @@ const PostItem: React.FC<PostItemProps> = ({ post, onLike, onComment, onDelete,
<UserProfileCard
user={{
id: comment.author.id,
name: comment.author.name,
avatar: comment.author.avatar,
title: 'Çalışan' // Default title for comments
name: comment.author.fullName,
avatar: comment.author.avatar || 'https://i.pravatar.cc/150?img=1',
title: comment.author.jobPosition?.name || 'Çalışan'
}}
position="bottom"
/>
@ -412,7 +424,7 @@ const PostItem: React.FC<PostItemProps> = ({ post, onLike, onComment, onDelete,
<div className="flex-1">
<div className="bg-gray-100 dark:bg-gray-700 rounded-lg px-4 py-2">
<h4 className="font-semibold text-sm text-gray-900 dark:text-gray-100">
{comment.author.name}
{comment.author.fullName}
</h4>
<p className="text-sm text-gray-800 dark:text-gray-200">{comment.content}</p>
</div>

View file

@ -1,21 +1,26 @@
import React, { useState } from 'react'
import { AnimatePresence } from 'framer-motion'
import PostItem from './PostItem'
import { MediaItem } from './MediaManager'
import { SocialMedia } from '@/types/intranet'
import CreatePost from './CreatePost'
import { Location, SocialPost } from '@/types/intranet'
import { SocialLocation, SocialPost } from '@/types/intranet'
import { HrEmployee } from '@/types/hr'
import { mockSocialPosts } from '@/mocks/mockIntranet'
import { mockEmployees } from '@/mocks/mockEmployees'
const SocialWall: React.FC = () => {
const [posts, setPosts] = useState<SocialPost[]>(mockSocialPosts)
const [filter, setFilter] = useState<'all' | 'mine'>('all')
// Ali Öztürk'ü "Siz" kullanıcısı olarak kullan
const currentUserAuthor: HrEmployee = { ...mockEmployees[0], fullName: 'Siz' }
const handleCreatePost = (postData: {
content: string
location?: Location
location?: SocialLocation
media?: {
type: 'mixed' | 'poll'
mediaItems?: MediaItem[]
mediaItems?: SocialMedia[]
poll?: {
question: string
options: Array<{ text: string }>
@ -33,7 +38,7 @@ const SocialWall: React.FC = () => {
if (images.length > 0 && videos.length === 0) {
mediaForPost = {
type: 'image' as const,
urls: images.map(i => i.url)
urls: images.map(i => i.url).filter(url => url !== undefined) as string[]
}
} else if (videos.length > 0 && images.length === 0) {
mediaForPost = {
@ -44,7 +49,7 @@ const SocialWall: React.FC = () => {
// Mixed media - use first image for now
mediaForPost = {
type: 'image' as const,
urls: images.map(i => i.url)
urls: images.map(i => i.url).filter(url => url !== undefined) as string[]
}
}
} else if (postData.media.type === 'poll' && postData.media.poll) {
@ -66,12 +71,7 @@ const SocialWall: React.FC = () => {
const newPost: SocialPost = {
id: Date.now().toString(),
author: {
id: 'currentUser',
name: 'Siz',
avatar: 'https://i.pravatar.cc/150?img=1',
title: 'Çalışan'
},
author: currentUserAuthor,
content: postData.content,
creationTime: new Date(),
media: mediaForPost,
@ -110,13 +110,11 @@ const SocialWall: React.FC = () => {
setPosts(
posts.map((post) => {
if (post.id === postId) {
const commentAuthor = currentUserAuthor
const newComment = {
id: Date.now().toString(),
author: {
id: 'currentUser',
name: 'Siz',
avatar: 'https://i.pravatar.cc/150?img=1'
},
author: commentAuthor,
content,
creationTime: new Date()
}