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.CoverImage,
input.CategoryId, input.CategoryId,
_currentUser.Id.Value, _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); await _postRepository.InsertAsync(post, autoSave: true);
return await GetPostAsync(post.Id); 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() { 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=1, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="InstitutionName", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" }, 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=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=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=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 EditingFormItemDto { Order=8, DataField="IsActive", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxCheckBox },
] ]
}, },
new() { Order=2, ColCount=1, ColSpan=2, ItemType="group", Items = 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=1, DataField="Address", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="Address2", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" }, 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=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=4, DataField="City", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=5, DataField="District", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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 = 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 = 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 = 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 = 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 = 6, DataField = "SelectCommand", ColSpan = 2, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order = 7, DataField = "TableName", ColSpan = 2, IsRequired = false, 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() { 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=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=2, DataField="Code", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=3, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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=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=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 EditingFormItemDto { Order=9, DataField="IsActive", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxCheckBox },
] ]
}, },
new() { Order=2, ColCount=1, ColSpan=2, ItemType="group", Items = 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=1, DataField="Address", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="Address2", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" }, 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=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=4, DataField="City", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=5, DataField="District", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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() { 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=1, DataField="Name", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=2, DataField="ValueType", ColSpan=2, IsRequired=true, EditorType2=EditorTypes.dxSelectBox }, 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=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=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=5, DataField="Regex", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=6, DataField="RegexDescription", ColSpan=2, IsRequired=false, EditorType2=EditorTypes.dxTextBox, EditorOptions="{ \"showClearButton\" : true }" }, 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, EditorOptions="{ \"showClearButton\" : true }" }, 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> EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{ {
new() new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
{ {
Order = 1, Order = 1,
DataField = "Name", ColCount = 1,
ColSpan = 2, ColSpan = 2,
IsRequired = true, ItemType = "group",
EditorType2 = EditorTypes.dxTextBox Items =
[
new EditingFormItemDto
{
Order = 1,
DataField = "Name",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
}
]
} }
]
}
}), }),
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
{ {
@ -8940,32 +8940,32 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}), }),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto> EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{ {
new() new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
{ {
Order = 1, Order = 1,
DataField = "Name", ColCount = 1,
ColSpan = 2, ColSpan = 2,
IsRequired = true, ItemType = "group",
EditorType2 = EditorTypes.dxTextBox Items =
}, [
new EditingFormItemDto new EditingFormItemDto
{ {
Order = 2, Order = 1,
DataField = "Category", DataField = "Name",
ColSpan = 2, ColSpan = 2,
IsRequired = true, IsRequired = true,
EditorType2 = EditorTypes.dxTextBox EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Category",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
}
]
} }
]
}
}), }),
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
{ {
@ -9154,49 +9154,49 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}), }),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto> EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{ {
new() new()
{
Order = 1,
ColCount = 1,
ColSpan = 2,
ItemType = "group",
Items =
[
new EditingFormItemDto
{ {
Order = 1, Order = 1,
DataField = "Title", ColCount = 1,
ColSpan = 2, ColSpan = 2,
IsRequired = true, ItemType = "group",
EditorType2 = EditorTypes.dxTextBox Items =
}, [
new EditingFormItemDto new EditingFormItemDto
{ {
Order = 2, Order = 1,
DataField = "Abbreviation", DataField = "Title",
ColSpan = 2, ColSpan = 2,
IsRequired = true, IsRequired = true,
EditorType2 = EditorTypes.dxTextBox EditorType2 = EditorTypes.dxTextBox
},
new EditingFormItemDto
{
Order = 2,
DataField = "Abbreviation",
ColSpan = 2,
IsRequired = true,
EditorType2 = EditorTypes.dxTextBox
}
]
} }
]
}
}), }),
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
{ {
new() new()
{ {
FieldName = "CreationTime", FieldName = "CreationTime",
FieldDbType = DbType.DateTime, FieldDbType = DbType.DateTime,
Value = "@NOW", Value = "@NOW",
CustomValueType = FieldCustomValueTypeEnum.CustomKey CustomValueType = FieldCustomValueTypeEnum.CustomKey
}, },
new() new()
{ {
FieldName = "CreatorId", FieldName = "CreatorId",
FieldDbType = DbType.Guid, FieldDbType = DbType.Guid,
Value = "@USERID", Value = "@USERID",
CustomValueType = FieldCustomValueTypeEnum.CustomKey CustomValueType = FieldCustomValueTypeEnum.CustomKey
} }
}) })
}); });
@ -9370,74 +9370,74 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}), }),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto> EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{ {
new() new()
{ {
Order = 1, Order = 1,
ColCount = 1, ColCount = 1,
ColSpan = 2, ColSpan = 2,
ItemType = "group", ItemType = "group",
Items = Items =
[ [
new EditingFormItemDto new EditingFormItemDto
{ {
Order = 1, Order = 1,
DataField = "Code", DataField = "Code",
ColSpan = 2, ColSpan = 2,
IsRequired = true, IsRequired = true,
EditorType2 = EditorTypes.dxTextBox EditorType2 = EditorTypes.dxTextBox
}, },
new EditingFormItemDto new EditingFormItemDto
{ {
Order = 2, Order = 2,
DataField = "Symbol", DataField = "Symbol",
ColSpan = 2, ColSpan = 2,
IsRequired = false, IsRequired = false,
EditorType2 = EditorTypes.dxTextBox EditorType2 = EditorTypes.dxTextBox
}, },
new EditingFormItemDto new EditingFormItemDto
{ {
Order = 3, Order = 3,
DataField = "Name", DataField = "Name",
ColSpan = 2, ColSpan = 2,
IsRequired = true, IsRequired = true,
EditorType2 = EditorTypes.dxTextBox EditorType2 = EditorTypes.dxTextBox
}, },
new EditingFormItemDto new EditingFormItemDto
{ {
Order = 4, Order = 4,
DataField = "Rate", DataField = "Rate",
ColSpan = 2, ColSpan = 2,
IsRequired = true, IsRequired = true,
EditorType2 = EditorTypes.dxNumberBox EditorType2 = EditorTypes.dxNumberBox
}, },
new EditingFormItemDto new EditingFormItemDto
{ {
Order = 5, Order = 5,
DataField = "IsActive", DataField = "IsActive",
ColSpan = 2, ColSpan = 2,
IsRequired = false, IsRequired = false,
EditorType2 = EditorTypes.dxCheckBox EditorType2 = EditorTypes.dxCheckBox
} }
] ]
} }
}), }),
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new[] InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new[]
{
new FieldsDefaultValue
{ {
FieldName = "CreationTime", new FieldsDefaultValue
FieldDbType = DbType.DateTime, {
Value = "@NOW", FieldName = "CreationTime",
CustomValueType = FieldCustomValueTypeEnum.CustomKey FieldDbType = DbType.DateTime,
}, Value = "@NOW",
new FieldsDefaultValue CustomValueType = FieldCustomValueTypeEnum.CustomKey
{ },
FieldName = "CreatorId", new FieldsDefaultValue
FieldDbType = DbType.Guid, {
Value = "@USERID", FieldName = "CreatorId",
CustomValueType = FieldCustomValueTypeEnum.CustomKey FieldDbType = DbType.Guid,
} Value = "@USERID",
}) CustomValueType = FieldCustomValueTypeEnum.CustomKey
}
})
}); });
#region Currency Fields #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 = 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 = 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 = 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 = 5, DataField = "PhoneCode", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 6, DataField = "TaxLabel", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxTextBox }, 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 = 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 = 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", ItemType = "group",
Items = 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 = 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 = 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 = 5, DataField = "DisplayOrder", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 6, DataField = "IsActive", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxCheckBox } new EditingFormItemDto { Order = 6, DataField = "IsActive", ColSpan = 2, IsRequired = false, EditorType2 = EditorTypes.dxCheckBox }
@ -11111,12 +11111,12 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
ItemType = "group", ItemType = "group",
Items = 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 = 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 = 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 = 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 = 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 = 8, DataField = "LikeCount", ColSpan = 2, IsRequired = true, EditorType2 = EditorTypes.dxNumberBox },
new EditingFormItemDto { Order = 9, DataField = "CommentCount", 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", DisplayExpr = "Name",
ValueExpr = "Key", ValueExpr = "Key",
LookupQuery = $"SELECT \"{DbTablePrefix}BlogCategories\".\"Id\" AS \"Key\", \"{DbTablePrefix}BlogCategories\".\"Name\" as \"Name\" FROM \"{DbTablePrefix}BlogCategories\" ORDER BY \"{DbTablePrefix}BlogCategories\".\"Name\"" 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 PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
{ {
C = AppCodes.BlogManagement.BlogPosts + ".Create", C = AppCodes.BlogManagement.BlogPosts + ".Create",
@ -11365,13 +11365,55 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}) })
}, },
new() 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, ListFormCode = listFormBlogPosts.ListFormCode,
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.Boolean, SourceDbType = DbType.Boolean,
FieldName = "IsPublished", FieldName = "IsPublished",
Width = 80, Width = 80,
ListOrderNo = 9, ListOrderNo = 10,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
IsDeleted = false, IsDeleted = false,
@ -11392,7 +11434,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
SourceDbType = DbType.DateTime, SourceDbType = DbType.DateTime,
FieldName = "PublishedAt", FieldName = "PublishedAt",
Width = 120, Width = 120,
ListOrderNo = 10, ListOrderNo = 11,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
IsDeleted = false, IsDeleted = false,
@ -11405,6 +11447,48 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
E = true, E = true,
Deny = false 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 #endregion

