diff --git a/api/src/Kurs.Platform.Application.Contracts/Question/QuestionOptionDto.cs b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionOptionDto.cs index e626a35f..49fe58e3 100644 --- a/api/src/Kurs.Platform.Application.Contracts/Question/QuestionOptionDto.cs +++ b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionOptionDto.cs @@ -8,5 +8,6 @@ public class QuestionOptionDto : FullAuditedEntityDto public string Text { get; set; } public bool IsCorrect { get; set; } + public Guid QuestionPoolId { get; set; } public Guid QuestionId { get; set; } } \ No newline at end of file diff --git a/api/src/Kurs.Platform.Application/ListForms/ListFormSelectAppService.cs b/api/src/Kurs.Platform.Application/ListForms/ListFormSelectAppService.cs index 335f1d92..e7076c34 100644 --- a/api/src/Kurs.Platform.Application/ListForms/ListFormSelectAppService.cs +++ b/api/src/Kurs.Platform.Application/ListForms/ListFormSelectAppService.cs @@ -379,7 +379,6 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe var lookup = JsonSerializer.Deserialize(field.LookupJson); if (!string.IsNullOrWhiteSpace(lookup?.LookupQuery)) { - var lookupQuery = defaultValueHelper.GetDefaultValue(lookup.LookupQuery); var parameters = new Dictionary(); if (input.Filters != null) { @@ -388,6 +387,7 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe parameters.Add("@param" + i, input.Filters[i]); } } + var lookupQuery = defaultValueHelper.GetDefaultValue(lookup.LookupQuery); return await dynamicDataRepository.QueryAsync(lookupQuery, connectionString, parameters); } } diff --git a/api/src/Kurs.Platform.Application/Question/QuestionAppService.cs b/api/src/Kurs.Platform.Application/Question/QuestionAppService.cs index 243968b2..601ca273 100644 --- a/api/src/Kurs.Platform.Application/Question/QuestionAppService.cs +++ b/api/src/Kurs.Platform.Application/Question/QuestionAppService.cs @@ -73,57 +73,30 @@ public class QuestionAppService : CrudAppService< entity.CorrectAnswer = input.CorrectAnswer; entity.QuestionType = input.QuestionType; - // 🟨 Şık tiplerine göre davranış belirle - var multiOptionTypes = new[] { "multiple-choice", "multiple-answer", "true-false" }; + // 🔹 Çoktan seçmeli türlerde mevcut mantık devam eder + var existingOptions = entity.Options.ToList(); + var incomingOptions = input.Options ?? []; - // 🔸 Eğer çoklu şık tipi değilse, sadece 1 option olacak - if (!multiOptionTypes.Contains(input.QuestionType)) + // Silinecekleri bul + var toDelete = existingOptions.Where(e => !incomingOptions.Any(i => i.Id == e.Id)).ToList(); + foreach (var del in toDelete) + entity.Options.Remove(del); + + // Güncelle / ekle + foreach (var optDto in incomingOptions) { - entity.Options.Clear(); - - // Sadece CorrectAnswer bilgisinden tek option oluştur - if (!string.IsNullOrWhiteSpace(input.CorrectAnswer)) + var existing = existingOptions.FirstOrDefault(o => o.Id == optDto.Id); + if (existing != null) { - var option = new QuestionOption( - Guid.NewGuid(), - entity.Id, - input.CorrectAnswer, - true // tek seçenek, doğru kabul edilir - ); - - entity.Options.Add(option); - - entity.CorrectAnswer = option.Id.ToString(); // CorrectAnswer alanına option Id'si yazılır + existing.Text = optDto.Text; + existing.IsCorrect = optDto.IsCorrect; } - } - else - { - // 🔹 Çoktan seçmeli türlerde mevcut mantık devam eder - var existingOptions = entity.Options.ToList(); - var incomingOptions = input.Options ?? new List(); - - // Silinecekleri bul - var toDelete = existingOptions.Where(e => !incomingOptions.Any(i => i.Id == e.Id)).ToList(); - foreach (var del in toDelete) - entity.Options.Remove(del); - - // Güncelle / ekle - foreach (var optDto in incomingOptions) + else { - var existing = existingOptions.FirstOrDefault(o => o.Id == optDto.Id); - if (existing != null) - { - existing.Text = optDto.Text; - existing.IsCorrect = optDto.IsCorrect; - } - else - { - entity.Options.Add(new QuestionOption(optDto.Id, entity.Id, optDto.Text, optDto.IsCorrect)); - } + entity.Options.Add(new QuestionOption(optDto.Id, optDto.QuestionPoolId, entity.Id, optDto.Text, optDto.IsCorrect)); } } - // 🟢 Kaydet await _questionRepository.UpdateAsync(entity, autoSave: true); return ObjectMapper.Map(entity); diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs index 0640e2b4..58025bcd 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs @@ -30900,11 +30900,11 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency new EditingFormItemDto { Order = 2, DataField = "QuestionType", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, new EditingFormItemDto { Order = 3, DataField = "Points", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox }, new EditingFormItemDto { Order = 4, DataField = "Title", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox }, - new EditingFormItemDto { Order = 5, DataField = "Content", ColSpan = 2, EditorType2 = EditorTypes.dxTextArea }, - new EditingFormItemDto { Order = 6, DataField = "MediaType", ColSpan = 2, EditorType2 = EditorTypes.dxSelectBox }, - new EditingFormItemDto { Order = 7, DataField = "MediaUrl", ColSpan = 2, EditorType2 = EditorTypes.dxTextBox }, - new EditingFormItemDto { Order = 8, DataField = "CorrectAnswer", ColSpan = 2, EditorType2 = EditorTypes.dxTextBox }, - new EditingFormItemDto { Order = 9, DataField = "Difficulty", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, + new EditingFormItemDto { Order = 5, DataField = "Content", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextArea }, + new EditingFormItemDto { Order = 6, DataField = "Difficulty", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, + new EditingFormItemDto { Order = 7, DataField = "MediaType", ColSpan = 2, EditorType2 = EditorTypes.dxSelectBox }, + new EditingFormItemDto { Order = 8, DataField = "MediaUrl", ColSpan = 2, EditorType2 = EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 9, DataField = "CorrectAnswer", ColSpan = 2, EditorType2 = EditorTypes.dxTextBox, EditorOptions="{\"disabled\": true}" }, new EditingFormItemDto { Order = 10, DataField = "TimeLimit", ColSpan = 2, EditorType2 = EditorTypes.dxNumberBox }, new EditingFormItemDto { Order = 11, DataField = "Explanation", ColSpan = 2, EditorType2 = EditorTypes.dxTextArea }, ] @@ -31083,7 +31083,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency { IsPivot = true }) - }, + }, new() { ListFormCode = listFormQuestion.ListFormCode, RoleId = null, @@ -31194,7 +31194,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency CultureName = LanguageCodes.En, SourceDbType = DbType.String, FieldName = "MediaUrl", - Width = 300, + Width = 170, ListOrderNo = 8, Visible = true, IsActive = true, @@ -31225,7 +31225,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency CultureName = LanguageCodes.En, SourceDbType = DbType.String, FieldName = "CorrectAnswer", - Width = 100, + Width = 150, ListOrderNo = 9, Visible = true, IsActive = true, @@ -31327,7 +31327,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency RoleId = null, UserId = null, CultureName = LanguageCodes.En, - SourceDbType = DbType.Int32, + SourceDbType = DbType.String, FieldName = "Explanation", Width = 100, ListOrderNo = 12, diff --git a/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionOption.cs b/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionOption.cs index f33bc2a1..fbc8d05e 100644 --- a/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionOption.cs +++ b/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionOption.cs @@ -11,19 +11,18 @@ public class QuestionOption : FullAuditedEntity, IMultiTenant public string Text { get; set; } public bool IsCorrect { get; set; } - // Foreign key + public Guid QuestionPoolId { get; set; } public Guid QuestionId { get; set; } public Question Question { get; set; } Guid? IMultiTenant.TenantId => TenantId; - // 🟢 EF Core ve ABP için parametresiz constructor ZORUNLU protected QuestionOption() { } - // 🟢 Yeni kayıt oluşturmak için custom constructor - public QuestionOption(Guid id, Guid questionId, string text, bool isCorrect) + public QuestionOption(Guid id, Guid questionPoolId, Guid questionId, string text, bool isCorrect) { - Id = id; // burada atayabilirsin çünkü ctor içinde protected set erişilebilir + Id = id; + QuestionPoolId = questionPoolId; QuestionId = questionId; Text = text; IsCorrect = isCorrect; diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index b83e6b0c..4d400187 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -1598,8 +1598,8 @@ public class PlatformDbContext : b.Property(x => x.Title).IsRequired().HasMaxLength(500); b.Property(x => x.Content).HasMaxLength(500); b.Property(x => x.MediaUrl).HasMaxLength(500); - b.Property(x => x.MediaType).HasMaxLength(500); - b.Property(x => x.CorrectAnswer).HasMaxLength(50); + b.Property(x => x.MediaType).HasMaxLength(10); + b.Property(x => x.CorrectAnswer).HasMaxLength(500); b.Property(x => x.Difficulty).HasMaxLength(10); b.Property(x => x.TimeLimit).HasDefaultValue(0); b.Property(x => x.Explanation).HasMaxLength(500); diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.Designer.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016203825_Initial.Designer.cs similarity index 99% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.Designer.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016203825_Initial.Designer.cs index 303a3a3a..5a9b53a9 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.Designer.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016203825_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Kurs.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20251016120353_Initial")] + [Migration("20251016203825_Initial")] partial class Initial { /// @@ -5113,8 +5113,8 @@ namespace Kurs.Platform.Migrations .HasColumnType("nvarchar(500)"); b.Property("CorrectAnswer") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); b.Property("CreationTime") .HasColumnType("datetime2") @@ -5155,8 +5155,8 @@ namespace Kurs.Platform.Migrations .HasColumnName("LastModifierId"); b.Property("MediaType") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); b.Property("MediaUrl") .HasMaxLength(500) @@ -5239,6 +5239,9 @@ namespace Kurs.Platform.Migrations b.Property("QuestionId") .HasColumnType("uniqueidentifier"); + b.Property("QuestionPoolId") + .HasColumnType("uniqueidentifier"); + b.Property("TenantId") .HasColumnType("uniqueidentifier") .HasColumnName("TenantId"); diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016203825_Initial.cs similarity index 99% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016203825_Initial.cs index 7a91ae18..918b334d 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016203825_Initial.cs @@ -2461,8 +2461,8 @@ namespace Kurs.Platform.Migrations Title = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), Content = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), MediaUrl = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - MediaType = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - CorrectAnswer = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + MediaType = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: true), + CorrectAnswer = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), Difficulty = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: true), TimeLimit = table.Column(type: "int", nullable: false, defaultValue: 0), Explanation = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), @@ -3280,6 +3280,7 @@ namespace Kurs.Platform.Migrations Id = table.Column(type: "uniqueidentifier", nullable: false), Text = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), IsCorrect = table.Column(type: "bit", nullable: false, defaultValue: false), + QuestionPoolId = table.Column(type: "uniqueidentifier", nullable: false), QuestionId = table.Column(type: "uniqueidentifier", nullable: false), TenantId = table.Column(type: "uniqueidentifier", nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 64317430..097c725f 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -5110,8 +5110,8 @@ namespace Kurs.Platform.Migrations .HasColumnType("nvarchar(500)"); b.Property("CorrectAnswer") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); b.Property("CreationTime") .HasColumnType("datetime2") @@ -5152,8 +5152,8 @@ namespace Kurs.Platform.Migrations .HasColumnName("LastModifierId"); b.Property("MediaType") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); b.Property("MediaUrl") .HasMaxLength(500) @@ -5236,6 +5236,9 @@ namespace Kurs.Platform.Migrations b.Property("QuestionId") .HasColumnType("uniqueidentifier"); + b.Property("QuestionPoolId") + .HasColumnType("uniqueidentifier"); + b.Property("TenantId") .HasColumnType("uniqueidentifier") .HasColumnName("TenantId"); diff --git a/ui/src/types/coordinator.ts b/ui/src/types/coordinator.ts index aab5f9b3..c851461e 100644 --- a/ui/src/types/coordinator.ts +++ b/ui/src/types/coordinator.ts @@ -51,7 +51,6 @@ export interface QuestionDto extends FullAuditedEntityDto { export interface QuestionOptionDto extends FullAuditedEntityDto { text: string; isCorrect: boolean; - order?: number; } export interface Exam { diff --git a/ui/src/views/coordinator/QuestionDialog.tsx b/ui/src/views/coordinator/QuestionDialog.tsx index f62c8f8e..6234f3e5 100644 --- a/ui/src/views/coordinator/QuestionDialog.tsx +++ b/ui/src/views/coordinator/QuestionDialog.tsx @@ -54,7 +54,6 @@ function QuestionDialog({ id: crypto.randomUUID(), text: '', isCorrect: false, - order: question.options?.length || 0, } setQuestion((prev) => prev ? { ...prev, options: [...(prev.options || []), newOption] } : prev, @@ -79,8 +78,8 @@ function QuestionDialog({ // 💾 Kaydetme const handleSave = async () => { - if (!question.title.trim() || !question.content.trim()) { - alert('Please fill in the title and content fields.') + if (!question.title.trim()) { + alert('Please fill in the title field.') return } @@ -122,23 +121,20 @@ function QuestionDialog({ const getCorrectAnswer = (q: QuestionDto): string | string[] => { switch (q.questionType) { - case 'multiple-choice': - return q.options?.find((opt) => opt.isCorrect)?.id || '' case 'multiple-answer': - return q.options?.filter((opt) => opt.isCorrect).map((opt) => opt.id!) || [] + const result = q.options?.filter((opt) => opt.isCorrect).map((opt) => opt.text!) || [] + return result.join(', ') + case 'multiple-choice': case 'true-false': case 'fill-blank': case 'open-ended': case 'calculation': - return q.correctAnswer as string + return q.options?.find((opt) => opt.isCorrect)?.text || '' case 'matching': - return q.options?.map((opt) => opt.id!) || [] case 'ordering': - return ( - q.options?.sort((a, b) => (a.order || 0) - (b.order || 0)).map((opt) => opt.id!) || [] - ) + return q.options?.map((opt) => opt.text!) || [] default: - return q.correctAnswer as string + return q.options?.find((opt) => opt.isCorrect)?.text || '' } } @@ -268,51 +264,61 @@ function QuestionDialog({ ) default: - // 🔹 Diğer tüm tipler: tek radio + text gösterilir - // Eğer hiç option yoksa bir tane oluştur - if (!question.options || question.options.length === 0) { - setQuestion((prev) => - prev - ? { - ...prev, - options: [ - { - id: crypto.randomUUID(), - text: prev.correctAnswer || 'Doğru cevap metni', - isCorrect: true, - order: 0, - }, - ], - } - : prev, - ) - } - return (
- +
+ {question?.options?.length! === 0 && ( + + )} +
- {question.options?.map((option) => ( + {question.options?.map((option, index) => (
+
+ {String.fromCharCode(65 + index)} +
handleChange('correctAnswer', option.id)} + name="correct-answer" + checked={option.isCorrect} + onChange={() => + setQuestion((prev) => { + if (!prev) return prev + const updated = prev.options?.map((opt, i) => ({ + ...opt, + isCorrect: i === index, + })) + return { ...prev, options: updated } + }) + } className="h-4 w-4 text-blue-600 border-gray-300" /> - {option.text} + updateOption(index, 'text', e.target.value)} + placeholder={`${String.fromCharCode(65 + index)} şıkkı`} + className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1" + /> +
))} -

- Bu soru tipinde yalnızca tek doğru cevap seçeneği bulunur. -

) } diff --git a/ui/src/views/coordinator/QuestionPools.tsx b/ui/src/views/coordinator/QuestionPools.tsx deleted file mode 100644 index e68c9ade..00000000 --- a/ui/src/views/coordinator/QuestionPools.tsx +++ /dev/null @@ -1,475 +0,0 @@ -import React, { useState } from "react"; -import { FaPlus, FaSearch, FaFilter, FaEdit, FaTrash } from "react-icons/fa"; -import { generateMockPools } from "@/mocks/mockPools"; -import { QuestionPoolDto, QuestionDto } from "@/types/coordinator"; -import QuestionDialog from "./QuestionDialog"; - -export const QuestionPoolManager: React.FC = () => { - const [pools, setPools] = useState(generateMockPools()); - const [searchTerm, setSearchTerm] = useState(""); - const [selectedTag, setSelectedTag] = useState(""); - const [isCreating, setIsCreating] = useState(false); - const [editingPool, setEditingPool] = useState(null); - const [editingQuestion, setEditingQuestion] = useState(null); - const [showQuestionEditor, setShowQuestionEditor] = useState(false); - const [selectedPoolForQuestion, setSelectedPoolForQuestion] = - useState(""); - - const [newPool, setNewPool] = useState({ - name: "", - description: "", - tags: [] as string[], - }); - - const filteredPools = pools.filter((pool) => { - const matchesSearch = - pool.name.toLowerCase().includes(searchTerm.toLowerCase()) || - pool.description.toLowerCase().includes(searchTerm.toLowerCase()); - const matchesTag = !selectedTag || pool.tags.includes(selectedTag); - return matchesSearch && matchesTag; - }); - - const allTags = Array.from(new Set(pools.flatMap((pool) => pool.tags))); - - const handleCreatePool = () => { - if (!newPool.name.trim()) return; - - const newPoolData: QuestionPoolDto = { - id: `pool-${Date.now()}`, - name: newPool.name, - description: newPool.description, - tags: newPool.tags, - questions: [], - createdBy: "current-user", - creationTime: new Date(), - }; - - setPools((prev) => [...prev, newPoolData]); - setNewPool({ name: "", description: "", tags: [] }); - setIsCreating(false); - }; - - const handleUpdatePool = () => { - if (!editingPool) return; - setPools((prev) => - prev.map((pool) => (pool.id === editingPool.id ? editingPool : pool)) - ); - setEditingPool(null); - }; - - const handleCreateQuestion = (poolId: string) => { - setSelectedPoolForQuestion(poolId); - setEditingQuestion(null); - setShowQuestionEditor(true); - }; - - const handleEditQuestion = (question: QuestionDto, poolId: string) => { - setSelectedPoolForQuestion(poolId); - setEditingQuestion(question); - setShowQuestionEditor(true); - }; - - const handleSaveQuestion = ( - questionData: Omit - ) => { - const pool = pools.find((p) => p.id === selectedPoolForQuestion); - if (!pool) return; - - const newQuestion: QuestionDto = { - ...questionData, - id: editingQuestion?.id || `q-${Date.now()}`, - creationTime: editingQuestion?.creationTime || new Date(), - lastModificationTime: new Date(), - }; - - const updatedQuestions = editingQuestion - ? pool.questions.map((q) => - q.id === editingQuestion.id ? newQuestion : q - ) - : [...pool.questions, newQuestion]; - - const updatedPool = { ...pool, questions: updatedQuestions }; - setPools((prev) => - prev.map((p) => (p.id === updatedPool.id ? updatedPool : p)) - ); - setShowQuestionEditor(false); - }; - - const handleDeleteQuestion = (questionId: string, poolId: string) => { - const pool = pools.find((p) => p.id === poolId); - if (!pool) return; - - if (window.confirm("Are you sure you want to delete this question?")) { - const updatedQuestions = pool.questions.filter( - (q) => q.id !== questionId - ); - const updatedPool = { ...pool, questions: updatedQuestions }; - setPools((prev) => - prev.map((p) => (p.id === updatedPool.id ? updatedPool : p)) - ); - } - }; - - return ( -
- {/* Header */} -
-
-

Soru Havuzları

-

- Soru havuzlarınızı oluşturun ve yönetin -

-
- - -
- - {/* Filters */} -
-
-
- - setSearchTerm(e.target.value)} - className="pl-8 w-full text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" - /> -
- -
- - -
- -
- Toplam: {filteredPools.length} havuz -
-
-
- - {/* Create/Edit Pool Modal */} - {(isCreating || editingPool) && ( -
-
-

- {isCreating ? "Yeni Soru Havuzu" : "Havuzu Düzenle"} -

- -
-
- - { - if (isCreating) { - setNewPool((prev) => ({ ...prev, name: e.target.value })); - } else if (editingPool) { - setEditingPool({ ...editingPool, name: e.target.value }); - } - }} - className="w-full text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" - placeholder="Havuz adını girin" - /> -
- -
- -