Question Pool ve Question

This commit is contained in:
Sedat ÖZTÜRK 2025-10-16 15:47:32 +03:00
parent f64f13557e
commit 7fdf4627d0
47 changed files with 2905 additions and 691 deletions

View file

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Questions;
public class QuestionDto : FullAuditedEntityDto<Guid>
{
public string QuestionType { get; set; }
public int Points { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string MediaUrl { get; set; }
public string MediaType { get; set; }
public string CorrectAnswer { get; set; }
public string Difficulty { get; set; }
public int TimeLimit { get; set; }
public string Explanation { get; set; }
public Guid QuestionPoolId { get; set; }
public List<QuestionOptionDto> Options { get; set; } = new();
}

View file

@ -0,0 +1,12 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Questions;
public class QuestionOptionDto : FullAuditedEntityDto<Guid>
{
public string Text { get; set; }
public bool IsCorrect { get; set; }
public Guid QuestionId { get; set; }
}

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Questions;
public class QuestionPoolDto : FullAuditedEntityDto<Guid>
{
public string Name { get; set; }
public string Description { get; set; }
public string Tags { get; set; }
public List<QuestionDto> Questions { get; set; } = new();
}

View file

@ -0,0 +1,26 @@
using System;
using Kurs.Platform.Entities;
using Kurs.Platform.Questions;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using static Kurs.Platform.Data.Seeds.SeedConsts;
namespace Kurs.Platform.DataSources;
public class QuestionAppService : CrudAppService<
Question,
QuestionDto,
Guid,
PagedAndSortedResultRequestDto>
{
public QuestionAppService(IRepository<Question, Guid> repo) : base(repo)
{
GetPolicyName = AppCodes.Definitions.Question;
GetListPolicyName = AppCodes.Definitions.Question;
CreatePolicyName = AppCodes.Definitions.Question + ".Create";
UpdatePolicyName = AppCodes.Definitions.Question + ".Update";
DeletePolicyName = AppCodes.Definitions.Question + ".Delete";
}
}

View file

@ -0,0 +1,13 @@
using AutoMapper;
using Kurs.Platform.Entities;
using Kurs.Platform.Questions;
namespace Kurs.Platform.DataSources;
public class QuestionAutoMapperProfile : Profile
{
public QuestionAutoMapperProfile()
{
CreateMap<Question, QuestionDto>().ReverseMap();
}
}

View file

@ -7881,10 +7881,22 @@
}, },
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Classroom.Tag", "key": "App.Definitions.Tag",
"tr": "Etiketler", "tr": "Etiketler",
"en": "Tags" "en": "Tags"
}, },
{
"resourceName": "Platform",
"key": "App.Definitions.QuestionPool",
"tr": "Soru Havuzu",
"en": "Question Pools"
},
{
"resourceName": "Platform",
"key": "App.Definitions.Question",
"tr": "Sorular",
"en": "Questions"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Definitions.RegistrationType", "key": "App.Definitions.RegistrationType",
@ -11985,31 +11997,31 @@
}, },
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Classroom", "key": "App.Coordinator.Classroom",
"tr": "Sınıf", "tr": "Sınıf",
"en": "Classroom" "en": "Classroom"
}, },
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Classroom.Dashboard", "key": "App.Coordinator.Classroom.Dashboard",
"tr": "Gösterge Paneli", "tr": "Gösterge Paneli",
"en": "Dashboard" "en": "Dashboard"
}, },
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Classroom.List", "key": "App.Coordinator.Classroom.List",
"tr": "Sınıflar", "tr": "Sınıflar",
"en": "Classes" "en": "Classes"
}, },
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Classroom.RoomDetail", "key": "App.Coordinator.Classroom.RoomDetail",
"tr": "Sanal Sınıf", "tr": "Sanal Sınıf",
"en": "Virtul Classroom" "en": "Virtul Classroom"
}, },
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Classroom.Planning", "key": "App.Coordinator.Classroom.Planning",
"tr": "Sınıf Planlama", "tr": "Sınıf Planlama",
"en": "Classroom Planning" "en": "Classroom Planning"
}, },
@ -12797,41 +12809,42 @@
] ]
}, },
{ {
"key": "admin.classroom.dashboard", "key": "admin.coordinator.classroom.dashboard",
"path": "/admin/classroom/dashboard", "path": "/admin/coordinator/classroom/dashboard",
"componentPath": "@/views/classroom/Dashboard", "componentPath": "@/views/coordinator/Classroom/Dashboard",
"routeType": "protected", "routeType": "protected",
"authority": [ "authority": [
"App.Classroom.Dashboard" "App.Coordinator.Classroom.Dashboard"
] ]
}, },
{ {
"key": "admin.classroom.classes", "key": "admin.coordinator.classroom.classes",
"path": "/admin/classroom/classes", "path": "/admin/coordinator/classroom/classes",
"componentPath": "@/views/classroom/ClassList", "componentPath": "@/views/coordinator/Classroom/ClassList",
"routeType": "protected", "routeType": "protected",
"authority": [ "authority": [
"App.Classroom.List" "App.Coordinator.Classroom.List"
] ]
}, },
{ {
"key": "admin.classroom.classroom", "key": "admin.coordinator.classroom.classroom",
"path": "/admin/classroom/room/:id", "path": "/admin/coordinator/classroom/room/:id",
"componentPath": "@/views/classroom/RoomDetail", "componentPath": "@/views/coordinator/Classroom/RoomDetail",
"routeType": "protected", "routeType": "protected",
"authority": [ "authority": [
"App.Classroom.RoomDetail" "App.Coordinator.Classroom.RoomDetail"
] ]
}, },
{ {
"key": "admin.classroom.planning", "key": "admin.coordinator.classroom.planning",
"path": "/admin/classroom/planning/:id", "path": "/admin/coordinator/classroom/planning/:id",
"componentPath": "@/views/classroom/PlanningPage", "componentPath": "@/views/coordinator/Classroom/PlanningPage",
"routeType": "protected", "routeType": "protected",
"authority": [ "authority": [
"App.Classroom.Planning" "App.Coordinator.Classroom.Planning"
] ]
}, },
{ {
"key": "admin.supplychain.materialTypes", "key": "admin.supplychain.materialTypes",
"path": "/admin/supplychain/materials/types", "path": "/admin/supplychain/materials/types",
@ -14599,22 +14612,32 @@
}, },
{ {
"ParentCode": "App.Coordinator.Definitions", "ParentCode": "App.Coordinator.Definitions",
"Code": "App.Classroom.Tag", "Code": "App.Definitions.Tag",
"DisplayName": "App.Classroom.Tag", "DisplayName": "App.Definitions.Tag",
"Order": 10, "Order": 10,
"Url": "/admin/list/list-tag", "Url": "/admin/list/list-tag",
"Icon": "FcTags", "Icon": "FcTags",
"RequiredPermissionName": "App.Classroom.Tag", "RequiredPermissionName": "App.Definitions.Tag",
"IsDisabled": false
},
{
"ParentCode": "App.Coordinator.Definitions",
"Code": "App.Definitions.QuestionPool",
"DisplayName": "App.Definitions.QuestionPool",
"Order": 11,
"Url": "/admin/list/list-questionpool",
"Icon": "FcQuestions",
"RequiredPermissionName": "App.Definitions.QuestionPool",
"IsDisabled": false "IsDisabled": false
}, },
{ {
"ParentCode": "App.Coordinator", "ParentCode": "App.Coordinator",
"Code": "App.Classroom", "Code": "App.Coordinator.Classroom",
"DisplayName": "App.Classroom", "DisplayName": "App.Coordinator.Classroom",
"Order": 2, "Order": 2,
"Url": "/admin/classroom/dashboard", "Url": "/admin/coordinator/classroom/dashboard",
"Icon": "FcNeutralDecision", "Icon": "FcNeutralDecision",
"RequiredPermissionName": "App.Classroom.Dashboard", "RequiredPermissionName": "App.Coordinator.Classroom.Dashboard",
"IsDisabled": false "IsDisabled": false
}, },
{ {
@ -21895,62 +21918,17 @@
}, },
{ {
"GroupName": "App.Coordinator", "GroupName": "App.Coordinator",
"Name": "App.Classroom", "Name": "App.Definitions.Tag",
"ParentName": null, "ParentName": null,
"DisplayName": "App.Classroom", "DisplayName": "App.Definitions.Tag",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Classroom.Dashboard",
"ParentName": "App.Classroom",
"DisplayName": "App.Classroom.Dashboard",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Classroom.List",
"ParentName": "App.Classroom",
"DisplayName": "App.Classroom.List",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Classroom.RoomDetail",
"ParentName": "App.Classroom",
"DisplayName": "App.Classroom.RoomDetail",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Classroom.Planning",
"ParentName": "App.Classroom",
"DisplayName": "App.Classroom.Planning",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Classroom.Tag",
"ParentName": null,
"DisplayName": "App.Classroom.Tag",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3, "MultiTenancySide": 3,
"MenuGroup": "Kurs" "MenuGroup": "Kurs"
}, },
{ {
"GroupName": "App.Coordinator", "GroupName": "App.Coordinator",
"Name": "App.Classroom.Tag.Create", "Name": "App.Definitions.Tag.Create",
"ParentName": "App.Classroom.Tag", "ParentName": "App.Definitions.Tag",
"DisplayName": "Create", "DisplayName": "Create",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3, "MultiTenancySide": 3,
@ -21958,8 +21936,8 @@
}, },
{ {
"GroupName": "App.Coordinator", "GroupName": "App.Coordinator",
"Name": "App.Classroom.Tag.Update", "Name": "App.Definitions.Tag.Update",
"ParentName": "App.Classroom.Tag", "ParentName": "App.Definitions.Tag",
"DisplayName": "Update", "DisplayName": "Update",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3, "MultiTenancySide": 3,
@ -21967,8 +21945,8 @@
}, },
{ {
"GroupName": "App.Coordinator", "GroupName": "App.Coordinator",
"Name": "App.Classroom.Tag.Delete", "Name": "App.Definitions.Tag.Delete",
"ParentName": "App.Classroom.Tag", "ParentName": "App.Definitions.Tag",
"DisplayName": "Delete", "DisplayName": "Delete",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3, "MultiTenancySide": 3,
@ -21976,8 +21954,8 @@
}, },
{ {
"GroupName": "App.Coordinator", "GroupName": "App.Coordinator",
"Name": "App.Classroom.Tag.Export", "Name": "App.Definitions.Tag.Export",
"ParentName": "App.Classroom.Tag", "ParentName": "App.Definitions.Tag",
"DisplayName": "Export", "DisplayName": "Export",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3, "MultiTenancySide": 3,
@ -21985,8 +21963,8 @@
}, },
{ {
"GroupName": "App.Coordinator", "GroupName": "App.Coordinator",
"Name": "App.Classroom.Tag.Import", "Name": "App.Definitions.Tag.Import",
"ParentName": "App.Classroom.Tag", "ParentName": "App.Definitions.Tag",
"DisplayName": "Import", "DisplayName": "Import",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3, "MultiTenancySide": 3,
@ -21994,13 +21972,185 @@
}, },
{ {
"GroupName": "App.Coordinator", "GroupName": "App.Coordinator",
"Name": "App.Classroom.Tag.Activity", "Name": "App.Definitions.Tag.Activity",
"ParentName": "App.Classroom.Tag", "ParentName": "App.Definitions.Tag",
"DisplayName": "Activity", "DisplayName": "Activity",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 3, "MultiTenancySide": 3,
"MenuGroup": "Kurs" "MenuGroup": "Kurs"
}, },
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.QuestionPool",
"ParentName": null,
"DisplayName": "App.Definitions.QuestionPool",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.QuestionPool.Create",
"ParentName": "App.Definitions.QuestionPool",
"DisplayName": "Create",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.QuestionPool.Update",
"ParentName": "App.Definitions.QuestionPool",
"DisplayName": "Update",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.QuestionPool.Delete",
"ParentName": "App.Definitions.QuestionPool",
"DisplayName": "Delete",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.QuestionPool.Export",
"ParentName": "App.Definitions.QuestionPool",
"DisplayName": "Export",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.QuestionPool.Import",
"ParentName": "App.Definitions.QuestionPool",
"DisplayName": "Import",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.QuestionPool.Activity",
"ParentName": "App.Definitions.QuestionPool",
"DisplayName": "Activity",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.Question",
"ParentName": null,
"DisplayName": "App.Definitions.Question",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.Question.Create",
"ParentName": "App.Definitions.Question",
"DisplayName": "Create",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.Question.Update",
"ParentName": "App.Definitions.Question",
"DisplayName": "Update",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.Question.Delete",
"ParentName": "App.Definitions.Question",
"DisplayName": "Delete",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.Question.Export",
"ParentName": "App.Definitions.Question",
"DisplayName": "Export",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.Question.Import",
"ParentName": "App.Definitions.Question",
"DisplayName": "Import",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Definitions.Question.Activity",
"ParentName": "App.Definitions.Question",
"DisplayName": "Activity",
"IsEnabled": true,
"MultiTenancySide": 3,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Coordinator.Classroom",
"ParentName": null,
"DisplayName": "App.Coordinator.Classroom",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Coordinator.Classroom.Dashboard",
"ParentName": "App.Coordinator.Classroom",
"DisplayName": "App.Coordinator.Classroom.Dashboard",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Coordinator.Classroom.List",
"ParentName": "App.Coordinator.Classroom",
"DisplayName": "App.Coordinator.Classroom.List",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Coordinator.Classroom.RoomDetail",
"ParentName": "App.Coordinator.Classroom",
"DisplayName": "App.Coordinator.Classroom.RoomDetail",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{
"GroupName": "App.Coordinator",
"Name": "App.Coordinator.Classroom.Planning",
"ParentName": "App.Coordinator.Classroom",
"DisplayName": "App.Coordinator.Classroom.Planning",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Kurs"
},
{ {
"GroupName": "App.SupplyChain", "GroupName": "App.SupplyChain",

File diff suppressed because it is too large Load diff

View file

@ -149,6 +149,22 @@ public static class PlatformConsts
$"\"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\".\"Name\" AS \"Name\" " + $"\"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\".\"Name\" AS \"Name\" " +
$"FROM \"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\" " + $"FROM \"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\" " +
$"ORDER BY \"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\".\"Installment\";"; $"ORDER BY \"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\".\"Installment\";";
public static string QuestionPoolValues =
$"SELECT " +
$"\"Id\" AS \"Key\", " +
$"\"Name\" AS \"Name\" " +
$"FROM \"{SelectCommandByTableName("QuestionPool", Prefix.DbTableCoordinator)}\" " +
$"WHERE " +
$"\"IsDeleted\" = 'false' ";
public static string QuestionTagValues =
$"SELECT " +
$"\"Id\" AS \"Key\", " +
$"\"Name\" AS \"Name\" " +
$"FROM \"{SelectCommandByTableName("Tag", Prefix.DbTableCoordinator)}\" " +
$"WHERE " +
$"\"IsDeleted\" = 'false' ";
} }
public static class Prefix public static class Prefix
@ -508,10 +524,11 @@ public static class PlatformConsts
{ {
public static class Forms public static class Forms
{ {
public const string FormTenant = "form-tenant";
public const string FormLanguage = "form-language"; public const string FormLanguage = "form-language";
public const string FormUomCategory = "form-uomcategory"; public const string FormUomCategory = "form-uomcategory";
public const string FormSkillType = "form-skilltype"; public const string FormSkillType = "form-skilltype";
public const string FormTenant = "form-tenant";
public const string FormQuestionPool = "form-questionpool";
public const string FormBank = "form-bank"; public const string FormBank = "form-bank";
} }
@ -596,6 +613,8 @@ public static class PlatformConsts
public const string Class = "list-class"; public const string Class = "list-class";
public const string Level = "list-level"; public const string Level = "list-level";
public const string Tag = "list-tag"; public const string Tag = "list-tag";
public const string QuestionPool = "list-questionpool";
public const string Question = "list-question";
} }
} }