View file

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

View file

@ -535,6 +535,228 @@
"en": "Forum", "en": "Forum",
"tr": "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", "resourceName": "Platform",
"key": "App.Home", "key": "App.Home",

View file

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

View file

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

View file

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

View file

@ -89,7 +89,7 @@ const Simple = ({ children, content, ...rest }: SimpleProps) => {
return ( return (
<div className="h-full"> <div className="h-full">
<Container className="flex flex-col flex-auto items-center justify-center min-w-0 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"> <div className="flex justify-between items-center mb-4">
<a <a
href={import.meta.env.VITE_COMPANY_URL} href={import.meta.env.VITE_COMPANY_URL}

View file

@ -20,7 +20,7 @@ const HorizontalMenuIcon = ({ icon }: HorizontalMenuIconProps) => {
return <></> 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 export default HorizontalMenuIcon

View file

@ -3,7 +3,8 @@ import ReactDOM from 'react-dom/client'
import App from './App' import App from './App'
import './index.css' import './index.css'
import { UiEvalService } from './services/UiEvalService' 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 />) ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(<App />)

View file

@ -5,6 +5,7 @@ import { TopicManagement } from './TopicManagement'
import { PostManagement } from './PostManagement' import { PostManagement } from './PostManagement'
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum' import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'
import { AdminStats } from './Dashboard' import { AdminStats } from './Dashboard'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface AdminViewProps { interface AdminViewProps {
categories: ForumCategory[] categories: ForumCategory[]
@ -74,12 +75,13 @@ export function AdminView({
onUnmarkPostAsAcceptedAnswer, onUnmarkPostAsAcceptedAnswer,
}: AdminViewProps) { }: AdminViewProps) {
const [activeSection, setActiveSection] = useState<AdminSection>('stats') const [activeSection, setActiveSection] = useState<AdminSection>('stats')
const { translate } = useLocalization();
const navigationItems = [ const navigationItems = [
{ id: 'stats' as AdminSection, label: 'Dashboard', icon: BarChart3 }, { id: 'stats' as AdminSection, label: translate('::App.Forum.Dashboard.Dashboard'), icon: BarChart3 },
{ id: 'categories' as AdminSection, label: 'Categories', icon: Folder }, { id: 'categories' as AdminSection, label: translate('::App.Forum.Dashboard.Categories'), icon: Folder },
{ id: 'topics' as AdminSection, label: 'Topics', icon: MessageSquare }, { id: 'topics' as AdminSection, label: translate('::App.Forum.Dashboard.Topics'), icon: MessageSquare },
{ id: 'posts' as AdminSection, label: 'Posts', icon: FileText }, { id: 'posts' as AdminSection, label: translate('::App.Forum.Dashboard.Posts'), icon: FileText },
] ]
return ( 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 { Plus, Edit2, Trash2, Lock, Unlock, Eye, EyeOff, Loader2 } from 'lucide-react'
import { ForumCategory } from '@/proxy/forum/forum' import { ForumCategory } from '@/proxy/forum/forum'
import { useStoreState } from '@/store/store' import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface CategoryManagementProps { interface CategoryManagementProps {
categories: ForumCategory[] categories: ForumCategory[]
@ -31,6 +32,7 @@ export function CategoryManagement({
onUpdateCategoryLockState, onUpdateCategoryLockState,
onUpdateCategoryActiveState, onUpdateCategoryActiveState,
}: CategoryManagementProps) { }: CategoryManagementProps) {
const { translate } = useLocalization()
const { tenant } = useStoreState((state) => state.auth) const { tenant } = useStoreState((state) => state.auth)
const [showCreateForm, setShowCreateForm] = useState(false) const [showCreateForm, setShowCreateForm] = useState(false)
const [editingCategory, setEditingCategory] = useState<ForumCategory | null>(null) const [editingCategory, setEditingCategory] = useState<ForumCategory | null>(null)
@ -42,7 +44,7 @@ export function CategoryManagement({
displayOrder: 0, displayOrder: 0,
isActive: true, isActive: true,
isLocked: false, isLocked: false,
tenantId: '' tenantId: '',
}) })
const [submitting, setSubmitting] = useState(false) const [submitting, setSubmitting] = useState(false)
@ -55,7 +57,7 @@ export function CategoryManagement({
displayOrder: 0, displayOrder: 0,
isActive: true, isActive: true,
isLocked: false, isLocked: false,
tenantId: '' tenantId: '',
}) })
setShowCreateForm(false) setShowCreateForm(false)
setEditingCategory(null) setEditingCategory(null)
@ -90,7 +92,7 @@ export function CategoryManagement({
displayOrder: category.displayOrder, displayOrder: category.displayOrder,
isActive: category.isActive, isActive: category.isActive,
isLocked: category.isLocked, isLocked: category.isLocked,
tenantId: tenant.tenantId ?? '' tenantId: tenant.tenantId ?? '',
}) })
setShowCreateForm(true) setShowCreateForm(true)
} }
@ -128,14 +130,16 @@ export function CategoryManagement({
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="flex items-center justify-between"> <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 <button
onClick={() => setShowCreateForm(true)} onClick={() => setShowCreateForm(true)}
disabled={loading} 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" 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" /> <Plus className="w-4 h-4" />
<span>Add Category</span> <span>{translate('::App.Forum.CategoryManagement.AddCategory')}</span>
</button> </button>
</div> </div>
@ -143,7 +147,9 @@ export function CategoryManagement({
{showCreateForm && ( {showCreateForm && (
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6"> <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"> <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> </h3>
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@ -251,13 +257,13 @@ export function CategoryManagement({
{/* Categories List */} {/* Categories List */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden"> <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"> <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> </div>
{loading ? ( {loading ? (
<div className="p-8 text-center"> <div className="p-8 text-center">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-4 text-blue-600" /> <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>
) : ( ) : (
<div className="divide-y divide-gray-200"> <div className="divide-y divide-gray-200">
@ -327,7 +333,7 @@ export function CategoryManagement({
<button <button
onClick={() => handleEdit(category)} onClick={() => handleEdit(category)}
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors" 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" /> <Edit2 className="w-4 h-4" />
</button> </button>
@ -335,7 +341,7 @@ export function CategoryManagement({
<button <button
onClick={() => handleDelete(category.id)} onClick={() => handleDelete(category.id)}
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors" 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" /> <Trash2 className="w-4 h-4" />
</button> </button>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,6 +10,7 @@ import { ForumPostCard } from './ForumPostCard'
import { ForumCategoryCard } from './ForumCategoryCard' import { ForumCategoryCard } from './ForumCategoryCard'
import { ForumTopicCard } from './ForumTopicCard' import { ForumTopicCard } from './ForumTopicCard'
import { useStoreState } from '@/store/store' import { useStoreState } from '@/store/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
interface ForumViewProps { interface ForumViewProps {
categories: ForumCategory[] categories: ForumCategory[]
@ -58,6 +59,7 @@ export function ForumView({
onTopicSelect, onTopicSelect,
onViewStateChange, onViewStateChange,
}: ForumViewProps) { }: ForumViewProps) {
const { translate } = useLocalization()
const [localViewState, setLocalViewState] = useState<'categories' | 'topics' | 'posts'>( const [localViewState, setLocalViewState] = useState<'categories' | 'topics' | 'posts'>(
'categories', '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" 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" /> <Plus className="w-4 h-4" />
<span>New Topic</span> <span>{translate('::App.Forum.TopicManagement.NewTopic')}</span>
</button> </button>
)} )}
{viewState === 'posts' && selectedTopic && !selectedTopic.isLocked && ( {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" 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" /> <Plus className="w-4 h-4" />
<span>New Post</span> <span>{translate('::App.Forum.PostManagement.NewPost')}</span>
</button> </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" 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" /> <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"> <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 K
</kbd> </kbd>
@ -452,7 +454,7 @@ export function ForumView({
isAcceptedAnswer: false, isAcceptedAnswer: false,
parentPostId: undefined, parentPostId: undefined,
creationTime: selectedTopic.creationTime, creationTime: selectedTopic.creationTime,
tenantId: selectedTopic.tenantId tenantId: selectedTopic.tenantId,
}} }}
onLike={handleLike} onLike={handleLike}
onReply={handleReply} onReply={handleReply}

View file

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