Seeder güncellemesi

This commit is contained in:
Sedat ÖZTÜRK 2025-06-27 18:00:47 +03:00
parent fbeba1bada
commit 6157b9b320
21 changed files with 700 additions and 334 deletions

View file

@ -131,16 +131,11 @@ namespace Kurs.Platform.Blog
input.CoverImage,
input.CategoryId,
_currentUser.Id.Value,
CurrentTenant.Id
true,
DateTime.UtcNow,
(Guid?)CurrentTenant.Id
);
post.CoverImage = input.CoverImage;
if (input.IsPublished)
{
post.Publish();
}
await _postRepository.InsertAsync(post, autoSave: true);
return await GetPostAsync(post.Id);

View file

@ -440,26 +440,26 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
{
new() { Order=1, ColCount=1, ColSpan=2, ItemType="group", Items =
[
new EditingFormItemDto { Order=1, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=2, DataField="InstitutionName", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=1, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="InstitutionName", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=3, DataField="VknTckn", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxNumberBox },
new EditingFormItemDto { Order=4, DataField="TaxOffice", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=4, DataField="TaxOffice", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=5, DataField="Mobile", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxNumberBox },
new EditingFormItemDto { Order=6, DataField="Phone", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxNumberBox },
new EditingFormItemDto { Order=7, DataField="Fax", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=7, DataField="Fax", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=8, DataField="IsActive", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxCheckBox },
]
},
new() { Order=2, ColCount=1, ColSpan=2, ItemType="group", Items =
[
new EditingFormItemDto { Order=1, DataField="Address", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=2, DataField="Address2", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=1, DataField="Address", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="Address2", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=3, DataField="Country", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=4, DataField="City", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=5, DataField="District", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=6, DataField="PostalCode", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=7, DataField="Email", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=8, DataField="Website", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=4, DataField="City", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=5, DataField="District", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=6, DataField="PostalCode", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=7, DataField="Email", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=8, DataField="Website", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
]
}
}),
@ -3089,7 +3089,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
new EditingFormItemDto { Order = 1, DataField = "CultureName", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 2, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "ListFormCode", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 4, DataField = "ListFormType", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 4, DataField = "ListFormType", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 5, DataField = "SelectCommandType", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 6, DataField = "SelectCommand", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 7, DataField = "TableName", ColSpan = 2, IsRequired = false, EditorType2=EditorTypes.dxTextBox },
@ -7742,26 +7742,26 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
new() { Order=1, ColCount=1, ColSpan=2, ItemType="group", Items =
[
new EditingFormItemDto { Order=1, DataField="TenantId", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=2, DataField="Code", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=3, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=4, DataField="VknTckn", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxNumberBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=5, DataField="TaxOffice", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=2, DataField="Code", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=3, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=4, DataField="VknTckn", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxNumberBox },
new EditingFormItemDto { Order=5, DataField="TaxOffice", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=6, DataField="Mobile", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxNumberBox },
new EditingFormItemDto { Order=7, DataField="Phone", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxNumberBox },
new EditingFormItemDto { Order=8, DataField="Fax", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=8, DataField="Fax", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=9, DataField="IsActive", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxCheckBox },
]
},
new() { Order=2, ColCount=1, ColSpan=2, ItemType="group", Items =
[
new EditingFormItemDto { Order=1, DataField="Address", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=2, DataField="Address2", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=1, DataField="Address", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="Address2", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=3, DataField="Country", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=4, DataField="City", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=5, DataField="District", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=6, DataField="PostalCode", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=7, DataField="Email", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=8, DataField="Website", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=4, DataField="City", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=5, DataField="District", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=6, DataField="PostalCode", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=7, DataField="Email", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=8, DataField="Website", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
]
}
}),
@ -8389,13 +8389,13 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
{
new() { Order=1, ColCount=1, ColSpan=2, ItemType="group", Items =
[
new EditingFormItemDto { Order=1, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=2, DataField="ValueType", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxSelectBox },
new EditingFormItemDto { Order=1, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="ValueType", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxSelectBox, EditorOptions = "{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=3, DataField="Required", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxCheckBox },
new EditingFormItemDto { Order=4, DataField="IsStatic", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxCheckBox },
new EditingFormItemDto { Order=5, DataField="Regex", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=6, DataField="RegexDescription", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=7, DataField="Description", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order=5, DataField="Regex", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=6, DataField="RegexDescription", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=7, DataField="Description", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
]
}
}),
@ -8760,24 +8760,24 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{
new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
new()
{
Order = 1,
DataField = "Name",
ColCount = 1,
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
ItemType = "group",
Items =
[
new EditingFormItemDto
{
Order = 1,
DataField = "Name",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
}
]
}
]
}
}),
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
{
@ -8940,32 +8940,32 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{
new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
new()
{
Order = 1,
DataField = "Name",
ColCount = 1,
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Category",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
ItemType = "group",
Items =
[
new EditingFormItemDto
{
Order = 1,
DataField = "Name",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Category",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
}
]
}
]
}
}),
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
{
@ -9154,49 +9154,49 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{
new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
new()
{
Order = 1,
DataField = "Title",
ColCount = 1,
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Abbreviation",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
ItemType = "group",
Items =
[
new EditingFormItemDto
{
Order = 1,
DataField = "Title",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Abbreviation",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
}
]
}
]
}
}),
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 = "CreationTime",
FieldDbType = DbType.DateTime,
Value = "@NOW",
CustomValueType = FieldCustomValueTypeEnum.CustomKey
},
new()
{
FieldName = "CreatorId",
FieldDbType = DbType.Guid,
Value = "@USERID",
CustomValueType = FieldCustomValueTypeEnum.CustomKey
}
})
});
@ -9370,74 +9370,74 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{
new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
{
Order = 1,
DataField = "Code",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Symbol",
ColSpan = 2,
IsRequired = false,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 3,
DataField = "Name",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 4,
DataField = "Rate",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxNumberBox
},
new EditingFormItemDto
{
Order = 5,
DataField = "IsActive",
ColSpan = 2,
IsRequired = false,
EditorType2 = EditorTypes.dxCheckBox
}
]
}
new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
{
Order = 1,
DataField = "Code",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Symbol",
ColSpan = 2,
IsRequired = false,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 3,
DataField = "Name",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 4,
DataField = "Rate",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxNumberBox
},
new EditingFormItemDto
{
Order = 5,
DataField = "IsActive",
ColSpan = 2,
IsRequired = false,
EditorType2 = EditorTypes.dxCheckBox
}
]
}
}),
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new[]
{
new FieldsDefaultValue
{
FieldName = "CreationTime",
FieldDbType = DbType.DateTime,
Value = "@NOW",
CustomValueType = FieldCustomValueTypeEnum.CustomKey
},
new FieldsDefaultValue
{
FieldName = "CreatorId",
FieldDbType = DbType.Guid,
Value = "@USERID",
CustomValueType = FieldCustomValueTypeEnum.CustomKey
}
})
new FieldsDefaultValue
{
FieldName = "CreationTime",
FieldDbType = DbType.DateTime,
Value = "@NOW",
CustomValueType = FieldCustomValueTypeEnum.CustomKey
},
new FieldsDefaultValue
{
FieldName = "CreatorId",
FieldDbType = DbType.Guid,
Value = "@USERID",
CustomValueType = FieldCustomValueTypeEnum.CustomKey
}
})
});
#region Currency Fields
@ -9860,7 +9860,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
[
new EditingFormItemDto { Order = 1, DataField = "Code", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 2, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "GroupName", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 3, DataField = "GroupName", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 4, DataField = "CurrencyCode", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 5, DataField = "PhoneCode", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 6, DataField = "TaxLabel", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
@ -10195,7 +10195,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
[
new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 2, DataField = "Code", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "CountryCode", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox }
new EditingFormItemDto { Order = 3, DataField = "CountryCode", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" }
]
}
}),
@ -10797,9 +10797,9 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
ItemType = "group",
Items =
[
new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 2, DataField = "Slug", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "Description", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 3, DataField = "Description", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 4, DataField = "Icon", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 5, DataField = "DisplayOrder", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 6, DataField = "IsActive", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxCheckBox }
@ -11111,12 +11111,12 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
ItemType = "group",
Items =
[
new EditingFormItemDto { Order = 1, DataField = "Title", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 1, DataField = "Title", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 2, DataField = "Slug", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 3, DataField = "Summary", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 3, DataField = "Summary", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 4, DataField = "CoverImage", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 5, DataField = "ReadTime", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 6, DataField = "CategoryId", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox },
new EditingFormItemDto { Order = 6, DataField = "CategoryId", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions="{ \"showClearButton\" : true }" },
new EditingFormItemDto { Order = 7, DataField = "ViewCount", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 8, DataField = "LikeCount", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 9, DataField = "CommentCount", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
@ -11333,7 +11333,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = $"SELECT \"{DbTablePrefix}BlogCategories\".\"Id\" AS \"Key\", \"{DbTablePrefix}BlogCategories\".\"Name\" as \"Name\" FROM \"{DbTablePrefix}BlogCategories\" ORDER BY \"{DbTablePrefix}BlogCategories\".\"Name\""
}),
}),
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
{
C = AppCodes.BlogManagement.BlogPosts + ".Create",
@ -11365,13 +11365,55 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
})
},
new()
{
ListFormCode = listFormBlogPosts.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Int32,
FieldName = "LikeCount",
Width = 80,
ListOrderNo = 9,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
{
C = AppCodes.BlogManagement.BlogPosts + ".Create",
R = AppCodes.BlogManagement.BlogPosts,
U = AppCodes.BlogManagement.BlogPosts + ".Update",
E = true,
Deny = false
})
},
new()
{
ListFormCode = listFormBlogPosts.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Int32,
FieldName = "CommentCount",
Width = 80,
ListOrderNo = 10,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
{
C = AppCodes.BlogManagement.BlogPosts + ".Create",
R = AppCodes.BlogManagement.BlogPosts,
U = AppCodes.BlogManagement.BlogPosts + ".Update",
E = true,
Deny = false
})
},
new()
{
ListFormCode = listFormBlogPosts.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Boolean,
FieldName = "IsPublished",
Width = 80,
ListOrderNo = 9,
ListOrderNo = 10,
Visible = true,
IsActive = true,
IsDeleted = false,
@ -11392,7 +11434,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
SourceDbType = DbType.DateTime,
FieldName = "PublishedAt",
Width = 120,
ListOrderNo = 10,
ListOrderNo = 11,
Visible = true,
IsActive = true,
IsDeleted = false,
@ -11405,6 +11447,48 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
E = true,
Deny = false
})
},
new()
{
ListFormCode = listFormBlogPosts.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.DateTime,
FieldName = "ContentTr",
Width = 120,
ListOrderNo = 11,
Visible = false,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
{
C = AppCodes.BlogManagement.BlogPosts + ".Create",
R = AppCodes.BlogManagement.BlogPosts,
U = AppCodes.BlogManagement.BlogPosts + ".Update",
E = true,
Deny = false
})
},
new()
{
ListFormCode = listFormBlogPosts.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.DateTime,
FieldName = "ContentEn",
Width = 120,
ListOrderNo = 12,
Visible = false,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
{
C = AppCodes.BlogManagement.BlogPosts + ".Create",
R = AppCodes.BlogManagement.BlogPosts,
U = AppCodes.BlogManagement.BlogPosts + ".Update",
E = true,
Deny = false
})
}
]);
#endregion

