diff --git a/api/src/Kurs.Platform.Application.Contracts/Question/QuestionDto.cs b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionDto.cs new file mode 100644 index 00000000..b0493ba3 --- /dev/null +++ b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionDto.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Application.Dtos; + +namespace Kurs.Platform.Questions; + +public class QuestionDto : FullAuditedEntityDto +{ + 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 Options { get; set; } = new(); +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.Application.Contracts/Question/QuestionOptionDto.cs b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionOptionDto.cs new file mode 100644 index 00000000..e626a35f --- /dev/null +++ b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionOptionDto.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Kurs.Platform.Questions; + +public class QuestionOptionDto : FullAuditedEntityDto +{ + public string Text { get; set; } + public bool IsCorrect { get; set; } + + public Guid QuestionId { get; set; } +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.Application.Contracts/Question/QuestionPoolDto.cs b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionPoolDto.cs new file mode 100644 index 00000000..a8a772af --- /dev/null +++ b/api/src/Kurs.Platform.Application.Contracts/Question/QuestionPoolDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Application.Dtos; + +namespace Kurs.Platform.Questions; + +public class QuestionPoolDto : FullAuditedEntityDto +{ + public string Name { get; set; } + public string Description { get; set; } + public string Tags { get; set; } + + public List Questions { get; set; } = new(); +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.Application/Question/QuestionAppService.cs b/api/src/Kurs.Platform.Application/Question/QuestionAppService.cs new file mode 100644 index 00000000..38391917 --- /dev/null +++ b/api/src/Kurs.Platform.Application/Question/QuestionAppService.cs @@ -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 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"; + } +} diff --git a/api/src/Kurs.Platform.Application/Question/QuestionAutoMapperProfile.cs b/api/src/Kurs.Platform.Application/Question/QuestionAutoMapperProfile.cs new file mode 100644 index 00000000..9dc488bb --- /dev/null +++ b/api/src/Kurs.Platform.Application/Question/QuestionAutoMapperProfile.cs @@ -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().ReverseMap(); + } +} diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json b/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json index 1bdf937b..48d98166 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json @@ -7881,10 +7881,22 @@ }, { "resourceName": "Platform", - "key": "App.Classroom.Tag", + "key": "App.Definitions.Tag", "tr": "Etiketler", "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", "key": "App.Definitions.RegistrationType", @@ -11985,31 +11997,31 @@ }, { "resourceName": "Platform", - "key": "App.Classroom", + "key": "App.Coordinator.Classroom", "tr": "Sınıf", "en": "Classroom" }, { "resourceName": "Platform", - "key": "App.Classroom.Dashboard", + "key": "App.Coordinator.Classroom.Dashboard", "tr": "Gösterge Paneli", "en": "Dashboard" }, { "resourceName": "Platform", - "key": "App.Classroom.List", + "key": "App.Coordinator.Classroom.List", "tr": "Sınıflar", "en": "Classes" }, { "resourceName": "Platform", - "key": "App.Classroom.RoomDetail", + "key": "App.Coordinator.Classroom.RoomDetail", "tr": "Sanal Sınıf", "en": "Virtul Classroom" }, { "resourceName": "Platform", - "key": "App.Classroom.Planning", + "key": "App.Coordinator.Classroom.Planning", "tr": "Sınıf Planlama", "en": "Classroom Planning" }, @@ -12797,41 +12809,42 @@ ] }, { - "key": "admin.classroom.dashboard", - "path": "/admin/classroom/dashboard", - "componentPath": "@/views/classroom/Dashboard", + "key": "admin.coordinator.classroom.dashboard", + "path": "/admin/coordinator/classroom/dashboard", + "componentPath": "@/views/coordinator/Classroom/Dashboard", "routeType": "protected", "authority": [ - "App.Classroom.Dashboard" + "App.Coordinator.Classroom.Dashboard" ] }, { - "key": "admin.classroom.classes", - "path": "/admin/classroom/classes", - "componentPath": "@/views/classroom/ClassList", + "key": "admin.coordinator.classroom.classes", + "path": "/admin/coordinator/classroom/classes", + "componentPath": "@/views/coordinator/Classroom/ClassList", "routeType": "protected", "authority": [ - "App.Classroom.List" + "App.Coordinator.Classroom.List" ] }, { - "key": "admin.classroom.classroom", - "path": "/admin/classroom/room/:id", - "componentPath": "@/views/classroom/RoomDetail", + "key": "admin.coordinator.classroom.classroom", + "path": "/admin/coordinator/classroom/room/:id", + "componentPath": "@/views/coordinator/Classroom/RoomDetail", "routeType": "protected", "authority": [ - "App.Classroom.RoomDetail" + "App.Coordinator.Classroom.RoomDetail" ] }, { - "key": "admin.classroom.planning", - "path": "/admin/classroom/planning/:id", - "componentPath": "@/views/classroom/PlanningPage", + "key": "admin.coordinator.classroom.planning", + "path": "/admin/coordinator/classroom/planning/:id", + "componentPath": "@/views/coordinator/Classroom/PlanningPage", "routeType": "protected", "authority": [ - "App.Classroom.Planning" + "App.Coordinator.Classroom.Planning" ] }, + { "key": "admin.supplychain.materialTypes", "path": "/admin/supplychain/materials/types", @@ -14599,22 +14612,32 @@ }, { "ParentCode": "App.Coordinator.Definitions", - "Code": "App.Classroom.Tag", - "DisplayName": "App.Classroom.Tag", + "Code": "App.Definitions.Tag", + "DisplayName": "App.Definitions.Tag", "Order": 10, "Url": "/admin/list/list-tag", "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 }, { "ParentCode": "App.Coordinator", - "Code": "App.Classroom", - "DisplayName": "App.Classroom", + "Code": "App.Coordinator.Classroom", + "DisplayName": "App.Coordinator.Classroom", "Order": 2, - "Url": "/admin/classroom/dashboard", + "Url": "/admin/coordinator/classroom/dashboard", "Icon": "FcNeutralDecision", - "RequiredPermissionName": "App.Classroom.Dashboard", + "RequiredPermissionName": "App.Coordinator.Classroom.Dashboard", "IsDisabled": false }, { @@ -21895,62 +21918,17 @@ }, { "GroupName": "App.Coordinator", - "Name": "App.Classroom", + "Name": "App.Definitions.Tag", "ParentName": null, - "DisplayName": "App.Classroom", - "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", + "DisplayName": "App.Definitions.Tag", "IsEnabled": true, "MultiTenancySide": 3, "MenuGroup": "Kurs" }, { "GroupName": "App.Coordinator", - "Name": "App.Classroom.Tag.Create", - "ParentName": "App.Classroom.Tag", + "Name": "App.Definitions.Tag.Create", + "ParentName": "App.Definitions.Tag", "DisplayName": "Create", "IsEnabled": true, "MultiTenancySide": 3, @@ -21958,8 +21936,8 @@ }, { "GroupName": "App.Coordinator", - "Name": "App.Classroom.Tag.Update", - "ParentName": "App.Classroom.Tag", + "Name": "App.Definitions.Tag.Update", + "ParentName": "App.Definitions.Tag", "DisplayName": "Update", "IsEnabled": true, "MultiTenancySide": 3, @@ -21967,8 +21945,8 @@ }, { "GroupName": "App.Coordinator", - "Name": "App.Classroom.Tag.Delete", - "ParentName": "App.Classroom.Tag", + "Name": "App.Definitions.Tag.Delete", + "ParentName": "App.Definitions.Tag", "DisplayName": "Delete", "IsEnabled": true, "MultiTenancySide": 3, @@ -21976,8 +21954,8 @@ }, { "GroupName": "App.Coordinator", - "Name": "App.Classroom.Tag.Export", - "ParentName": "App.Classroom.Tag", + "Name": "App.Definitions.Tag.Export", + "ParentName": "App.Definitions.Tag", "DisplayName": "Export", "IsEnabled": true, "MultiTenancySide": 3, @@ -21985,8 +21963,8 @@ }, { "GroupName": "App.Coordinator", - "Name": "App.Classroom.Tag.Import", - "ParentName": "App.Classroom.Tag", + "Name": "App.Definitions.Tag.Import", + "ParentName": "App.Definitions.Tag", "DisplayName": "Import", "IsEnabled": true, "MultiTenancySide": 3, @@ -21994,13 +21972,185 @@ }, { "GroupName": "App.Coordinator", - "Name": "App.Classroom.Tag.Activity", - "ParentName": "App.Classroom.Tag", + "Name": "App.Definitions.Tag.Activity", + "ParentName": "App.Definitions.Tag", "DisplayName": "Activity", "IsEnabled": true, "MultiTenancySide": 3, "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", diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs index 46c01465..0640e2b4 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs @@ -2099,6 +2099,292 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency } #endregion + #region Question Pool + if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == ListFormCodes.Forms.FormQuestionPool)) + { + var formQuestionPool = await _listFormRepository.InsertAsync( + new ListForm() + { + ListFormType = ListFormTypeEnum.Form, + IsSubForm = false, + LayoutJson = JsonSerializer.Serialize(new LayoutDto() + { + Grid = true, + Card = true, + Pivot = true, + Chart = true, + DefaultLayout = "grid", + CardLayoutColumn = 4 + }), + SubFormsJson = JsonSerializer.Serialize(new List() { + new { + TabTitle = "Questions", + TabType = ListFormTabTypeEnum.List, + Code = ListFormCodes.Lists.Question, + Relation = new List() { + new { + ParentFieldName = "Id", + ChildFieldName = "QuestionPoolId" + } + } + } + }), + CultureName = LanguageCodes.En, + ListFormCode = ListFormCodes.Forms.FormQuestionPool, + Name = AppCodes.Definitions.QuestionPool, + Title = AppCodes.Definitions.QuestionPool, + DataSourceCode = SeedConsts.DataSources.DefaultCode, + IsTenant = true, + IsBranch = false, + IsOrganizationUnit = false, + Description = AppCodes.Definitions.QuestionPool, + SelectCommandType = SelectCommandTypeEnum.Table, + SelectCommand = SelectCommandByTableName("QuestionPool", Prefix.DbTableCoordinator), + KeyFieldName = "Id", + KeyFieldDbSourceType = DbType.Guid, + DefaultFilter = "\"IsDeleted\" = 'false'", + SortMode = GridOptions.SortModeSingle, + FilterRowJson = JsonSerializer.Serialize(new GridFilterRowDto { Visible = true }), + HeaderFilterJson = JsonSerializer.Serialize(new { Visible = true }), + SearchPanelJson = JsonSerializer.Serialize(new { Visible = true }), + GroupPanelJson = JsonSerializer.Serialize(new { Visible = true }), + SelectionJson = JsonSerializer.Serialize(new SelectionDto + { + Mode = GridOptions.SelectionModeSingle, + AllowSelectAll = false + }), + ColumnOptionJson = JsonSerializer.Serialize(new + { + ColumnFixingEnabled = true, + ColumnAutoWidth = true, + ColumnChooserEnabled = true, + AllowColumnResizing = true, + AllowColumnReordering = true, + ColumnResizingMode = "widget", + }), + PermissionJson = JsonSerializer.Serialize(new PermissionCrudDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + D = AppCodes.Definitions.QuestionPool + ".Delete", + E = AppCodes.Definitions.QuestionPool + ".Export", + I = AppCodes.Definitions.QuestionPool + ".Import", + A = AppCodes.Definitions.QuestionPool + ".Activity", + }), + DeleteCommand = $"UPDATE \"{SelectCommandByTableName("QuestionPool", Prefix.DbTableCoordinator)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id", + DeleteFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { + new() { + FieldName = "DeleterId", + FieldDbType = DbType.Guid, + Value = "@USERID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "Id", + FieldDbType = DbType.Guid, + Value = "@ID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey } + }), + PagerOptionJson = JsonSerializer.Serialize(new GridPagerOptionDto + { + Visible = true, + AllowedPageSizes = "10,20,50,100", + ShowPageSizeSelector = true, + ShowNavigationButtons = true, + ShowInfo = false, + InfoText = "Page {0} of {1} ({2} items)", + DisplayMode = GridColumnOptions.PagerDisplayModeAdaptive, + ScrollingMode = GridColumnOptions.ScrollingModeStandard, + LoadPanelEnabled = "auto", + LoadPanelText = "Loading..." + }), + EditingOptionJson = JsonSerializer.Serialize(new GridEditingDto + { + Popup = new GridEditingPopupDto() + { + Title = "Question Pool Form", + Width = 500, + Height = 250 + }, + AllowDeleting = true, + AllowAdding = true, + AllowUpdating = true, + SendOnlyChangedFormValuesUpdate = false, + }), + EditingFormJson = JsonSerializer.Serialize(new List() { + new() { Order=1,ColCount=1,ColSpan=2,ItemType="group", Items= + [ + new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 2, DataField = "Description", ColSpan = 2, EditorType2=EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 3, DataField = "Tags", ColSpan = 2, EditorType2=EditorTypes.dxTagBox }, + ] + } + }), + InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { + new() { + FieldName = "CreationTime", + FieldDbType = DbType.DateTime, + Value = "@NOW", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "CreatorId", + FieldDbType = DbType.Guid, + Value = "@USERID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "IsDeleted", + FieldDbType = DbType.Boolean, + Value = "false", + CustomValueType = FieldCustomValueTypeEnum.Value } + }), + } + ); + + #region Question Pool Fields + await _listFormFieldRepository.InsertManyAsync(new ListFormField[] { + new() { + ListFormCode = formQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.Guid, + FieldName = "Id", + Width = 100, + ListOrderNo = 1, + Visible = false, + IsActive = true, + IsDeleted = false, + SortIndex = 0, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = formQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Name", + Width = 250, + ListOrderNo = 2, + Visible = true, + IsActive = true, + IsDeleted = false, + SortIndex = 1, + SortDirection = GridColumnOptions.SortOrderAsc, + AllowSearch = true, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = formQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Description", + Width = 400, + ListOrderNo = 3, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = formQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Tags", + Width = 100, + ListOrderNo = 4, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + LookupJson = JsonSerializer.Serialize(new LookupDto { + DataSourceType = UiLookupDataSourceTypeEnum.Query, + DisplayExpr = "Name", + ValueExpr = "Key", + LookupQuery = LookUpQueryValues.QuestionTagValues + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + } + }); + #endregion + } + #endregion + #endregion #region List @@ -5256,7 +5542,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency Hint = "Manage", Text ="Manage", UrlTarget="_blank", - AuthName = AppCodes.Languages.Language + ".Update", + AuthName = AppCodes.Languages.LanguageText, Url=$"/admin/form/{ListFormCodes.Forms.FormLanguage}/@Id" }, }), @@ -18604,7 +18890,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency Hint = "Manage", Text ="Manage", UrlTarget="_blank", - AuthName = AppCodes.Definitions.SkillType + ".Update", + AuthName = AppCodes.Definitions.Skill, Url=$"/admin/form/{ListFormCodes.Forms.FormSkillType}/@Id" }, }), @@ -19411,7 +19697,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency Hint = "Manage", Text ="Manage", UrlTarget="_blank", - AuthName = AppCodes.Definitions.UomCategory + ".Update", + AuthName = AppCodes.Definitions.Uom, Url=$"/admin/form/{ListFormCodes.Forms.FormUomCategory}/@Id" }, }), @@ -29119,7 +29405,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency Hint = "Manage", Text ="Manage", UrlTarget="_blank", - AuthName = AppCodes.Accounting.Bank + ".Update", + AuthName = AppCodes.Accounting.BankAccount, Url=$"/admin/form/{ListFormCodes.Forms.FormBank}/@Id" }, }), @@ -29949,13 +30235,13 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency }), CultureName = LanguageCodes.En, ListFormCode = ListFormCodes.Lists.Tag, - Name = AppCodes.Classroom.Tag, - Title = AppCodes.Classroom.Tag, + Name = AppCodes.Definitions.Tag, + Title = AppCodes.Definitions.Tag, DataSourceCode = SeedConsts.DataSources.DefaultCode, IsTenant = true, IsBranch = false, IsOrganizationUnit = false, - Description = AppCodes.Classroom.Tag, + Description = AppCodes.Definitions.Tag, SelectCommandType = SelectCommandTypeEnum.Table, SelectCommand = SelectCommandByTableName("Tag", Prefix.DbTableCoordinator), KeyFieldName = "Id", @@ -29982,13 +30268,13 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency }), PermissionJson = JsonSerializer.Serialize(new PermissionCrudDto { - C = AppCodes.Classroom.Tag + ".Create", - R = AppCodes.Classroom.Tag, - U = AppCodes.Classroom.Tag + ".Update", - D = AppCodes.Classroom.Tag + ".Delete", - E = AppCodes.Classroom.Tag + ".Export", - I = AppCodes.Classroom.Tag + ".Import", - A = AppCodes.Classroom.Tag + ".Activity", + C = AppCodes.Definitions.Tag + ".Create", + R = AppCodes.Definitions.Tag, + U = AppCodes.Definitions.Tag + ".Update", + D = AppCodes.Definitions.Tag + ".Delete", + E = AppCodes.Definitions.Tag + ".Export", + I = AppCodes.Definitions.Tag + ".Import", + A = AppCodes.Definitions.Tag + ".Activity", }), DeleteCommand = $"UPDATE \"{SelectCommandByTableName("Tag", Prefix.DbTableCoordinator)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id", DeleteFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { @@ -30082,9 +30368,9 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency }), PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto { - C = AppCodes.Classroom.Tag + ".Create", - R = AppCodes.Classroom.Tag, - U = AppCodes.Classroom.Tag + ".Update", + C = AppCodes.Definitions.Tag + ".Create", + R = AppCodes.Definitions.Tag, + U = AppCodes.Definitions.Tag + ".Update", E = true, I = true, Deny = false @@ -30118,9 +30404,9 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency }), PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto { - C = AppCodes.Classroom.Tag + ".Create", - R = AppCodes.Classroom.Tag, - U = AppCodes.Classroom.Tag + ".Update", + C = AppCodes.Definitions.Tag + ".Create", + R = AppCodes.Definitions.Tag, + U = AppCodes.Definitions.Tag + ".Update", E = true, I = true, Deny = false @@ -30149,9 +30435,9 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency }), PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto { - C = AppCodes.Classroom.Tag + ".Create", - R = AppCodes.Classroom.Tag, - U = AppCodes.Classroom.Tag + ".Update", + C = AppCodes.Definitions.Tag + ".Create", + R = AppCodes.Definitions.Tag, + U = AppCodes.Definitions.Tag + ".Update", E = true, I = true, Deny = false @@ -30180,9 +30466,9 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency }), PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto { - C = AppCodes.Classroom.Tag + ".Create", - R = AppCodes.Classroom.Tag, - U = AppCodes.Classroom.Tag + ".Update", + C = AppCodes.Definitions.Tag + ".Create", + R = AppCodes.Definitions.Tag, + U = AppCodes.Definitions.Tag + ".Update", E = true, I = true, Deny = false @@ -30197,6 +30483,880 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency } #endregion + #region Question Pool + if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == ListFormCodes.Lists.QuestionPool)) + { + var listFormQuestionPool = await _listFormRepository.InsertAsync( + new ListForm() + { + ListFormType = ListFormTypeEnum.List, + IsSubForm = false, + LayoutJson = JsonSerializer.Serialize(new LayoutDto() + { + Grid = true, + Card = true, + Pivot = true, + Chart = true, + DefaultLayout = "grid", + CardLayoutColumn = 3 + }), + CultureName = LanguageCodes.En, + ListFormCode = ListFormCodes.Lists.QuestionPool, + Name = AppCodes.Definitions.QuestionPool, + Title = AppCodes.Definitions.QuestionPool, + DataSourceCode = SeedConsts.DataSources.DefaultCode, + IsTenant = true, + IsBranch = false, + IsOrganizationUnit = false, + Description = AppCodes.Definitions.QuestionPool, + SelectCommandType = SelectCommandTypeEnum.Table, + SelectCommand = SelectCommandByTableName("QuestionPool", Prefix.DbTableCoordinator), + KeyFieldName = "Id", + KeyFieldDbSourceType = DbType.Guid, + DefaultFilter = "\"IsDeleted\" = 'false'", + SortMode = GridOptions.SortModeSingle, + FilterRowJson = JsonSerializer.Serialize(new GridFilterRowDto { Visible = true }), + HeaderFilterJson = JsonSerializer.Serialize(new { Visible = true }), + SearchPanelJson = JsonSerializer.Serialize(new { Visible = true }), + GroupPanelJson = JsonSerializer.Serialize(new { Visible = true }), + SelectionJson = JsonSerializer.Serialize(new SelectionDto + { + Mode = GridOptions.SelectionModeSingle, + AllowSelectAll = false + }), + ColumnOptionJson = JsonSerializer.Serialize(new + { + ColumnFixingEnabled = true, + ColumnAutoWidth = true, + ColumnChooserEnabled = true, + AllowColumnResizing = true, + AllowColumnReordering = true, + ColumnResizingMode = "widget", + }), + PermissionJson = JsonSerializer.Serialize(new PermissionCrudDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + D = AppCodes.Definitions.QuestionPool + ".Delete", + E = AppCodes.Definitions.QuestionPool + ".Export", + I = AppCodes.Definitions.QuestionPool + ".Import", + A = AppCodes.Definitions.QuestionPool + ".Activity", + }), + DeleteCommand = $"UPDATE \"{SelectCommandByTableName("QuestionPool", Prefix.DbTableCoordinator)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id", + DeleteFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { + new() { + FieldName = "DeleterId", + FieldDbType = DbType.Guid, + Value = "@USERID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "Id", + FieldDbType = DbType.Guid, + Value = "@ID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey } + }), + PagerOptionJson = JsonSerializer.Serialize(new GridPagerOptionDto + { + Visible = true, + AllowedPageSizes = "10,20,50,100", + ShowPageSizeSelector = true, + ShowNavigationButtons = true, + ShowInfo = false, + InfoText = "Page {0} of {1} ({2} items)", + DisplayMode = GridColumnOptions.PagerDisplayModeAdaptive, + ScrollingMode = GridColumnOptions.ScrollingModeStandard, + LoadPanelEnabled = "auto", + LoadPanelText = "Loading..." + }), + EditingOptionJson = JsonSerializer.Serialize(new GridEditingDto + { + Popup = new GridEditingPopupDto() + { + Title = "Question Pool Form", + Width = 500, + Height = 250 + }, + AllowDeleting = true, + AllowAdding = true, + AllowUpdating = true, + SendOnlyChangedFormValuesUpdate = false, + }), + EditingFormJson = JsonSerializer.Serialize(new List() { + new() { Order=1,ColCount=1,ColSpan=2,ItemType="group", Items= + [ + new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 2, DataField = "Description", ColSpan = 2, EditorType2=EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 3, DataField = "Tags", ColSpan = 2, EditorType2=EditorTypes.dxTagBox }, + ] + } + }), + InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { + new() { + FieldName = "CreationTime", + FieldDbType = DbType.DateTime, + Value = "@NOW", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "CreatorId", + FieldDbType = DbType.Guid, + Value = "@USERID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "IsDeleted", + FieldDbType = DbType.Boolean, + Value = "false", + CustomValueType = FieldCustomValueTypeEnum.Value } + }), + CommandColumnJson = JsonSerializer.Serialize(new CommandColumnDto[] { + new() { + Hint = "Questions", + Text ="Questions", + UrlTarget="_blank", + AuthName = AppCodes.Definitions.Question, + Url=$"/admin/form/{ListFormCodes.Forms.FormQuestionPool}/@Id" + }, + }), + + } + ); + + #region Question Pool Fields + await _listFormFieldRepository.InsertManyAsync(new ListFormField[] { + new() { + ListFormCode = listFormQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.Guid, + FieldName = "Id", + Width = 100, + ListOrderNo = 1, + Visible = false, + IsActive = true, + IsDeleted = false, + SortIndex = 0, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Name", + Width = 250, + ListOrderNo = 2, + Visible = true, + IsActive = true, + IsDeleted = false, + SortIndex = 1, + SortDirection = GridColumnOptions.SortOrderAsc, + AllowSearch = true, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Description", + Width = 400, + ListOrderNo = 3, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestionPool.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Tags", + Width = 100, + ListOrderNo = 4, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + LookupJson = JsonSerializer.Serialize(new LookupDto { + DataSourceType = UiLookupDataSourceTypeEnum.Query, + DisplayExpr = "Name", + ValueExpr = "Key", + LookupQuery = LookUpQueryValues.QuestionTagValues + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.QuestionPool + ".Create", + R = AppCodes.Definitions.QuestionPool, + U = AppCodes.Definitions.QuestionPool + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + } + }); + #endregion + } + #endregion + + #region Question + if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == ListFormCodes.Lists.Question)) + { + var listFormQuestion = await _listFormRepository.InsertAsync( + new ListForm() + { + ListFormType = ListFormTypeEnum.List, + IsSubForm = true, + LayoutJson = JsonSerializer.Serialize(new LayoutDto() + { + Grid = true, + Card = true, + Pivot = true, + Chart = true, + DefaultLayout = "grid", + CardLayoutColumn = 3 + }), + CultureName = LanguageCodes.En, + ListFormCode = ListFormCodes.Lists.Question, + Name = AppCodes.Definitions.Question, + Title = AppCodes.Definitions.Question, + DataSourceCode = SeedConsts.DataSources.DefaultCode, + IsTenant = true, + IsBranch = false, + IsOrganizationUnit = false, + Description = AppCodes.Definitions.Question, + SelectCommandType = SelectCommandTypeEnum.Table, + SelectCommand = SelectCommandByTableName("Question", Prefix.DbTableCoordinator), + KeyFieldName = "Id", + KeyFieldDbSourceType = DbType.Guid, + DefaultFilter = "\"IsDeleted\" = 'false'", + SortMode = GridOptions.SortModeSingle, + FilterRowJson = JsonSerializer.Serialize(new GridFilterRowDto { Visible = true }), + HeaderFilterJson = JsonSerializer.Serialize(new { Visible = true }), + SearchPanelJson = JsonSerializer.Serialize(new { Visible = true }), + GroupPanelJson = JsonSerializer.Serialize(new { Visible = true }), + SelectionJson = JsonSerializer.Serialize(new SelectionDto + { + Mode = GridOptions.SelectionModeSingle, + AllowSelectAll = false + }), + ColumnOptionJson = JsonSerializer.Serialize(new + { + ColumnFixingEnabled = true, + ColumnAutoWidth = true, + ColumnChooserEnabled = true, + AllowColumnResizing = true, + AllowColumnReordering = true, + ColumnResizingMode = "widget", + }), + PermissionJson = JsonSerializer.Serialize(new PermissionCrudDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + D = AppCodes.Definitions.Question + ".Delete", + E = AppCodes.Definitions.Question + ".Export", + I = AppCodes.Definitions.Question + ".Import", + A = AppCodes.Definitions.Question + ".Activity", + }), + DeleteCommand = $"UPDATE \"{SelectCommandByTableName("Question", Prefix.DbTableCoordinator)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id", + DeleteFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { + new() { + FieldName = "DeleterId", + FieldDbType = DbType.Guid, + Value = "@USERID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "Id", + FieldDbType = DbType.Guid, + Value = "@ID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey } + }), + PagerOptionJson = JsonSerializer.Serialize(new GridPagerOptionDto + { + Visible = true, + AllowedPageSizes = "10,20,50,100", + ShowPageSizeSelector = true, + ShowNavigationButtons = true, + ShowInfo = false, + InfoText = "Page {0} of {1} ({2} items)", + DisplayMode = GridColumnOptions.PagerDisplayModeAdaptive, + ScrollingMode = GridColumnOptions.ScrollingModeStandard, + LoadPanelEnabled = "auto", + LoadPanelText = "Loading..." + }), + EditingOptionJson = JsonSerializer.Serialize(new GridEditingDto + { + Popup = new GridEditingPopupDto() + { + Title = "Question Form", + Width = 500, + Height = 520 + }, + AllowDeleting = true, + AllowAdding = true, + AllowUpdating = true, + SendOnlyChangedFormValuesUpdate = false, + }), + InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { + new() { + FieldName = "CreationTime", + FieldDbType = DbType.DateTime, + Value = "@NOW", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "CreatorId", + FieldDbType = DbType.Guid, + Value = "@USERID", + CustomValueType = FieldCustomValueTypeEnum.CustomKey }, + new() { + FieldName = "IsDeleted", + FieldDbType = DbType.Boolean, + Value = "false", + CustomValueType = FieldCustomValueTypeEnum.Value } + }), + CommandColumnJson = JsonSerializer.Serialize(new CommandColumnDto[] { + new() { + Hint = "Answers", + Text = "Answers", + AuthName = AppCodes.Definitions.Question, + DialogName = "QuestionAnswers", + DialogParameters = JsonSerializer.Serialize(new { + id = "@Id" + }) + }, + }), + EditingFormJson = JsonSerializer.Serialize(new List() { + new() { Order=1,ColCount=1,ColSpan=2,ItemType="group", Items= + [ + new EditingFormItemDto { Order = 1, DataField = "QuestionPoolId", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, + new EditingFormItemDto { Order = 2, DataField = "QuestionType", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, + new EditingFormItemDto { Order = 3, DataField = "Points", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox }, + new EditingFormItemDto { Order = 4, DataField = "Title", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 5, DataField = "Content", ColSpan = 2, EditorType2 = EditorTypes.dxTextArea }, + new EditingFormItemDto { Order = 6, DataField = "MediaType", ColSpan = 2, EditorType2 = EditorTypes.dxSelectBox }, + new EditingFormItemDto { Order = 7, DataField = "MediaUrl", ColSpan = 2, EditorType2 = EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 8, DataField = "CorrectAnswer", ColSpan = 2, EditorType2 = EditorTypes.dxTextBox }, + new EditingFormItemDto { Order = 9, DataField = "Difficulty", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }, + new EditingFormItemDto { Order = 10, DataField = "TimeLimit", ColSpan = 2, EditorType2 = EditorTypes.dxNumberBox }, + new EditingFormItemDto { Order = 11, DataField = "Explanation", ColSpan = 2, EditorType2 = EditorTypes.dxTextArea }, + ] + } + }), + FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { + new() { + FieldName = "MediaType", + FieldDbType = DbType.String, + Value = "image", + CustomValueType = FieldCustomValueTypeEnum.Value }, + new() { + FieldName = "Difficulty", + FieldDbType = DbType.String, + Value = "easy", + CustomValueType = FieldCustomValueTypeEnum.Value } + }) + } + ); + + #region Question Fields + await _listFormFieldRepository.InsertManyAsync([ + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.Guid, + FieldName = "Id", + Width = 100, + ListOrderNo = 1, + Visible = false, + IsActive = true, + IsDeleted = false, + SortIndex = 0, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.Guid, + FieldName = "QuestionPoolId", + Width = 100, + ListOrderNo = 2, + Visible = false, + IsActive = true, + IsDeleted = false, + SortIndex = 0, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + LookupJson = JsonSerializer.Serialize(new LookupDto { + DataSourceType = UiLookupDataSourceTypeEnum.Query, + DisplayExpr = "Name", + ValueExpr = "Key", + LookupQuery = LookUpQueryValues.QuestionPoolValues + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "QuestionType", + Width = 120, + ListOrderNo = 3, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + LookupJson = JsonSerializer.Serialize(new LookupDto + { + DataSourceType = UiLookupDataSourceTypeEnum.StaticData, + DisplayExpr = "name", + ValueExpr = "key", + LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] { + new () { Key= "multiple-choice", Name= "Çoktan Seçmeli" }, + new () { Key= "fill-blank", Name= "Boşluk Doldurma" }, + new () { Key= "multiple-answer", Name= "Çok Yanıtlı"}, + new () { Key= "matching", Name= "Eşleştirme" }, + new () { Key= "ordering", Name= "Sıralama" }, + new () { Key= "open-ended", Name= "Açık Uçlu" }, + new () { Key= "true-false", Name= "Doğru-Yanlış" }, + new () { Key= "calculation", Name="Hesaplama" } + }), + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.Decimal, + FieldName = "Points", + Width = 60, + ListOrderNo = 4, + Visible = true, + IsActive = true, + IsDeleted = false, + ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { + new ValidationRuleDto() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) } + }), + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Title", + Width = 150, + ListOrderNo = 5, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Content", + Width = 350, + ListOrderNo = 6, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "MediaType", + Width = 100, + ListOrderNo = 7, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + LookupJson = JsonSerializer.Serialize(new LookupDto + { + DataSourceType = UiLookupDataSourceTypeEnum.StaticData, + DisplayExpr = "name", + ValueExpr = "key", + LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] { + new () { Key= "image", Name= "Image" }, + new () { Key= "video", Name= "Video" }, + }), + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "MediaUrl", + Width = 300, + ListOrderNo = 8, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "CorrectAnswer", + Width = 100, + ListOrderNo = 9, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Difficulty", + Width = 100, + ListOrderNo = 10, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + LookupJson = JsonSerializer.Serialize(new LookupDto + { + DataSourceType = UiLookupDataSourceTypeEnum.StaticData, + DisplayExpr = "name", + ValueExpr = "key", + LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] { + new () { Key= "easy", Name= "Easy" }, + new () { Key= "medium", Name= "Medium" }, + new () { Key= "hard", Name= "Hard" }, + }), + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.Int32, + FieldName = "TimeLimit", + Width = 100, + ListOrderNo = 11, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + new() { + ListFormCode = listFormQuestion.ListFormCode, + RoleId = null, + UserId = null, + CultureName = LanguageCodes.En, + SourceDbType = DbType.Int32, + FieldName = "Explanation", + Width = 100, + ListOrderNo = 12, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = JsonSerializer.Serialize(new ColumnCustomizationDto + { + AllowReordering = true, + }), + PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto + { + C = AppCodes.Definitions.Question + ".Create", + R = AppCodes.Definitions.Question, + U = AppCodes.Definitions.Question + ".Update", + E = true, + I = true, + Deny = false + }), + PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto + { + IsPivot = true + }) + }, + ]); + #endregion + } + #endregion #endregion } diff --git a/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs b/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs index 0361419f..04d38387 100644 --- a/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs +++ b/api/src/Kurs.Platform.Domain.Shared/PlatformConsts.cs @@ -149,6 +149,22 @@ public static class PlatformConsts $"\"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\".\"Name\" AS \"Name\" " + $"FROM \"{SelectCommandByTableName("InstallmentOption", Prefix.DbTableWeb)}\" " + $"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 @@ -508,10 +524,11 @@ public static class PlatformConsts { public static class Forms { - public const string FormTenant = "form-tenant"; public const string FormLanguage = "form-language"; public const string FormUomCategory = "form-uomcategory"; public const string FormSkillType = "form-skilltype"; + public const string FormTenant = "form-tenant"; + public const string FormQuestionPool = "form-questionpool"; public const string FormBank = "form-bank"; } @@ -596,6 +613,8 @@ public static class PlatformConsts public const string Class = "list-class"; public const string Level = "list-level"; public const string Tag = "list-tag"; + public const string QuestionPool = "list-questionpool"; + public const string Question = "list-question"; } } diff --git a/api/src/Kurs.Platform.Domain/Data/SeedConsts.cs b/api/src/Kurs.Platform.Domain/Data/SeedConsts.cs index 329d618a..2447c126 100644 --- a/api/src/Kurs.Platform.Domain/Data/SeedConsts.cs +++ b/api/src/Kurs.Platform.Domain/Data/SeedConsts.cs @@ -438,12 +438,14 @@ public static class SeedConsts public const string ClassType = Default + ".ClassType"; public const string Class = Default + ".Class"; 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 Tag = Default + ".Tag"; } public static class Accounting diff --git a/api/src/Kurs.Platform.Domain/Entities/Tenant/Question.cs b/api/src/Kurs.Platform.Domain/Entities/Tenant/Question.cs new file mode 100644 index 00000000..7b51fe44 --- /dev/null +++ b/api/src/Kurs.Platform.Domain/Entities/Tenant/Question.cs @@ -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, 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 Options { get; set; } = []; + + // Foreign key + public Guid QuestionPoolId { get; set; } + public QuestionPool QuestionPool { get; set; } + + + Guid? IMultiTenant.TenantId => TenantId; +} + diff --git a/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionOption.cs b/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionOption.cs new file mode 100644 index 00000000..f6487fe5 --- /dev/null +++ b/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionOption.cs @@ -0,0 +1,19 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Kurs.Platform.Entities; + +public class QuestionOption : FullAuditedEntity, 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; +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionPool.cs b/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionPool.cs new file mode 100644 index 00000000..390aa5fd --- /dev/null +++ b/api/src/Kurs.Platform.Domain/Entities/Tenant/QuestionPool.cs @@ -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, 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 Questions { get; set; } = new List(); + + Guid? IMultiTenant.TenantId => TenantId; +} diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index 824937f4..b83e6b0c 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -141,6 +141,9 @@ public class PlatformDbContext : public DbSet ChatMessages { get; set; } public DbSet Tags { get; set; } + public DbSet QuestionPools { get; set; } + public DbSet Questions { get; set; } + public DbSet QuestionOptions { get; set; } #endregion @@ -488,6 +491,16 @@ public class PlatformDbContext : b.Property(x => x.ErrorsJson).HasColumnType("text"); }); + builder.Entity(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(b => { b.ToTable(Prefix.DbTableDefault + nameof(BackgroundWorker), Prefix.DbSchema); @@ -1487,7 +1500,7 @@ public class PlatformDbContext : //Coordinator builder.Entity(b => { - b.ToTable(Prefix.DbTableDefault + nameof(Classroom), Prefix.DbSchema); + b.ToTable(Prefix.DbTableCoordinator + nameof(Classroom), Prefix.DbSchema); b.ConfigureByConvention(); b.Property(x => x.Name).IsRequired().HasMaxLength(200); @@ -1516,7 +1529,7 @@ public class PlatformDbContext : builder.Entity(b => { - b.ToTable(Prefix.DbTableDefault + nameof(ClassroomParticipant), Prefix.DbSchema); + b.ToTable(Prefix.DbTableCoordinator + nameof(ClassroomParticipant), Prefix.DbSchema); b.ConfigureByConvention(); b.Property(x => x.UserName).IsRequired().HasMaxLength(100); @@ -1531,7 +1544,7 @@ public class PlatformDbContext : builder.Entity(b => { - b.ToTable(Prefix.DbTableDefault + nameof(ClassroomAttandance), Prefix.DbSchema); + b.ToTable(Prefix.DbTableCoordinator + nameof(ClassroomAttandance), Prefix.DbSchema); b.ConfigureByConvention(); b.Property(x => x.StudentName).IsRequired().HasMaxLength(100); @@ -1543,7 +1556,7 @@ public class PlatformDbContext : builder.Entity(b => { - b.ToTable(Prefix.DbTableDefault + nameof(ClassroomChat), Prefix.DbSchema); + b.ToTable(Prefix.DbTableCoordinator + nameof(ClassroomChat), Prefix.DbSchema); b.ConfigureByConvention(); b.Property(x => x.SenderName).IsRequired().HasMaxLength(100); @@ -1554,18 +1567,6 @@ public class PlatformDbContext : b.HasIndex(x => x.Timestamp); }); - // Form Activity Entities - builder.Entity(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(b => { 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.UsageCount).HasDefaultValue(0); }); + + builder.Entity(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(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(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); + }); } } diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251015192202_Initial.Designer.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.Designer.cs similarity index 97% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251015192202_Initial.Designer.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.Designer.cs index b2ded844..303a3a3a 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251015192202_Initial.Designer.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Kurs.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20251015192202_Initial")] + [Migration("20251016120353_Initial")] partial class Initial { /// @@ -1780,7 +1780,7 @@ namespace Kurs.Platform.Migrations b.HasIndex("TeacherId"); - b.ToTable("PClassroom", (string)null); + b.ToTable("CClassroom", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.ClassroomAttandance", b => @@ -1853,7 +1853,7 @@ namespace Kurs.Platform.Migrations b.HasIndex("StudentId"); - b.ToTable("PClassroomAttandance", (string)null); + b.ToTable("CClassroomAttandance", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.ClassroomChat", b => @@ -1937,7 +1937,7 @@ namespace Kurs.Platform.Migrations b.HasIndex("Timestamp"); - b.ToTable("PClassroomChat", (string)null); + b.ToTable("CClassroomChat", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.ClassroomParticipant", b => @@ -2032,7 +2032,7 @@ namespace Kurs.Platform.Migrations .IsUnique() .HasFilter("[UserId] IS NOT NULL"); - b.ToTable("PClassroomParticipant", (string)null); + b.ToTable("CClassroomParticipant", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.Contact", b => @@ -5103,6 +5103,214 @@ namespace Kurs.Platform.Migrations b.ToTable("DPsychologist", (string)null); }); + modelBuilder.Entity("Kurs.Platform.Entities.Question", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CorrectAnswer") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Difficulty") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Explanation") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MediaType") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("MediaUrl") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Points") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("QuestionPoolId") + .HasColumnType("uniqueidentifier"); + + b.Property("QuestionType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TimeLimit") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("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("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsCorrect") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("QuestionId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("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("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Tags") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("CQuestionPool", (string)null); + }); + modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b => { b.Property("Id") @@ -6115,7 +6323,6 @@ namespace Kurs.Platform.Migrations modelBuilder.Entity("Kurs.Platform.Entities.Tag", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Color") @@ -8969,6 +9176,28 @@ namespace Kurs.Platform.Migrations 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 => { b.HasOne("Kurs.Platform.Entities.RegistrationType", "RegistrationType") @@ -9298,6 +9527,16 @@ namespace Kurs.Platform.Migrations 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 => { b.Navigation("ClassTypes"); diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251015192202_Initial.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.cs similarity index 96% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251015192202_Initial.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.cs index 7cb3c933..7a91ae18 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251015192202_Initial.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016120353_Initial.cs @@ -434,6 +434,61 @@ namespace Kurs.Platform.Migrations table.PrimaryKey("PK_AbpUsers", x => x.Id); }); + migrationBuilder.CreateTable( + name: "CClassroom", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + BranchId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), + Subject = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + TeacherId = table.Column(type: "uniqueidentifier", nullable: true), + TeacherName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + ScheduledStartTime = table.Column(type: "datetime2", nullable: false), + ScheduledEndTime = table.Column(type: "datetime2", nullable: true), + Duration = table.Column(type: "int", nullable: false), + ActualStartTime = table.Column(type: "datetime2", nullable: true), + ActualEndTime = table.Column(type: "datetime2", nullable: true), + MaxParticipants = table.Column(type: "int", nullable: false), + ParticipantCount = table.Column(type: "int", nullable: false), + SettingsJson = table.Column(type: "nvarchar(max)", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CClassroom", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "CQuestionPool", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Description = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + Tags = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CQuestionPool", x => x.Id); + }); + migrationBuilder.CreateTable( name: "CTag", columns: table => new @@ -1370,39 +1425,6 @@ namespace Kurs.Platform.Migrations table.PrimaryKey("PK_PBranchUsers", x => new { x.UserId, x.BranchId }); }); - migrationBuilder.CreateTable( - name: "PClassroom", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - BranchId = table.Column(type: "uniqueidentifier", nullable: true), - Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), - Description = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), - Subject = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - TeacherId = table.Column(type: "uniqueidentifier", nullable: true), - TeacherName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), - ScheduledStartTime = table.Column(type: "datetime2", nullable: false), - ScheduledEndTime = table.Column(type: "datetime2", nullable: true), - Duration = table.Column(type: "int", nullable: false), - ActualStartTime = table.Column(type: "datetime2", nullable: true), - ActualEndTime = table.Column(type: "datetime2", nullable: true), - MaxParticipants = table.Column(type: "int", nullable: false), - ParticipantCount = table.Column(type: "int", nullable: false), - SettingsJson = table.Column(type: "nvarchar(max)", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_PClassroom", x => x.Id); - }); - migrationBuilder.CreateTable( name: "PCustomComponent", columns: table => new @@ -2325,6 +2347,146 @@ namespace Kurs.Platform.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "CClassroomAttandance", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + BranchId = table.Column(type: "uniqueidentifier", nullable: true), + SessionId = table.Column(type: "uniqueidentifier", nullable: false), + StudentId = table.Column(type: "uniqueidentifier", nullable: true), + StudentName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + JoinTime = table.Column(type: "datetime2", nullable: false), + LeaveTime = table.Column(type: "datetime2", nullable: true), + TotalDurationMinutes = table.Column(type: "int", nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(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(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + BranchId = table.Column(type: "uniqueidentifier", nullable: true), + SessionId = table.Column(type: "uniqueidentifier", nullable: false), + SenderId = table.Column(type: "uniqueidentifier", nullable: true), + SenderName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Message = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: false), + Timestamp = table.Column(type: "datetime2", nullable: false), + RecipientId = table.Column(type: "uniqueidentifier", nullable: true), + RecipientName = table.Column(type: "nvarchar(max)", nullable: true), + IsTeacher = table.Column(type: "bit", nullable: false), + MessageType = table.Column(type: "nvarchar(max)", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(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(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + BranchId = table.Column(type: "uniqueidentifier", nullable: true), + SessionId = table.Column(type: "uniqueidentifier", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: true), + UserName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + IsTeacher = table.Column(type: "bit", nullable: false), + IsAudioMuted = table.Column(type: "bit", nullable: false), + IsVideoMuted = table.Column(type: "bit", nullable: false), + IsHandRaised = table.Column(type: "bit", nullable: false), + IsKicked = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + JoinTime = table.Column(type: "datetime2", nullable: false), + ConnectionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(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(type: "uniqueidentifier", nullable: false), + QuestionType = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Points = table.Column(type: "int", nullable: false, defaultValue: 0), + Title = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), + Content = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + MediaUrl = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + MediaType = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + CorrectAnswer = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + Difficulty = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: true), + TimeLimit = table.Column(type: "int", nullable: false, defaultValue: 0), + Explanation = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + QuestionPoolId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(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( name: "DCountry", columns: table => new @@ -2674,110 +2836,6 @@ namespace Kurs.Platform.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "PClassroomAttandance", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - BranchId = table.Column(type: "uniqueidentifier", nullable: true), - SessionId = table.Column(type: "uniqueidentifier", nullable: false), - StudentId = table.Column(type: "uniqueidentifier", nullable: true), - StudentName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), - JoinTime = table.Column(type: "datetime2", nullable: false), - LeaveTime = table.Column(type: "datetime2", nullable: true), - TotalDurationMinutes = table.Column(type: "int", nullable: false), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(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(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - BranchId = table.Column(type: "uniqueidentifier", nullable: true), - SessionId = table.Column(type: "uniqueidentifier", nullable: false), - SenderId = table.Column(type: "uniqueidentifier", nullable: true), - SenderName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), - Message = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: false), - Timestamp = table.Column(type: "datetime2", nullable: false), - RecipientId = table.Column(type: "uniqueidentifier", nullable: true), - RecipientName = table.Column(type: "nvarchar(max)", nullable: true), - IsTeacher = table.Column(type: "bit", nullable: false), - MessageType = table.Column(type: "nvarchar(max)", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(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(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - BranchId = table.Column(type: "uniqueidentifier", nullable: true), - SessionId = table.Column(type: "uniqueidentifier", nullable: false), - UserId = table.Column(type: "uniqueidentifier", nullable: true), - UserName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), - IsTeacher = table.Column(type: "bit", nullable: false), - IsAudioMuted = table.Column(type: "bit", nullable: false), - IsVideoMuted = table.Column(type: "bit", nullable: false), - IsHandRaised = table.Column(type: "bit", nullable: false), - IsKicked = table.Column(type: "bit", nullable: false, defaultValue: false), - IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), - JoinTime = table.Column(type: "datetime2", nullable: false), - ConnectionId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(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( name: "PApiEndpoint", columns: table => new @@ -3215,6 +3273,34 @@ namespace Kurs.Platform.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "CQuestionOption", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Text = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + IsCorrect = table.Column(type: "bit", nullable: false, defaultValue: false), + QuestionId = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(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( name: "DCity", columns: table => new @@ -3748,6 +3834,73 @@ namespace Kurs.Platform.Migrations table: "AbpUsers", 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( name: "IX_DBankAccount_BankId", table: "DBankAccount", @@ -3905,63 +4058,6 @@ namespace Kurs.Platform.Migrations columns: new[] { "TableName", "Order" }, 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( name: "IX_PCustomEntityField_EntityId", table: "PCustomEntityField", @@ -4165,6 +4261,18 @@ namespace Kurs.Platform.Migrations migrationBuilder.DropTable( name: "AbpUserTokens"); + migrationBuilder.DropTable( + name: "CClassroomAttandance"); + + migrationBuilder.DropTable( + name: "CClassroomChat"); + + migrationBuilder.DropTable( + name: "CClassroomParticipant"); + + migrationBuilder.DropTable( + name: "CQuestionOption"); + migrationBuilder.DropTable( name: "CTag"); @@ -4294,15 +4402,6 @@ namespace Kurs.Platform.Migrations migrationBuilder.DropTable( name: "PBranchUsers"); - migrationBuilder.DropTable( - name: "PClassroomAttandance"); - - migrationBuilder.DropTable( - name: "PClassroomChat"); - - migrationBuilder.DropTable( - name: "PClassroomParticipant"); - migrationBuilder.DropTable( name: "PCustomComponent"); @@ -4396,6 +4495,12 @@ namespace Kurs.Platform.Migrations migrationBuilder.DropTable( name: "AbpUsers"); + migrationBuilder.DropTable( + name: "CClassroom"); + + migrationBuilder.DropTable( + name: "CQuestion"); + migrationBuilder.DropTable( name: "DBank"); @@ -4429,9 +4534,6 @@ namespace Kurs.Platform.Migrations migrationBuilder.DropTable( name: "PBackgroundWorker_MailQueue"); - migrationBuilder.DropTable( - name: "PClassroom"); - migrationBuilder.DropTable( name: "PCustomEntity"); @@ -4462,6 +4564,9 @@ namespace Kurs.Platform.Migrations migrationBuilder.DropTable( name: "AbpAuditLogs"); + migrationBuilder.DropTable( + name: "CQuestionPool"); + migrationBuilder.DropTable( name: "DCountry"); diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index a601097c..64317430 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -1777,7 +1777,7 @@ namespace Kurs.Platform.Migrations b.HasIndex("TeacherId"); - b.ToTable("PClassroom", (string)null); + b.ToTable("CClassroom", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.ClassroomAttandance", b => @@ -1850,7 +1850,7 @@ namespace Kurs.Platform.Migrations b.HasIndex("StudentId"); - b.ToTable("PClassroomAttandance", (string)null); + b.ToTable("CClassroomAttandance", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.ClassroomChat", b => @@ -1934,7 +1934,7 @@ namespace Kurs.Platform.Migrations b.HasIndex("Timestamp"); - b.ToTable("PClassroomChat", (string)null); + b.ToTable("CClassroomChat", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.ClassroomParticipant", b => @@ -2029,7 +2029,7 @@ namespace Kurs.Platform.Migrations .IsUnique() .HasFilter("[UserId] IS NOT NULL"); - b.ToTable("PClassroomParticipant", (string)null); + b.ToTable("CClassroomParticipant", (string)null); }); modelBuilder.Entity("Kurs.Platform.Entities.Contact", b => @@ -5100,6 +5100,214 @@ namespace Kurs.Platform.Migrations b.ToTable("DPsychologist", (string)null); }); + modelBuilder.Entity("Kurs.Platform.Entities.Question", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CorrectAnswer") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Difficulty") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Explanation") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MediaType") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("MediaUrl") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Points") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("QuestionPoolId") + .HasColumnType("uniqueidentifier"); + + b.Property("QuestionType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TimeLimit") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("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("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsCorrect") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("QuestionId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("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("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Tags") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("CQuestionPool", (string)null); + }); + modelBuilder.Entity("Kurs.Platform.Entities.RegistrationMethod", b => { b.Property("Id") @@ -6112,7 +6320,6 @@ namespace Kurs.Platform.Migrations modelBuilder.Entity("Kurs.Platform.Entities.Tag", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Color") @@ -8966,6 +9173,28 @@ namespace Kurs.Platform.Migrations 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 => { b.HasOne("Kurs.Platform.Entities.RegistrationType", "RegistrationType") @@ -9295,6 +9524,16 @@ namespace Kurs.Platform.Migrations 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 => { b.Navigation("ClassTypes"); diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json index 2ffadc06..b8e556e9 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json @@ -1827,6 +1827,61 @@ "Name": "Writing", "Description": "Writing skills development", "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": "" } ] } diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs index 8d6766ff..677c42d2 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs @@ -10,6 +10,8 @@ using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Collections.Generic; namespace Kurs.Platform.Data.Seeds; @@ -53,6 +55,8 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency private readonly IRepository _workHourRepository; private readonly IRepository _classroomRepository; private readonly IRepository _tagRepository; + private readonly IRepository _questionPoolRepository; + private readonly IRepository _questionRepository; public TenantDataSeeder( IRepository globalSearch, @@ -92,7 +96,9 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency IRepository interestingRepository, IRepository programRepository, IRepository forumCategoryRepository, - IRepository tagRepository + IRepository tagRepository, + IRepository questionPoolRepository, + IRepository questionRepository ) { _globalSearch = globalSearch; @@ -133,6 +139,8 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency _programRepository = programRepository; _forumCategoryRepository = forumCategoryRepository; _tagRepository = tagRepository; + _questionPoolRepository = questionPoolRepository; + _questionRepository = questionRepository; } 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(); + 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 + }); + } + } + } } } diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs index 17544a20..442f5a46 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs @@ -27,6 +27,8 @@ public class TenantSeederDto public List Contacts { get; set; } public List Classrooms { get; set; } public List Tags { get; set; } + public List QuestionPools { get; set; } + public List Questions { get; set; } //Tanımlamalar public List Sectors { get; set; } @@ -351,4 +353,26 @@ public class TagSeedDto public string Name { get; set; } public string Description { 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; } } \ No newline at end of file diff --git a/ui/src/mocks/mockPools.ts b/ui/src/mocks/mockPools.ts index 68741bbb..b820c12f 100644 --- a/ui/src/mocks/mockPools.ts +++ b/ui/src/mocks/mockPools.ts @@ -1,7 +1,7 @@ -import { QuestionPool } from "@/types/coordinator"; +import { QuestionPoolDto } from "@/types/coordinator"; // Dynamic data - no hardcoded content -export const generateMockPools = (): QuestionPool[] => [ +export const generateMockPools = (): QuestionPoolDto[] => [ { id: "pool-1", name: "Grammar Fundamentals", diff --git a/ui/src/routes/route.constant.ts b/ui/src/routes/route.constant.ts index bc312e4a..ef541e61 100644 --- a/ui/src/routes/route.constant.ts +++ b/ui/src/routes/route.constant.ts @@ -73,13 +73,19 @@ export const ROUTES_ENUM = { formEdit: '/admin/form/:listFormCode/:id/edit', chart: '/admin/chart/:listFormCode', pivot: '/admin/pivot/:listFormCode', + }, + + participant: {}, + + coordinator: { classroom: { - dashboard: '/admin/classroom/dashboard', - classes: '/admin/classroom/classes', - roomDetail: '/admin/classroom/room/:id', - planning: '/admin/classroom/planning/:id', + dashboard: '/admin/coordinator/classroom/dashboard', + classes: '/admin/coordinator/classroom/classes', + roomDetail: '/admin/coordinator/classroom/room/:id', + planning: '/admin/coordinator/classroom/planning/:id', }, }, + supplychain: { materialTypes: '/admin/supplychain/materials/types', materialGroups: '/admin/supplychain/materials/groups', diff --git a/ui/src/services/classroom/signalr.tsx b/ui/src/services/classroom/signalr.tsx index 7bf9d260..06303e58 100644 --- a/ui/src/services/classroom/signalr.tsx +++ b/ui/src/services/classroom/signalr.tsx @@ -186,7 +186,7 @@ export class SignalRService { } this.currentSessionId = undefined - window.location.href = ROUTES_ENUM.protected.admin.classroom.classes + window.location.href = ROUTES_ENUM.protected.coordinator.classroom.classes }) } diff --git a/ui/src/services/contact.ts b/ui/src/services/contact.service.ts similarity index 100% rename from ui/src/services/contact.ts rename to ui/src/services/contact.service.ts diff --git a/ui/src/services/question.service.ts b/ui/src/services/question.service.ts new file mode 100644 index 00000000..cfbf4c6a --- /dev/null +++ b/ui/src/services/question.service.ts @@ -0,0 +1,48 @@ +import { PagedResultDto } from '@/proxy' +import apiService from './api.service' +import { QuestionDto } from '@/types/coordinator' + +class QuestionService { + async getQuestions(): Promise> { + const response = await apiService.fetchData>({ + url: '/api/app/question', + method: 'GET', + }) + return response.data + } + + async getQuestion(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/question/${id}`, + method: 'GET', + }) + return response.data + } + + async updateQuestion(id: string, input: QuestionDto) { + const response = await apiService.fetchData({ + url: `/api/app/question/${id}`, + method: 'PUT', + data: input as any, + }) + return response.data + } + + async createQuestion(input: QuestionDto) { + const response = await apiService.fetchData({ + method: 'POST', + url: '/api/app/question', + data: input as any, + }) + return response.data + } + + async deleteQuestion(id: string) { + await apiService.fetchData({ + method: 'DELETE', + url: `/api/app/question/${id}`, + }) + } +} + +export const questionService = new QuestionService() diff --git a/ui/src/types/coordinator.ts b/ui/src/types/coordinator.ts index cf8401c8..67b69214 100644 --- a/ui/src/types/coordinator.ts +++ b/ui/src/types/coordinator.ts @@ -14,24 +14,24 @@ export type MediaType = 'image' | 'video'; export type QuestionDifficulty = 'easy' | 'medium' | 'hard'; export type ExamSessionStatus = 'in-progress' | 'completed' | 'submitted'; -export interface QuestionPool { +export interface QuestionPoolDto { id: string; name: string; description: string; - questions: Question[]; + questions: QuestionDto[]; tags: string[]; createdBy: string; creationTime: Date; } -export interface Question { +export interface QuestionDto { id: string; type: QuestionType; title: string; content: string; mediaUrl?: string; mediaType?: MediaType; - options?: QuestionOption[]; + options?: QuestionOptionDto[]; correctAnswer?: string | string[]; points: number; timeLimit?: number; @@ -42,7 +42,7 @@ export interface Question { lastModificationTime: Date; } -export interface QuestionOption { +export interface QuestionOptionDto { id: string; text: string; isCorrect: boolean; @@ -60,7 +60,7 @@ export interface Exam { name: string; }; answerKeyTemplate?: AnswerKeyItem[]; - questions: Question[]; + questions: QuestionDto[]; timeLimit: number; totalPoints: number; passingScore: number; diff --git a/ui/src/utils/hooks/useCoordinator.ts b/ui/src/utils/hooks/useCoordinator.ts index 1a73a519..40d12b75 100644 --- a/ui/src/utils/hooks/useCoordinator.ts +++ b/ui/src/utils/hooks/useCoordinator.ts @@ -1,9 +1,9 @@ -import { QuestionPool, Exam, TagItem, ExamSession } from "@/types/coordinator"; +import { QuestionPoolDto, Exam, TagItem, ExamSession } from "@/types/coordinator"; import { useState } from "react"; export function useCoordinator() { const [currentPath, setCurrentPath] = useState("/admin/dashboard"); - const [pools, setPools] = useState([]); + const [pools, setPools] = useState([]); const [exams, setExams] = useState([]); const [tags, setTags] = useState([]); const [currentExam, setCurrentExam] = useState(null); @@ -19,9 +19,9 @@ export function useCoordinator() { }; const handleCreatePool = ( - poolData: Omit + poolData: Omit ) => { - const newPool: QuestionPool = { + const newPool: QuestionPoolDto = { ...poolData, id: `pool-${Date.now()}`, creationTime: new Date(), @@ -29,7 +29,7 @@ export function useCoordinator() { setPools((prev) => [...prev, newPool]); }; - const handleUpdatePool = (updatedPool: QuestionPool) => { + const handleUpdatePool = (updatedPool: QuestionPoolDto) => { setPools((prev) => prev.map((pool) => (pool.id === updatedPool.id ? updatedPool : pool)) ); diff --git a/ui/src/views/coordinator/AdminPanel/ExamCreator.tsx b/ui/src/views/coordinator/AdminPanel/ExamCreator.tsx index 0c9a3e95..8bf0a750 100644 --- a/ui/src/views/coordinator/AdminPanel/ExamCreator.tsx +++ b/ui/src/views/coordinator/AdminPanel/ExamCreator.tsx @@ -1,9 +1,9 @@ -import { QuestionPool, Exam, Question } from "@/types/coordinator"; +import { QuestionPoolDto, Exam, QuestionDto } from "@/types/coordinator"; import React, { useState } from "react"; import { FaPlus, FaClock, FaUsers, FaCog, FaSave } from "react-icons/fa"; interface ExamCreatorProps { - pools: QuestionPool[]; + pools: QuestionPoolDto[]; onCreateExam: (exam: Omit) => void; onCancel?: () => void; editingExam?: Exam; @@ -30,7 +30,7 @@ export const ExamCreator: React.FC = ({ isActive: editingExam?.isActive ?? true, }); - const [selectedQuestions, setSelectedQuestions] = useState( + const [selectedQuestions, setSelectedQuestions] = useState( editingExam?.questions || [] ); diff --git a/ui/src/views/coordinator/Assignments.tsx b/ui/src/views/coordinator/Assignments.tsx index 1a82dab6..6328b030 100644 --- a/ui/src/views/coordinator/Assignments.tsx +++ b/ui/src/views/coordinator/Assignments.tsx @@ -14,12 +14,12 @@ import { import { useNavigate } from 'react-router-dom' import { generateMockExam } from '@/mocks/mockExams' import { generateMockPools } from '@/mocks/mockPools' -import { Exam, QuestionPool } from '@/types/coordinator' +import { Exam, QuestionPoolDto } from '@/types/coordinator' const Assignments: React.FC = () => { const navigate = useNavigate() const [assignments, setAssignments] = useState(generateMockExam()) - const [pools] = useState(generateMockPools()) + const [pools] = useState(generateMockPools()) const [searchTerm, setSearchTerm] = useState('') const [statusFilter, setStatusFilter] = useState('') const [isCreating, setIsCreating] = useState(false) diff --git a/ui/src/views/classroom/ClassList.tsx b/ui/src/views/coordinator/Classroom/ClassList.tsx similarity index 99% rename from ui/src/views/classroom/ClassList.tsx rename to ui/src/views/coordinator/Classroom/ClassList.tsx index 9b3894bd..193bf7e6 100644 --- a/ui/src/views/classroom/ClassList.tsx +++ b/ui/src/views/coordinator/Classroom/ClassList.tsx @@ -179,13 +179,13 @@ const ClassList: React.FC = () => { const handleJoinClass = (classSession: ClassroomDto) => { 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) => { 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 = () => { <> diff --git a/ui/src/views/classroom/Dashboard.tsx b/ui/src/views/coordinator/Classroom/Dashboard.tsx similarity index 95% rename from ui/src/views/classroom/Dashboard.tsx rename to ui/src/views/coordinator/Classroom/Dashboard.tsx index 1c26cf94..09795b85 100644 --- a/ui/src/views/classroom/Dashboard.tsx +++ b/ui/src/views/coordinator/Classroom/Dashboard.tsx @@ -20,14 +20,14 @@ const Dashboard: React.FC = () => { role, }) - navigate(ROUTES_ENUM.protected.admin.classroom.classes, { replace: true }) + navigate(ROUTES_ENUM.protected.coordinator.classroom.classes, { replace: true }) } return ( <>
diff --git a/ui/src/views/classroom/PlanningPage.tsx b/ui/src/views/coordinator/Classroom/PlanningPage.tsx similarity index 99% rename from ui/src/views/classroom/PlanningPage.tsx rename to ui/src/views/coordinator/Classroom/PlanningPage.tsx index cc46a759..025d4d15 100644 --- a/ui/src/views/classroom/PlanningPage.tsx +++ b/ui/src/views/coordinator/Classroom/PlanningPage.tsx @@ -164,7 +164,7 @@ const PlanningPage: React.FC = () => { <> diff --git a/ui/src/views/classroom/RoomDetail.tsx b/ui/src/views/coordinator/Classroom/RoomDetail.tsx similarity index 99% rename from ui/src/views/classroom/RoomDetail.tsx rename to ui/src/views/coordinator/Classroom/RoomDetail.tsx index b47a9b7a..4c4ba658 100644 --- a/ui/src/views/classroom/RoomDetail.tsx +++ b/ui/src/views/coordinator/Classroom/RoomDetail.tsx @@ -482,12 +482,12 @@ const RoomDetail: React.FC = () => { await cleanup() // Başka sayfaya yönlendir - navigate(ROUTES_ENUM.protected.admin.classroom.classes) + navigate(ROUTES_ENUM.protected.coordinator.classroom.classes) } catch (err) { toast.push(, { 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 = () => { <> diff --git a/ui/src/views/coordinator/ExamInterface/ExamNavigation.tsx b/ui/src/views/coordinator/ExamInterface/ExamNavigation.tsx index baf5fe05..cd32589c 100644 --- a/ui/src/views/coordinator/ExamInterface/ExamNavigation.tsx +++ b/ui/src/views/coordinator/ExamInterface/ExamNavigation.tsx @@ -1,8 +1,8 @@ -import { Question, StudentAnswer } from '@/types/coordinator'; +import { QuestionDto, StudentAnswer } from '@/types/coordinator'; import React from 'react'; interface ExamNavigationProps { - questions: Question[]; + questions: QuestionDto[]; answers: StudentAnswer[]; currentQuestionIndex: number; onQuestionSelect: (index: number) => void; diff --git a/ui/src/views/coordinator/ExamInterface/QuestionRenderer.tsx b/ui/src/views/coordinator/ExamInterface/QuestionRenderer.tsx index 3926621a..dcf88dcd 100644 --- a/ui/src/views/coordinator/ExamInterface/QuestionRenderer.tsx +++ b/ui/src/views/coordinator/ExamInterface/QuestionRenderer.tsx @@ -7,10 +7,10 @@ import { MultipleChoiceQuestion } from '../QuestionTypes/MultipleChoiceQuestion' import { OpenEndedQuestion } from '../QuestionTypes/OpenEndedQuestion'; import { OrderingQuestion } from '../QuestionTypes/OrderingQuestion'; import { TrueFalseQuestion } from '../QuestionTypes/TrueFalseQuestion'; -import { Question, StudentAnswer } from '@/types/coordinator'; +import { QuestionDto, StudentAnswer } from '@/types/coordinator'; interface QuestionRendererProps { - question: Question; + question: QuestionDto; answer?: StudentAnswer; onAnswerChange: (questionId: string, answer: string | string[]) => void; disabled?: boolean; diff --git a/ui/src/views/coordinator/Exams.tsx b/ui/src/views/coordinator/Exams.tsx index 4cef133d..aba5466f 100644 --- a/ui/src/views/coordinator/Exams.tsx +++ b/ui/src/views/coordinator/Exams.tsx @@ -14,12 +14,12 @@ import { import { useNavigate } from 'react-router-dom' import { generateMockExam } from '@/mocks/mockExams' import { generateMockPools } from '@/mocks/mockPools' -import { Exam, QuestionPool } from '@/types/coordinator' +import { Exam, QuestionPoolDto } from '@/types/coordinator' const Exams: React.FC = () => { const navigate = useNavigate() const [exams, setExams] = useState(generateMockExam()) - const [pools] = useState(generateMockPools()) + const [pools] = useState(generateMockPools()) const [searchTerm, setSearchTerm] = useState('') const [statusFilter, setStatusFilter] = useState('') const [isCreating, setIsCreating] = useState(false) diff --git a/ui/src/views/coordinator/AdminPanel/QuestionEditor.tsx b/ui/src/views/coordinator/QuestionDialog.tsx similarity index 67% rename from ui/src/views/coordinator/AdminPanel/QuestionEditor.tsx rename to ui/src/views/coordinator/QuestionDialog.tsx index 5dff196b..29e3cb2f 100644 --- a/ui/src/views/coordinator/AdminPanel/QuestionEditor.tsx +++ b/ui/src/views/coordinator/QuestionDialog.tsx @@ -1,34 +1,49 @@ -import { Question, QuestionType, QuestionDifficulty, QuestionOption } from "@/types/coordinator"; -import React, { useState, useEffect } from "react"; -import { FaSave, FaPlus, FaTrash, FaTimes } from "react-icons/fa"; +import { questionService } from '@/services/question.service' +import { + QuestionDto, + QuestionType, + QuestionDifficulty, + QuestionOptionDto, +} from '@/types/coordinator' +import React, { useState, useEffect } from 'react' +import { FaSave, FaPlus, FaTrash, FaTimes } from 'react-icons/fa' -interface QuestionEditorProps { - question?: Question; - onSave: (question: Omit) => void; - onCancel: () => void; -} - -export const QuestionEditor: React.FC = ({ - question, - onSave, - onCancel, -}) => { +function QuestionDialog({ + open, + onDialogClose, + id, +}: { + open: boolean + onDialogClose: () => void + id: string +}) { + const [question, setQuestion] = useState() const [formData, setFormData] = useState({ - type: "multiple-choice" as QuestionType, - title: "", - content: "", - mediaUrl: "", - mediaType: "image" as "image" | "video", + type: 'multiple-choice' as QuestionType, + title: '', + content: '', + mediaUrl: '', + mediaType: 'image' as 'image' | 'video', points: 10, timeLimit: 0, - explanation: "", - tags: [] as string[], - difficulty: "medium" as QuestionDifficulty, - }); + explanation: '', + difficulty: 'medium' as QuestionDifficulty, + }) - const [options, setOptions] = useState([]); - const [correctAnswer, setCorrectAnswer] = useState(""); - const [tagInput, setTagInput] = useState(""); + const [options, setOptions] = useState([]) + const [correctAnswer, setCorrectAnswer] = useState('') + const [tagInput, setTagInput] = useState('') + + useEffect(() => { + const fetchQuestion = async () => { + if (open) { + const entity = await questionService.getQuestion(id) + setQuestion(entity) + } + } + + fetchQuestion() + }, [open]) useEffect(() => { if (question) { @@ -36,136 +51,103 @@ export const QuestionEditor: React.FC = ({ type: question.type, title: question.title, content: question.content, - mediaUrl: question.mediaUrl || "", - mediaType: question.mediaType || "image", + mediaUrl: question.mediaUrl || '', + mediaType: question.mediaType || 'image', points: question.points, timeLimit: question.timeLimit || 0, - explanation: question.explanation || "", - tags: question.tags, + explanation: question.explanation || '', difficulty: question.difficulty, - }); - setOptions(question.options || []); - setCorrectAnswer(question.correctAnswer || ""); + }) + setOptions(question.options || []) + setCorrectAnswer(question.correctAnswer || '') } - }, [question]); + }, [question]) const handleInputChange = (field: string, value: any) => { - setFormData((prev) => ({ ...prev, [field]: value })); - }; + setFormData((prev) => ({ ...prev, [field]: value })) + } const addOption = () => { - const newOption: QuestionOption = { + const newOption: QuestionOptionDto = { id: `opt-${Date.now()}`, - text: "", + text: '', isCorrect: false, order: options.length, - }; - setOptions((prev) => [...prev, newOption]); - }; + } + setOptions((prev) => [...prev, newOption]) + } const updateOption = (index: number, field: string, value: any) => { - setOptions((prev) => - prev.map((opt, i) => (i === index ? { ...opt, [field]: value } : opt)) - ); - }; + setOptions((prev) => prev.map((opt, i) => (i === index ? { ...opt, [field]: value } : opt))) + } const removeOption = (index: number) => { - 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), - })); - }; + setOptions((prev) => prev.filter((_, i) => i !== index)) + } const handleSave = () => { if (!formData.title.trim() || !formData.content.trim()) { - alert("Please fill in the title and content fields."); - return; + alert('Please fill in the title and content fields.') + return } if (formData.points <= 0) { - alert("Points must be greater than 0."); - return; + alert('Points must be greater than 0.') + return } // Validate based on question type - if ( - ["multiple-choice", "multiple-answer"].includes(formData.type) && - options.length < 2 - ) { - alert("Please add at least 2 options."); - return; + if (['multiple-choice', 'multiple-answer'].includes(formData.type) && options.length < 2) { + alert('Please add at least 2 options.') + return } - if ( - formData.type === "multiple-choice" && - !options.some((opt) => opt.isCorrect) - ) { - alert("Please mark the correct answer."); - return; + if (formData.type === 'multiple-choice' && !options.some((opt) => opt.isCorrect)) { + alert('Please mark the correct answer.') + return } const questionData = { ...formData, - options: [ - "multiple-choice", - "multiple-answer", - "matching", - "ordering", - ].includes(formData.type) + options: ['multiple-choice', 'multiple-answer', 'matching', 'ordering'].includes( + formData.type, + ) ? options : undefined, correctAnswer: getCorrectAnswer(), - }; + } - onSave(questionData); - }; + // onSave(questionData) + } const getCorrectAnswer = (): string | string[] => { switch (formData.type) { - case "multiple-choice": - return options.find((opt) => opt.isCorrect)?.id || ""; - case "multiple-answer": - return options.filter((opt) => opt.isCorrect).map((opt) => opt.id); - case "true-false": - return correctAnswer as string; - case "fill-blank": - case "open-ended": - case "calculation": - return correctAnswer as string; - case "matching": - return options.map((opt) => opt.id); - case "ordering": - return options - .sort((a, b) => (a.order || 0) - (b.order || 0)) - .map((opt) => opt.id); + case 'multiple-choice': + return options.find((opt) => opt.isCorrect)?.id || '' + case 'multiple-answer': + return options.filter((opt) => opt.isCorrect).map((opt) => opt.id) + case 'true-false': + return correctAnswer as string + case 'fill-blank': + case 'open-ended': + case 'calculation': + return correctAnswer as string + case 'matching': + return options.map((opt) => opt.id) + case 'ordering': + return options.sort((a, b) => (a.order || 0) - (b.order || 0)).map((opt) => opt.id) default: - return correctAnswer as string; + return correctAnswer as string } - }; + } const renderQuestionTypeSpecificFields = () => { switch (formData.type) { - case "multiple-choice": + case 'multiple-choice': return (
-

- Yanıtlar (A, B, C, D, E) -

+

Yanıtlar (A, B, C, D, E)

))}
- ); + ) - case "multiple-answer": + case 'multiple-answer': return (
@@ -243,21 +225,17 @@ export const QuestionEditor: React.FC = ({ - updateOption(index, "isCorrect", e.target.checked) - } + onChange={(e) => updateOption(index, 'isCorrect', e.target.checked)} className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" /> updateOption(index, "text", e.target.value)} + onChange={(e) => updateOption(index, 'text', e.target.value)} 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" /> -
- {option.isCorrect ? "Doğru" : "Yanlış"} -
+
{option.isCorrect ? 'Doğru' : 'Yanlış'}
))}
- ); + ) - case "true-false": + case 'true-false': return (
- +
- ); + ) - case "fill-blank": + case 'fill-blank': return (
@@ -314,9 +290,7 @@ export const QuestionEditor: React.FC = ({
{Array.from({ length: 10 }, (_, index) => { - const blankAnswers = ((correctAnswer as string) || "").split( - "|" - ); + const blankAnswers = ((correctAnswer as string) || '').split('|') return (
@@ -324,41 +298,35 @@ export const QuestionEditor: React.FC = ({
{ - const newAnswers = [...blankAnswers]; - newAnswers[index] = e.target.value; + const newAnswers = [...blankAnswers] + newAnswers[index] = e.target.value // Remove empty answers from the end - while ( - newAnswers.length > 0 && - !newAnswers[newAnswers.length - 1] - ) { - newAnswers.pop(); + while (newAnswers.length > 0 && !newAnswers[newAnswers.length - 1]) { + newAnswers.pop() } - setCorrectAnswer(newAnswers.join("|")); + setCorrectAnswer(newAnswers.join('|')) }} 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" />
- ); + ) })}

- Soru içeriğinde _____ veya [blank] kullanarak boşlukları - işaretleyin + Soru içeriğinde _____ veya [blank] kullanarak boşlukları işaretleyin

- ); + ) - case "matching": + case 'matching': return (
-

- Eşleştirme Çiftleri -

+

Eşleştirme Çiftleri

))}
- ); + ) - case "ordering": + case 'ordering': return (
-

- Sıralanacak Öğeler -

+

Sıralanacak Öğeler

- ); + ) - case "open-ended": + case 'open-ended': return (
- ); + ) - case "calculation": + case 'calculation': return (
- ); + ) default: - return null; + return null } - }; + } return (

- {question ? "Edit Question" : "Create New Question"} + {question ? 'Edit Question' : 'Create New Question'}

-
@@ -540,7 +490,7 @@ export const QuestionEditor: React.FC = ({ - handleInputChange("points", parseInt(e.target.value)) - } + onChange={(e) => handleInputChange('points', parseInt(e.target.value))} 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" /> @@ -571,25 +517,21 @@ export const QuestionEditor: React.FC = ({
- + handleInputChange("title", e.target.value)} + onChange={(e) => handleInputChange('title', e.target.value)} 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" />
- +