View file

@ -438,12 +438,14 @@ public static class SeedConsts
public const string ClassType = Default + ".ClassType"; public const string ClassType = Default + ".ClassType";
public const string Class = Default + ".Class"; public const string Class = Default + ".Class";
public const string Level = Default + ".Level"; public const string Level = Default + ".Level";
public const string Tag = Default + ".Tag";
public const string QuestionPool = Default + ".QuestionPool";
public const string Question = Default + ".Question";
} }
public static class Classroom public static class Coordinator
{ {
public const string Default = Prefix.App + ".Classroom"; public const string Default = Prefix.App + ".Classroom";
public const string Tag = Default + ".Tag";
} }
public static class Accounting public static class Accounting

View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Entities;
public class Question : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId;
public string QuestionType { get; set; } // "multiple-choice", "fill-blank" vb.
public int Points { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string MediaUrl { get; set; }
public string MediaType { get; set; }
public string CorrectAnswer { get; set; }
public string Difficulty { get; set; } // "easy", "medium", "hard"
public int TimeLimit { get; set; }
public string Explanation { get; set; }
public ICollection<QuestionOption> Options { get; set; } = [];
// Foreign key
public Guid QuestionPoolId { get; set; }
public QuestionPool QuestionPool { get; set; }
Guid? IMultiTenant.TenantId => TenantId;
}

View file

@ -0,0 +1,19 @@
using System;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Entities;
public class QuestionOption : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId;
public string Text { get; set; }
public bool IsCorrect { get; set; }
// Foreign key
public Guid QuestionId { get; set; }
public Question Question { get; set; }
Guid? IMultiTenant.TenantId => TenantId;
}

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Entities;
public class QuestionPool : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId;
public string Name { get; set; }
public string Description { get; set; }
public string Tags { get; set; } // İstersen JSON veya ayrı tablo da olabilir
public ICollection<Question> Questions { get; set; } = new List<Question>();
Guid? IMultiTenant.TenantId => TenantId;
}

View file