View file

@ -593,7 +593,9 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
item.ReadTime,
item.CoverImage,
item.CategoryId,
item.AuthorId
item.AuthorId,
true,
DateTime.UtcNow
));
}
}

View file

@ -535,6 +535,228 @@
"en": "Forum",
"tr": "Forum"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.Dashboard",
"en": "Dashboard",
"tr": "Gösterge Paneli"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.Categories",
"en": "Categories",
"tr": "Kategoriler"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.Topics",
"en": "Topics",
"tr": "Konular"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.Posts",
"en": "Posts",
"tr": "Yazılar"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.Statistics",
"en": "Statistics",
"tr": "İstatistikler"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.TotalCategories",
"en": "Total Categories",
"tr": "Toplam Kategoriler"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.TotalTopics",
"en": "Total Topics",
"tr": "Toplam Konular"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.TotalPosts",
"en": "Total Posts",
"tr": "Toplam Yazılar"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.EngagementRate",
"en": "Engagement Rate",
"tr": "Katılım Oranı"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.RecentActivity",
"en": "Recent Activity",
"tr": "Son Etkinlik"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.GeneralDiscussion",
"en": "New topic created in General Discussion",
"tr": "Genel Tartışmada yeni konu oluşturuldu"
},
{
"resourceName": "Platform",
"key": "App.Forum.CategoryManagement.Title",
"en": "Category Management",
"tr": "Kategori Yönetimi"
},
{
"resourceName": "Platform",
"key": "App.Forum.CategoryManagement.AddCategory",
"en": "Add Category",
"tr": "Kategori Ekle"
},
{
"resourceName": "Platform",
"key": "App.Forum.CategoryManagement.EditCategory",
"en": "Edit Category",
"tr": "Kategoriyi Düzenle"
},
{
"resourceName": "Platform",
"key": "App.Forum.CategoryManagement.DeleteCategory",
"en": "Delete Category",
"tr": "Kategori Sil"
},
{
"resourceName": "Platform",
"key": "App.Forum.CategoryManagement.Categories",
"en": "Categories",
"tr": "Kategoriler"
},
{
"resourceName": "Platform",
"key": "App.Forum.CategoryManagement.Loadingcategories",
"en": "Loading categories...",
"tr": "Kategoriler Yükleniyor..."
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.Title",
"en": "Topic Management",
"tr": "Konu Yönetimi"
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.NewTopic",
"en": "New Topic",
"tr": "Yeni Konu"
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.AddTopic",
"en": "Add Topic",
"tr": "Konu Ekle"
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.EditTopic",
"en": "Edit Topic",
"tr": "Konu Düzenle"
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.DeleteTopic",
"en": "Delete Topic",
"tr": "Konu Sil"
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.Searchtopics",
"en": "Search topics...",
"tr": "Konular arama..."
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.Topics",
"en": "Topics",
"tr": "Konular"
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.Loadingtopics",
"en": "Loading topics...",
"tr": "Konular Yük"
},
{
"resourceName": "Platform",
"key": "App.Forum.TopicManagement.Lastreplyby",
"en": "Last reply by",
"tr": "Son cevaplayan"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.Title",
"en": "Post Management",
"tr": "Yazı Yönetimi"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.NewPost",
"en": "New Post",
"tr": "Yeni Yazı"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.ReplytoTopic",
"en": "Reply to Topic",
"tr": "Konuya Cevap Ver"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.AddPost",
"en": "Add Post",
"tr": "Yazı Ekle"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.AcceptedAnswer",
"en": "Accepted Answer",
"tr": "Kabul Edilen Cevap"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.PostReply",
"en": "Post Reply",
"tr": "Yazı Cevabı"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.EditPost",
"en": "Edit Post",
"tr": "Yazıyı Düzenle"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.DeletePost",
"en": "Delete Post",
"tr": "Yazıyı Sil"
},
{
"resourceName": "Platform",
"key": "App.Forum.PostManagement.Loadingtopics",
"en": "Loading posts...",
"tr": "Yazılar Yükleniyor..."
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.Postmarked",
"en": "Post marked as accepted answer",
"tr": "Cevap olarak işaretlenen yazı"
},
{
"resourceName": "Platform",
"key": "App.Forum.Dashboard.FeatureRequests",
"en": "New category created: Feature Requests",
"tr": "Yeni kategori oluşturuldu: Özellik Talepleri"
},
{
"resourceName": "Platform",
"key": "App.Home",

View file

@ -224,6 +224,8 @@ public class BlogPostSeedDto
public string CoverImage { get; set; }
public Guid CategoryId { get; set; }
public Guid AuthorId { get; set; }
public Boolean IsPublished { get; set; }
public DateTime PublishedAt { get; set; }
}
public class ForumCategorySeedDto

