From 0203ffb8b3522ae70d27dd896f45eb1000764df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Tue, 28 Oct 2025 00:08:36 +0300 Subject: [PATCH] Hr SocialPost interface --- ui/src/mocks/mockIntranet.ts | 104 ++++------------- ui/src/types/intranet.ts | 105 ++++++++---------- .../views/intranet/SocialWall/CreatePost.tsx | 13 ++- .../views/intranet/SocialWall/LocationMap.tsx | 4 +- .../intranet/SocialWall/LocationPicker.tsx | 12 +- .../intranet/SocialWall/MediaLightbox.tsx | 4 +- .../intranet/SocialWall/MediaManager.tsx | 13 ++- ui/src/views/intranet/SocialWall/PostItem.tsx | 34 ++++-- ui/src/views/intranet/SocialWall/index.tsx | 32 +++--- 9 files changed, 126 insertions(+), 195 deletions(-) diff --git a/ui/src/mocks/mockIntranet.ts b/ui/src/mocks/mockIntranet.ts index d3acf368..8b47dcbc 100644 --- a/ui/src/mocks/mockIntranet.ts +++ b/ui/src/mocks/mockIntranet.ts @@ -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: { diff --git a/ui/src/types/intranet.ts b/ui/src/types/intranet.ts index 156a2102..dbbd0f42 100644 --- a/ui/src/types/intranet.ts +++ b/ui/src/types/intranet.ts @@ -232,81 +232,66 @@ 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 lat: number lng: number placeId?: string -} \ No newline at end of file +} + +// 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 +} diff --git a/ui/src/views/intranet/SocialWall/CreatePost.tsx b/ui/src/views/intranet/SocialWall/CreatePost.tsx index 0cd5eb17..404b2bec 100644 --- a/ui/src/views/intranet/SocialWall/CreatePost.tsx +++ b/ui/src/views/intranet/SocialWall/CreatePost.tsx @@ -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 = ({ onCreatePost }) => { const [content, setContent] = useState('') const [mediaType, setMediaType] = useState<'media' | 'poll' | null>(null) - const [mediaItems, setMediaItems] = useState([]) - const [location, setLocation] = useState(null) + const [mediaItems, setMediaItems] = useState([]) + const [location, setLocation] = useState(null) const [pollQuestion, setPollQuestion] = useState('') const [pollOptions, setPollOptions] = useState(['', '']) const [isExpanded, setIsExpanded] = useState(false) @@ -126,7 +126,8 @@ const CreatePost: React.FC = ({ onCreatePost }) => { setPollOptions(['', '']) } - const removeMediaItem = (id: string) => { + const removeMediaItem = (id: string | undefined) => { + if (!id) return setMediaItems(mediaItems.filter((m) => m.id !== id)) } diff --git a/ui/src/views/intranet/SocialWall/LocationMap.tsx b/ui/src/views/intranet/SocialWall/LocationMap.tsx index 3eb55f4a..7d068372 100644 --- a/ui/src/views/intranet/SocialWall/LocationMap.tsx +++ b/ui/src/views/intranet/SocialWall/LocationMap.tsx @@ -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 } diff --git a/ui/src/views/intranet/SocialWall/LocationPicker.tsx b/ui/src/views/intranet/SocialWall/LocationPicker.tsx index 9fc41933..da1ecacd 100644 --- a/ui/src/views/intranet/SocialWall/LocationPicker.tsx +++ b/ui/src/views/intranet/SocialWall/LocationPicker.tsx @@ -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 = ({ onSelect, onClose }) => { const [searchQuery, setSearchQuery] = useState('') - const [locations, setLocations] = useState([]) - const [selectedLocation, setSelectedLocation] = useState(null) + const [locations, setLocations] = useState([]) + const [selectedLocation, setSelectedLocation] = useState(null) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const [isGoogleLoaded, setIsGoogleLoaded] = useState(false) @@ -146,7 +146,7 @@ const LocationPicker: React.FC = ({ 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 = ({ onSelect, onClose }) => } }, [searchQuery, isGoogleLoaded]) - const handleSelect = (location: Location) => { + const handleSelect = (location: SocialLocation) => { setSelectedLocation(location) } diff --git a/ui/src/views/intranet/SocialWall/MediaLightbox.tsx b/ui/src/views/intranet/SocialWall/MediaLightbox.tsx index 4b9f9418..f4200271 100644 --- a/ui/src/views/intranet/SocialWall/MediaLightbox.tsx +++ b/ui/src/views/intranet/SocialWall/MediaLightbox.tsx @@ -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 } diff --git a/ui/src/views/intranet/SocialWall/MediaManager.tsx b/ui/src/views/intranet/SocialWall/MediaManager.tsx index b8baff89..dccbc132 100644 --- a/ui/src/views/intranet/SocialWall/MediaManager.tsx +++ b/ui/src/views/intranet/SocialWall/MediaManager.tsx @@ -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 = ({ 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 = ({ 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 = ({ media, onChange, onClose }) setUrlInput('') } - const removeMedia = (id: string) => { + const removeMedia = (id: string | undefined) => { + if (!id) return onChange(media.filter((m) => m.id !== id)) } diff --git a/ui/src/views/intranet/SocialWall/PostItem.tsx b/ui/src/views/intranet/SocialWall/PostItem.tsx index 249cade1..75d5df85 100644 --- a/ui/src/views/intranet/SocialWall/PostItem.tsx +++ b/ui/src/views/intranet/SocialWall/PostItem.tsx @@ -281,22 +281,34 @@ const PostItem: React.FC = ({ post, onLike, onComment, onDelete, onMouseLeave={() => setShowUserCard(false)} > {post.author.name} {showUserCard && ( - + )}

- {post.author.name} + {post.author.fullName}

- {post.author.title} • {dayjs(post.creationTime).fromNow()} + {post.author.jobPosition?.name || 'Çalışan'} • {dayjs(post.creationTime).fromNow()}

@@ -391,8 +403,8 @@ const PostItem: React.FC = ({ post, onLike, onComment, onDelete, onMouseLeave={() => setHoveredCommentAuthor(null)} > {comment.author.name} @@ -400,9 +412,9 @@ const PostItem: React.FC = ({ post, onLike, onComment, onDelete, @@ -412,7 +424,7 @@ const PostItem: React.FC = ({ post, onLike, onComment, onDelete,

- {comment.author.name} + {comment.author.fullName}

{comment.content}

diff --git a/ui/src/views/intranet/SocialWall/index.tsx b/ui/src/views/intranet/SocialWall/index.tsx index 9be056f5..91542a51 100644 --- a/ui/src/views/intranet/SocialWall/index.tsx +++ b/ui/src/views/intranet/SocialWall/index.tsx @@ -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(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() }