@ -141,6 +141,9 @@ public class PlatformDbContext :
public DbSet<ClassroomChat> ChatMessages { get; set; } public DbSet<ClassroomChat> ChatMessages { get; set; }
public DbSet<Tag> Tags { get; set; } public DbSet<Tag> Tags { get; set; }
public DbSet<QuestionPool> QuestionPools { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<QuestionOption> QuestionOptions { get; set; }
#endregion #endregion
@ -488,6 +491,16 @@ public class PlatformDbContext :
b.Property(x => x.ErrorsJson).HasColumnType("text"); b.Property(x => x.ErrorsJson).HasColumnType("text");
}); });
builder.Entity<Activity>(b =>
{
b.ToTable(Prefix.DbTableDefault + nameof(Activity), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Type).IsRequired();
b.Property(x => x.Subject).IsRequired().HasMaxLength(256);
b.Property(x => x.Content).IsRequired().HasMaxLength(2000);
});
builder.Entity<BackgroundWorker>(b => builder.Entity<BackgroundWorker>(b =>
{ {
b.ToTable(Prefix.DbTableDefault + nameof(BackgroundWorker), Prefix.DbSchema); b.ToTable(Prefix.DbTableDefault + nameof(BackgroundWorker), Prefix.DbSchema);
@ -1487,7 +1500,7 @@ public class PlatformDbContext :
//Coordinator //Coordinator
builder.Entity<Classroom>(b => builder.Entity<Classroom>(b =>
{ {
b.ToTable(Prefix.DbTableDefault + nameof(Classroom), Prefix.DbSchema); b.ToTable(Prefix.DbTableCoordinator + nameof(Classroom), Prefix.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(200); b.Property(x => x.Name).IsRequired().HasMaxLength(200);
@ -1516,7 +1529,7 @@ public class PlatformDbContext :
builder.Entity<ClassroomParticipant>(b => builder.Entity<ClassroomParticipant>(b =>
{ {
b.ToTable(Prefix.DbTableDefault + nameof(ClassroomParticipant), Prefix.DbSchema); b.ToTable(Prefix.DbTableCoordinator + nameof(ClassroomParticipant), Prefix.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.UserName).IsRequired().HasMaxLength(100); b.Property(x => x.UserName).IsRequired().HasMaxLength(100);
@ -1531,7 +1544,7 @@ public class PlatformDbContext :
builder.Entity<ClassroomAttandance>(b => builder.Entity<ClassroomAttandance>(b =>
{ {
b.ToTable(Prefix.DbTableDefault + nameof(ClassroomAttandance), Prefix.DbSchema); b.ToTable(Prefix.DbTableCoordinator + nameof(ClassroomAttandance), Prefix.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.StudentName).IsRequired().HasMaxLength(100); b.Property(x => x.StudentName).IsRequired().HasMaxLength(100);
@ -1543,7 +1556,7 @@ public class PlatformDbContext :
builder.Entity<ClassroomChat>(b => builder.Entity<ClassroomChat>(b =>
{ {
b.ToTable(Prefix.DbTableDefault + nameof(ClassroomChat), Prefix.DbSchema); b.ToTable(Prefix.DbTableCoordinator + nameof(ClassroomChat), Prefix.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.SenderName).IsRequired().HasMaxLength(100); b.Property(x => x.SenderName).IsRequired().HasMaxLength(100);
@ -1554,18 +1567,6 @@ public class PlatformDbContext :
b.HasIndex(x => x.Timestamp); b.HasIndex(x => x.Timestamp);
}); });
// Form Activity Entities
builder.Entity<Activity>(b =>
{
b.ToTable(Prefix.DbTableDefault + nameof(Activity), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Type).IsRequired();
b.Property(x => x.Subject).IsRequired().HasMaxLength(256);
b.Property(x => x.Content).IsRequired().HasMaxLength(2000);
});
// Classroom
builder.Entity<Tag>(b => builder.Entity<Tag>(b =>
{ {
b.ToTable(Prefix.DbTableCoordinator + nameof(Tag), Prefix.DbSchema); b.ToTable(Prefix.DbTableCoordinator + nameof(Tag), Prefix.DbSchema);
@ -1576,5 +1577,41 @@ public class PlatformDbContext :
b.Property(x => x.Color).HasMaxLength(7); b.Property(x => x.Color).HasMaxLength(7);
b.Property(x => x.UsageCount).HasDefaultValue(0); b.Property(x => x.UsageCount).HasDefaultValue(0);
}); });
builder.Entity<QuestionPool>(b =>
{
b.ToTable(Prefix.DbTableCoordinator + nameof(QuestionPool), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(100);
b.Property(x => x.Description).HasMaxLength(500);
b.Property(x => x.Tags).HasMaxLength(500);
});
builder.Entity<Question>(b =>
{
b.ToTable(Prefix.DbTableCoordinator + nameof(Question), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.QuestionType).IsRequired().HasMaxLength(100);
b.Property(x => x.Points).HasDefaultValue(0);
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.Difficulty).HasMaxLength(10);
b.Property(x => x.TimeLimit).HasDefaultValue(0);
b.Property(x => x.Explanation).HasMaxLength(500);
});
builder.Entity<QuestionOption>(b =>
{
b.ToTable(Prefix.DbTableCoordinator + nameof(QuestionOption), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Text).HasMaxLength(100);
b.Property(x => x.IsCorrect).HasDefaultValue(false);
});
} }
} }

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Kurs.Platform.Migrations namespace Kurs.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20251015192202_Initial")] [Migration("20251016120353_Initial")]
partial class Initial partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -1780,7 +1780,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("TeacherId"); b.HasIndex("TeacherId");
b.ToTable("PClassroom", (string)null); b.ToTable("CClassroom", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ClassroomAttandance", b => modelBuilder.Entity("Kurs.Platform.Entities.ClassroomAttandance", b =>
@ -1853,7 +1853,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("StudentId"); b.HasIndex("StudentId");
b.ToTable("PClassroomAttandance", (string)null); b.ToTable("CClassroomAttandance", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ClassroomChat", b => modelBuilder.Entity("Kurs.Platform.Entities.ClassroomChat", b =>
@ -1937,7 +1937,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("Timestamp"); b.HasIndex("Timestamp");
b.ToTable("PClassroomChat", (string)null); b.ToTable("CClassroomChat", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ClassroomParticipant", b => modelBuilder.Entity("Kurs.Platform.Entities.ClassroomParticipant", b =>
@ -2032,7 +2032,7 @@ namespace Kurs.Platform.Migrations
.IsUnique() .IsUnique()
.HasFilter("[UserId] IS NOT NULL"); .HasFilter("[UserId] IS NOT NULL");
b.ToTable("PClassroomParticipant", (string)null); b.ToTable("CClassroomParticipant", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Contact", b => modelBuilder.Entity("Kurs.Platform.Entities.Contact", b =>
@ -5103,6 +5103,214 @@ namespace Kurs.Platform.Migrations
b.ToTable("DPsychologist", (string)null); b.ToTable("DPsychologist", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Question", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("Content")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<string>("CorrectAnswer")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Difficulty")
.HasMaxLength(10)
.HasColumnType("nvarchar(10)");
b.Property<string>("Explanation")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("MediaType")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<string>("MediaUrl")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<int>("Points")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<Guid>("QuestionPoolId")
.HasColumnType("uniqueidentifier");
b.Property<string>("QuestionType")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<int>("TimeLimit")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.HasKey("Id");
b.HasIndex("QuestionPoolId");
b.ToTable("CQuestion", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionOption", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<bool>("IsCorrect")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<Guid>("QuestionId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Text")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.ToTable("CQuestionOption", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionPool", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Tags")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.ToTable("CQuestionPool", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b => modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -6115,7 +6323,6 @@ namespace Kurs.Platform.Migrations
modelBuilder.Entity("Kurs.Platform.Entities.Tag", b => modelBuilder.Entity("Kurs.Platform.Entities.Tag", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<string>("Color") b.Property<string>("Color")
@ -8969,6 +9176,28 @@ namespace Kurs.Platform.Migrations
b.Navigation("Order"); b.Navigation("Order");
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Question", b =>
{
b.HasOne("Kurs.Platform.Entities.QuestionPool", "QuestionPool")
.WithMany("Questions")
.HasForeignKey("QuestionPoolId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("QuestionPool");
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionOption", b =>
{
b.HasOne("Kurs.Platform.Entities.Question", "Question")
.WithMany("Options")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
});
modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b => modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b =>
{ {
b.HasOne("Kurs.Platform.Entities.RegistrationType", "RegistrationType") b.HasOne("Kurs.Platform.Entities.RegistrationType", "RegistrationType")
@ -9298,6 +9527,16 @@ namespace Kurs.Platform.Migrations
b.Navigation("Items"); b.Navigation("Items");
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Question", b =>
{
b.Navigation("Options");
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionPool", b =>
{
b.Navigation("Questions");
});
modelBuilder.Entity("Kurs.Platform.Entities.RegistrationType", b => modelBuilder.Entity("Kurs.Platform.Entities.RegistrationType", b =>
{ {
b.Navigation("ClassTypes"); b.Navigation("ClassTypes");

View file

@ -434,6 +434,61 @@ namespace Kurs.Platform.Migrations
table.PrimaryKey("PK_AbpUsers", x => x.Id); table.PrimaryKey("PK_AbpUsers", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "CClassroom",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
Subject = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
TeacherId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
TeacherName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
ScheduledStartTime = table.Column<DateTime>(type: "datetime2", nullable: false),
ScheduledEndTime = table.Column<DateTime>(type: "datetime2", nullable: true),
Duration = table.Column<int>(type: "int", nullable: false),
ActualStartTime = table.Column<DateTime>(type: "datetime2", nullable: true),
ActualEndTime = table.Column<DateTime>(type: "datetime2", nullable: true),
MaxParticipants = table.Column<int>(type: "int", nullable: false),
ParticipantCount = table.Column<int>(type: "int", nullable: false),
SettingsJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CClassroom", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CQuestionPool",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Description = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
Tags = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CQuestionPool", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "CTag", name: "CTag",
columns: table => new columns: table => new
@ -1370,39 +1425,6 @@ namespace Kurs.Platform.Migrations
table.PrimaryKey("PK_PBranchUsers", x => new { x.UserId, x.BranchId }); table.PrimaryKey("PK_PBranchUsers", x => new { x.UserId, x.BranchId });
}); });
migrationBuilder.CreateTable(
name: "PClassroom",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: true),
Subject = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
TeacherId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
TeacherName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
ScheduledStartTime = table.Column<DateTime>(type: "datetime2", nullable: false),
ScheduledEndTime = table.Column<DateTime>(type: "datetime2", nullable: true),
Duration = table.Column<int>(type: "int", nullable: false),
ActualStartTime = table.Column<DateTime>(type: "datetime2", nullable: true),
ActualEndTime = table.Column<DateTime>(type: "datetime2", nullable: true),
MaxParticipants = table.Column<int>(type: "int", nullable: false),
ParticipantCount = table.Column<int>(type: "int", nullable: false),
SettingsJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PClassroom", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "PCustomComponent", name: "PCustomComponent",
columns: table => new columns: table => new
@ -2325,6 +2347,146 @@ namespace Kurs.Platform.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "CClassroomAttandance",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SessionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
StudentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
StudentName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
JoinTime = table.Column<DateTime>(type: "datetime2", nullable: false),
LeaveTime = table.Column<DateTime>(type: "datetime2", nullable: true),
TotalDurationMinutes = table.Column<int>(type: "int", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CClassroomAttandance", x => x.Id);
table.ForeignKey(
name: "FK_CClassroomAttandance_CClassroom_SessionId",
column: x => x.SessionId,
principalTable: "CClassroom",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "CClassroomChat",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SessionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
SenderId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SenderName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Message = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false),
Timestamp = table.Column<DateTime>(type: "datetime2", nullable: false),
RecipientId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
RecipientName = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsTeacher = table.Column<bool>(type: "bit", nullable: false),
MessageType = table.Column<string>(type: "nvarchar(max)", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CClassroomChat", x => x.Id);
table.ForeignKey(
name: "FK_CClassroomChat_CClassroom_SessionId",
column: x => x.SessionId,
principalTable: "CClassroom",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "CClassroomParticipant",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SessionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
UserName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
IsTeacher = table.Column<bool>(type: "bit", nullable: false),
IsAudioMuted = table.Column<bool>(type: "bit", nullable: false),
IsVideoMuted = table.Column<bool>(type: "bit", nullable: false),
IsHandRaised = table.Column<bool>(type: "bit", nullable: false),
IsKicked = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
JoinTime = table.Column<DateTime>(type: "datetime2", nullable: false),
ConnectionId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CClassroomParticipant", x => x.Id);
table.ForeignKey(
name: "FK_CClassroomParticipant_CClassroom_SessionId",
column: x => x.SessionId,
principalTable: "CClassroom",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "CQuestion",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
QuestionType = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Points = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
Title = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
Content = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
MediaUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
MediaType = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
CorrectAnswer = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
Difficulty = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: true),
TimeLimit = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
Explanation = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
QuestionPoolId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CQuestion", x => x.Id);
table.ForeignKey(
name: "FK_CQuestion_CQuestionPool_QuestionPoolId",
column: x => x.QuestionPoolId,
principalTable: "CQuestionPool",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "DCountry", name: "DCountry",
columns: table => new columns: table => new
@ -2674,110 +2836,6 @@ namespace Kurs.Platform.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "PClassroomAttandance",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SessionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
StudentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
StudentName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
JoinTime = table.Column<DateTime>(type: "datetime2", nullable: false),
LeaveTime = table.Column<DateTime>(type: "datetime2", nullable: true),
TotalDurationMinutes = table.Column<int>(type: "int", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PClassroomAttandance", x => x.Id);
table.ForeignKey(
name: "FK_PClassroomAttandance_PClassroom_SessionId",
column: x => x.SessionId,
principalTable: "PClassroom",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PClassroomChat",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SessionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
SenderId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SenderName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Message = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false),
Timestamp = table.Column<DateTime>(type: "datetime2", nullable: false),
RecipientId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
RecipientName = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsTeacher = table.Column<bool>(type: "bit", nullable: false),
MessageType = table.Column<string>(type: "nvarchar(max)", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PClassroomChat", x => x.Id);
table.ForeignKey(
name: "FK_PClassroomChat_PClassroom_SessionId",
column: x => x.SessionId,
principalTable: "PClassroom",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PClassroomParticipant",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
BranchId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SessionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
UserName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
IsTeacher = table.Column<bool>(type: "bit", nullable: false),
IsAudioMuted = table.Column<bool>(type: "bit", nullable: false),
IsVideoMuted = table.Column<bool>(type: "bit", nullable: false),
IsHandRaised = table.Column<bool>(type: "bit", nullable: false),
IsKicked = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
JoinTime = table.Column<DateTime>(type: "datetime2", nullable: false),
ConnectionId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PClassroomParticipant", x => x.Id);
table.ForeignKey(
name: "FK_PClassroomParticipant_PClassroom_SessionId",
column: x => x.SessionId,
principalTable: "PClassroom",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "PApiEndpoint", name: "PApiEndpoint",
columns: table => new columns: table => new
@ -3215,6 +3273,34 @@ namespace Kurs.Platform.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "CQuestionOption",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Text = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
IsCorrect = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
QuestionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CQuestionOption", x => x.Id);
table.ForeignKey(
name: "FK_CQuestionOption_CQuestion_QuestionId",
column: x => x.QuestionId,
principalTable: "CQuestion",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "DCity", name: "DCity",
columns: table => new columns: table => new
@ -3748,6 +3834,73 @@ namespace Kurs.Platform.Migrations
table: "AbpUsers", table: "AbpUsers",
column: "UserName"); column: "UserName");
migrationBuilder.CreateIndex(
name: "IX_CClassroom_ScheduledStartTime",
table: "CClassroom",
column: "ScheduledStartTime");
migrationBuilder.CreateIndex(
name: "IX_CClassroom_TeacherId",
table: "CClassroom",
column: "TeacherId");
migrationBuilder.CreateIndex(
name: "IX_CClassroomAttandance_JoinTime",
table: "CClassroomAttandance",
column: "JoinTime");
migrationBuilder.CreateIndex(
name: "IX_CClassroomAttandance_SessionId",
table: "CClassroomAttandance",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_CClassroomAttandance_StudentId",
table: "CClassroomAttandance",
column: "StudentId");
migrationBuilder.CreateIndex(
name: "IX_CClassroomChat_SenderId",
table: "CClassroomChat",
column: "SenderId");
migrationBuilder.CreateIndex(
name: "IX_CClassroomChat_SessionId",
table: "CClassroomChat",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_CClassroomChat_Timestamp",
table: "CClassroomChat",
column: "Timestamp");
migrationBuilder.CreateIndex(
name: "IX_CClassroomParticipant_SessionId",
table: "CClassroomParticipant",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_CClassroomParticipant_SessionId_UserId",
table: "CClassroomParticipant",
columns: new[] { "SessionId", "UserId" },
unique: true,
filter: "[UserId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_CClassroomParticipant_UserId",
table: "CClassroomParticipant",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_CQuestion_QuestionPoolId",
table: "CQuestion",
column: "QuestionPoolId");
migrationBuilder.CreateIndex(
name: "IX_CQuestionOption_QuestionId",
table: "CQuestionOption",
column: "QuestionId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_DBankAccount_BankId", name: "IX_DBankAccount_BankId",
table: "DBankAccount", table: "DBankAccount",
@ -3905,63 +4058,6 @@ namespace Kurs.Platform.Migrations
columns: new[] { "TableName", "Order" }, columns: new[] { "TableName", "Order" },
unique: true); unique: true);
migrationBuilder.CreateIndex(
name: "IX_PClassroom_ScheduledStartTime",
table: "PClassroom",
column: "ScheduledStartTime");
migrationBuilder.CreateIndex(
name: "IX_PClassroom_TeacherId",
table: "PClassroom",
column: "TeacherId");
migrationBuilder.CreateIndex(
name: "IX_PClassroomAttandance_JoinTime",
table: "PClassroomAttandance",
column: "JoinTime");
migrationBuilder.CreateIndex(
name: "IX_PClassroomAttandance_SessionId",
table: "PClassroomAttandance",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_PClassroomAttandance_StudentId",
table: "PClassroomAttandance",
column: "StudentId");
migrationBuilder.CreateIndex(
name: "IX_PClassroomChat_SenderId",
table: "PClassroomChat",
column: "SenderId");
migrationBuilder.CreateIndex(
name: "IX_PClassroomChat_SessionId",
table: "PClassroomChat",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_PClassroomChat_Timestamp",
table: "PClassroomChat",
column: "Timestamp");
migrationBuilder.CreateIndex(
name: "IX_PClassroomParticipant_SessionId",
table: "PClassroomParticipant",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_PClassroomParticipant_SessionId_UserId",
table: "PClassroomParticipant",
columns: new[] { "SessionId", "UserId" },
unique: true,
filter: "[UserId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_PClassroomParticipant_UserId",
table: "PClassroomParticipant",
column: "UserId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PCustomEntityField_EntityId", name: "IX_PCustomEntityField_EntityId",
table: "PCustomEntityField", table: "PCustomEntityField",
@ -4165,6 +4261,18 @@ namespace Kurs.Platform.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AbpUserTokens"); name: "AbpUserTokens");
migrationBuilder.DropTable(
name: "CClassroomAttandance");
migrationBuilder.DropTable(
name: "CClassroomChat");
migrationBuilder.DropTable(
name: "CClassroomParticipant");
migrationBuilder.DropTable(
name: "CQuestionOption");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "CTag"); name: "CTag");
@ -4294,15 +4402,6 @@ namespace Kurs.Platform.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PBranchUsers"); name: "PBranchUsers");
migrationBuilder.DropTable(
name: "PClassroomAttandance");
migrationBuilder.DropTable(
name: "PClassroomChat");
migrationBuilder.DropTable(
name: "PClassroomParticipant");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PCustomComponent"); name: "PCustomComponent");
@ -4396,6 +4495,12 @@ namespace Kurs.Platform.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AbpUsers"); name: "AbpUsers");
migrationBuilder.DropTable(
name: "CClassroom");
migrationBuilder.DropTable(
name: "CQuestion");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "DBank"); name: "DBank");
@ -4429,9 +4534,6 @@ namespace Kurs.Platform.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PBackgroundWorker_MailQueue"); name: "PBackgroundWorker_MailQueue");
migrationBuilder.DropTable(
name: "PClassroom");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PCustomEntity"); name: "PCustomEntity");
@ -4462,6 +4564,9 @@ namespace Kurs.Platform.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AbpAuditLogs"); name: "AbpAuditLogs");
migrationBuilder.DropTable(
name: "CQuestionPool");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "DCountry"); name: "DCountry");

View file

@ -1777,7 +1777,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("TeacherId"); b.HasIndex("TeacherId");
b.ToTable("PClassroom", (string)null); b.ToTable("CClassroom", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ClassroomAttandance", b => modelBuilder.Entity("Kurs.Platform.Entities.ClassroomAttandance", b =>
@ -1850,7 +1850,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("StudentId"); b.HasIndex("StudentId");
b.ToTable("PClassroomAttandance", (string)null); b.ToTable("CClassroomAttandance", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ClassroomChat", b => modelBuilder.Entity("Kurs.Platform.Entities.ClassroomChat", b =>
@ -1934,7 +1934,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("Timestamp"); b.HasIndex("Timestamp");
b.ToTable("PClassroomChat", (string)null); b.ToTable("CClassroomChat", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.ClassroomParticipant", b => modelBuilder.Entity("Kurs.Platform.Entities.ClassroomParticipant", b =>
@ -2029,7 +2029,7 @@ namespace Kurs.Platform.Migrations
.IsUnique() .IsUnique()
.HasFilter("[UserId] IS NOT NULL"); .HasFilter("[UserId] IS NOT NULL");
b.ToTable("PClassroomParticipant", (string)null); b.ToTable("CClassroomParticipant", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Contact", b => modelBuilder.Entity("Kurs.Platform.Entities.Contact", b =>
@ -5100,6 +5100,214 @@ namespace Kurs.Platform.Migrations
b.ToTable("DPsychologist", (string)null); b.ToTable("DPsychologist", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Question", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("Content")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<string>("CorrectAnswer")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Difficulty")
.HasMaxLength(10)
.HasColumnType("nvarchar(10)");
b.Property<string>("Explanation")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("MediaType")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<string>("MediaUrl")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<int>("Points")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<Guid>("QuestionPoolId")
.HasColumnType("uniqueidentifier");
b.Property<string>("QuestionType")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<int>("TimeLimit")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.HasKey("Id");
b.HasIndex("QuestionPoolId");
b.ToTable("CQuestion", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionOption", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<bool>("IsCorrect")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<Guid>("QuestionId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("Text")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.ToTable("CQuestionOption", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionPool", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Tags")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.ToTable("CQuestionPool", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b => modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -6112,7 +6320,6 @@ namespace Kurs.Platform.Migrations
modelBuilder.Entity("Kurs.Platform.Entities.Tag", b => modelBuilder.Entity("Kurs.Platform.Entities.Tag", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<string>("Color") b.Property<string>("Color")
@ -8966,6 +9173,28 @@ namespace Kurs.Platform.Migrations
b.Navigation("Order"); b.Navigation("Order");
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Question", b =>
{
b.HasOne("Kurs.Platform.Entities.QuestionPool", "QuestionPool")
.WithMany("Questions")
.HasForeignKey("QuestionPoolId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("QuestionPool");
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionOption", b =>
{
b.HasOne("Kurs.Platform.Entities.Question", "Question")
.WithMany("Options")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
});
modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b => modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b =>
{ {
b.HasOne("Kurs.Platform.Entities.RegistrationType", "RegistrationType") b.HasOne("Kurs.Platform.Entities.RegistrationType", "RegistrationType")
@ -9295,6 +9524,16 @@ namespace Kurs.Platform.Migrations
b.Navigation("Items"); b.Navigation("Items");
}); });
modelBuilder.Entity("Kurs.Platform.Entities.Question", b =>
{
b.Navigation("Options");
});
modelBuilder.Entity("Kurs.Platform.Entities.QuestionPool", b =>
{
b.Navigation("Questions");
});
modelBuilder.Entity("Kurs.Platform.Entities.RegistrationType", b => modelBuilder.Entity("Kurs.Platform.Entities.RegistrationType", b =>
{ {
b.Navigation("ClassTypes"); b.Navigation("ClassTypes");

View file

@ -1827,6 +1827,61 @@
"Name": "Writing", "Name": "Writing",
"Description": "Writing skills development", "Description": "Writing skills development",
"Color": "#EF4444" "Color": "#EF4444"
},
{
"Name": "Verbs",
"Description": "Verbs and their usage",
"Color": "#EA580C"
}
],
"QuestionPools": [
{
"Name": "Grammar Fundamentals",
"Description": "Essential grammar concepts and structures",
"Tags": "Grammar|Verbs"
},
{
"Name": "Vocabulary Expansion",
"Description": "Exercises to enhance vocabulary",
"Tags": "Vocabulary|Reading"
},
{
"Name": "Reading Comprehension",
"Description": "Passages and questions to improve reading skills",
"Tags": "Reading|Verbs"
},
{
"Name": "Writing Practice",
"Description": "Prompts and exercises for writing improvement",
"Tags": "Writing"
}
],
"Questions": [
{
"QuestionPoolName": "Grammar Fundamentals",
"QuestionType": "multiple-choice",
"Points": 10,
"Title": "Verb Tenses",
"Content": "Select the appropriate tense form for the given context.",
"MediaType": "image",
"MediaUrl": "",
"CorrectAnswer": "opt2",
"Difficulty": "easy",
"TimeLimit": 0,
"Explanation": ""
},
{
"QuestionPoolName": "Grammar Fundamentals",
"QuestionType": "fill-blank",
"Points": 15,
"Title": "Articles",
"Content": "Fill in the blank: \"I saw _____ elephant at the zoo yesterday.\"",
"MediaType": "image",
"MediaUrl": "",
"CorrectAnswer": "an",
"Difficulty": "medium",
"TimeLimit": 0,
"Explanation": ""
} }
] ]
} }

View file

@ -10,6 +10,8 @@ using Volo.Abp.Data;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Collections.Generic;
namespace Kurs.Platform.Data.Seeds; namespace Kurs.Platform.Data.Seeds;
@ -53,6 +55,8 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
private readonly IRepository<WorkHour, Guid> _workHourRepository; private readonly IRepository<WorkHour, Guid> _workHourRepository;
private readonly IRepository<Classroom, Guid> _classroomRepository; private readonly IRepository<Classroom, Guid> _classroomRepository;
private readonly IRepository<Tag, Guid> _tagRepository; private readonly IRepository<Tag, Guid> _tagRepository;
private readonly IRepository<QuestionPool, Guid> _questionPoolRepository;
private readonly IRepository<Question, Guid> _questionRepository;
public TenantDataSeeder( public TenantDataSeeder(
IRepository<GlobalSearch, int> globalSearch, IRepository<GlobalSearch, int> globalSearch,
@ -92,7 +96,9 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
IRepository<Interesting, Guid> interestingRepository, IRepository<Interesting, Guid> interestingRepository,
IRepository<Program, Guid> programRepository, IRepository<Program, Guid> programRepository,
IRepository<ForumCategory, Guid> forumCategoryRepository, IRepository<ForumCategory, Guid> forumCategoryRepository,
IRepository<Tag, Guid> tagRepository IRepository<Tag, Guid> tagRepository,
IRepository<QuestionPool, Guid> questionPoolRepository,
IRepository<Question, Guid> questionRepository
) )
{ {
_globalSearch = globalSearch; _globalSearch = globalSearch;
@ -133,6 +139,8 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
_programRepository = programRepository; _programRepository = programRepository;
_forumCategoryRepository = forumCategoryRepository; _forumCategoryRepository = forumCategoryRepository;
_tagRepository = tagRepository; _tagRepository = tagRepository;
_questionPoolRepository = questionPoolRepository;
_questionRepository = questionRepository;
} }
private static IConfigurationRoot BuildConfiguration() private static IConfigurationRoot BuildConfiguration()
@ -743,5 +751,74 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
}); });
} }
} }
foreach (var item in items.QuestionPools)
{
var exists = await _questionPoolRepository.AnyAsync(x => x.Name == item.Name);
if (exists)
continue;
var tagNames = item.Tags.Split(PlatformConsts.MultiValueDelimiter, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
var tagIds = new List<Guid>();
foreach (var tagName in tagNames)
{
var tagEntity = items.Tags.FirstOrDefault(t =>
string.Equals(t.Name, tagName, StringComparison.OrdinalIgnoreCase));
if (tagEntity != null)
{
var existingTag = await _tagRepository.FirstOrDefaultAsync(x => x.Name == tagEntity.Name);
if (existingTag == null)
{
existingTag = await _tagRepository.InsertAsync(new Tag
{
Name = tagEntity.Name,
Description = tagEntity.Description,
Color = tagEntity.Color
});
}
tagIds.Add(existingTag.Id);
}
}
var tagIdString = string.Join(PlatformConsts.MultiValueDelimiter, tagIds);
await _questionPoolRepository.InsertAsync(new QuestionPool
{
Name = item.Name,
Description = item.Description,
Tags = tagIdString
}, autoSave: true);
}
foreach (var item in items.Questions)
{
var exists = await _questionRepository.AnyAsync(x => x.Title == item.Title);
if (!exists)
{
var questionPool = await _questionPoolRepository.FirstOrDefaultAsync(x => x.Name == item.QuestionPoolName);
if (questionPool != null)
{
await _questionRepository.InsertAsync(new Question
{
QuestionPoolId = questionPool.Id,
Title = item.Title,
QuestionType = item.QuestionType,
Points = item.Points,
Content = item.Content,
MediaUrl = item.MediaUrl,
MediaType = item.MediaType,
CorrectAnswer = item.CorrectAnswer,
Difficulty = item.Difficulty,
TimeLimit = item.TimeLimit,
Explanation = item.Explanation
});
}
}
}
} }
} }

View file

@ -27,6 +27,8 @@ public class TenantSeederDto
public List<ContactSeedDto> Contacts { get; set; } public List<ContactSeedDto> Contacts { get; set; }
public List<ClassroomSeedDto> Classrooms { get; set; } public List<ClassroomSeedDto> Classrooms { get; set; }
public List<TagSeedDto> Tags { get; set; } public List<TagSeedDto> Tags { get; set; }
public List<QuestionPoolSeedDto> QuestionPools { get; set; }
public List<QuestionSeedDto> Questions { get; set; }
//Tanımlamalar //Tanımlamalar
public List<SectorSeedDto> Sectors { get; set; } public List<SectorSeedDto> Sectors { get; set; }
@ -352,3 +354,25 @@ public class TagSeedDto
public string Description { get; set; } public string Description { get; set; }
public string Color { get; set; } public string Color { get; set; }
} }
public class QuestionPoolSeedDto
{
public string Name { get; set; }
public string Description { get; set; }
public string Tags { get; set; }
}
public class QuestionSeedDto
{
public string QuestionPoolName { get; set; }
public string Title { get; set; }
public string QuestionType { get; set; }
public int Points { get; set; }
public string Content { get; set; }
public string MediaUrl { get; set; }
public string MediaType { get; set; }
public string CorrectAnswer { get; set; }
public string Difficulty { get; set; }
public int TimeLimit { get; set; }
public string Explanation { get; set; }
}

View file

@ -1,7 +1,7 @@
import { QuestionPool } from "@/types/coordinator"; import { QuestionPoolDto } from "@/types/coordinator";
// Dynamic data - no hardcoded content // Dynamic data - no hardcoded content
export const generateMockPools = (): QuestionPool[] => [ export const generateMockPools = (): QuestionPoolDto[] => [
{ {
id: "pool-1", id: "pool-1",
name: "Grammar Fundamentals", name: "Grammar Fundamentals",

View file

@ -73,13 +73,19 @@ export const ROUTES_ENUM = {
formEdit: '/admin/form/:listFormCode/:id/edit', formEdit: '/admin/form/:listFormCode/:id/edit',
chart: '/admin/chart/:listFormCode', chart: '/admin/chart/:listFormCode',
pivot: '/admin/pivot/:listFormCode', pivot: '/admin/pivot/:listFormCode',
},
participant: {},
coordinator: {
classroom: { classroom: {
dashboard: '/admin/classroom/dashboard', dashboard: '/admin/coordinator/classroom/dashboard',
classes: '/admin/classroom/classes', classes: '/admin/coordinator/classroom/classes',
roomDetail: '/admin/classroom/room/:id', roomDetail: '/admin/coordinator/classroom/room/:id',
planning: '/admin/classroom/planning/:id', planning: '/admin/coordinator/classroom/planning/:id',
}, },
}, },
supplychain: { supplychain: {
materialTypes: '/admin/supplychain/materials/types', materialTypes: '/admin/supplychain/materials/types',
materialGroups: '/admin/supplychain/materials/groups', materialGroups: '/admin/supplychain/materials/groups',

View file

@ -186,7 +186,7 @@ export class SignalRService {
} }
this.currentSessionId = undefined this.currentSessionId = undefined
window.location.href = ROUTES_ENUM.protected.admin.classroom.classes window.location.href = ROUTES_ENUM.protected.coordinator.classroom.classes
}) })
} }

View file

@ -0,0 +1,48 @@
import { PagedResultDto } from '@/proxy'
import apiService from './api.service'
import { QuestionDto } from '@/types/coordinator'
class QuestionService {
async getQuestions(): Promise<PagedResultDto<QuestionDto>> {
const response = await apiService.fetchData<PagedResultDto<QuestionDto>>({
url: '/api/app/question',
method: 'GET',
})
return response.data
}
async getQuestion(id: string): Promise<QuestionDto> {
const response = await apiService.fetchData<QuestionDto>({
url: `/api/app/question/${id}`,
method: 'GET',
})
return response.data
}
async updateQuestion(id: string, input: QuestionDto) {
const response = await apiService.fetchData<QuestionDto>({
url: `/api/app/question/${id}`,
method: 'PUT',
data: input as any,
})
return response.data
}
async createQuestion(input: QuestionDto) {
const response = await apiService.fetchData<QuestionDto>({
method: 'POST',
url: '/api/app/question',
data: input as any,
})
return response.data
}
async deleteQuestion(id: string) {
await apiService.fetchData<void>({
method: 'DELETE',
url: `/api/app/question/${id}`,
})
}
}
export const questionService = new QuestionService()

View file

@ -14,24 +14,24 @@ export type MediaType = 'image' | 'video';
export type QuestionDifficulty = 'easy' | 'medium' | 'hard'; export type QuestionDifficulty = 'easy' | 'medium' | 'hard';
export type ExamSessionStatus = 'in-progress' | 'completed' | 'submitted'; export type ExamSessionStatus = 'in-progress' | 'completed' | 'submitted';
export interface QuestionPool { export interface QuestionPoolDto {
id: string; id: string;
name: string; name: string;
description: string; description: string;
questions: Question[]; questions: QuestionDto[];
tags: string[]; tags: string[];
createdBy: string; createdBy: string;
creationTime: Date; creationTime: Date;
} }
export interface Question { export interface QuestionDto {
id: string; id: string;
type: QuestionType; type: QuestionType;
title: string; title: string;
content: string; content: string;
mediaUrl?: string; mediaUrl?: string;
mediaType?: MediaType; mediaType?: MediaType;
options?: QuestionOption[]; options?: QuestionOptionDto[];
correctAnswer?: string | string[]; correctAnswer?: string | string[];
points: number; points: number;
timeLimit?: number; timeLimit?: number;
@ -42,7 +42,7 @@ export interface Question {
lastModificationTime: Date; lastModificationTime: Date;
} }
export interface QuestionOption { export interface QuestionOptionDto {
id: string; id: string;
text: string; text: string;
isCorrect: boolean; isCorrect: boolean;
@ -60,7 +60,7 @@ export interface Exam {
name: string; name: string;
}; };
answerKeyTemplate?: AnswerKeyItem[]; answerKeyTemplate?: AnswerKeyItem[];
questions: Question[]; questions: QuestionDto[];
timeLimit: number; timeLimit: number;
totalPoints: number; totalPoints: number;
passingScore: number; passingScore: number;

View file

@ -1,9 +1,9 @@
import { QuestionPool, Exam, TagItem, ExamSession } from "@/types/coordinator"; import { QuestionPoolDto, Exam, TagItem, ExamSession } from "@/types/coordinator";
import { useState } from "react"; import { useState } from "react";
export function useCoordinator() { export function useCoordinator() {
const [currentPath, setCurrentPath] = useState("/admin/dashboard"); const [currentPath, setCurrentPath] = useState("/admin/dashboard");
const [pools, setPools] = useState<QuestionPool[]>([]); const [pools, setPools] = useState<QuestionPoolDto[]>([]);
const [exams, setExams] = useState<Exam[]>([]); const [exams, setExams] = useState<Exam[]>([]);
const [tags, setTags] = useState<TagItem[]>([]); const [tags, setTags] = useState<TagItem[]>([]);
const [currentExam, setCurrentExam] = useState<Exam | null>(null); const [currentExam, setCurrentExam] = useState<Exam | null>(null);
@ -19,9 +19,9 @@ export function useCoordinator() {
}; };
const handleCreatePool = ( const handleCreatePool = (
poolData: Omit<QuestionPool, "id" | "creationTime"> poolData: Omit<QuestionPoolDto, "id" | "creationTime">
) => { ) => {
const newPool: QuestionPool = { const newPool: QuestionPoolDto = {
...poolData, ...poolData,
id: `pool-${Date.now()}`, id: `pool-${Date.now()}`,
creationTime: new Date(), creationTime: new Date(),
@ -29,7 +29,7 @@ export function useCoordinator() {
setPools((prev) => [...prev, newPool]); setPools((prev) => [...prev, newPool]);
}; };
const handleUpdatePool = (updatedPool: QuestionPool) => { const handleUpdatePool = (updatedPool: QuestionPoolDto) => {
setPools((prev) => setPools((prev) =>
prev.map((pool) => (pool.id === updatedPool.id ? updatedPool : pool)) prev.map((pool) => (pool.id === updatedPool.id ? updatedPool : pool))
); );

View file

@ -1,9 +1,9 @@
import { QuestionPool, Exam, Question } from "@/types/coordinator"; import { QuestionPoolDto, Exam, QuestionDto } from "@/types/coordinator";
import React, { useState } from "react"; import React, { useState } from "react";
import { FaPlus, FaClock, FaUsers, FaCog, FaSave } from "react-icons/fa"; import { FaPlus, FaClock, FaUsers, FaCog, FaSave } from "react-icons/fa";
interface ExamCreatorProps { interface ExamCreatorProps {
pools: QuestionPool[]; pools: QuestionPoolDto[];
onCreateExam: (exam: Omit<Exam, "id" | "creationTime" | "lastModificationTime">) => void; onCreateExam: (exam: Omit<Exam, "id" | "creationTime" | "lastModificationTime">) => void;
onCancel?: () => void; onCancel?: () => void;
editingExam?: Exam; editingExam?: Exam;
@ -30,7 +30,7 @@ export const ExamCreator: React.FC<ExamCreatorProps> = ({
isActive: editingExam?.isActive ?? true, isActive: editingExam?.isActive ?? true,
}); });
const [selectedQuestions, setSelectedQuestions] = useState<Question[]>( const [selectedQuestions, setSelectedQuestions] = useState<QuestionDto[]>(
editingExam?.questions || [] editingExam?.questions || []
); );

View file

@ -14,12 +14,12 @@ import {
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { generateMockExam } from '@/mocks/mockExams' import { generateMockExam } from '@/mocks/mockExams'
import { generateMockPools } from '@/mocks/mockPools' import { generateMockPools } from '@/mocks/mockPools'
import { Exam, QuestionPool } from '@/types/coordinator' import { Exam, QuestionPoolDto } from '@/types/coordinator'
const Assignments: React.FC = () => { const Assignments: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [assignments, setAssignments] = useState<Exam[]>(generateMockExam()) const [assignments, setAssignments] = useState<Exam[]>(generateMockExam())
const [pools] = useState<QuestionPool[]>(generateMockPools()) const [pools] = useState<QuestionPoolDto[]>(generateMockPools())
const [searchTerm, setSearchTerm] = useState('') const [searchTerm, setSearchTerm] = useState('')
const [statusFilter, setStatusFilter] = useState('') const [statusFilter, setStatusFilter] = useState('')
const [isCreating, setIsCreating] = useState(false) const [isCreating, setIsCreating] = useState(false)

View file

@ -179,13 +179,13 @@ const ClassList: React.FC = () => {
const handleJoinClass = (classSession: ClassroomDto) => { const handleJoinClass = (classSession: ClassroomDto) => {
if (classSession.id) { if (classSession.id) {
navigate(ROUTES_ENUM.protected.admin.classroom.roomDetail.replace(':id', classSession.id)) navigate(ROUTES_ENUM.protected.coordinator.classroom.roomDetail.replace(':id', classSession.id))
} }
} }
const handlePlanningClass = (classSession: ClassroomDto) => { const handlePlanningClass = (classSession: ClassroomDto) => {
if (classSession.id) { if (classSession.id) {
navigate(ROUTES_ENUM.protected.admin.classroom.planning.replace(':id', classSession.id)) navigate(ROUTES_ENUM.protected.coordinator.classroom.planning.replace(':id', classSession.id))
} }
} }
@ -258,7 +258,7 @@ const ClassList: React.FC = () => {
<> <>
<Helmet <Helmet
titleTemplate="%s | Sözsoft Kurs Platform" titleTemplate="%s | Sözsoft Kurs Platform"
title={translate('::' + 'App.Classroom.List')} title={translate('::' + 'App.Coordinator.Classroom.List')}
defaultTitle="Sözsoft Kurs Platform" defaultTitle="Sözsoft Kurs Platform"
></Helmet> ></Helmet>
<Container> <Container>

View file

@ -20,14 +20,14 @@ const Dashboard: React.FC = () => {
role, role,
}) })
navigate(ROUTES_ENUM.protected.admin.classroom.classes, { replace: true }) navigate(ROUTES_ENUM.protected.coordinator.classroom.classes, { replace: true })
} }
return ( return (
<> <>
<Helmet <Helmet
titleTemplate="%s | Sözsoft Kurs Platform" titleTemplate="%s | Sözsoft Kurs Platform"
title={translate('::' + 'App.Classroom.Dashboard')} title={translate('::' + 'App.Coordinator.Classroom.Dashboard')}
defaultTitle="Sözsoft Kurs Platform" defaultTitle="Sözsoft Kurs Platform"
></Helmet> ></Helmet>
<div className="flex items-center justify-center p-4"> <div className="flex items-center justify-center p-4">

View file

@ -164,7 +164,7 @@ const PlanningPage: React.FC = () => {
<> <>
<Helmet <Helmet
titleTemplate="%s | Sözsoft Kurs Platform" titleTemplate="%s | Sözsoft Kurs Platform"
title={translate('::' + 'App.Classroom.Planning')} title={translate('::' + 'App.Coordinator.Classroom.Planning')}
defaultTitle="Sözsoft Kurs Platform" defaultTitle="Sözsoft Kurs Platform"
/> />
<Container> <Container>

View file

@ -482,12 +482,12 @@ const RoomDetail: React.FC = () => {
await cleanup() await cleanup()
// Başka sayfaya yönlendir // Başka sayfaya yönlendir
navigate(ROUTES_ENUM.protected.admin.classroom.classes) navigate(ROUTES_ENUM.protected.coordinator.classroom.classes)
} catch (err) { } catch (err) {
toast.push(<Notification title="⚠️ Çıkış sırasında hata oluştu" type="warning" />, { toast.push(<Notification title="⚠️ Çıkış sırasında hata oluştu" type="warning" />, {
placement: 'top-end', placement: 'top-end',
}) })
navigate(ROUTES_ENUM.protected.admin.classroom.classes) navigate(ROUTES_ENUM.protected.coordinator.classroom.classes)
} }
} }
@ -855,7 +855,7 @@ const RoomDetail: React.FC = () => {
<> <>
<Helmet <Helmet
titleTemplate="%s | Sözsoft Kurs Platform" titleTemplate="%s | Sözsoft Kurs Platform"
title={translate('::' + 'App.Classroom.RoomDetail')} title={translate('::' + 'App.Coordinator.Classroom.RoomDetail')}
defaultTitle="Sözsoft Kurs Platform" defaultTitle="Sözsoft Kurs Platform"
></Helmet> ></Helmet>

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React from 'react'; import React from 'react';
interface ExamNavigationProps { interface ExamNavigationProps {
questions: Question[]; questions: QuestionDto[];
answers: StudentAnswer[]; answers: StudentAnswer[];
currentQuestionIndex: number; currentQuestionIndex: number;
onQuestionSelect: (index: number) => void; onQuestionSelect: (index: number) => void;

View file

@ -7,10 +7,10 @@ import { MultipleChoiceQuestion } from '../QuestionTypes/MultipleChoiceQuestion'
import { OpenEndedQuestion } from '../QuestionTypes/OpenEndedQuestion'; import { OpenEndedQuestion } from '../QuestionTypes/OpenEndedQuestion';
import { OrderingQuestion } from '../QuestionTypes/OrderingQuestion'; import { OrderingQuestion } from '../QuestionTypes/OrderingQuestion';
import { TrueFalseQuestion } from '../QuestionTypes/TrueFalseQuestion'; import { TrueFalseQuestion } from '../QuestionTypes/TrueFalseQuestion';
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
interface QuestionRendererProps { interface QuestionRendererProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string | string[]) => void; onAnswerChange: (questionId: string, answer: string | string[]) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -14,12 +14,12 @@ import {
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { generateMockExam } from '@/mocks/mockExams' import { generateMockExam } from '@/mocks/mockExams'
import { generateMockPools } from '@/mocks/mockPools' import { generateMockPools } from '@/mocks/mockPools'
import { Exam, QuestionPool } from '@/types/coordinator' import { Exam, QuestionPoolDto } from '@/types/coordinator'
const Exams: React.FC = () => { const Exams: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [exams, setExams] = useState<Exam[]>(generateMockExam()) const [exams, setExams] = useState<Exam[]>(generateMockExam())
const [pools] = useState<QuestionPool[]>(generateMockPools()) const [pools] = useState<QuestionPoolDto[]>(generateMockPools())
const [searchTerm, setSearchTerm] = useState('') const [searchTerm, setSearchTerm] = useState('')
const [statusFilter, setStatusFilter] = useState('') const [statusFilter, setStatusFilter] = useState('')
const [isCreating, setIsCreating] = useState(false) const [isCreating, setIsCreating] = useState(false)

View file

@ -1,34 +1,49 @@
import { Question, QuestionType, QuestionDifficulty, QuestionOption } from "@/types/coordinator"; import { questionService } from '@/services/question.service'
import React, { useState, useEffect } from "react"; import {
import { FaSave, FaPlus, FaTrash, FaTimes } from "react-icons/fa"; QuestionDto,
QuestionType,
QuestionDifficulty,
QuestionOptionDto,
} from '@/types/coordinator'
import React, { useState, useEffect } from 'react'
import { FaSave, FaPlus, FaTrash, FaTimes } from 'react-icons/fa'
interface QuestionEditorProps { function QuestionDialog({
question?: Question; open,
onSave: (question: Omit<Question, "id" | "creationTime" | "lastModificationTime">) => void; onDialogClose,
onCancel: () => void; id,
} }: {
open: boolean
export const QuestionEditor: React.FC<QuestionEditorProps> = ({ onDialogClose: () => void
question, id: string
onSave, }) {
onCancel, const [question, setQuestion] = useState<QuestionDto>()
}) => {
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
type: "multiple-choice" as QuestionType, type: 'multiple-choice' as QuestionType,
title: "", title: '',
content: "", content: '',
mediaUrl: "", mediaUrl: '',
mediaType: "image" as "image" | "video", mediaType: 'image' as 'image' | 'video',
points: 10, points: 10,
timeLimit: 0, timeLimit: 0,
explanation: "", explanation: '',
tags: [] as string[], difficulty: 'medium' as QuestionDifficulty,
difficulty: "medium" as QuestionDifficulty, })
});
const [options, setOptions] = useState<QuestionOption[]>([]); const [options, setOptions] = useState<QuestionOptionDto[]>([])
const [correctAnswer, setCorrectAnswer] = useState<string | string[]>(""); const [correctAnswer, setCorrectAnswer] = useState<string | string[]>('')
const [tagInput, setTagInput] = useState(""); const [tagInput, setTagInput] = useState('')
useEffect(() => {
const fetchQuestion = async () => {
if (open) {
const entity = await questionService.getQuestion(id)
setQuestion(entity)
}
}
fetchQuestion()
}, [open])
useEffect(() => { useEffect(() => {
if (question) { if (question) {
@ -36,136 +51,103 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
type: question.type, type: question.type,
title: question.title, title: question.title,
content: question.content, content: question.content,
mediaUrl: question.mediaUrl || "", mediaUrl: question.mediaUrl || '',
mediaType: question.mediaType || "image", mediaType: question.mediaType || 'image',
points: question.points, points: question.points,
timeLimit: question.timeLimit || 0, timeLimit: question.timeLimit || 0,
explanation: question.explanation || "", explanation: question.explanation || '',
tags: question.tags,
difficulty: question.difficulty, difficulty: question.difficulty,
}); })
setOptions(question.options || []); setOptions(question.options || [])
setCorrectAnswer(question.correctAnswer || ""); setCorrectAnswer(question.correctAnswer || '')
} }
}, [question]); }, [question])
const handleInputChange = (field: string, value: any) => { const handleInputChange = (field: string, value: any) => {
setFormData((prev) => ({ ...prev, [field]: value })); setFormData((prev) => ({ ...prev, [field]: value }))
}; }
const addOption = () => { const addOption = () => {
const newOption: QuestionOption = { const newOption: QuestionOptionDto = {
id: `opt-${Date.now()}`, id: `opt-${Date.now()}`,
text: "", text: '',
isCorrect: false, isCorrect: false,
order: options.length, order: options.length,
}; }
setOptions((prev) => [...prev, newOption]); setOptions((prev) => [...prev, newOption])
}; }
const updateOption = (index: number, field: string, value: any) => { const updateOption = (index: number, field: string, value: any) => {
setOptions((prev) => setOptions((prev) => prev.map((opt, i) => (i === index ? { ...opt, [field]: value } : opt)))
prev.map((opt, i) => (i === index ? { ...opt, [field]: value } : opt)) }
);
};
const removeOption = (index: number) => { const removeOption = (index: number) => {
setOptions((prev) => prev.filter((_, i) => i !== index)); setOptions((prev) => prev.filter((_, i) => i !== index))
}; }
const addTag = () => {
if (tagInput.trim() && !formData.tags.includes(tagInput.trim())) {
setFormData((prev) => ({
...prev,
tags: [...prev.tags, tagInput.trim()],
}));
setTagInput("");
}
};
const removeTag = (tag: string) => {
setFormData((prev) => ({
...prev,
tags: prev.tags.filter((t) => t !== tag),
}));
};
const handleSave = () => { const handleSave = () => {
if (!formData.title.trim() || !formData.content.trim()) { if (!formData.title.trim() || !formData.content.trim()) {
alert("Please fill in the title and content fields."); alert('Please fill in the title and content fields.')
return; return
} }
if (formData.points <= 0) { if (formData.points <= 0) {
alert("Points must be greater than 0."); alert('Points must be greater than 0.')
return; return
} }
// Validate based on question type // Validate based on question type
if ( if (['multiple-choice', 'multiple-answer'].includes(formData.type) && options.length < 2) {
["multiple-choice", "multiple-answer"].includes(formData.type) && alert('Please add at least 2 options.')
options.length < 2 return
) {
alert("Please add at least 2 options.");
return;
} }
if ( if (formData.type === 'multiple-choice' && !options.some((opt) => opt.isCorrect)) {
formData.type === "multiple-choice" && alert('Please mark the correct answer.')
!options.some((opt) => opt.isCorrect) return
) {
alert("Please mark the correct answer.");
return;
} }
const questionData = { const questionData = {
...formData, ...formData,
options: [ options: ['multiple-choice', 'multiple-answer', 'matching', 'ordering'].includes(
"multiple-choice", formData.type,
"multiple-answer", )
"matching",
"ordering",
].includes(formData.type)
? options ? options
: undefined, : undefined,
correctAnswer: getCorrectAnswer(), correctAnswer: getCorrectAnswer(),
}; }
onSave(questionData); // onSave(questionData)
}; }
const getCorrectAnswer = (): string | string[] => { const getCorrectAnswer = (): string | string[] => {
switch (formData.type) { switch (formData.type) {
case "multiple-choice": case 'multiple-choice':
return options.find((opt) => opt.isCorrect)?.id || ""; return options.find((opt) => opt.isCorrect)?.id || ''
case "multiple-answer": case 'multiple-answer':
return options.filter((opt) => opt.isCorrect).map((opt) => opt.id); return options.filter((opt) => opt.isCorrect).map((opt) => opt.id)
case "true-false": case 'true-false':
return correctAnswer as string; return correctAnswer as string
case "fill-blank": case 'fill-blank':
case "open-ended": case 'open-ended':
case "calculation": case 'calculation':
return correctAnswer as string; return correctAnswer as string
case "matching": case 'matching':
return options.map((opt) => opt.id); return options.map((opt) => opt.id)
case "ordering": case 'ordering':
return options return options.sort((a, b) => (a.order || 0) - (b.order || 0)).map((opt) => opt.id)
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((opt) => opt.id);
default: default:
return correctAnswer as string; return correctAnswer as string
} }
}; }
const renderQuestionTypeSpecificFields = () => { const renderQuestionTypeSpecificFields = () => {
switch (formData.type) { switch (formData.type) {
case "multiple-choice": case 'multiple-choice':
return ( return (
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h4 className="text-sm font-medium text-gray-900"> <h4 className="text-sm font-medium text-gray-900">Yanıtlar (A, B, C, D, E)</h4>
Yanıtlar (A, B, C, D, E)
</h4>
<button <button
type="button" type="button"
onClick={addOption} onClick={addOption}
@ -194,15 +176,15 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
prev.map((opt, i) => ({ prev.map((opt, i) => ({
...opt, ...opt,
isCorrect: i === index ? e.target.checked : false, isCorrect: i === index ? e.target.checked : false,
})) })),
); )
}} }}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
/> />
<input <input
type="text" type="text"
value={option.text} value={option.text}
onChange={(e) => updateOption(index, "text", e.target.value)} onChange={(e) => updateOption(index, 'text', e.target.value)}
placeholder={`${String.fromCharCode(65 + index)} şıkkı`} placeholder={`${String.fromCharCode(65 + index)} şıkkı`}
className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
@ -216,9 +198,9 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</div> </div>
))} ))}
</div> </div>
); )
case "multiple-answer": case 'multiple-answer':
return ( return (
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -243,21 +225,17 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
<input <input
type="checkbox" type="checkbox"
checked={option.isCorrect} checked={option.isCorrect}
onChange={(e) => onChange={(e) => updateOption(index, 'isCorrect', e.target.checked)}
updateOption(index, "isCorrect", e.target.checked)
}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/> />
<input <input
type="text" type="text"
value={option.text} value={option.text}
onChange={(e) => updateOption(index, "text", e.target.value)} onChange={(e) => updateOption(index, 'text', e.target.value)}
placeholder={`Yanıt ${index + 1}`} placeholder={`Yanıt ${index + 1}`}
className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
<div className="text-sm text-gray-600"> <div className="text-sm text-gray-600">{option.isCorrect ? 'Doğru' : 'Yanlış'}</div>
{option.isCorrect ? "Doğru" : "Yanlış"}
</div>
<button <button
type="button" type="button"
onClick={() => removeOption(index)} onClick={() => removeOption(index)}
@ -268,21 +246,19 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</div> </div>
))} ))}
</div> </div>
); )
case "true-false": case 'true-false':
return ( return (
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">Doğru Cevap</label>
Doğru Cevap
</label>
<div className="flex space-x-4"> <div className="flex space-x-4">
<label className="flex items-center"> <label className="flex items-center">
<input <input
type="radio" type="radio"
name="true-false" name="true-false"
value="true" value="true"
checked={correctAnswer === "true"} checked={correctAnswer === 'true'}
onChange={(e) => setCorrectAnswer(e.target.value)} onChange={(e) => setCorrectAnswer(e.target.value)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
/> />
@ -293,7 +269,7 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
type="radio" type="radio"
name="true-false" name="true-false"
value="false" value="false"
checked={correctAnswer === "false"} checked={correctAnswer === 'false'}
onChange={(e) => setCorrectAnswer(e.target.value)} onChange={(e) => setCorrectAnswer(e.target.value)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
/> />
@ -301,9 +277,9 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</label> </label>
</div> </div>
</div> </div>
); )
case "fill-blank": case 'fill-blank':
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -314,9 +290,7 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
<div className="space-y-3"> <div className="space-y-3">
{Array.from({ length: 10 }, (_, index) => { {Array.from({ length: 10 }, (_, index) => {
const blankAnswers = ((correctAnswer as string) || "").split( const blankAnswers = ((correctAnswer as string) || '').split('|')
"|"
);
return ( return (
<div key={index} className="flex items-center space-x-3"> <div key={index} className="flex items-center space-x-3">
<div className="bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1 rounded min-w-16 text-center"> <div className="bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1 rounded min-w-16 text-center">
@ -324,41 +298,35 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</div> </div>
<input <input
type="text" type="text"
value={blankAnswers[index] || ""} value={blankAnswers[index] || ''}
onChange={(e) => { onChange={(e) => {
const newAnswers = [...blankAnswers]; const newAnswers = [...blankAnswers]
newAnswers[index] = e.target.value; newAnswers[index] = e.target.value
// Remove empty answers from the end // Remove empty answers from the end
while ( while (newAnswers.length > 0 && !newAnswers[newAnswers.length - 1]) {
newAnswers.length > 0 && newAnswers.pop()
!newAnswers[newAnswers.length - 1]
) {
newAnswers.pop();
} }
setCorrectAnswer(newAnswers.join("|")); setCorrectAnswer(newAnswers.join('|'))
}} }}
placeholder={`${index + 1}. boşluk için kelime/cümle`} placeholder={`${index + 1}. boşluk için kelime/cümle`}
className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
</div> </div>
); )
})} })}
</div> </div>
<p className="text-xs text-gray-500 mt-2"> <p className="text-xs text-gray-500 mt-2">
Soru içeriğinde _____ veya [blank] kullanarak boşlukları Soru içeriğinde _____ veya [blank] kullanarak boşlukları işaretleyin
işaretleyin
</p> </p>
</div> </div>
); )
case "matching": case 'matching':
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h4 className="text-md font-medium text-gray-900"> <h4 className="text-md font-medium text-gray-900">Eşleştirme Çiftleri</h4>
Eşleştirme Çiftleri
</h4>
<button <button
type="button" type="button"
onClick={addOption} onClick={addOption}
@ -376,14 +344,10 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
> >
<input <input
type="text" type="text"
value={option.text.split("|")[0] || ""} value={option.text.split('|')[0] || ''}
onChange={(e) => { onChange={(e) => {
const rightSide = option.text.split("|")[1] || ""; const rightSide = option.text.split('|')[1] || ''
updateOption( updateOption(index, 'text', `${e.target.value}|${rightSide}`)
index,
"text",
`${e.target.value}|${rightSide}`
);
}} }}
placeholder="Sol taraf (örn: PLUS)" placeholder="Sol taraf (örn: PLUS)"
className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
@ -391,14 +355,10 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
<span className="text-gray-400"></span> <span className="text-gray-400"></span>
<input <input
type="text" type="text"
value={option.text.split("|")[1] || ""} value={option.text.split('|')[1] || ''}
onChange={(e) => { onChange={(e) => {
const leftSide = option.text.split("|")[0] || ""; const leftSide = option.text.split('|')[0] || ''
updateOption( updateOption(index, 'text', `${leftSide}|${e.target.value}`)
index,
"text",
`${leftSide}|${e.target.value}`
);
}} }}
placeholder="Sağ taraf (örn: ARTI)" placeholder="Sağ taraf (örn: ARTI)"
className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
@ -413,15 +373,13 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</div> </div>
))} ))}
</div> </div>
); )
case "ordering": case 'ordering':
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h4 className="text-md font-medium text-gray-900"> <h4 className="text-md font-medium text-gray-900">Sıralanacak Öğeler</h4>
Sıralanacak Öğeler
</h4>
<button <button
type="button" type="button"
onClick={addOption} onClick={addOption}
@ -443,16 +401,14 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
<input <input
type="number" type="number"
value={option.order || index + 1} value={option.order || index + 1}
onChange={(e) => onChange={(e) => updateOption(index, 'order', parseInt(e.target.value))}
updateOption(index, "order", parseInt(e.target.value))
}
min="1" min="1"
className="w-20 text-sm border border-gray-300 rounded-lg px-2 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-20 text-sm border border-gray-300 rounded-lg px-2 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
<input <input
type="text" type="text"
value={option.text} value={option.text}
onChange={(e) => updateOption(index, "text", e.target.value)} onChange={(e) => updateOption(index, 'text', e.target.value)}
placeholder={`Öğe ${index + 1} (örn: I, TAKE, A SHOWER)`} placeholder={`Öğe ${index + 1} (örn: I, TAKE, A SHOWER)`}
className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500" className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
@ -467,13 +423,12 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
))} ))}
<p className="text-xs text-gray-500 mt-2"> <p className="text-xs text-gray-500 mt-2">
Öğrenciler bu öğeleri doğru sıraya göre düzenleyecek (drag & drop Öğrenciler bu öğeleri doğru sıraya göre düzenleyecek (drag & drop veya butonlarla)
veya butonlarla)
</p> </p>
</div> </div>
); )
case "open-ended": case 'open-ended':
return ( return (
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
@ -486,13 +441,11 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
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" 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"
rows={4} rows={4}
/> />
<p className="text-xs text-gray-500 mt-1"> <p className="text-xs text-gray-500 mt-1">Öğrenci bu soruya ıklama yazabilecek</p>
Öğrenci bu soruya ıklama yazabilecek
</p>
</div> </div>
); )
case "calculation": case 'calculation':
return ( return (
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
@ -509,24 +462,21 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
Öğrenci matematiksel hesaplama yapıp sayısal sonuç yazacak Öğrenci matematiksel hesaplama yapıp sayısal sonuç yazacak
</p> </p>
</div> </div>
); )
default: default:
return null; return null
} }
}; }
return ( return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto"> <div className="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto">
<div className="sticky top-0 bg-white border-b border-gray-200 px-5 py-3 flex items-center justify-between"> <div className="sticky top-0 bg-white border-b border-gray-200 px-5 py-3 flex items-center justify-between">
<h2 className="text-lg font-semibold text-gray-900"> <h2 className="text-lg font-semibold text-gray-900">
{question ? "Edit Question" : "Create New Question"} {question ? 'Edit Question' : 'Create New Question'}
</h2> </h2>
<button <button onClick={onDialogClose} className="text-gray-400 hover:text-gray-600">
onClick={onCancel}
className="text-gray-400 hover:text-gray-600"
>
<FaTimes className="w-5 h-5" /> <FaTimes className="w-5 h-5" />
</button> </button>
</div> </div>
@ -540,7 +490,7 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</label> </label>
<select <select
value={formData.type} value={formData.type}
onChange={(e) => handleInputChange("type", e.target.value)} onChange={(e) => handleInputChange('type', 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" 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"
> >
<option value="multiple-choice">Multiple Choice</option> <option value="multiple-choice">Multiple Choice</option>
@ -555,15 +505,11 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">Points</label>
Points
</label>
<input <input
type="number" type="number"
value={formData.points} value={formData.points}
onChange={(e) => onChange={(e) => handleInputChange('points', parseInt(e.target.value))}
handleInputChange("points", parseInt(e.target.value))
}
min="1" min="1"
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" 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"
/> />
@ -571,25 +517,21 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">Question Title</label>
Question Title
</label>
<input <input
type="text" type="text"
value={formData.title} value={formData.title}
onChange={(e) => handleInputChange("title", e.target.value)} onChange={(e) => handleInputChange('title', e.target.value)}
placeholder="Enter question title..." placeholder="Enter question title..."
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" 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"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">Question Content</label>
Question Content
</label>
<textarea <textarea
value={formData.content} value={formData.content}
onChange={(e) => handleInputChange("content", e.target.value)} onChange={(e) => handleInputChange('content', e.target.value)}
placeholder="Enter the question content..." placeholder="Enter the question content..."
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" 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"
rows={4} rows={4}
@ -605,19 +547,17 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
<input <input
type="url" type="url"
value={formData.mediaUrl} value={formData.mediaUrl}
onChange={(e) => handleInputChange("mediaUrl", e.target.value)} onChange={(e) => handleInputChange('mediaUrl', e.target.value)}
placeholder="https://example.com/image.jpg" placeholder="https://example.com/image.jpg"
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" 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"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1.5"> <label className="block text-sm font-medium text-gray-700 mb-1.5">Media Type</label>
Media Type
</label>
<select <select
value={formData.mediaType} value={formData.mediaType}
onChange={(e) => handleInputChange("mediaType", e.target.value)} onChange={(e) => handleInputChange('mediaType', 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" 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"
> >
<option value="image">Image</option> <option value="image">Image</option>
@ -632,14 +572,10 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
{/* Additional Settings */} {/* Additional Settings */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">Difficulty</label>
Difficulty
</label>
<select <select
value={formData.difficulty} value={formData.difficulty}
onChange={(e) => onChange={(e) => handleInputChange('difficulty', e.target.value)}
handleInputChange("difficulty", 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" 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"
> >
<option value="easy">Easy</option> <option value="easy">Easy</option>
@ -655,71 +591,20 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
<input <input
type="number" type="number"
value={formData.timeLimit} value={formData.timeLimit}
onChange={(e) => onChange={(e) => handleInputChange('timeLimit', parseInt(e.target.value))}
handleInputChange("timeLimit", parseInt(e.target.value))
}
min="0" min="0"
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" 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"
/> />
</div> </div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Add Tag
</label>
<div className="flex space-x-2">
<input
type="text"
value={tagInput}
onChange={(e) => setTagInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && addTag()}
placeholder="grammar, vocabulary..."
className="flex-1 text-sm border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
type="button"
onClick={addTag}
className="px-3 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
<FaPlus className="w-4 h-4" />
</button>
</div>
</div>
</div> </div>
{/* Tags Display */}
{formData.tags.length > 0 && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1.5">
Tags
</label>
<div className="flex flex-wrap gap-1.5">
{formData.tags.map((tag) => (
<span
key={tag}
className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
>
{tag}
<button
type="button"
onClick={() => removeTag(tag)}
className="ml-1.5 text-blue-600 hover:text-blue-800"
>
<FaTimes className="w-2.5 h-2.5" />
</button>
</span>
))}
</div>
</div>
)}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1.5"> <label className="block text-sm font-medium text-gray-700 mb-1.5">
Explanation (Optional) Explanation (Optional)
</label> </label>
<textarea <textarea
value={formData.explanation} value={formData.explanation}
onChange={(e) => handleInputChange("explanation", e.target.value)} onChange={(e) => handleInputChange('explanation', e.target.value)}
placeholder="Provide an explanation for the correct answer..." placeholder="Provide an explanation for the correct answer..."
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" 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"
rows={3} rows={3}
@ -730,7 +615,7 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
{/* Footer */} {/* Footer */}
<div className="sticky bottom-0 bg-gray-50 border-t border-gray-200 px-5 py-3 flex justify-end space-x-2"> <div className="sticky bottom-0 bg-gray-50 border-t border-gray-200 px-5 py-3 flex justify-end space-x-2">
<button <button
onClick={onCancel} onClick={onDialogClose}
className="px-3 py-1.5 text-sm border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 font-medium transition-colors" className="px-3 py-1.5 text-sm border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 font-medium transition-colors"
> >
Cancel Cancel
@ -745,5 +630,7 @@ export const QuestionEditor: React.FC<QuestionEditorProps> = ({
</div> </div>
</div> </div>
</div> </div>
); )
}; }
export default QuestionDialog

View file

@ -1,16 +1,16 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { QuestionEditor } from "./QuestionEditor";
import { FaPlus, FaSearch, FaFilter, FaEdit, FaTrash } from "react-icons/fa"; import { FaPlus, FaSearch, FaFilter, FaEdit, FaTrash } from "react-icons/fa";
import { generateMockPools } from "@/mocks/mockPools"; import { generateMockPools } from "@/mocks/mockPools";
import { QuestionPool, Question } from "@/types/coordinator"; import { QuestionPoolDto, QuestionDto } from "@/types/coordinator";
import QuestionDialog from "./QuestionDialog";
export const QuestionPoolManager: React.FC = () => { export const QuestionPoolManager: React.FC = () => {
const [pools, setPools] = useState<QuestionPool[]>(generateMockPools()); const [pools, setPools] = useState<QuestionPoolDto[]>(generateMockPools());
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [selectedTag, setSelectedTag] = useState(""); const [selectedTag, setSelectedTag] = useState("");
const [isCreating, setIsCreating] = useState(false); const [isCreating, setIsCreating] = useState(false);
const [editingPool, setEditingPool] = useState<QuestionPool | null>(null); const [editingPool, setEditingPool] = useState<QuestionPoolDto | null>(null);
const [editingQuestion, setEditingQuestion] = useState<Question | null>(null); const [editingQuestion, setEditingQuestion] = useState<QuestionDto | null>(null);
const [showQuestionEditor, setShowQuestionEditor] = useState(false); const [showQuestionEditor, setShowQuestionEditor] = useState(false);
const [selectedPoolForQuestion, setSelectedPoolForQuestion] = const [selectedPoolForQuestion, setSelectedPoolForQuestion] =
useState<string>(""); useState<string>("");
@ -34,7 +34,7 @@ export const QuestionPoolManager: React.FC = () => {
const handleCreatePool = () => { const handleCreatePool = () => {
if (!newPool.name.trim()) return; if (!newPool.name.trim()) return;
const newPoolData: QuestionPool = { const newPoolData: QuestionPoolDto = {
id: `pool-${Date.now()}`, id: `pool-${Date.now()}`,
name: newPool.name, name: newPool.name,
description: newPool.description, description: newPool.description,
@ -63,19 +63,19 @@ export const QuestionPoolManager: React.FC = () => {
setShowQuestionEditor(true); setShowQuestionEditor(true);
}; };
const handleEditQuestion = (question: Question, poolId: string) => { const handleEditQuestion = (question: QuestionDto, poolId: string) => {
setSelectedPoolForQuestion(poolId); setSelectedPoolForQuestion(poolId);
setEditingQuestion(question); setEditingQuestion(question);
setShowQuestionEditor(true); setShowQuestionEditor(true);
}; };
const handleSaveQuestion = ( const handleSaveQuestion = (
questionData: Omit<Question, "id" | "creationTime" | "lastModificationTime"> questionData: Omit<QuestionDto, "id" | "creationTime" | "lastModificationTime">
) => { ) => {
const pool = pools.find((p) => p.id === selectedPoolForQuestion); const pool = pools.find((p) => p.id === selectedPoolForQuestion);
if (!pool) return; if (!pool) return;
const newQuestion: Question = { const newQuestion: QuestionDto = {
...questionData, ...questionData,
id: editingQuestion?.id || `q-${Date.now()}`, id: editingQuestion?.id || `q-${Date.now()}`,
creationTime: editingQuestion?.creationTime || new Date(), creationTime: editingQuestion?.creationTime || new Date(),
@ -278,7 +278,7 @@ export const QuestionPoolManager: React.FC = () => {
{/* Question Editor Modal */} {/* Question Editor Modal */}
{showQuestionEditor && ( {showQuestionEditor && (
<QuestionEditor <QuestionDialog
question={editingQuestion || undefined} question={editingQuestion || undefined}
onSave={handleSaveQuestion} onSave={handleSaveQuestion}
onCancel={() => setShowQuestionEditor(false)} onCancel={() => setShowQuestionEditor(false)}

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface CalculationQuestionProps { interface CalculationQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string) => void; onAnswerChange: (questionId: string, answer: string) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface FillBlankQuestionProps { interface FillBlankQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string[]) => void; onAnswerChange: (questionId: string, answer: string[]) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface MatchingQuestionProps { interface MatchingQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string[]) => void; onAnswerChange: (questionId: string, answer: string[]) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface MultipleAnswerQuestionProps { interface MultipleAnswerQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string[]) => void; onAnswerChange: (questionId: string, answer: string[]) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React from 'react'; import React from 'react';
interface MultipleChoiceQuestionProps { interface MultipleChoiceQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string) => void; onAnswerChange: (questionId: string, answer: string) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface OpenEndedQuestionProps { interface OpenEndedQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string) => void; onAnswerChange: (questionId: string, answer: string) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface OrderingQuestionProps { interface OrderingQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string[]) => void; onAnswerChange: (questionId: string, answer: string[]) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -1,8 +1,8 @@
import { Question, StudentAnswer } from '@/types/coordinator'; import { QuestionDto, StudentAnswer } from '@/types/coordinator';
import React from 'react'; import React from 'react';
interface TrueFalseQuestionProps { interface TrueFalseQuestionProps {
question: Question; question: QuestionDto;
answer?: StudentAnswer; answer?: StudentAnswer;
onAnswerChange: (questionId: string, answer: string) => void; onAnswerChange: (questionId: string, answer: string) => void;
disabled?: boolean; disabled?: boolean;

View file

@ -32,8 +32,8 @@ const TagBoxEditorComponent = ({
{...editorOptions} {...editorOptions}
{...(setDefaultValue ? { defaultValue: val } : { value: val })} {...(setDefaultValue ? { defaultValue: val } : { value: val })}
dataSource={editorOptions?.dataSource} dataSource={editorOptions?.dataSource}
valueExpr={lookupDto?.valueExpr} valueExpr={lookupDto?.valueExpr?.toLocaleLowerCase()}
displayExpr={lookupDto?.displayExpr} displayExpr={lookupDto?.displayExpr?.toLocaleLowerCase()}
showClearButton={options?.showClearButton} showClearButton={options?.showClearButton}
showSelectionControls={options?.showSelectionControls ?? true} showSelectionControls={options?.showSelectionControls ?? true}
maxDisplayedTags={options?.maxDisplayedTags} maxDisplayedTags={options?.maxDisplayedTags}

View file

@ -5,6 +5,7 @@ import AuditLogDetail from '@/views/admin/auditLog/AuditLogDetail'
import RolesPermission from '@/views/admin/role-management/RolesPermission' import RolesPermission from '@/views/admin/role-management/RolesPermission'
import UsersPermission from '@/views/admin/user-management/UsersPermission' import UsersPermission from '@/views/admin/user-management/UsersPermission'
import BranchSeed from '@/views/branch/BranchSeed' import BranchSeed from '@/views/branch/BranchSeed'
import QuestionDialog from '@/views/coordinator/QuestionDialog'
const DialogShowComponent = (): JSX.Element => { const DialogShowComponent = (): JSX.Element => {
const dialogContext: any = useDialogContext() const dialogContext: any = useDialogContext()
@ -59,6 +60,14 @@ const DialogShowComponent = (): JSX.Element => {
{...dialogContext.config?.props} {...dialogContext.config?.props}
></BranchSeed> ></BranchSeed>
) )
case 'QuestionAnswers':
return (
<QuestionDialog
open={true}
onDialogClose={() => dialogContext.setConfig({})}
{...dialogContext.config?.props}
></QuestionDialog>
)
default: default:
return <></> return <></>
} }