Video Rooms düzenlemesi

This commit is contained in:
Sedat ÖZTÜRK 2026-05-08 11:38:57 +03:00
parent bdc7f744aa
commit cbd96fd8f2
7 changed files with 667 additions and 257 deletions

View file

@ -3636,6 +3636,48 @@
"en": "Audit Logs",
"tr": "Audit Günlükleri"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.RoleSelector",
"en": "Please select your role",
"tr": "Lütfen rolünüzü seçin"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.Host",
"en": "Host",
"tr": "Oda Sahibi"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.HostDescription",
"en": "The host creates the room, starts the session, manages participants and views reports.",
"tr": "Odayı oluşturur, oturumu başlatır, katılımcıları yönetir ve raporları görüntüler."
},
{
"resourceName": "Platform",
"key": "App.Videoroom.Participant",
"en": "Participant",
"tr": "Katılımcı"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.ParticipantDescription",
"en": "Joins active rooms and interacts with the host and other participants.",
"tr": "Aktif odalara katılır, oda sahibi ve diğer katılımcılarla etkileşim kurar."
},
{
"resourceName": "Platform",
"key": "App.Videoroom.Observer",
"en": "Observer",
"tr": "İzleyici"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.ObserverDescription",
"en": "Joins active rooms and observes without interacting.",
"tr": "Aktif odalara katılır, ancak etkileşimde bulunmaz."
},
{
"resourceName": "Platform",
"key": "App.Videoroom",
@ -3663,8 +3705,230 @@
{
"resourceName": "Platform",
"key": "App.Videoroom.Planning",
"en": "Video Room Planning",
"tr": "Video Oda Planlama"
"en": "Planning",
"tr": "Planlama"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.NewRoom",
"en": "New Room",
"tr": "Yeni Oda Oluştur"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.TotalRooms",
"en": "Total Rooms",
"tr": "Toplam Oda"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.ActiveRooms",
"en": "Active Rooms",
"tr": "Aktif Oda"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.OpenRooms",
"en": "Open Rooms",
"tr": "Katılıma Açık"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.PassiveRooms",
"en": "Passive Rooms",
"tr": "Pasif Oda"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.TotalParticipants",
"en": "Total Participants",
"tr": "Toplam Katılımcı"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.RoomSettings",
"en": "Room Settings",
"tr": "Oda Ayarları"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.ParticipantPermissions",
"en": "Participant Permissions",
"tr": "Katılımcı İzinleri"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.AllowHandRaise",
"en": "Allow Hand Raise",
"tr": "Parmak Kaldırma İzni"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.AllowStudentChat",
"en": "Allow Student Chat",
"tr": "Öğrenci Sohbet İzni"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.AllowPrivateMessages",
"en": "Allow Private Messages",
"tr": "Özel Mesaj İzni"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.DefaultSettings",
"en": "Default Settings",
"tr": "Varsayılan Ayarlar"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.AllowStudentScreenShare",
"en": "Allow Student Screen Share",
"tr": "Öğrenci Ekran Paylaşımı"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.MicrophoneMuted",
"en": "Microphone Muted",
"tr": "Mikrofon Kapalı"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.MicrophoneUnmuted",
"en": "Microphone Unmuted",
"tr": "Mikrofon Açık"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.DefaultMicrophoneState",
"en": "Microphone State",
"tr": "Mikrofon Durumu"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.DefaultCameraState",
"en": "Camera State",
"tr": "Kamera Durumu"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.CameraOn",
"en": "Camera On",
"tr": "Kamera Açık"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.DefaultLayout",
"en": "Default Layout",
"tr": "Varsayılan Layout"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.LayoutGridView",
"en": "Grid View",
"tr": "Izgara Görünümü"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.LayoutTeacherFocus",
"en": "Teacher Focus",
"tr": "Öğretmen Odaklı"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.LayoutPresentation",
"en": "Presentation",
"tr": "Sunum Modu"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.LayoutSidebar",
"en": "Sidebar",
"tr": "Yan Panel"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.DeleteRoom",
"en": "Delete Room",
"tr": "Odayı Sil"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.DeleteRoomWarning",
"en": "This action cannot be undone",
"tr": "Bu işlem geri alınamaz"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.AutomaticallyMuteNewParticipants",
"en": "Automatically Mute New Participants",
"tr": "Yeni katılımcıları otomatik sustur"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.Start",
"en": "Start",
"tr": "Başlat"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.Join",
"en": "Join",
"tr": "Katıl"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.CreateRoom",
"en": "Create Room",
"tr": "Oda Oluştur"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.EditRoom",
"en": "Edit Room",
"tr": "Odayı Düzenle"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.SaveChanges",
"en": "Save Changes",
"tr": "Değişiklikleri Kaydet"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.CameraOff",
"en": "Camera Off",
"tr": "Kamera Kapalı"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.RoomName",
"en": "Room Name",
"tr": "Oda Adı"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.RoomNamePlaceholder",
"en": "E.g. Math 101 - Differential Equations",
"tr": "Örn: Matematik 101 - Diferansiyel Denklemler"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.DescriptionPlaceholder",
"en": "E.g. Brief description about the class...",
"tr": "Örn: Ders hakkında kısa açıklama..."
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.SubjectPlaceholder",
"en": "E.g. Math, Physics, Chemistry",
"tr": "Örn: Matematik, Fizik, Kimya"
},
{
"resourceName": "Platform",
"key": "App.Videoroom.NoScheduledRooms",
"en": "No Scheduled Rooms",
"tr": "Henüz programlanmış oda bulunmamaktadır."
},
{
"resourceName": "Platform",
@ -6156,6 +6420,24 @@
"tr": "Anketler",
"en": "Surveys"
},
{
"resourceName": "Platform",
"key": "App.Intranet.Events.EventComment",
"tr": "Henüz yorum yok. İlk yorumu sen yap!",
"en": "No comments yet. Be the first to comment!"
},
{
"resourceName": "Platform",
"key": "App.Intranet.Events.Comments",
"tr": "Yorumlar",
"en": "Comments"
},
{
"resourceName": "Platform",
"key": "App.Intranet.Events.EventAttendance",
"tr": "Bir yorum yaz...",
"en": "Write a comment..."
},
{
"resourceName": "Platform",
"key": "App.Intranet.Events",
@ -11958,6 +12240,18 @@
"tr": "Çalışıyor...",
"en": "Running..."
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SurveyModal.RequiredUserName",
"tr": "Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız kaydedilecektir.",
"en": "This survey requires your name. Your responses will be recorded."
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SurveyModal.AnonymousNotice",
"tr": "Bu anket anonimdir. Kimlik bilgileriniz kaydedilmeyecektir.",
"en": "This survey is anonymous. Your identity will not be recorded."
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SurveyModal.RequiredField",
@ -13706,6 +14000,12 @@
"en": "Duration",
"tr": "Süre"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Minutes",
"en": "Minutes",
"tr": "Dakika"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.ElementId",
@ -15758,6 +16058,12 @@
"en": "Subject",
"tr": "Konu"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.StartDateTime",
"en": "Start Date and Time",
"tr": "Başlangıç Tarihi ve Saati"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.SubmissionTime",
@ -18033,4 +18339,4 @@
"tr": "Başarıyla Kaydedildi"
}
]
}
}