View file

@ -42,6 +42,8 @@ public class BlogPost : FullAuditedAggregateRoot<Guid>
string coverImage,
Guid categoryId,
Guid authorId,
bool isPublished,
DateTime? publishedAt = null,
Guid? tenantId = null) : base(id)
{
Title = title;
@ -53,12 +55,13 @@ public class BlogPost : FullAuditedAggregateRoot<Guid>
CoverImage = coverImage;
CategoryId = categoryId;
AuthorId = authorId;
IsPublished = isPublished;
PublishedAt = publishedAt;
TenantId = tenantId;
ViewCount = 0;
LikeCount = 0;
CommentCount = 0;
IsPublished = false;
}
public void Publish()

View file

@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "index.html",
"revision": "0.mj87dckq3bo"
"revision": "0.ru7ltd9thg8"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

View file

@ -89,7 +89,7 @@ const Simple = ({ children, content, ...rest }: SimpleProps) => {
return (
<div className="h-full">
<Container className="flex flex-col flex-auto items-center justify-center min-w-0 h-full">
<Card className="min-w-[320px] md:min-w-[450px]" bodyClass="md:p-10">
<Card className="min-w-[320px] md:min-w-[450px]" bodyClass="md:p-6">
<div className="flex justify-between items-center mb-4">
<a
href={import.meta.env.VITE_COMPANY_URL}

View file

@ -20,7 +20,7 @@ const HorizontalMenuIcon = ({ icon }: HorizontalMenuIconProps) => {
return <></>
}
return <span className="text-2xl ltr:mr-2 rtl:ml-2">{navigationIcon[icon]}</span>
return <span className="text-xl ltr:mr-2 rtl:ml-2">{navigationIcon[icon]}</span>
}
export default HorizontalMenuIcon

View file

@ -3,7 +3,8 @@ import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import { UiEvalService } from './services/UiEvalService'
import 'devextreme-react/text-area';
import 'devextreme-react/text-area'
import 'devextreme-react/html-editor'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(<App />)

View file

@ -5,6 +5,7 @@ import { TopicManagement } from './TopicManagement'
import { PostManagement } from './PostManagement'
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'
import { AdminStats } from './Dashboard'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface AdminViewProps {
categories: ForumCategory[]
@ -74,12 +75,13 @@ export function AdminView({
onUnmarkPostAsAcceptedAnswer,
}: AdminViewProps) {
const [activeSection, setActiveSection] = useState<AdminSection>('stats')
const { translate } = useLocalization();
const navigationItems = [
{ id: 'stats' as AdminSection, label: 'Dashboard', icon: BarChart3 },
{ id: 'categories' as AdminSection, label: 'Categories', icon: Folder },
{ id: 'topics' as AdminSection, label: 'Topics', icon: MessageSquare },
{ id: 'posts' as AdminSection, label: 'Posts', icon: FileText },
{ id: 'stats' as AdminSection, label: translate('::App.Forum.Dashboard.Dashboard'), icon: BarChart3 },
{ id: 'categories' as AdminSection, label: translate('::App.Forum.Dashboard.Categories'), icon: Folder },
{ id: 'topics' as AdminSection, label: translate('::App.Forum.Dashboard.Topics'), icon: MessageSquare },
{ id: 'posts' as AdminSection, label: translate('::App.Forum.Dashboard.Posts'), icon: FileText },
]
return (

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react'
import { Plus, Edit2, Trash2, Lock, Unlock, Eye, EyeOff, Loader2 } from 'lucide-react'
import { ForumCategory } from '@/proxy/forum/forum'
import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface CategoryManagementProps {
categories: ForumCategory[]
@ -31,6 +32,7 @@ export function CategoryManagement({
onUpdateCategoryLockState,
onUpdateCategoryActiveState,
}: CategoryManagementProps) {
const { translate } = useLocalization()
const { tenant } = useStoreState((state) => state.auth)
const [showCreateForm, setShowCreateForm] = useState(false)
const [editingCategory, setEditingCategory] = useState<ForumCategory | null>(null)
@ -42,7 +44,7 @@ export function CategoryManagement({
displayOrder: 0,
isActive: true,
isLocked: false,
tenantId: ''
tenantId: '',
})
const [submitting, setSubmitting] = useState(false)
@ -55,7 +57,7 @@ export function CategoryManagement({
displayOrder: 0,
isActive: true,
isLocked: false,
tenantId: ''
tenantId: '',
})
setShowCreateForm(false)
setEditingCategory(null)
@ -90,7 +92,7 @@ export function CategoryManagement({
displayOrder: category.displayOrder,
isActive: category.isActive,
isLocked: category.isLocked,
tenantId: tenant.tenantId ?? ''
tenantId: tenant.tenantId ?? '',
})
setShowCreateForm(true)
}
@ -128,14 +130,16 @@ export function CategoryManagement({
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold text-gray-900">Category Management</h2>
<h2 className="text-2xl font-bold text-gray-900">
{translate('::App.Forum.CategoryManagement.Title')}
</h2>
<button
onClick={() => setShowCreateForm(true)}
disabled={loading}
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
>
<Plus className="w-4 h-4" />
<span>Add Category</span>
<span>{translate('::App.Forum.CategoryManagement.AddCategory')}</span>
</button>
</div>
@ -143,7 +147,9 @@ export function CategoryManagement({
{showCreateForm && (
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
{editingCategory ? 'Edit Category' : 'Create New Category'}
{editingCategory
? translate('::App.Forum.CategoryManagement.EditCategory')
: translate('::App.Forum.CategoryManagement.CreateCategory')}
</h3>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@ -251,13 +257,13 @@ export function CategoryManagement({
{/* Categories List */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">Categories ({categories.length})</h3>
<h3 className="text-lg font-semibold text-gray-900">{translate('::App.Forum.CategoryManagement.Categories')} ({categories.length})</h3>
</div>
{loading ? (
<div className="p-8 text-center">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-4 text-blue-600" />
<p className="text-gray-500">Loading categories...</p>
<p className="text-gray-500">{translate('::App.Forum.CategoryManagement.Loadingcategories')}</p>
</div>
) : (
<div className="divide-y divide-gray-200">
@ -327,7 +333,7 @@ export function CategoryManagement({
<button
onClick={() => handleEdit(category)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title="Edit Category"
title={translate('::App.Forum.CategoryManagement.EditCategory')}
>
<Edit2 className="w-4 h-4" />
</button>
@ -335,7 +341,7 @@ export function CategoryManagement({
<button
onClick={() => handleDelete(category.id)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title="Delete Category"
title={translate('::App.Forum.CategoryManagement.DeleteCategory')}
>
<Trash2 className="w-4 h-4" />
</button>

View file

@ -1,6 +1,7 @@
import React from 'react';
import { Folder, MessageSquare, FileText, TrendingUp } from 'lucide-react';
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum';
import { useLocalization } from '@/utils/hooks/useLocalization';
interface AdminStatsProps {
categories: ForumCategory[];
@ -9,6 +10,7 @@ interface AdminStatsProps {
}
export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
const { translate } = useLocalization();
const totalCategories = categories.length;
const activeCategories = categories.filter(c => c.isActive).length;
const totalTopics = topics.length;
@ -18,28 +20,28 @@ export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
const stats = [
{
title: 'Total Categories',
title: translate('::App.Forum.Dashboard.TotalCategories'),
value: totalCategories,
subtitle: `${activeCategories} active`,
icon: Folder,
color: 'bg-blue-500',
},
{
title: 'Total Topics',
title: translate('::App.Forum.Dashboard.TotalTopics'),
value: totalTopics,
subtitle: `${solvedTopics} solved`,
icon: MessageSquare,
color: 'bg-emerald-500',
},
{
title: 'Total Posts',
title: translate('::App.Forum.Dashboard.TotalPosts'),
value: totalPosts,
subtitle: `${acceptedAnswers} accepted answers`,
icon: FileText,
color: 'bg-orange-500',
},
{
title: 'Engagement Rate',
title: translate('::App.Forum.Dashboard.EngagementRate'),
value: totalTopics > 0 ? Math.round((totalPosts / totalTopics) * 100) / 100 : 0,
subtitle: 'posts per topic',
icon: TrendingUp,
@ -50,7 +52,7 @@ export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-6">Forum Statistics</h2>
<h2 className="text-2xl font-bold text-gray-900 mb-6">{translate('::App.Forum.Dashboard.Statistics')}</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{stats.map((stat, index) => {
@ -75,26 +77,26 @@ export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
{/* Recent Activity */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Recent Activity</h3>
<h3 className="text-lg font-semibold text-gray-900 mb-4">{translate('::App.Forum.Dashboard.RecentActivity')}</h3>
<div className="space-y-4">
<div className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
<div className="w-2 h-2 bg-blue-500 rounded-full mt-2"></div>
<div>
<p className="text-sm text-gray-900">New topic created in General Discussion</p>
<p className="text-sm text-gray-900">{translate('::App.Forum.Dashboard.GeneralDiscussion')}</p>
<p className="text-xs text-gray-500">2 hours ago</p>
</div>
</div>
<div className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
<div className="w-2 h-2 bg-emerald-500 rounded-full mt-2"></div>
<div>
<p className="text-sm text-gray-900">Post marked as accepted answer</p>
<p className="text-sm text-gray-900">{translate('::App.Forum.Dashboard.Postmarked')}</p>
<p className="text-xs text-gray-500">4 hours ago</p>
</div>
</div>
<div className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
<div className="w-2 h-2 bg-orange-500 rounded-full mt-2"></div>
<div>
<p className="text-sm text-gray-900">New category created: Feature Requests</p>
<p className="text-sm text-gray-900">{translate('::App.Forum.Dashboard.FeatureRequests')}</p>
<p className="text-xs text-gray-500">1 day ago</p>
</div>
</div>

View file

@ -4,6 +4,7 @@ import { ForumPost, ForumTopic } from '@/proxy/forum/forum'
import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'
import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface PostManagementProps {
posts: ForumPost[]
@ -31,6 +32,7 @@ export function PostManagement({
onMarkPostAsAcceptedAnswer,
onUnmarkPostAsAcceptedAnswer,
}: PostManagementProps) {
const { translate } = useLocalization()
const { tenant } = useStoreState((state) => state.auth)
const [content, setContent] = useState('')
const [showCreateForm, setShowCreateForm] = useState(false)
@ -131,14 +133,16 @@ export function PostManagement({
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold text-gray-900">Post Management</h2>
<h2 className="text-2xl font-bold text-gray-900">
{translate('::App.Forum.PostManagement.Title')}
</h2>
<button
onClick={() => setShowCreateForm(true)}
disabled={loading}
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
>
<Plus className="w-4 h-4" />
<span>Add Post</span>
<span>{translate('::App.Forum.PostManagement.AddPost')}</span>
</button>
</div>
@ -146,7 +150,9 @@ export function PostManagement({
{showCreateForm && (
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
{editingPost ? 'Edit Post' : 'Create New Post'}
{editingPost
? translate('::App.Forum.PostManagement.EditPost')
: translate('::App.Forum.PostManagement.AddPost')}
</h3>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
@ -220,7 +226,7 @@ export function PostManagement({
{loading ? (
<div className="p-8 text-center">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-4 text-blue-600" />
<p className="text-gray-500">Loading posts...</p>
<p className="text-gray-500">{translate('::App.Forum.PostManagement.Loadingtopics')}</p>
</div>
) : (
<div className="divide-y divide-gray-200">
@ -288,7 +294,7 @@ export function PostManagement({
<button
onClick={() => handleEdit(post)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title="Edit Post"
title={translate('::App.Forum.PostManagement.EditPost')}
>
<Edit2 className="w-4 h-4" />
</button>
@ -296,7 +302,7 @@ export function PostManagement({
<button
onClick={() => handleDelete(post.id)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title="Delete Post"
title={translate('::App.Forum.PostManagement.DeletePost')}
>
<Trash2 className="w-4 h-4" />
</button>

View file

@ -14,6 +14,7 @@ import {
} from 'lucide-react'
import { ForumCategory, ForumTopic } from '@/proxy/forum/forum'
import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface TopicManagementProps {
topics: ForumTopic[]
@ -51,6 +52,7 @@ export function TopicManagement({
onMarkTopicAsSolved,
onMarkTopicAsUnsolved,
}: TopicManagementProps) {
const { translate } = useLocalization()
const { tenant } = useStoreState((state) => state.auth)
const [showCreateForm, setShowCreateForm] = useState(false)
const [editingTopic, setEditingTopic] = useState<ForumTopic | null>(null)
@ -107,7 +109,7 @@ export function TopicManagement({
isPinned: topic.isPinned,
isLocked: topic.isLocked,
isSolved: topic.isSolved,
tenantId: tenant.tenantId ?? ''
tenantId: tenant.tenantId ?? '',
})
setShowCreateForm(true)
}
@ -183,14 +185,16 @@ export function TopicManagement({
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold text-gray-900">Topic Management</h2>
<h2 className="text-2xl font-bold text-gray-900">
{translate('::App.Forum.TopicManagement.Title')}
</h2>
<button
onClick={() => setShowCreateForm(true)}
disabled={loading}
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
>
<Plus className="w-4 h-4" />
<span>Add Topic</span>
<span>{translate('::App.Forum.TopicManagement.AddTopic')}</span>
</button>
</div>
@ -198,7 +202,9 @@ export function TopicManagement({
{showCreateForm && (
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
{editingTopic ? 'Edit Topic' : 'Create New Topic'}
{editingTopic
? translate('::App.Forum.TopicManagement.AddTopic')
: translate('::App.Forum.TopicManagement.EditTopic')}
</h3>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
@ -295,13 +301,13 @@ export function TopicManagement({
{/* Topics List */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">Topics ({topics.length})</h3>
<h3 className="text-lg font-semibold text-gray-900">{translate('::App.Forum.TopicManagement.Topics')} ({topics.length})</h3>
</div>
{loading ? (
<div className="p-8 text-center">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-4 text-blue-600" />
<p className="text-gray-500">Loading topics...</p>
<p className="text-gray-500">{translate('::App.Forum.TopicManagement.Loadingtopics')}</p>
</div>
) : (
<div className="divide-y divide-gray-200">
@ -393,7 +399,7 @@ export function TopicManagement({
<button
onClick={() => handleEdit(topic)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
title="Edit Topic"
title={translate('::App.Forum.TopicManagement.EditTopic')}
>
<Edit2 className="w-4 h-4" />
</button>
@ -401,7 +407,7 @@ export function TopicManagement({
<button
onClick={() => handleDelete(topic.id)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
title="Delete Topic"
title={translate('::App.Forum.TopicManagement.DeleteTopic')}
>
<Trash2 className="w-4 h-4" />
</button>

View file

@ -1,38 +1,39 @@
import React, { useState } from 'react';
import { X } from 'lucide-react';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import { useStoreState } from '@/store/store';
import React, { useState } from 'react'
import { X } from 'lucide-react'
import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'
import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface CreatePostModalProps {
onClose: () => void;
onSubmit: (data: { content: string, parentPostId?: string, tenantId?: string }) => void;
onClose: () => void
onSubmit: (data: { content: string; parentPostId?: string; tenantId?: string }) => void
parentPostId: string
}
export function CreatePostModal({ onClose, onSubmit, parentPostId }: CreatePostModalProps) {
const [content, setContent] = useState('');
const { translate } = useLocalization()
const [content, setContent] = useState('')
const { tenant } = useStoreState((state) => state.auth)
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const plainText = content.replace(/<[^>]+>/g, '').trim(); // HTML etiketlerini temizle
e.preventDefault()
const plainText = content.replace(/<[^>]+>/g, '').trim() // HTML etiketlerini temizle
if (plainText) {
onSubmit({ content, parentPostId, tenantId: tenant.tenantId });
onSubmit({ content, parentPostId, tenantId: tenant.tenantId })
}
};
}
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between p-6 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">
{parentPostId ? 'Reply to Topic' : 'New Post'}
{parentPostId
? translate('::App.Forum.PostManagement.ReplytoTopic')
: translate('::App.Forum.PostManagement.NewPost')}
</h3>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
<X className="w-5 h-5" />
</button>
</div>
@ -57,18 +58,18 @@ export function CreatePostModal({ onClose, onSubmit, parentPostId }: CreatePostM
onClick={onClose}
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors"
>
Cancel
{translate('::Cancel')}
</button>
<button
type="submit"
className="px-4 py-2 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 transition-colors disabled:opacity-50"
disabled={!content || content.replace(/<[^>]+>/g, '').trim() === ''}
>
Post Reply
{translate('::App.Forum.PostManagement.PostReply')}
</button>
</div>
</form>
</div>
</div>
);
)
}

View file

@ -1,87 +1,113 @@
import React, { useState } from 'react';
import { X } from 'lucide-react';
import { useStoreState } from '@/store/store';
import { X } from 'lucide-react'
import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Field, FieldProps, Form, Formik } from 'formik'
import { Button, FormContainer, FormItem, Input } from '@/components/ui'
import * as Yup from 'yup'
interface CreateTopicModalProps {
onClose: () => void;
onSubmit: (data: { title: string; content: string, tenantId?: string }) => void;
onClose: () => void
onSubmit: (data: { title: string; content: string; tenantId?: string }) => void
}
export const topicInitialValues = {
title: '',
content: '',
}
export const topicValidationSchema = Yup.object().shape({
title: Yup.string().required('Başlık zorunludur'),
content: Yup.string().required('İçerik zorunludur'),
})
export function CreateTopicModal({ onClose, onSubmit }: CreateTopicModalProps) {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const { translate } = useLocalization()
const { tenant } = useStoreState((state) => state.auth)
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (title.trim() && content.trim()) {
onSubmit({ title: title.trim(), content: content.trim(), tenantId: tenant.tenantId });
}
};
const handleSubmit = (
values: { title: string; content: string },
{ setSubmitting }: { setSubmitting: (isSubmitting: boolean) => void },
) => {
onSubmit({
title: values.title.trim(),
content: values.content.trim(),
tenantId: tenant.tenantId,
})
setSubmitting(false)
}
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between p-6 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">Create New Topic</h3>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<div className="bg-white rounded-2xl shadow-2xl w-full max-w-lg max-h-[90vh] overflow-y-auto p-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900">
{translate('::App.Forum.TopicManagement.NewTopic')}
</h3>
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors">
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSubmit} className="p-6 space-y-4">
<div>
<label htmlFor="title" className="block text-sm font-medium text-gray-700 mb-2">
Title
</label>
<input
type="text"
id="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Enter topic title..."
required
autoFocus
/>
</div>
<div>
<label htmlFor="content" className="block text-sm font-medium text-gray-700 mb-2">
Content
</label>
<textarea
id="content"
value={content}
onChange={(e) => setContent(e.target.value)}
rows={8}
className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Write your topic content..."
required
/>
</div>
<div className="flex justify-end space-x-3 pt-4">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors"
>
Cancel
</button>
<button
type="submit"
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
disabled={!title.trim() || !content.trim()}
>
Create Topic
</button>
</div>
</form>
<Formik
initialValues={topicInitialValues}
validationSchema={topicValidationSchema}
onSubmit={handleSubmit}
enableReinitialize={true}
>
{({ values, touched, errors, isSubmitting }) => (
<Form>
<FormContainer className="space-y-4">
<FormItem
label={translate('::App.Forum.TopicManagement.Title')}
invalid={errors.title && touched.title}
errorMessage={errors.title}
>
<Field
type="text"
name="title"
placeholder="Başlık girin..."
autoFocus
className="w-full text-sm border border-gray-300 rounded-md px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
component={Input}
/>
</FormItem>
<FormItem
asterisk
label={translate('::App.Forum.TopicManagement.Content')}
invalid={errors.content && touched.content}
errorMessage={errors.content}
>
<Field name="content">
{({ field }: FieldProps) => (
<textarea
{...field}
rows={6}
placeholder="Konu içeriğini yazın..."
className="w-full text-sm border border-gray-300 rounded-md px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
/>
)}
</Field>
</FormItem>
<div className="flex justify-end space-x-3 pt-2">
<Button variant="plain" type="button" onClick={onClose}>
{translate('::Cancel')}
</Button>
<Button
variant="solid"
type="submit"
disabled={!values.title.trim() || !values.content.trim()}
loading={isSubmitting}
>
{translate('::App.Forum.TopicManagement.AddTopic')}
</Button>
</div>
</FormContainer>
</Form>
)}
</Formik>
</div>
</div>
);
}
)
}

View file

@ -2,6 +2,7 @@ import React from 'react'
import { Heart, User, CheckCircle, Reply } from 'lucide-react'
import { ForumPost } from '@/proxy/forum/forum'
import { AVATAR_URL } from '@/constants/app.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface PostCardProps {
post: ForumPost
@ -18,8 +19,9 @@ export function ForumPostCard({
isFirst = false,
isLiked = false,
}: PostCardProps) {
const { translate } = useLocalization()
const formatDate = (value: string | Date) => {
const date = value instanceof Date ? value : new Date(value)
const date = value instanceof Date ? value : new Date(value)
if (isNaN(date.getTime())) return 'Invalid Date'
return new Intl.DateTimeFormat('en', {
@ -55,7 +57,7 @@ export function ForumPostCard({
{post.isAcceptedAnswer && (
<div className="flex items-center space-x-1 bg-emerald-100 text-emerald-700 px-2 py-1 rounded-full text-xs">
<CheckCircle className="w-3 h-3" />
<span>Accepted Answer</span>
<span>{translate('::App.Forum.PostManagement.AcceptedAnswer')}</span>
</div>
)}
</div>
@ -82,13 +84,15 @@ export function ForumPostCard({
<span>{post.likeCount}</span>
</button>
{!isFirst && (<button
onClick={() => onReply(post.id)}
className="flex items-center space-x-1 px-3 py-1 rounded-full text-sm bg-gray-100 text-gray-600 hover:bg-gray-200 transition-colors"
>
<Reply className="w-4 h-4" />
<span>Reply</span>
</button>)}
{!isFirst && (
<button
onClick={() => onReply(post.id)}
className="flex items-center space-x-1 px-3 py-1 rounded-full text-sm bg-gray-100 text-gray-600 hover:bg-gray-200 transition-colors"
>
<Reply className="w-4 h-4" />
<span>{translate('::App.Forum.PostManagement.PostReply')}</span>
</button>
)}
</div>
</div>
</div>

View file

@ -2,6 +2,7 @@ import React from 'react'
import { MessageSquare, Heart, Eye, Pin, Lock, CheckCircle } from 'lucide-react'
import { ForumTopic } from '@/proxy/forum/forum'
import { AVATAR_URL } from '@/constants/app.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface TopicCardProps {
topic: ForumTopic
@ -9,6 +10,7 @@ interface TopicCardProps {
}
export function ForumTopicCard({ topic, onClick }: TopicCardProps) {
const { translate } = useLocalization()
const formatDate = (dateString?: string) => {
if (!dateString) return 'Never'
@ -78,7 +80,7 @@ export function ForumTopicCard({ topic, onClick }: TopicCardProps) {
<div className="mt-4 pt-4 border-t border-gray-100">
<div className="flex items-center justify-between text-sm text-gray-500">
<span>
Last reply by{' '}
{translate('::App.Forum.TopicManagement.Lastreplyby')}{' '}
<span className="font-medium text-gray-700">{topic.lastPostUserName}</span>
{' '}
<span>{formatDate(topic.lastPostDate)}</span>

View file

@ -10,6 +10,7 @@ import { ForumPostCard } from './ForumPostCard'
import { ForumCategoryCard } from './ForumCategoryCard'
import { ForumTopicCard } from './ForumTopicCard'
import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface ForumViewProps {
categories: ForumCategory[]
@ -58,6 +59,7 @@ export function ForumView({
onTopicSelect,
onViewStateChange,
}: ForumViewProps) {
const { translate } = useLocalization()
const [localViewState, setLocalViewState] = useState<'categories' | 'topics' | 'posts'>(
'categories',
)
@ -356,7 +358,7 @@ export function ForumView({
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
>
<Plus className="w-4 h-4" />
<span>New Topic</span>
<span>{translate('::App.Forum.TopicManagement.NewTopic')}</span>
</button>
)}
{viewState === 'posts' && selectedTopic && !selectedTopic.isLocked && (
@ -365,7 +367,7 @@ export function ForumView({
className="flex items-center space-x-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors"
>
<Plus className="w-4 h-4" />
<span>New Post</span>
<span>{translate('::App.Forum.PostManagement.NewPost')}</span>
</button>
)}
@ -375,7 +377,7 @@ export function ForumView({
className="hidden md:flex items-center space-x-2 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
>
<Search className="w-4 h-4 text-gray-400" />
<span className="text-gray-500">Search topics...</span>
<span className="text-gray-500">{translate('::App.Forum.TopicManagement.Searchtopics')}</span>
<kbd className="hidden sm:inline-block px-2 py-1 text-xs font-semibold text-gray-500 bg-gray-100 border border-gray-200 rounded">
K
</kbd>
@ -452,7 +454,7 @@ export function ForumView({
isAcceptedAnswer: false,
parentPostId: undefined,
creationTime: selectedTopic.creationTime,
tenantId: selectedTopic.tenantId
tenantId: selectedTopic.tenantId,
}}
onLike={handleLike}
onReply={handleReply}

View file

@ -40,7 +40,7 @@ import DataGrid, {
Sorting,
Summary,
Toolbar,
TotalItem,
TotalItem
} from 'devextreme-react/data-grid'
import { Item } from 'devextreme-react/toolbar'
import { DataType } from 'devextreme/common'