Hr Visitors Dashboard

This commit is contained in:
Sedat Öztürk 2025-10-25 22:32:31 +03:00
parent fa5ed66ae2
commit 68482e0a8d
6 changed files with 234 additions and 112 deletions

View file

@ -6,7 +6,6 @@ import {
Document, Document,
Certificate, Certificate,
ExpenseRequest, ExpenseRequest,
Task,
Training, Training,
Reservation, Reservation,
MealMenu, MealMenu,
@ -15,97 +14,6 @@ import {
SocialPost, SocialPost,
} from '@/types/intranet' } from '@/types/intranet'
export const mockExpenseRequests: ExpenseRequest[] = [
{
id: 'exp1',
employee: mockEmployees[0],
category: 'travel',
amount: 850,
currency: 'TRY',
date: new Date('2024-10-15'),
description: 'Ankara ofis ziyareti - uçak bileti',
project: 'Intranet v2',
receipts: [{ name: 'ucak_bileti.pdf', url: '#', size: '234 KB' }],
status: 'approved',
approver: mockEmployees[4],
approvalDate: new Date('2024-10-16T10:00:00'),
creationTime: new Date('2024-10-15T18:00:00'),
},
{
id: 'exp2',
employee: mockEmployees[2],
category: 'meal',
amount: 320,
currency: 'TRY',
date: new Date('2024-10-17'),
description: 'Müşteri toplantısı - öğle yemeği',
receipts: [{ name: 'restoran_fisi.jpg', url: '#', size: '156 KB' }],
status: 'pending',
creationTime: new Date('2024-10-17T20:00:00'),
},
{
id: 'exp3',
employee: mockEmployees[1],
category: 'accommodation',
amount: 1200,
currency: 'TRY',
date: new Date('2024-10-14'),
description: 'İzmir workshop - otel konaklaması (2 gece)',
project: 'UX Workshop',
receipts: [{ name: 'otel_fatura.pdf', url: '#', size: '445 KB' }],
status: 'approved',
approver: mockEmployees[4],
approvalDate: new Date('2024-10-15T09:00:00'),
creationTime: new Date('2024-10-14T22:00:00'),
},
]
export const mockTasks: Task[] = [
{
id: 'task1',
title: 'API Endpoint Geliştirme',
description: "Kullanıcı yönetimi için RESTful API endpoint'leri oluşturulacak",
project: 'Intranet v2',
assignedTo: [mockEmployees[0]],
assignedBy: mockEmployees[4],
priority: 'high',
status: 'in-progress',
dueDate: new Date('2024-10-25'),
creationTime: new Date('2024-10-14'),
labels: ['backend', 'api'],
comments: 3,
},
{
id: 'task2',
title: 'Dashboard UI Tasarımı',
description: 'Yeni dashboard için UI/UX tasarımı yapılacak',
project: 'Intranet v2',
assignedTo: [mockEmployees[1]],
assignedBy: mockEmployees[4],
priority: 'medium',
status: 'review',
dueDate: new Date('2024-10-22'),
creationTime: new Date('2024-10-10'),
labels: ['design', 'ui/ux'],
comments: 5,
},
{
id: 'task3',
title: 'Kubernetes Deployment',
description: 'Production ortamı için Kubernetes yapılandırması',
project: 'Infrastructure',
assignedTo: [mockEmployees[2]],
assignedBy: mockEmployees[4],
priority: 'urgent',
status: 'todo',
dueDate: new Date('2024-10-20'),
creationTime: new Date('2024-10-17'),
labels: ['devops', 'infrastructure'],
comments: 1,
},
]
2
export const mockDocuments: Document[] = [ export const mockDocuments: Document[] = [
{ {
id: 'doc1', id: 'doc1',
@ -979,4 +887,49 @@ export const mockVisitors: Visitor[] = [
badgeNumber: 'V-002', badgeNumber: 'V-002',
photo: 'https://i.pravatar.cc/150?img=68', photo: 'https://i.pravatar.cc/150?img=68',
}, },
]
export const mockExpenseRequests: ExpenseRequest[] = [
{
id: 'exp1',
employee: mockEmployees[0],
category: 'travel',
amount: 850,
currency: 'TRY',
date: new Date('2024-10-15'),
description: 'Ankara ofis ziyareti - uçak bileti',
project: 'Intranet v2',
receipts: [{ name: 'ucak_bileti.pdf', url: '#', size: '234 KB' }],
status: 'approved',
approver: mockEmployees[4],
approvalDate: new Date('2024-10-16T10:00:00'),
creationTime: new Date('2024-10-15T18:00:00'),
},
{
id: 'exp2',
employee: mockEmployees[2],
category: 'meal',
amount: 320,
currency: 'TRY',
date: new Date('2024-10-17'),
description: 'Müşteri toplantısı - öğle yemeği',
receipts: [{ name: 'restoran_fisi.jpg', url: '#', size: '156 KB' }],
status: 'pending',
creationTime: new Date('2024-10-17T20:00:00'),
},
{
id: 'exp3',
employee: mockEmployees[1],
category: 'accommodation',
amount: 1200,
currency: 'TRY',
date: new Date('2024-10-14'),
description: 'İzmir workshop - otel konaklaması (2 gece)',
project: 'UX Workshop',
receipts: [{ name: 'otel_fatura.pdf', url: '#', size: '445 KB' }],
status: 'approved',
approver: mockEmployees[4],
approvalDate: new Date('2024-10-15T09:00:00'),
creationTime: new Date('2024-10-14T22:00:00'),
},
] ]

View file

@ -82,4 +82,56 @@ export const mockProjectTasks: PsProjectTask[] = [
creationTime: new Date("2024-01-15"), creationTime: new Date("2024-01-15"),
lastModificationTime: new Date("2024-02-08"), lastModificationTime: new Date("2024-02-08"),
}, },
{
id: "4",
projectId: "1",
phaseId: "1",
phase: mockProjectPhases.find((phase) => phase.id === "1"),
taskCode: "TSK-004",
name: "Güvenlik Açığı Düzeltme",
description: "Kritik güvenlik açığının acil olarak düzeltilmesi gerekiyor",
taskType: TaskTypeEnum.Development,
status: TaskStatusEnum.InProgress,
priority: PriorityEnum.Urgent,
assignedTo: "1",
assignee: mockEmployees.find((emp) => emp.id === "1"),
startDate: new Date("2024-10-20"),
endDate: new Date("2024-10-25"),
actualStartDate: new Date("2024-10-20"),
actualEndDate: undefined,
estimatedHours: 20,
actualHours: 8,
progress: 40,
activities: [],
comments: [],
isActive: true,
creationTime: new Date("2024-10-20"),
lastModificationTime: new Date("2024-10-20"),
},
{
id: "5",
projectId: "2",
phaseId: "2",
phase: mockProjectPhases.find((phase) => phase.id === "2"),
taskCode: "TSK-005",
name: "Performans Optimizasyonu",
description: "Uygulama performansının iyileştirilmesi",
taskType: TaskTypeEnum.Development,
status: TaskStatusEnum.NotStarted,
priority: PriorityEnum.High,
assignedTo: "2",
assignee: mockEmployees.find((emp) => emp.id === "2"),
startDate: new Date("2024-10-28"),
endDate: new Date("2024-11-15"),
actualStartDate: undefined,
actualEndDate: undefined,
estimatedHours: 100,
actualHours: 0,
progress: 0,
activities: [],
comments: [],
isActive: true,
creationTime: new Date("2024-10-15"),
lastModificationTime: new Date("2024-10-15"),
},
]; ];