View file

@ -1123,21 +1123,11 @@
"RequiredPermissionName": "App.Intranet.Events.Event",
"IsDisabled": false
},
{
"ParentCode": "App.Administration",
"Code": "App.Videoroom.Dashboard",
"DisplayName": "App.Videoroom.Dashboard",
"Order": 4,
"Url": "/admin/videoroom/dashboard",
"Icon": "FcVideoCall",
"RequiredPermissionName": "App.Videoroom.Dashboard",
"IsDisabled": false
},
{
"ParentCode": "App.Administration",
"Code": "App.Administration.Restrictions",
"DisplayName": "App.Restrictions",
"Order": 5,
"Order": 4,
"Url": null,
"Icon": "FaLock",
"RequiredPermissionName": null,
@ -1167,7 +1157,7 @@
"ParentCode": "App.Administration",
"Code": "Abp.Identity",
"DisplayName": "Abp.Identity",
"Order": 6,
"Order": 5,
"Url": null,
"Icon": "FcConferenceCall",
"RequiredPermissionName": null,
@ -1257,7 +1247,7 @@
"ParentCode": "App.Administration",
"Code": "App.Reports.Management",
"DisplayName": "App.Reports.Management",
"Order": 7,
"Order": 6,
"Url": null,
"Icon": "FcDocument",
"RequiredPermissionName": null,
@ -1283,6 +1273,16 @@
"RequiredPermissionName": "App.Reports.ReportTemplates",
"IsDisabled": false
},
{
"ParentCode": "App.Administration",
"Code": "App.Videoroom.Dashboard",
"DisplayName": "App.Videoroom.Dashboard",
"Order": 7,
"Url": "/admin/videoroom/dashboard",
"Icon": "FcVideoCall",
"RequiredPermissionName": "App.Videoroom.Dashboard",
"IsDisabled": false
},
{
"ParentCode": "App.Administration",
"Code": "App.Files",

View file

@ -12,7 +12,7 @@ import {
FaExclamationTriangle,
FaSearch,
FaEdit,
FaUser,
FaCalendarAlt,
} from 'react-icons/fa'
import { FcAcceptDatabase } from 'react-icons/fc'
import { deleteWizardFile, getWizardFiles } from '@/services/wizard.service'
@ -161,9 +161,12 @@ const WizardFileManager = () => {
<div className="mt-4">
{filteredFiles.length === 0 && !loading && (
<p className="text-xs text-gray-400 text-center py-4">
{translate('::App.Listforms.WizardNoFiles') || 'No wizard files found.'}
</p>
<div className="text-center py-12">
<FaCalendarAlt size={48} className="mx-auto text-gray-400 mb-4" />
<p className="text-sm text-gray-400 text-center py-4">
{translate('::App.Listforms.WizardNoFiles') || 'No wizard files found.'}
</p>
</div>
)}
{loading && (
@ -187,7 +190,9 @@ const WizardFileManager = () => {
{f.wizardName || f.fileName} <Badge content={f.defaultLayout} />
</div>
<div className="text-xs text-gray-400 flex gap-3 mt-0.5">
<span className="truncate">{f.fileName} - {f.listFormCode}</span>
<span className="truncate">
{f.fileName} - {f.listFormCode}
</span>
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
import React from 'react'
import { motion } from 'framer-motion'
import { FaGraduationCap, FaUserCheck, FaEye } from 'react-icons/fa'
import { FaEye, FaUsers, FaCrown } from 'react-icons/fa'
import { useStoreActions, useStoreState } from '@/store/store'
import { useNavigate } from 'react-router-dom'
import { ROUTES_ENUM } from '@/routes/route.constant'
@ -36,7 +36,9 @@ const Dashboard: React.FC = () => {
animate={{ opacity: 1, y: 0 }}
className="text-center w-full max-w-4xl"
>
<p className="text-lg sm:text-xl text-gray-600 mb-8 sm:mb-12">Lütfen rolünüzü seçin</p>
<p className="text-lg sm:text-xl text-gray-600 mb-8 sm:mb-12">
{translate('::' + 'App.Videoroom.RoleSelector')}
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8">
<motion.button
@ -45,10 +47,12 @@ const Dashboard: React.FC = () => {
onClick={() => handleRoleSelect('teacher')}
className="bg-white rounded-lg shadow-lg p-6 sm:p-8 hover:shadow-xl transition-all duration-300 border-2 border-transparent hover:border-blue-500"
>
<FaGraduationCap size={48} className="mx-auto text-blue-600 mb-4 sm:mb-4" />
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-2">Öğretmen</h2>
<FaCrown size={48} className="mx-auto text-blue-600 mb-4 sm:mb-4" />
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-2">
{translate('::' + 'App.Videoroom.Host')}
</h2>
<p className="text-gray-600 text-sm sm:text-base">
Ders başlatın, öğrencilerle iletişim kurun ve katılım raporlarını görün
{translate('::' + 'App.Videoroom.HostDescription')}
</p>
</motion.button>
@ -58,10 +62,12 @@ const Dashboard: React.FC = () => {
onClick={() => handleRoleSelect('student')}
className="bg-white rounded-lg shadow-lg p-6 sm:p-8 hover:shadow-xl transition-all duration-300 border-2 border-transparent hover:border-green-500"
>
<FaUserCheck size={48} className="mx-auto text-green-600 mb-4 sm:mb-4" />
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-2">Öğrenci</h2>
<FaUsers size={48} className="mx-auto text-green-600 mb-4 sm:mb-4" />
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-2">
{translate('::' + 'App.Videoroom.Participant')}
</h2>
<p className="text-gray-600 text-sm sm:text-base">
Aktif derslere katılın, öğretmeniniz ve diğer öğrencilerle etkileşim kurun
{translate('::' + 'App.Videoroom.ParticipantDescription')}
</p>
</motion.button>
@ -72,9 +78,11 @@ const Dashboard: React.FC = () => {
className="bg-white rounded-lg shadow-lg p-6 sm:p-8 hover:shadow-xl transition-all duration-300 border-2 border-transparent hover:border-purple-500 md:col-span-2 lg:col-span-1"
>
<FaEye size={48} className="mx-auto text-purple-600 mb-4 sm:mb-4" />
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-2">Gözlemci</h2>
<h2 className="text-xl sm:text-2xl font-bold text-gray-800 mb-2">
{translate('::' + 'App.Videoroom.Observer')}
</h2>
<p className="text-gray-600 text-sm sm:text-base">
Sınıfı gözlemleyin, eğitim sürecini takip edin (ses/video paylaşımı yok)
{translate('::' + 'App.Videoroom.ObserverDescription')}
</p>
</motion.button>
</div>

View file

@ -14,7 +14,6 @@ import {
FaHourglassEnd,
FaDoorOpen,
FaSearch,
FaFilter,
} from 'react-icons/fa'
import { useStoreState } from '@/store/store'
@ -30,6 +29,9 @@ import {
import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { VideoroomDto } from '@/proxy/videoroom/models'
import classNames from 'classnames'
import { Button, Input } from '@/components/ui'
import { FcVideoCall } from 'react-icons/fc'
export interface RoomProps {
status: string
@ -70,6 +72,7 @@ const RoomList: React.FC = () => {
}
const [videoList, setVideoist] = useState<VideoroomDto[]>([])
const [videoroom, setVideoroom] = useState<VideoroomDto>(newClassEntity)
const mode = useStoreState((state) => state.theme.mode)
const [showCreateModal, setShowCreateModal] = useState(false)
const [showEditModal, setShowEditModal] = useState(false)
@ -77,7 +80,6 @@ const RoomList: React.FC = () => {
// Filter/search state
const [searchTerm, setSearchTerm] = useState('')
const [statusFilter, setStatusFilter] = useState('')
const getVideoroomList = async (
skipCount = 0,
@ -110,8 +112,8 @@ const RoomList: React.FC = () => {
}
useEffect(() => {
getVideoroomList(0, 1000, '', searchTerm, statusFilter)
}, [searchTerm, statusFilter])
getVideoroomList(0, 1000, '', searchTerm)
}, [searchTerm])
const handleCreateClass = async (e: React.FormEvent) => {
e.preventDefault()
@ -208,7 +210,9 @@ const RoomList: React.FC = () => {
className: 'bg-blue-100 text-blue-800',
showButtons: true,
title:
user.role === 'teacher' && classSession.teacherId === user.id ? 'Dersi Başlat' : 'Katıl',
user.role === 'teacher' && classSession.teacherId === user.id
? translate('::App.Videoroom.Start')
: translate('::App.Videoroom.Join'),
classes:
user.role === 'teacher' && classSession.teacherId === user.id
? 'bg-green-600 text-white hover:bg-green-700'
@ -232,7 +236,9 @@ const RoomList: React.FC = () => {
className: 'bg-yellow-100 text-yellow-800',
showButtons: true,
title:
user.role === 'teacher' && classSession.teacherId === user.id ? 'Sınıfa Git' : 'Katıl',
user.role === 'teacher' && classSession.teacherId === user.id
? translate('::App.Videoroom.Start')
: translate('::App.Videoroom.Join'),
classes:
user.role === 'teacher' && classSession.teacherId === user.id
? 'bg-green-600 text-white hover:bg-green-700'
@ -258,12 +264,50 @@ const RoomList: React.FC = () => {
<>
<Helmet
titleTemplate="%s | Erp Platform"
title={translate('::' + 'App.Coordinator.Videoroom.List')}
title={translate('::' + 'App.Videoroom.List')}
defaultTitle="Erp Platform"
></Helmet>
<Container>
{/* ── Header ─────────────────────────────────────────────── */}
<div
className={classNames(
'flex items-center gap-2 pb-1 border-b',
mode === 'light' ? 'border-gray-200' : 'border-neutral-700',
)}
>
<FcVideoCall size={24} />
<h4 className="text-sm font-medium">{translate('::App.Videoroom.List')}</h4>
<div className="flex gap-1 ml-auto items-center">
<div className="flex-1 relative">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-slate-400" />
<Input
size="sm"
type="text"
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Search..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
{user.role === 'teacher' && (
<Button
size="sm"
variant="solid"
onClick={() => setShowCreateModal(true)}
className="flex items-center justify-center space-x-2 text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-900/20"
>
<FaPlus size={15} />
<span>{translate('::App.Videoroom.NewRoom')}</span>
</Button>
)}
</div>
</div>
{/* Stats Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4 sm:gap-6 mb-3">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4 sm:gap-6 mt-4 mb-3">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
@ -274,7 +318,9 @@ const RoomList: React.FC = () => {
<FaCalendarAlt className="text-blue-600" size={20} />
</div>
<div className="ml-3 sm:ml-4">
<p className="text-xs sm:text-sm font-medium text-gray-600">Toplam Sınıf</p>
<p className="text-xs sm:text-sm font-medium text-gray-600">
{translate('::App.Videoroom.TotalRooms')}
</p>
<p className="text-xl sm:text-2xl font-bold text-gray-900">
{widgets().totalCount}{' '}
</p>
@ -294,7 +340,9 @@ const RoomList: React.FC = () => {
<FaPlay className="text-green-600" size={20} />
</div>
<div className="ml-3 sm:ml-4">
<p className="text-xs sm:text-sm font-medium text-gray-600">Aktif Sınıf</p>
<p className="text-xs sm:text-sm font-medium text-gray-600">
{translate('::App.Videoroom.ActiveRooms')}
</p>
<p className="text-xl sm:text-2xl font-bold text-gray-900">
{widgets().activeCount}
</p>
@ -314,7 +362,9 @@ const RoomList: React.FC = () => {
<FaDoorOpen className="text-blue-600" size={20} />
</div>
<div className="ml-3 sm:ml-4">
<p className="text-xs sm:text-sm font-medium text-gray-600">Katılıma ık</p>
<p className="text-xs sm:text-sm font-medium text-gray-600">
{translate('::App.Videoroom.OpenRooms')}
</p>
<p className="text-xl sm:text-2xl font-bold text-gray-900">{widgets().openCount}</p>
</div>
</div>
@ -332,7 +382,9 @@ const RoomList: React.FC = () => {
<FaHourglassEnd className="text-gray-600" size={20} />
</div>
<div className="ml-3 sm:ml-4">
<p className="text-xs sm:text-sm font-medium text-gray-600">Pasif Sınıf</p>
<p className="text-xs sm:text-sm font-medium text-gray-600">
{translate('::App.Videoroom.PassiveRooms')}
</p>
<p className="text-xl sm:text-2xl font-bold text-gray-900">
{widgets().passiveCount}
</p>
@ -352,7 +404,9 @@ const RoomList: React.FC = () => {
<FaUsers className="text-purple-600" size={20} />
</div>
<div className="ml-3 sm:ml-4">
<p className="text-xs sm:text-sm font-medium text-gray-600">Toplam Katılımcı</p>
<p className="text-xs sm:text-sm font-medium text-gray-600">
{translate('::App.Videoroom.TotalParticipants')}
</p>
<p className="text-xl sm:text-2xl font-bold text-gray-900">
{videoList.reduce((sum, c) => sum + c.participantCount, 0)}
</p>
@ -361,188 +415,158 @@ const RoomList: React.FC = () => {
</motion.div>
</div>
{/* Filter Bar */}
<div className="bg-white rounded-lg border border-slate-200 p-6 mb-6 shadow-sm">
<div className="flex flex-col lg:flex-row gap-4">
<div className="flex-1 relative">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-slate-400" />
<input
type="text"
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Search class"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="flex items-center gap-4">
<FaFilter className="w-5 h-5 text-slate-500" />
<select
className="ml-2 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
style={{ minWidth: 120 }}
>
<option value="">All Status</option>
<option value="Active">Aktif</option>
<option value="Open">Katılıma ık</option>
<option value="Passive">Pasif</option>
</select>
</div>
</div>
</div>
{/* Scheduled Classes */}
<div className="bg-white rounded-lg shadow-md">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 p-4 sm:px-6 border-b border-gray-200">
<h2 className="text-lg sm:text-xl font-semibold text-gray-900">Programlı Sınıflar</h2>
{user.role === 'teacher' && (
<button
onClick={() => setShowCreateModal(true)}
className="flex items-center justify-center space-x-2 bg-blue-600 text-white px-3 sm:px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors sm:w-auto"
>
<FaPlus size={15} />
<span className="hidden sm:inline">Yeni Sınıf Oluştur</span>
<span className="sm:hidden">Yeni Sınıf</span>
</button>
)}
</div>
<div className="p-4 sm:p-6">
{videoList.length === 0 ? (
<div className="text-center py-12">
<FaCalendarAlt size={48} className="mx-auto text-gray-400 mb-4" />
<p className="text-gray-500">Henüz programlanmış sınıf bulunmamaktadır.</p>
</div>
) : (
<div className="grid gap-4 sm:gap-6">
{videoList.map((classSession, index) => {
const { status, className, showButtons, title, classes, event } =
getClassProps(classSession)
return (
<motion.div
key={classSession.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="border border-gray-200 rounded-lg p-4 sm:p-6 hover:shadow-md transition-shadow"
>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-center space-x-3">
<h3 className="text-base sm:text-lg font-semibold text-gray-900 break-words">
{videoList.length === 0 ? (
<div className="text-center py-12">
<FaCalendarAlt size={48} className="mx-auto text-gray-400 mb-4" />
<p className="text-sm text-gray-400 text-center py-4">
{translate('::App.Videoroom.NoScheduledRooms') ||
'No scheduled classes found. Please create a new class.'}
</p>
</div>
) : (
<div className="grid gap-3">
{videoList.map((classSession, index) => {
const { status, className, showButtons, title, classes, event } =
getClassProps(classSession)
const isActive = !classSession.actualStartTime && !classSession.actualEndTime
const isOpen = classSession.actualStartTime && !classSession.actualEndTime
const accentColor = isOpen
? 'border-l-yellow-400'
: isActive
? 'border-l-blue-500'
: 'border-l-gray-300'
return (
<motion.div
key={classSession.id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.05 }}
className={`bg-white border border-gray-100 border-l-4 ${accentColor} rounded-xl shadow-sm hover:shadow-md transition-all duration-200`}
>
{/* Card Header */}
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 p-4 pb-3">
<div className="flex flex-col gap-1 min-w-0">
<div className="flex items-center gap-2 flex-wrap">
<h3 className="text-sm font-semibold text-gray-900 truncate">
{classSession.name}
</h3>
<span
className={`px-2 py-1 rounded-full text-xs font-medium ${className}`}
className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${className}`}
>
{status}
</span>
</div>
{/* Sağ kısım: buton */}
{showButtons && (
<div className="flex space-x-2">
{/* {user.role === 'teacher' && classSession.teacherId === user.id && ( */}
{user.role === 'teacher' && (
<>
<button
onClick={() => handlePlanningClass(classSession)}
disabled={classSession.actualStartTime ? true : false}
className="flex px-3 sm:px-4 py-2 rounded-lg bg-yellow-600 text-white
hover:bg-yellow-700
disabled:bg-gray-400 disabled:cursor-not-allowed disabled:hover:bg-gray-400"
title="Sınıfı Planla"
>
<FaUsers size={14} />
Planlama
</button>
<button
onClick={() => openEditModal(classSession)}
disabled={classSession.actualStartTime ? true : false}
className="flex px-3 sm:px-4 py-2 rounded-lg bg-blue-600 text-white
hover:bg-blue-700
disabled:bg-gray-400 disabled:cursor-not-allowed disabled:hover:bg-gray-400"
title="Sınıfı Düzenle"
>
<FaEdit size={14} />
Düzenle
</button>
<button
onClick={() => openDeleteModal(classSession)}
disabled={classSession.actualStartTime ? true : false}
className="flex px-3 sm:px-4 py-2 rounded-lg bg-red-600 text-white
hover:bg-red-700
disabled:bg-gray-400 disabled:cursor-not-allowed disabled:hover:bg-gray-400"
title="Sınıfı Sil"
>
<FaTrash size={14} />
Sil
</button>
</>
{(classSession.subject || classSession.description) && (
<div className="flex flex-col gap-0.5">
{classSession.subject && (
<span className="text-xs font-medium text-indigo-600">
{classSession.subject}
</span>
)}
{classSession.description && (
<p className="text-xs text-gray-400 line-clamp-1">
{classSession.description}
</p>
)}
<button
onClick={event}
disabled={status === 'Katılıma Açık' ? true : false}
className={`px-3 sm:px-4 py-2 rounded-lg transition-colors ${
classes
}`}
>
{title}
</button>
</div>
)}
</div>
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4">
<p className="text-gray-600 text-sm sm:text-base">{classSession.subject}</p>
</div>
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4">
<sub className="text-gray-500 mb-3 text-xs sm:text-sm">
{classSession.description}
</sub>
</div>
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 md:gap-3 w-full text-xs sm:text-sm text-gray-600">
<div className="col-span-1 flex items-center gap-2 p-1 rounded-lg">
<FaCalendarAlt size={14} className="text-gray-500" />
<span className="truncate">
{showDbDateAsIs(classSession.scheduledStartTime)}
</span>
</div>
<div className="col-span-1 flex items-center gap-2 p-1 rounded-lg">
<FaClock size={14} className="text-gray-500" />
<span>{classSession.duration} dakika</span>
</div>
<div className="col-span-1 flex items-center gap-2 p-1 rounded-lg">
{classSession.scheduledEndTime && (
<>
<FaEye size={14} className="text-gray-500" />
<span className="truncate">
{showDbDateAsIs(classSession.scheduledEndTime!)}
{/* Action Buttons */}
{showButtons && (
<div className="flex items-center gap-1.5 flex-shrink-0">
{user.role === 'teacher' && (
<>
<Button
size="sm"
variant="solid"
color="red-600"
className="flex items-center gap-1"
onClick={() => handlePlanningClass(classSession)}
disabled={!!classSession.actualStartTime}
title={translate('::App.Videoroom.Planning') || 'Planning'}
>
<FaUsers size={11} />
<span className="hidden sm:inline">
{translate('::App.Videoroom.Planning') || 'Planning'}
</span>
</>
)}
</div>
</Button>
<div className="col-span-1 flex items-center gap-2 p-1 rounded-lg">
<FaUsers size={14} className="text-gray-500" />
<span>
{classSession.participantCount}/{classSession.maxParticipants}
</span>
</div>
<Button
size="sm"
variant="solid"
color="orange-600"
className="flex items-center gap-1"
onClick={() => openEditModal(classSession)}
disabled={!!classSession.actualStartTime}
title={translate('::App.Platform.Edit')}
>
<FaEdit size={11} />
<span className="hidden sm:inline">
{translate('::App.Platform.Edit')}
</span>
</Button>
<Button
size="sm"
variant="solid"
color="sky-800"
className="flex items-center gap-1"
onClick={() => openDeleteModal(classSession)}
disabled={!!classSession.actualStartTime}
title={translate('::App.Platform.Delete')}
>
<FaTrash size={11} />
<span className="hidden sm:inline">
{translate('::App.Platform.Delete')}
</span>
</Button>
</>
)}
<Button
size="sm"
variant="solid"
onClick={event}
className="flex items-center gap-1"
color="emerald-600"
disabled={status === 'Katılıma Açık' ? true : false}
>
<FaPlay size={10} />
{title}
</Button>
</div>
</div>
</motion.div>
)
})}
</div>
)}
</div>
)}
</div>
{/* Card Footer — Meta Info */}
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-2.5 bg-gray-50 rounded-b-xl border-t border-gray-100 text-xs text-gray-500">
<span className="flex items-center gap-1.5">
<FaCalendarAlt size={11} className="text-blue-400" />
{showDbDateAsIs(classSession.scheduledStartTime)}
</span>
<span className="flex items-center gap-1.5">
<FaClock size={11} className="text-purple-400" />
{classSession.duration} dk
</span>
{classSession.scheduledEndTime && (
<span className="flex items-center gap-1.5">
<FaEye size={11} className="text-green-400" />
{showDbDateAsIs(classSession.scheduledEndTime!)}
</span>
)}
<span className="flex items-center gap-1.5">
<FaUsers size={11} className="text-indigo-400" />
{classSession.participantCount}/{classSession.maxParticipants}
</span>
</div>
</motion.div>
)
})}
</div>
)}
</div>
{/* Class Modal (Create/Edit) */}
@ -555,7 +579,9 @@ const RoomList: React.FC = () => {
>
<div className="p-3 sm:p-3 border-b border-gray-200">
<h2 className="text-xl sm:text-2xl font-bold text-gray-900">
{showCreateModal ? 'Yeni Sınıf Oluştur' : 'Sınıfı Düzenle'}
{showCreateModal
? translate('::App.Videoroom.CreateRoom') || 'Yeni Sınıf Oluştur'
: translate('::App.Videoroom.EditRoom') || 'Sınıfı Düzenle'}
</h2>
</div>
@ -565,7 +591,7 @@ const RoomList: React.FC = () => {
>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Sınıf Adı *
{translate('::App.Listform.ListformField.RoomName') || 'Room Name'} *
</label>
<input
type="text"
@ -574,38 +600,51 @@ const RoomList: React.FC = () => {
value={videoroom.name}
onChange={(e) => setVideoroom({ ...videoroom, name: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Örn: Matematik 101 - Diferansiyel Denklemler"
placeholder={
translate('::App.Listform.ListformField.RoomNamePlaceholder') ||
'Enter room name...'
}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">ıklama</label>
<label className="block text-sm font-medium text-gray-700 mb-2">
{translate('::App.Listform.ListformField.Description') || 'Description'}
</label>
<textarea
value={videoroom.description}
onChange={(e) => setVideoroom({ ...videoroom, description: e.target.value })}
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Ders hakkında kısa açıklama..."
placeholder={
translate('::App.Listform.ListformField.DescriptionPlaceholder') ||
'Ders hakkında kısa açıklama...'
}
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Ders Konusu
{translate('::App.Listform.ListformField.Subject') || 'Subject'}
</label>
<input
type="text"
value={videoroom.subject}
onChange={(e) => setVideoroom({ ...videoroom, subject: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Örn: Matematik, Fizik, Kimya"
placeholder={
translate('::App.Listform.ListformField.SubjectPlaceholder') ||
'E.g. Math, Physics, Chemistry'
}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Başlangıç Tarihi ve Saati *
{translate('::App.Listform.ListformField.StartDateTime') ||
'Start Date and Time'}{' '}
*
</label>
<input
type="datetime-local"
@ -629,7 +668,8 @@ const RoomList: React.FC = () => {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Süre (dakika)
{translate('::App.Listform.ListformField.Duration') || 'Duration'} (
{translate('::App.Listform.ListformField.Minutes') || 'minutes'})
</label>
<input
type="number"
@ -648,7 +688,8 @@ const RoomList: React.FC = () => {
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Maksimum Katılımcı
{translate('::App.Listform.ListformField.MaxParticipants') ||
'Maximum Participants'}
</label>
<input
type="number"
@ -668,11 +709,16 @@ const RoomList: React.FC = () => {
{/* Sınıf Ayarları */}
<div>
<h3 className="text-lg font-semibold text-gray-800 mb-2">Sınıf Ayarları</h3>
<h3 className="text-lg font-semibold text-gray-800 mb-2">
{translate('::App.Videoroom.RoomSettings') || 'Sınıf Ayarları'}
</h3>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
<div className="space-y-4">
<h4 className="font-medium text-gray-700">Katılımcı İzinleri</h4>{' '}
<h4 className="font-medium text-gray-700">
{translate('::App.Videoroom.ParticipantPermissions') ||
'Katılımcı İzinleri'}
</h4>{' '}
<label className="flex items-center space-x-3">
<input
type="checkbox"
@ -688,7 +734,9 @@ const RoomList: React.FC = () => {
}
className="rounded"
/>
<span className="text-sm">Parmak kaldırma izni</span>
<span className="text-sm">
{translate('::App.Videoroom.AllowHandRaise') || 'Parmak kaldırma izni'}
</span>
</label>
<label className="flex items-center space-x-3">
<input
@ -705,7 +753,9 @@ const RoomList: React.FC = () => {
}
className="rounded"
/>
<span className="text-sm">Öğrenci sohbet izni</span>
<span className="text-sm">
{translate('::App.Videoroom.AllowStudentChat') || 'Öğrenci sohbet izni'}
</span>
</label>
<label className="flex items-center space-x-3">
<input
@ -722,7 +772,9 @@ const RoomList: React.FC = () => {
}
className="rounded"
/>
<span className="text-sm">Özel mesaj izni</span>
<span className="text-sm">
{translate('::App.Videoroom.AllowPrivateMessages') || 'Özel mesaj izni'}
</span>
</label>
<label className="flex items-center space-x-3">
<input
@ -739,16 +791,22 @@ const RoomList: React.FC = () => {
}
className="rounded"
/>
<span className="text-sm">Öğrenci ekran paylaşımı</span>
<span className="text-sm">
{translate('::App.Videoroom.AllowStudentScreenShare') ||
'Öğrenci ekran paylaşımı'}
</span>
</label>
</div>
<div className="space-y-4">
<h4 className="font-medium text-gray-700">Varsayılan Ayarlar</h4>
<h4 className="font-medium text-gray-700">
{translate('::App.Videoroom.DefaultSettings') || 'Varsayılan Ayarlar'}
</h4>
<div className="flex items-center justify-between">
<label className="block text-sm font-medium text-gray-700 mb-1">
Varsayılan mikrofon durumu
{translate('::App.Videoroom.DefaultMicrophoneState') ||
'Varsayılan mikrofon durumu'}
</label>
<select
value={videoroom.settingsDto?.defaultMicrophoneState}
@ -763,14 +821,19 @@ const RoomList: React.FC = () => {
}
className="border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="muted">Kapalı</option>
<option value="unmuted">ık</option>
<option value="muted">
{translate('::App.Videoroom.MicrophoneMuted') || 'Kapalı'}
</option>
<option value="unmuted">
{translate('::App.Videoroom.MicrophoneUnmuted') || 'Açık'}
</option>
</select>
</div>
<div className="flex items-center justify-between">
<label className="block text-sm font-medium text-gray-700 mb-1">
Varsayılan kamera durumu
{translate('::App.Videoroom.DefaultCameraState') ||
'Varsayılan kamera durumu'}
</label>
<select
value={videoroom.settingsDto?.defaultCameraState}
@ -785,14 +848,18 @@ const RoomList: React.FC = () => {
}
className="border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="on">ık</option>
<option value="off">Kapalı</option>
<option value="on">
{translate('::App.Videoroom.CameraOn') || 'Açık'}
</option>
<option value="off">
{translate('::App.Videoroom.CameraOff') || 'Kapalı'}
</option>
</select>
</div>
<div className="flex items-center justify-between">
<label className="block text-sm font-medium text-gray-700 mb-1">
Varsayılan layout
{translate('::App.Videoroom.DefaultLayout') || 'Varsayılan layout'}
</label>
<select
value={videoroom.settingsDto?.defaultLayout}
@ -807,10 +874,18 @@ const RoomList: React.FC = () => {
}
className="border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="grid">Izgara Görünümü</option>
<option value="teacher-focus">Öğretmen Odaklı</option>
<option value="presentation">Sunum Modu</option>
<option value="sidebar">Yan Panel</option>
<option value="grid">
{translate('::App.Videoroom.LayoutGridView') || 'Izgara Görünümü'}
</option>
<option value="teacher-focus">
{translate('::App.Videoroom.LayoutTeacherFocus') || 'Öğretmen Odaklı'}
</option>
<option value="presentation">
{translate('::App.Videoroom.LayoutPresentation') || 'Sunum Modu'}
</option>
<option value="sidebar">
{translate('::App.Videoroom.LayoutSidebar') || 'Yan Panel'}
</option>
</select>
</div>
@ -829,7 +904,10 @@ const RoomList: React.FC = () => {
}
className="rounded"
/>
<span className="text-sm">Yeni katılımcıları otomatik sustur</span>
<span className="text-sm">
{translate('::App.Videoroom.AutomaticallyMuteNewParticipants') ||
'Yeni katılımcıları otomatik sustur'}
</span>
</label>
</div>
</div>
@ -849,15 +927,17 @@ const RoomList: React.FC = () => {
resetForm()
}
}}
className="px-6 py-3 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-3 py-1 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
>
İptal
{translate('::Cancel') || 'İptal'}
</button>
<button
type="submit"
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
className="px-3 py-1 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
{showCreateModal ? 'Sınıf Oluştur' : 'Değişiklikleri Kaydet'}
{showCreateModal
? translate('::App.Videoroom.CreateRoom') || 'Sınıf Oluştur'
: translate('::App.Videoroom.SaveChanges') || 'Değişiklikleri Kaydet'}
</button>
</div>
</form>
@ -879,14 +959,18 @@ const RoomList: React.FC = () => {
<FaTrash className="text-red-600" size={24} />
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900">Sınıfı Sil</h3>
<p className="text-sm text-gray-600">Bu işlem geri alınamaz</p>
<h3 className="text-lg font-semibold text-gray-900">
{translate('::App.Videoroom.DeleteRoom') || 'Sınıfı Sil'}
</h3>
<p className="text-sm text-gray-600">
{translate('::App.Videoroom.DeleteRoomWarning') || 'Bu işlem geri alınamaz'}
</p>
</div>
</div>
<p className="text-gray-700 mb-6">
<strong>"{videoroom.name}"</strong> adlı sınıfı silmek istediğinizden emin
misiniz?
{translate('::DeleteConfirmation') ||
'Bu sınıfı silmek istediğinize emin misiniz?'}
</p>
<div className="flex items-center justify-end space-x-4">
@ -897,13 +981,13 @@ const RoomList: React.FC = () => {
}}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
>
İptal
{translate('::App.Common.Cancel') || 'İptal'}
</button>
<button
onClick={handleDeleteClass}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
>
Sil
{translate('::App.Platform.Delete') || 'Sil'}
</button>
</div>
</div>

View file

@ -19,6 +19,7 @@ import { AVATAR_URL } from '@/constants/app.constant'
import { intranetService } from '@/services/intranet.service'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { useLocalization } from '@/utils/hooks/useLocalization'
dayjs.extend(relativeTime)
@ -41,6 +42,7 @@ const imgSrc = (img: string) => {
const EventModal: React.FC<EventModalProps> = ({ event, onClose }) => {
const currentLocale = useLocale()
const photos = (event.photos || '').split('|').filter(Boolean)
const { translate } = useLocalization()
// Photo slider state
const [activePhoto, setActivePhoto] = useState(0)
@ -254,14 +256,14 @@ const EventModal: React.FC<EventModalProps> = ({ event, onClose }) => {
<div className="p-5">
<h3 className="text-sm font-semibold text-gray-900 dark:text-white flex items-center gap-2 mb-4">
<FaCommentAlt className="w-4 h-4 text-green-500" />
Yorumlar ({comments.length})
{translate('::App.Intranet.Events.Comments')} ({comments.length})
</h3>
{/* Comment List */}
<div className="space-y-4 mb-4 max-h-64 overflow-y-auto">
{comments.length === 0 ? (
<p className="text-sm text-gray-400 dark:text-gray-500 text-center py-6">
Henüz yorum yok. İlk yorumu sen yap!
{translate('::App.Intranet.Events.EventComment')}
</p>
) : (
comments.map((comment) => (
@ -298,9 +300,10 @@ const EventModal: React.FC<EventModalProps> = ({ event, onClose }) => {
<textarea
ref={commentInputRef}
value={commentText}
autoFocus
onChange={(e) => setCommentText(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Bir yorum yaz... (Enter ile gönder)"
placeholder={translate('::App.Intranet.Events.EventAttendance')}
rows={2}
className="flex-1 resize-none px-3 py-2 text-sm rounded-xl border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-green-500 transition-colors"
/>
@ -321,7 +324,7 @@ const EventModal: React.FC<EventModalProps> = ({ event, onClose }) => {
onClick={onClose}
className="w-full px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg transition-colors text-sm font-medium"
>
Kapat
{translate('::App.Platform.Close')}
</button>
</div>
</motion.div>

View file

@ -11,7 +11,7 @@ interface SurveyModalProps {
import { useLocalization } from '@/utils/hooks/useLocalization'
const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit }) => {
const { translate } = useLocalization();
const { translate } = useLocalization()
const isUpdate = !!survey.myResponse
const [answers, setAnswers] = useState<{ [questionId: string]: any }>(() => {
@ -20,7 +20,7 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
survey.myResponse.answers.map((a) => [
a.questionId,
a.questionType === 'rating' ? Number(a.value) : a.value,
])
]),
)
}
return {}
@ -48,7 +48,11 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
survey.questions.forEach((question) => {
if (question.isRequired) {
const val = answers[question.id]
const isEmpty = val === undefined || val === null || val === '' || (question.type === 'rating' && Number(val) === 0)
const isEmpty =
val === undefined ||
val === null ||
val === '' ||
(question.type === 'rating' && Number(val) === 0)
if (isEmpty) {
newErrors[question.id] = translate('::App.Platform.Intranet.SurveyModal.RequiredField')
}
@ -269,7 +273,7 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
{!survey.isAnonymous && (
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
<p className="text-sm text-blue-700 dark:text-blue-300">
Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız kaydedilecektir.
{translate('::App.Platform.Intranet.SurveyModal.RequiredUserName')}
</p>
</div>
)}
@ -277,7 +281,7 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
{survey.isAnonymous && (
<div className="bg-green-50 dark:bg-green-900/20 rounded-lg p-3">
<p className="text-sm text-green-700 dark:text-green-300">
Bu anket anonimdir. Kimlik bilgileriniz kaydedilmeyecektir.
{translate('::App.Platform.Intranet.SurveyModal.AnonymousNotice')}
</p>
</div>
)}