View file

@ -19,6 +19,7 @@ import ExpenseManagement from './widgets/ExpenseManagement'
import UpcomingTrainings from './widgets/UpcomingTrainings' import UpcomingTrainings from './widgets/UpcomingTrainings'
import ActiveReservations from './widgets/ActiveReservations' import ActiveReservations from './widgets/ActiveReservations'
import ActiveSurveys from './widgets/ActiveSurveys' import ActiveSurveys from './widgets/ActiveSurveys'
import Visitors from './widgets/Visitors'
// Modals // Modals
import SurveyModal from './modals/SurveyModal' import SurveyModal from './modals/SurveyModal'
@ -92,6 +93,7 @@ const IntranetDashboard: React.FC = () => {
<UpcomingTrainings /> <UpcomingTrainings />
<ActiveReservations onNewReservation={() => setShowReservationModal(true)} /> <ActiveReservations onNewReservation={() => setShowReservationModal(true)} />
<ActiveSurveys onTakeSurvey={handleTakeSurvey} /> <ActiveSurveys onTakeSurvey={handleTakeSurvey} />
<Visitors />
</div> </div>
<div className="lg:col-span-5 space-y-6"> <div className="lg:col-span-5 space-y-6">

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { FaTimes } from 'react-icons/fa' import { FaTimes } from 'react-icons/fa'
import { Survey } from '../../../mocks/mockIntranet' import { Survey } from '@/types/intranet'
interface SurveyModalProps { interface SurveyModalProps {
survey: Survey survey: Survey

View file

@ -1,19 +1,21 @@
import React from 'react' import React from 'react'
import { FaChartBar, FaClock, FaUsers } from 'react-icons/fa' import { FaChartBar, FaClock, FaUser } from 'react-icons/fa'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { mockTasks } from '../../../mocks/mockIntranet' import { mockProjectTasks } from '../../../mocks/mockProjectTasks'
import { PriorityEnum } from '../../../types/common'
import { TaskStatusEnum } from '../../../types/ps'
const PriorityTasks: React.FC = () => { const PriorityTasks: React.FC = () => {
const priorityTasks = mockTasks const priorityTasks = mockProjectTasks
.filter((t) => t.priority === 'high' || t.priority === 'urgent') .filter((t) => t.priority === PriorityEnum.High || t.priority === PriorityEnum.Urgent)
.slice(0, 3) .slice(0, 3)
const getPriorityColor = (priority: string) => { const getPriorityColor = (priority: PriorityEnum) => {
const colors: Record<string, string> = { const colors: Record<PriorityEnum, string> = {
low: 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300', [PriorityEnum.Low]: 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300',
medium: 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-300', [PriorityEnum.Normal]: 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-300',
high: 'bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-300', [PriorityEnum.High]: 'bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-300',
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300', [PriorityEnum.Urgent]: 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300',
} }
return colors[priority] return colors[priority]
} }
@ -33,21 +35,21 @@ const PriorityTasks: React.FC = () => {
<input <input
type="checkbox" type="checkbox"
className="mt-1 rounded border-gray-300 dark:border-gray-600" className="mt-1 rounded border-gray-300 dark:border-gray-600"
checked={task.status === 'done'} checked={task.status === TaskStatusEnum.Completed}
readOnly readOnly
/> />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1"> <div className="flex items-center gap-2 mb-1">
<h3 className="text-sm font-medium text-gray-900 dark:text-white"> <h3 className="text-sm font-medium text-gray-900 dark:text-white">
{task.title} {task.name}
</h3> </h3>
<span <span
className={`px-2 py-0.5 text-xs rounded ${getPriorityColor(task.priority)}`} className={`px-2 py-0.5 text-xs rounded ${getPriorityColor(task.priority)}`}
> >
{task.priority === 'urgent' && '🔥 Acil'} {task.priority === PriorityEnum.Urgent && '🔥 Acil'}
{task.priority === 'high' && 'Yüksek'} {task.priority === PriorityEnum.High && 'Yüksek'}
{task.priority === 'medium' && 'Orta'} {task.priority === PriorityEnum.Normal && 'Orta'}
{task.priority === 'low' && 'Düşük'} {task.priority === PriorityEnum.Low && 'Düşük'}
</span> </span>
</div> </div>
<p className="text-xs text-gray-600 dark:text-gray-400 mb-2"> <p className="text-xs text-gray-600 dark:text-gray-400 mb-2">
@ -56,11 +58,11 @@ const PriorityTasks: React.FC = () => {
<div className="flex items-center gap-3 text-xs text-gray-500"> <div className="flex items-center gap-3 text-xs text-gray-500">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<FaClock className="w-3 h-3" /> <FaClock className="w-3 h-3" />
{dayjs(task.dueDate).format('DD MMM')} {dayjs(task.endDate).format('DD MMM')}
</span> </span>
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<FaUsers className="w-3 h-3" /> <FaUser className="w-3 h-3" />
{task.assignedTo.length} kişi {task.assignee?.firstName || 'Atanmadı'}
</span> </span>
</div> </div>
</div> </div>

View file

@ -0,0 +1,113 @@
import React from 'react'
import { FaUser, FaUserCheck, FaUserClock } from 'react-icons/fa'
import dayjs from 'dayjs'
import { mockVisitors } from '../../../mocks/mockIntranet'
const Visitors: React.FC = () => {
const todayVisitors = mockVisitors.filter((visitor) =>
dayjs(visitor.visitDate).isSame(dayjs(), 'day')
)
const getStatusIcon = (status: string) => {
switch (status) {
case 'checked-in':
return <FaUserCheck className="w-4 h-4 text-green-600" />
case 'checked-out':
return <FaUser className="w-4 h-4 text-gray-600" />
case 'scheduled':
return <FaUserClock className="w-4 h-4 text-blue-600" />
default:
return <FaUser className="w-4 h-4 text-gray-600" />
}
}
const getStatusText = (status: string) => {
switch (status) {
case 'checked-in':
return 'Giriş Yaptı'
case 'checked-out':
return ıkış Yaptı'
case 'scheduled':
return 'Planlandı'
default:
return 'Bilinmiyor'
}
}
const getStatusColor = (status: string) => {
switch (status) {
case 'checked-in':
return 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800'
case 'checked-out':
return 'bg-gray-50 dark:bg-gray-900/20 border-gray-200 dark:border-gray-700'
case 'scheduled':
return 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
default:
return 'bg-gray-50 dark:bg-gray-900/20 border-gray-200 dark:border-gray-700'
}
}
return (
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
<FaUser className="w-5 h-5" />
Bugünkü Ziyaretçiler
</h2>
</div>
<div className="p-4 space-y-3">
{todayVisitors.length > 0 ? (
todayVisitors.map((visitor) => (
<div
key={visitor.id}
className={`p-3 rounded-lg border ${getStatusColor(visitor.status)}`}
>
<div className="flex items-start gap-3">
<img
src={visitor.photo}
alt={visitor.fullName}
className="w-10 h-10 rounded-full border-2 border-gray-300 dark:border-gray-600"
/>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between mb-1">
<h4 className="text-sm font-medium text-gray-900 dark:text-white truncate">
{visitor.fullName}
</h4>
<div className="flex items-center gap-1">
{getStatusIcon(visitor.status)}
</div>
</div>
<p className="text-xs text-gray-600 dark:text-gray-400 truncate">
{visitor.company}
</p>
<p className="text-xs text-gray-500 dark:text-gray-500 mt-1">
{visitor.purpose}
</p>
<div className="flex items-center justify-between mt-2">
<span className="text-xs text-gray-500 dark:text-gray-500">
{dayjs(visitor.visitDate).format('HH:mm')}
</span>
<span className="text-xs px-2 py-1 rounded-full bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
{getStatusText(visitor.status)}
</span>
</div>
{visitor.host && (
<p className="text-xs text-gray-500 dark:text-gray-500 mt-1">
Karşılayan: {visitor.host.fullName}
</p>
)}
</div>
</div>
</div>
))
) : (
<p className="text-sm text-gray-500 dark:text-gray-400 text-center py-4">
Bugün ziyaretçi yok
</p>
)}
</div>
</div>
)
}
export default Visitors