About ve Contact sayfaları dinamik şekilde değiştirildi.
This commit is contained in:
parent
6fdc2c3231
commit
1240c8dfb4
22 changed files with 1177 additions and 605 deletions
|
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.Abouts;
|
||||||
|
|
||||||
|
public class AboutDto : EntityDto<Guid>
|
||||||
|
{
|
||||||
|
public Guid? TenantId { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string StatsJson { get; set; }
|
||||||
|
public List<StatDto> StatsDto
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(StatsJson))
|
||||||
|
return JsonSerializer.Deserialize<List<StatDto>>(StatsJson);
|
||||||
|
return new List<StatDto>();
|
||||||
|
}
|
||||||
|
set { StatsJson = JsonSerializer.Serialize(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string DescriptionsJson { get; set; }
|
||||||
|
public List<string> DescriptionsDto
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(DescriptionsJson))
|
||||||
|
return JsonSerializer.Deserialize<List<string>>(DescriptionsJson);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
set { DescriptionsJson = JsonSerializer.Serialize(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string SectionsJson { get; set; }
|
||||||
|
public List<SectionDto> SectionsDto
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(SectionsJson))
|
||||||
|
return JsonSerializer.Deserialize<List<SectionDto>>(SectionsJson);
|
||||||
|
return new List<SectionDto>();
|
||||||
|
}
|
||||||
|
set { SectionsJson = JsonSerializer.Serialize(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StatDto
|
||||||
|
{
|
||||||
|
public string Icon { get; set; }
|
||||||
|
public string Value { get; set; } // number/string farkını normalize ettik
|
||||||
|
public string LabelKey { get; set; }
|
||||||
|
public bool? UseCounter { get; set; }
|
||||||
|
public int? CounterEnd { get; set; }
|
||||||
|
public string CounterSuffix { get; set; }
|
||||||
|
public int? CounterDuration { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SectionDto
|
||||||
|
{
|
||||||
|
public string Key { get; set; }
|
||||||
|
public string DescKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.Contacts;
|
||||||
|
|
||||||
|
public class ContactDto : EntityDto<Guid>
|
||||||
|
{
|
||||||
|
public string Address { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Location { get; set; }
|
||||||
|
public string TaxNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string BankJson { get; set; }
|
||||||
|
public BankDto BankDto
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(BankJson))
|
||||||
|
return JsonSerializer.Deserialize<BankDto>(BankJson);
|
||||||
|
return new BankDto();
|
||||||
|
}
|
||||||
|
set { BankJson = JsonSerializer.Serialize(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string WorkHoursJson { get; set; }
|
||||||
|
public WorkHoursDto WorkHoursDto
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(WorkHoursJson))
|
||||||
|
return JsonSerializer.Deserialize<WorkHoursDto>(WorkHoursJson);
|
||||||
|
return new WorkHoursDto();
|
||||||
|
}
|
||||||
|
set { WorkHoursJson = JsonSerializer.Serialize(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string MapJson { get; set; }
|
||||||
|
public MapDto MapDto
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(MapJson))
|
||||||
|
return JsonSerializer.Deserialize<MapDto>(MapJson);
|
||||||
|
return new MapDto();
|
||||||
|
}
|
||||||
|
set { MapJson = JsonSerializer.Serialize(value); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BankDto
|
||||||
|
{
|
||||||
|
public string AccountHolder { get; set; }
|
||||||
|
public string Branch { get; set; }
|
||||||
|
public string AccountNumber { get; set; }
|
||||||
|
public string Iban { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WorkHoursDto
|
||||||
|
{
|
||||||
|
public string Weekday { get; set; }
|
||||||
|
public string Weekend { get; set; }
|
||||||
|
public string Whatsapp { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MapDto
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Src { get; set; }
|
||||||
|
public string Width { get; set; }
|
||||||
|
public string Height { get; set; }
|
||||||
|
public string Style { get; set; }
|
||||||
|
public bool? AllowFullScreen { get; set; }
|
||||||
|
public string Loading { get; set; }
|
||||||
|
public string ReferrerPolicy { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,8 @@ using System.Linq;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Kurs.Platform.Orders;
|
using Kurs.Platform.Orders;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Kurs.Platform.Abouts;
|
||||||
|
using Kurs.Platform.Contacts;
|
||||||
|
|
||||||
namespace Kurs.Platform.Public;
|
namespace Kurs.Platform.Public;
|
||||||
|
|
||||||
|
|
@ -30,6 +32,8 @@ public class PublicAppService : PlatformAppService
|
||||||
private readonly IRepository<PaymentMethod, string> _paymentMethodRepository;
|
private readonly IRepository<PaymentMethod, string> _paymentMethodRepository;
|
||||||
private readonly IRepository<InstallmentOption> _installmentOptionRepository;
|
private readonly IRepository<InstallmentOption> _installmentOptionRepository;
|
||||||
private readonly IRepository<Order, Guid> _orderRepository;
|
private readonly IRepository<Order, Guid> _orderRepository;
|
||||||
|
private readonly IRepository<About, Guid> _aboutRepository;
|
||||||
|
private readonly IRepository<Contact, Guid> _contactRepository;
|
||||||
|
|
||||||
public PublicAppService(
|
public PublicAppService(
|
||||||
IRepository<Service, Guid> serviceRepository,
|
IRepository<Service, Guid> serviceRepository,
|
||||||
|
|
@ -41,7 +45,9 @@ public class PublicAppService : PlatformAppService
|
||||||
IRepository<Product, Guid> productRepository,
|
IRepository<Product, Guid> productRepository,
|
||||||
IRepository<PaymentMethod, string> paymentMethodRepository,
|
IRepository<PaymentMethod, string> paymentMethodRepository,
|
||||||
IRepository<InstallmentOption> installmentOptionRepository,
|
IRepository<InstallmentOption> installmentOptionRepository,
|
||||||
IRepository<Order, Guid> orderRepository
|
IRepository<Order, Guid> orderRepository,
|
||||||
|
IRepository<About, Guid> aboutRepository,
|
||||||
|
IRepository<Contact, Guid> contactRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_serviceRepository = serviceRepository;
|
_serviceRepository = serviceRepository;
|
||||||
|
|
@ -54,6 +60,8 @@ public class PublicAppService : PlatformAppService
|
||||||
_paymentMethodRepository = paymentMethodRepository;
|
_paymentMethodRepository = paymentMethodRepository;
|
||||||
_installmentOptionRepository = installmentOptionRepository;
|
_installmentOptionRepository = installmentOptionRepository;
|
||||||
_orderRepository = orderRepository;
|
_orderRepository = orderRepository;
|
||||||
|
_aboutRepository = aboutRepository;
|
||||||
|
_contactRepository = contactRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<ServiceDto>> GetServicesListAsync()
|
public async Task<List<ServiceDto>> GetServicesListAsync()
|
||||||
|
|
@ -268,4 +276,17 @@ public class PublicAppService : PlatformAppService
|
||||||
PaymentMethod = entity.PaymentMethod
|
PaymentMethod = entity.PaymentMethod
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<AboutDto> GetAboutAsync()
|
||||||
|
{
|
||||||
|
var entity = await _aboutRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(About));
|
||||||
|
return ObjectMapper.Map<About, AboutDto>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ContactDto> GetContactAsync()
|
||||||
|
{
|
||||||
|
var entity = await _contactRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Contact));
|
||||||
|
|
||||||
|
return ObjectMapper.Map<Contact, ContactDto>(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using Kurs.Platform.Abouts;
|
||||||
using Kurs.Platform.Blog;
|
using Kurs.Platform.Blog;
|
||||||
|
using Kurs.Platform.Contacts;
|
||||||
using Kurs.Platform.Demos;
|
using Kurs.Platform.Demos;
|
||||||
using Kurs.Platform.Entities;
|
using Kurs.Platform.Entities;
|
||||||
using Kurs.Platform.Orders;
|
using Kurs.Platform.Orders;
|
||||||
|
|
@ -20,5 +22,7 @@ public class PublicAutoMapperProfile : Profile
|
||||||
CreateMap<PaymentMethod, PaymentMethodDto>();
|
CreateMap<PaymentMethod, PaymentMethodDto>();
|
||||||
CreateMap<InstallmentOption, InstallmentOptionDto>();
|
CreateMap<InstallmentOption, InstallmentOptionDto>();
|
||||||
CreateMap<Order, OrderDto>();
|
CreateMap<Order, OrderDto>();
|
||||||
|
CreateMap<About, AboutDto>();
|
||||||
|
CreateMap<Contact, ContactDto>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
private readonly IRepository<CustomComponent, Guid> _customComponentRepository;
|
private readonly IRepository<CustomComponent, Guid> _customComponentRepository;
|
||||||
private readonly IRepository<ReportCategory, Guid> _reportCategoriesRepository;
|
private readonly IRepository<ReportCategory, Guid> _reportCategoriesRepository;
|
||||||
private readonly IRepository<Service, Guid> _servicesRepository;
|
private readonly IRepository<Service, Guid> _servicesRepository;
|
||||||
|
private readonly IRepository<About, Guid> _aboutRepository;
|
||||||
|
private readonly IRepository<Contact, Guid> _contactRepository;
|
||||||
|
|
||||||
public PlatformDataSeeder(
|
public PlatformDataSeeder(
|
||||||
IRepository<Language, Guid> languages,
|
IRepository<Language, Guid> languages,
|
||||||
|
|
@ -104,7 +106,9 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
IRepository<InstallmentOption, Guid> InstallmentOptionRepository,
|
IRepository<InstallmentOption, Guid> InstallmentOptionRepository,
|
||||||
IRepository<CustomComponent, Guid> CustomComponentRepository,
|
IRepository<CustomComponent, Guid> CustomComponentRepository,
|
||||||
IRepository<ReportCategory, Guid> ReportCategoriesRepository,
|
IRepository<ReportCategory, Guid> ReportCategoriesRepository,
|
||||||
IRepository<Service, Guid> ServicesRepository
|
IRepository<Service, Guid> ServicesRepository,
|
||||||
|
IRepository<About, Guid> AboutRepository,
|
||||||
|
IRepository<Contact, Guid> ContactRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_languages = languages;
|
_languages = languages;
|
||||||
|
|
@ -144,6 +148,8 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
_customComponentRepository = CustomComponentRepository;
|
_customComponentRepository = CustomComponentRepository;
|
||||||
_reportCategoriesRepository = ReportCategoriesRepository;
|
_reportCategoriesRepository = ReportCategoriesRepository;
|
||||||
_servicesRepository = ServicesRepository;
|
_servicesRepository = ServicesRepository;
|
||||||
|
_aboutRepository = AboutRepository;
|
||||||
|
_contactRepository = ContactRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IConfigurationRoot BuildConfiguration()
|
private static IConfigurationRoot BuildConfiguration()
|
||||||
|
|
@ -156,6 +162,243 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
return builder.Build();
|
return builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task SeedCountyGroupsAsync()
|
||||||
|
{
|
||||||
|
var dbCtx = await _countryRepository.GetDbContextAsync();
|
||||||
|
|
||||||
|
// DB’de mevcut kodları set olarak al
|
||||||
|
var existingCodes = (await dbCtx.Set<CountryGroup>()
|
||||||
|
.Select(c => c.Name)
|
||||||
|
.ToListAsync())
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||||
|
|
||||||
|
using var fs = File.OpenRead(Path.Combine("Seeds", "CountryGroups.json"));
|
||||||
|
|
||||||
|
var buffer = new List<CountryGroup>(capacity: 1000);
|
||||||
|
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
|
||||||
|
|
||||||
|
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CountryGroupDto>(fs, options))
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
if (string.IsNullOrWhiteSpace(item.Name)) continue; // boş kodları atla
|
||||||
|
|
||||||
|
// hem DB’de hem JSON içinde duplicate engelle
|
||||||
|
if (!seenCodes.Add(item.Name) || existingCodes.Contains(item.Name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
buffer.Add(new CountryGroup(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
item.Name
|
||||||
|
));
|
||||||
|
|
||||||
|
if (buffer.Count >= 1000)
|
||||||
|
{
|
||||||
|
await BulkCountryGroupInsertAsync(buffer);
|
||||||
|
buffer.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Count > 0)
|
||||||
|
{
|
||||||
|
await BulkCountryGroupInsertAsync(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task BulkCountryGroupInsertAsync(List<CountryGroup> entities)
|
||||||
|
{
|
||||||
|
var dbCtx = await _countryGroupRepository.GetDbContextAsync();
|
||||||
|
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
||||||
|
{
|
||||||
|
BatchSize = 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SeedCountriesAsync()
|
||||||
|
{
|
||||||
|
var dbCtx = await _countryRepository.GetDbContextAsync();
|
||||||
|
|
||||||
|
// DB’de mevcut kodları set olarak al
|
||||||
|
var existingCodes = (await dbCtx.Set<Country>()
|
||||||
|
.Select(c => c.Code)
|
||||||
|
.ToListAsync())
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||||
|
|
||||||
|
using var fs = File.OpenRead(Path.Combine("Seeds", "Countries.json"));
|
||||||
|
|
||||||
|
var buffer = new List<Country>(capacity: 1000);
|
||||||
|
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
|
||||||
|
|
||||||
|
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CountryDto>(fs, options))
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
if (string.IsNullOrWhiteSpace(item.Code)) continue; // boş kodları atla
|
||||||
|
|
||||||
|
// hem DB’de hem JSON içinde duplicate engelle
|
||||||
|
if (!seenCodes.Add(item.Code) || existingCodes.Contains(item.Code))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
buffer.Add(new Country(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
item.Code,
|
||||||
|
item.Name,
|
||||||
|
item.GroupName,
|
||||||
|
item.CurrencyCode,
|
||||||
|
item.PhoneCode,
|
||||||
|
item.TaxLabel
|
||||||
|
));
|
||||||
|
|
||||||
|
if (buffer.Count >= 1000)
|
||||||
|
{
|
||||||
|
await BulkCountryInsertAsync(buffer);
|
||||||
|
buffer.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Count > 0)
|
||||||
|
{
|
||||||
|
await BulkCountryInsertAsync(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task BulkCountryInsertAsync(List<Country> entities)
|
||||||
|
{
|
||||||
|
var dbCtx = await _countryRepository.GetDbContextAsync();
|
||||||
|
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
||||||
|
{
|
||||||
|
BatchSize = 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SeedCitiesAsync()
|
||||||
|
{
|
||||||
|
var dbCtx = await _cityRepository.GetDbContextAsync();
|
||||||
|
|
||||||
|
// 1. Mevcut kayıtları çek (tek sorguda)
|
||||||
|
var existingCities = await dbCtx.Set<City>()
|
||||||
|
.Select(d => new { d.Code })
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var existingSet = existingCities
|
||||||
|
.Select(d => d.Code)
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. JSON’u stream et
|
||||||
|
using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Cities.json"));
|
||||||
|
|
||||||
|
var buffer = new List<City>(capacity: 5000);
|
||||||
|
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CityDto>(fs, options))
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
|
||||||
|
var key = $"{item.CountryCode}.{item.Code}";
|
||||||
|
if (existingSet.Contains(key)) continue; // duplicate kontrolü
|
||||||
|
|
||||||
|
buffer.Add(new City(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
item.CountryCode,
|
||||||
|
item.Name,
|
||||||
|
key,
|
||||||
|
item.PlateCode
|
||||||
|
));
|
||||||
|
|
||||||
|
if (buffer.Count >= 1000) // 3. Batch
|
||||||
|
{
|
||||||
|
await BulkCityInsertAsync(buffer);
|
||||||
|
buffer.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Count > 0)
|
||||||
|
{
|
||||||
|
await BulkCityInsertAsync(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task BulkCityInsertAsync(List<City> entities)
|
||||||
|
{
|
||||||
|
var dbCtx = await _cityRepository.GetDbContextAsync();
|
||||||
|
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
||||||
|
{
|
||||||
|
PreserveInsertOrder = true,
|
||||||
|
SetOutputIdentity = true,
|
||||||
|
BatchSize = 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SeedDistrictsAsync()
|
||||||
|
{
|
||||||
|
var dbCtx = await _districtRepository.GetDbContextAsync();
|
||||||
|
|
||||||
|
// 1. Mevcut kayıtları çek (tek sorguda)
|
||||||
|
var existingDistricts = await dbCtx.Set<District>()
|
||||||
|
.Select(d => new { d.CountryCode, d.CityCode, d.Name, d.Township, d.Street, d.ZipCode })
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var existingSet = existingDistricts
|
||||||
|
.Select(d => $"{d.CountryCode}:{d.CityCode}:{d.Name}:{d.Township}:{d.Street}:{d.ZipCode}")
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. JSON’u stream et
|
||||||
|
using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Districts.json"));
|
||||||
|
|
||||||
|
var buffer = new List<District>(capacity: 5000);
|
||||||
|
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<DistrictDto>(fs, options))
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
|
||||||
|
var key = $"{item.CountryCode}:{item.CountryCode}.{item.CityCode}:{item.Name}:{item.Township}:{item.Street}:{item.ZipCode}";
|
||||||
|
var cityCode = $"{item.CountryCode}.{item.CityCode}";
|
||||||
|
if (existingSet.Contains(key)) continue;
|
||||||
|
|
||||||
|
buffer.Add(new District(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
item.CountryCode,
|
||||||
|
cityCode,
|
||||||
|
item.Name,
|
||||||
|
item.Township,
|
||||||
|
item.Street,
|
||||||
|
item.ZipCode
|
||||||
|
));
|
||||||
|
|
||||||
|
if (buffer.Count >= 5000) // 3. Batch
|
||||||
|
{
|
||||||
|
await BulkDistrictInsertAsync(buffer);
|
||||||
|
buffer.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Count > 0)
|
||||||
|
{
|
||||||
|
await BulkDistrictInsertAsync(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task BulkDistrictInsertAsync(List<District> entities)
|
||||||
|
{
|
||||||
|
var dbCtx = await _districtRepository.GetDbContextAsync();
|
||||||
|
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
||||||
|
{
|
||||||
|
PreserveInsertOrder = true,
|
||||||
|
SetOutputIdentity = true,
|
||||||
|
BatchSize = 5000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SeedAsync(DataSeedContext context)
|
public async Task SeedAsync(DataSeedContext context)
|
||||||
{
|
{
|
||||||
var settings = await _settings.GetListAsync();
|
var settings = await _settings.GetListAsync();
|
||||||
|
|
@ -172,13 +415,7 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
.Build();
|
.Build();
|
||||||
var items = configuration.Get<SeederDto>();
|
var items = configuration.Get<SeederDto>();
|
||||||
|
|
||||||
await SeedCountyGroupsAsync();
|
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||||
|
|
||||||
await SeedCountriesAsync();
|
|
||||||
|
|
||||||
await SeedCitiesAsync();
|
|
||||||
|
|
||||||
await SeedDistrictsAsync();
|
|
||||||
|
|
||||||
foreach (var item in items.Charts)
|
foreach (var item in items.Charts)
|
||||||
{
|
{
|
||||||
|
|
@ -758,240 +995,46 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SeedCountyGroupsAsync()
|
foreach (var item in items.Abouts)
|
||||||
{
|
|
||||||
var dbCtx = await _countryRepository.GetDbContextAsync();
|
|
||||||
|
|
||||||
// DB’de mevcut kodları set olarak al
|
|
||||||
var existingCodes = (await dbCtx.Set<CountryGroup>()
|
|
||||||
.Select(c => c.Name)
|
|
||||||
.ToListAsync())
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
|
||||||
|
|
||||||
using var fs = File.OpenRead(Path.Combine("Seeds", "CountryGroups.json"));
|
|
||||||
|
|
||||||
var buffer = new List<CountryGroup>(capacity: 1000);
|
|
||||||
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
|
|
||||||
|
|
||||||
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CountryGroupDto>(fs, options))
|
|
||||||
{
|
{
|
||||||
if (item == null) continue;
|
var exists = await _aboutRepository.FirstOrDefaultAsync();
|
||||||
if (string.IsNullOrWhiteSpace(item.Name)) continue; // boş kodları atla
|
if (exists == null)
|
||||||
|
|
||||||
// hem DB’de hem JSON içinde duplicate engelle
|
|
||||||
if (!seenCodes.Add(item.Name) || existingCodes.Contains(item.Name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
buffer.Add(new CountryGroup(
|
|
||||||
Guid.NewGuid(),
|
|
||||||
item.Name
|
|
||||||
));
|
|
||||||
|
|
||||||
if (buffer.Count >= 1000)
|
|
||||||
{
|
{
|
||||||
await BulkCountryGroupInsertAsync(buffer);
|
await _aboutRepository.InsertAsync(new About(
|
||||||
buffer.Clear();
|
Guid.NewGuid(),
|
||||||
|
JsonSerializer.Serialize(item.Stats),
|
||||||
|
JsonSerializer.Serialize(item.Descriptions),
|
||||||
|
JsonSerializer.Serialize(item.Sections)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.Count > 0)
|
foreach (var item in items.Contacts)
|
||||||
{
|
{
|
||||||
await BulkCountryGroupInsertAsync(buffer);
|
var exists = await _contactRepository.FirstOrDefaultAsync();
|
||||||
}
|
if (exists == null)
|
||||||
}
|
|
||||||
|
|
||||||
private async Task BulkCountryGroupInsertAsync(List<CountryGroup> entities)
|
|
||||||
{
|
|
||||||
var dbCtx = await _countryGroupRepository.GetDbContextAsync();
|
|
||||||
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
|
||||||
{
|
|
||||||
BatchSize = 1000
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SeedCountriesAsync()
|
|
||||||
{
|
|
||||||
var dbCtx = await _countryRepository.GetDbContextAsync();
|
|
||||||
|
|
||||||
// DB’de mevcut kodları set olarak al
|
|
||||||
var existingCodes = (await dbCtx.Set<Country>()
|
|
||||||
.Select(c => c.Code)
|
|
||||||
.ToListAsync())
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
|
||||||
|
|
||||||
using var fs = File.OpenRead(Path.Combine("Seeds", "Countries.json"));
|
|
||||||
|
|
||||||
var buffer = new List<Country>(capacity: 1000);
|
|
||||||
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
|
|
||||||
|
|
||||||
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CountryDto>(fs, options))
|
|
||||||
{
|
|
||||||
if (item == null) continue;
|
|
||||||
if (string.IsNullOrWhiteSpace(item.Code)) continue; // boş kodları atla
|
|
||||||
|
|
||||||
// hem DB’de hem JSON içinde duplicate engelle
|
|
||||||
if (!seenCodes.Add(item.Code) || existingCodes.Contains(item.Code))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
buffer.Add(new Country(
|
|
||||||
Guid.NewGuid(),
|
|
||||||
item.Code,
|
|
||||||
item.Name,
|
|
||||||
item.GroupName,
|
|
||||||
item.CurrencyCode,
|
|
||||||
item.PhoneCode,
|
|
||||||
item.TaxLabel
|
|
||||||
));
|
|
||||||
|
|
||||||
if (buffer.Count >= 1000)
|
|
||||||
{
|
{
|
||||||
await BulkCountryInsertAsync(buffer);
|
await _contactRepository.InsertAsync(new Contact(
|
||||||
buffer.Clear();
|
Guid.NewGuid(),
|
||||||
|
item.Address,
|
||||||
|
item.Phone,
|
||||||
|
item.Email,
|
||||||
|
item.Location,
|
||||||
|
item.TaxNumber,
|
||||||
|
JsonSerializer.Serialize(item.Bank),
|
||||||
|
JsonSerializer.Serialize(item.WorkHour),
|
||||||
|
JsonSerializer.Serialize(item.Map)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.Count > 0)
|
await SeedCountyGroupsAsync();
|
||||||
{
|
|
||||||
await BulkCountryInsertAsync(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task BulkCountryInsertAsync(List<Country> entities)
|
await SeedCountriesAsync();
|
||||||
{
|
|
||||||
var dbCtx = await _countryRepository.GetDbContextAsync();
|
|
||||||
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
|
||||||
{
|
|
||||||
BatchSize = 1000
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SeedCitiesAsync()
|
await SeedCitiesAsync();
|
||||||
{
|
|
||||||
var dbCtx = await _cityRepository.GetDbContextAsync();
|
|
||||||
|
|
||||||
// 1. Mevcut kayıtları çek (tek sorguda)
|
await SeedDistrictsAsync();
|
||||||
var existingCities = await dbCtx.Set<City>()
|
|
||||||
.Select(d => new { d.Code })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var existingSet = existingCities
|
|
||||||
.Select(d => d.Code)
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
var options = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
};
|
|
||||||
|
|
||||||
// 2. JSON’u stream et
|
|
||||||
using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Cities.json"));
|
|
||||||
|
|
||||||
var buffer = new List<City>(capacity: 5000);
|
|
||||||
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CityDto>(fs, options))
|
|
||||||
{
|
|
||||||
if (item == null) continue;
|
|
||||||
|
|
||||||
var key = $"{item.Code}";
|
|
||||||
if (existingSet.Contains(key)) continue; // duplicate kontrolü
|
|
||||||
|
|
||||||
buffer.Add(new City(
|
|
||||||
Guid.NewGuid(),
|
|
||||||
item.CountryCode,
|
|
||||||
item.Name,
|
|
||||||
$"{item.CountryCode}.{item.Code}",
|
|
||||||
item.PlateCode
|
|
||||||
));
|
|
||||||
|
|
||||||
if (buffer.Count >= 1000) // 3. Batch
|
|
||||||
{
|
|
||||||
await BulkCityInsertAsync(buffer);
|
|
||||||
buffer.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.Count > 0)
|
|
||||||
{
|
|
||||||
await BulkCityInsertAsync(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task BulkCityInsertAsync(List<City> entities)
|
|
||||||
{
|
|
||||||
var dbCtx = await _cityRepository.GetDbContextAsync();
|
|
||||||
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
|
||||||
{
|
|
||||||
PreserveInsertOrder = true,
|
|
||||||
SetOutputIdentity = true,
|
|
||||||
BatchSize = 1000
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SeedDistrictsAsync()
|
|
||||||
{
|
|
||||||
var dbCtx = await _districtRepository.GetDbContextAsync();
|
|
||||||
|
|
||||||
// 1. Mevcut kayıtları çek (tek sorguda)
|
|
||||||
var existingDistricts = await dbCtx.Set<District>()
|
|
||||||
.Select(d => new { d.CountryCode, d.CityCode, d.Name, d.Township, d.Street, d.ZipCode })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var existingSet = existingDistricts
|
|
||||||
.Select(d => $"{d.CountryCode}:{d.CityCode}:{d.Name}:{d.Township}:{d.Street}:{d.ZipCode}")
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
var options = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
};
|
|
||||||
|
|
||||||
// 2. JSON’u stream et
|
|
||||||
using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Districts.json"));
|
|
||||||
|
|
||||||
var buffer = new List<District>(capacity: 5000);
|
|
||||||
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<DistrictDto>(fs, options))
|
|
||||||
{
|
|
||||||
if (item == null) continue;
|
|
||||||
|
|
||||||
var key = $"{item.CountryCode}:{item.CityCode}:{item.Name}:{item.Township}:{item.Street}:{item.ZipCode}";
|
|
||||||
if (existingSet.Contains(key)) continue; // duplicate kontrolü
|
|
||||||
|
|
||||||
buffer.Add(new District(
|
|
||||||
Guid.NewGuid(),
|
|
||||||
item.CountryCode,
|
|
||||||
$"{item.CountryCode}.{item.CityCode}",
|
|
||||||
item.Name,
|
|
||||||
item.Township,
|
|
||||||
item.Street,
|
|
||||||
item.ZipCode
|
|
||||||
));
|
|
||||||
|
|
||||||
if (buffer.Count >= 5000) // 3. Batch
|
|
||||||
{
|
|
||||||
await BulkDistrictInsertAsync(buffer);
|
|
||||||
buffer.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.Count > 0)
|
|
||||||
{
|
|
||||||
await BulkDistrictInsertAsync(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task BulkDistrictInsertAsync(List<District> entities)
|
|
||||||
{
|
|
||||||
var dbCtx = await _districtRepository.GetDbContextAsync();
|
|
||||||
await dbCtx.BulkInsertAsync(entities, new BulkConfig
|
|
||||||
{
|
|
||||||
PreserveInsertOrder = true,
|
|
||||||
SetOutputIdentity = true,
|
|
||||||
BatchSize = 5000
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17360,5 +17360,88 @@
|
||||||
"Public.services.support.sms.features.api"
|
"Public.services.support.sms.features.api"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"Abouts": [
|
||||||
|
{
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"icon": "FaUsers",
|
||||||
|
"value": 300,
|
||||||
|
"labelKey": "Public.about.stats.clients",
|
||||||
|
"useCounter": true,
|
||||||
|
"counterEnd": 300,
|
||||||
|
"counterSuffix": "+",
|
||||||
|
"counterDuration": 2500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "FaAward",
|
||||||
|
"value": 20,
|
||||||
|
"labelKey": "Public.about.stats.experience",
|
||||||
|
"useCounter": true,
|
||||||
|
"counterEnd": 20,
|
||||||
|
"counterSuffix": "+",
|
||||||
|
"counterDuration": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "FaClock",
|
||||||
|
"value": "7/24",
|
||||||
|
"labelKey": "Public.about.stats.support",
|
||||||
|
"useCounter": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "FaGlobe",
|
||||||
|
"value": 3,
|
||||||
|
"labelKey": "Public.about.stats.countries",
|
||||||
|
"useCounter": true,
|
||||||
|
"counterEnd": 3,
|
||||||
|
"counterDuration": 1500
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"descriptions": [
|
||||||
|
"Public.about.description.part1",
|
||||||
|
"Public.about.description.motto",
|
||||||
|
"Public.about.description.part2",
|
||||||
|
"Public.about.description.closing"
|
||||||
|
],
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"key": "Public.about.mission",
|
||||||
|
"descKey": "Public.about.mission.desc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Public.about.vision",
|
||||||
|
"descKey": "Public.about.vision.desc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Contacts": [
|
||||||
|
{
|
||||||
|
"address": "Public.contact.address.full",
|
||||||
|
"phone": "+90 (544) 769 7 638",
|
||||||
|
"email": "destek@sozsoft.com",
|
||||||
|
"location": "Kozyatağı",
|
||||||
|
"taxNumber": "32374982750",
|
||||||
|
"bank": {
|
||||||
|
"accountHolder": "Özlem Öztürk",
|
||||||
|
"branch": "03663 / Enpara",
|
||||||
|
"accountNumber": "73941177",
|
||||||
|
"iban": "TR65 0011 1000 0000 0073 9411 77"
|
||||||
|
},
|
||||||
|
"workHour": {
|
||||||
|
"weekday": "Public.contact.workHours.weekday",
|
||||||
|
"weekend": "Public.contact.workHours.weekend",
|
||||||
|
"whatsapp": "Public.contact.workHours.whatsapp"
|
||||||
|
},
|
||||||
|
"map": {
|
||||||
|
"title": "Public.contact.location",
|
||||||
|
"src": "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3006.209566407676!2d28.757000999999992!3d41.10811400000001!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x8a0bbbdfcfd3fd24!2zU8O2enNvZnQ!5e0!3m2!1str!2str!4v1450816303558",
|
||||||
|
"width": "100%",
|
||||||
|
"height": "700",
|
||||||
|
"allowFullScreen": true,
|
||||||
|
"loading": "lazy",
|
||||||
|
"referrerPolicy": "no-referrer-when-downgrade"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Kurs.Languages.Entities;
|
using Kurs.Languages.Entities;
|
||||||
using Kurs.Platform.Charts.Dto;
|
using Kurs.Platform.Charts.Dto;
|
||||||
|
using Kurs.Platform.Contacts;
|
||||||
using Kurs.Platform.Entities;
|
using Kurs.Platform.Entities;
|
||||||
using Kurs.Platform.ListForms;
|
using Kurs.Platform.ListForms;
|
||||||
using Kurs.Settings.Entities;
|
using Kurs.Settings.Entities;
|
||||||
|
using static Kurs.Platform.Abouts.AboutDto;
|
||||||
|
|
||||||
namespace Kurs.Platform.Seeds;
|
namespace Kurs.Platform.Seeds;
|
||||||
|
|
||||||
|
|
@ -42,6 +44,8 @@ public class SeederDto
|
||||||
public List<CustomComponentSeedDto> CustomComponents { get; set; }
|
public List<CustomComponentSeedDto> CustomComponents { get; set; }
|
||||||
public List<ReportCategorySeedDto> ReportCategories { get; set; }
|
public List<ReportCategorySeedDto> ReportCategories { get; set; }
|
||||||
public List<ServiceSeedDto> Services { get; set; }
|
public List<ServiceSeedDto> Services { get; set; }
|
||||||
|
public List<AboutSeedDto> Abouts { get; set; }
|
||||||
|
public List<ContactSeedDto> Contacts { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ChartsSeedDto
|
public class ChartsSeedDto
|
||||||
|
|
@ -301,3 +305,22 @@ public class ServiceSeedDto
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
public string[] Features { get; set; }
|
public string[] Features { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AboutSeedDto
|
||||||
|
{
|
||||||
|
public List<StatDto> Stats { get; set; }
|
||||||
|
public List<string> Descriptions { get; set; }
|
||||||
|
public List<SectionDto> Sections { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContactSeedDto
|
||||||
|
{
|
||||||
|
public string Address { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Location { get; set; }
|
||||||
|
public string TaxNumber { get; set; }
|
||||||
|
public BankDto Bank { get; set; }
|
||||||
|
public WorkHoursDto WorkHour { get; set; }
|
||||||
|
public MapDto Map { get; set; }
|
||||||
|
}
|
||||||
28
api/src/Kurs.Platform.Domain/Entities/About.cs
Normal file
28
api/src/Kurs.Platform.Domain/Entities/About.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using Volo.Abp.Domain.Entities.Auditing;
|
||||||
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.Entities;
|
||||||
|
|
||||||
|
public class About : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
|
{
|
||||||
|
public Guid? TenantId { get; set; }
|
||||||
|
|
||||||
|
public string StatsJson { get; set; }
|
||||||
|
public string DescriptionsJson { get; set; }
|
||||||
|
public string SectionsJson { get; set; }
|
||||||
|
|
||||||
|
protected About() { }
|
||||||
|
|
||||||
|
public About(
|
||||||
|
Guid id,
|
||||||
|
string statsJson,
|
||||||
|
string descriptionsJson,
|
||||||
|
string sectionsJson
|
||||||
|
) : base(id)
|
||||||
|
{
|
||||||
|
StatsJson = statsJson;
|
||||||
|
DescriptionsJson = descriptionsJson;
|
||||||
|
SectionsJson = sectionsJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
api/src/Kurs.Platform.Domain/Entities/Contact.cs
Normal file
45
api/src/Kurs.Platform.Domain/Entities/Contact.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using Volo.Abp.Domain.Entities.Auditing;
|
||||||
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.Entities;
|
||||||
|
|
||||||
|
public class Contact : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
|
{
|
||||||
|
public Guid? TenantId { get; set; }
|
||||||
|
|
||||||
|
public string Address { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Location { get; set; }
|
||||||
|
public string TaxNumber { get; set; }
|
||||||
|
|
||||||
|
// JSON string kolonlar
|
||||||
|
public string BankJson { get; set; }
|
||||||
|
public string WorkHoursJson { get; set; }
|
||||||
|
public string MapJson { get; set; }
|
||||||
|
|
||||||
|
protected Contact() { }
|
||||||
|
|
||||||
|
public Contact(
|
||||||
|
Guid id,
|
||||||
|
string address,
|
||||||
|
string phone,
|
||||||
|
string email,
|
||||||
|
string location,
|
||||||
|
string taxNumber,
|
||||||
|
string bankJson,
|
||||||
|
string workHoursJson,
|
||||||
|
string mapJson
|
||||||
|
) : base(id)
|
||||||
|
{
|
||||||
|
Address = address;
|
||||||
|
Phone = phone;
|
||||||
|
Email = email;
|
||||||
|
Location = location;
|
||||||
|
TaxNumber = taxNumber;
|
||||||
|
BankJson = bankJson;
|
||||||
|
WorkHoursJson = workHoursJson;
|
||||||
|
MapJson = mapJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -80,6 +80,9 @@ public class PlatformDbContext :
|
||||||
public DbSet<PaymentMethod> PaymentMethods { get; set; }
|
public DbSet<PaymentMethod> PaymentMethods { get; set; }
|
||||||
public DbSet<InstallmentOption> InstallmentOptions { get; set; }
|
public DbSet<InstallmentOption> InstallmentOptions { get; set; }
|
||||||
|
|
||||||
|
public DbSet<About> Abouts { get; set; }
|
||||||
|
public DbSet<Contact> Contacts { get; set; }
|
||||||
|
|
||||||
public DbSet<Order> Orders { get; set; }
|
public DbSet<Order> Orders { get; set; }
|
||||||
public DbSet<OrderItem> OrderItems { get; set; }
|
public DbSet<OrderItem> OrderItems { get; set; }
|
||||||
|
|
||||||
|
|
@ -832,5 +835,33 @@ public class PlatformDbContext :
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Entity<About>(b =>
|
||||||
|
{
|
||||||
|
b.ToTable(PlatformConsts.DbTablePrefix + nameof(About), PlatformConsts.DbSchema);
|
||||||
|
b.ConfigureByConvention();
|
||||||
|
|
||||||
|
// JSON alanlar
|
||||||
|
b.Property(x => x.StatsJson).HasColumnType("nvarchar(max)");
|
||||||
|
b.Property(x => x.DescriptionsJson).HasColumnType("nvarchar(max)");
|
||||||
|
b.Property(x => x.SectionsJson).HasColumnType("nvarchar(max)");
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<Contact>(b =>
|
||||||
|
{
|
||||||
|
b.ToTable(PlatformConsts.DbTablePrefix + nameof(Contact), PlatformConsts.DbSchema);
|
||||||
|
b.ConfigureByConvention();
|
||||||
|
|
||||||
|
b.Property(x => x.Address).HasMaxLength(500);
|
||||||
|
b.Property(x => x.Phone).HasMaxLength(50);
|
||||||
|
b.Property(x => x.Email).HasMaxLength(150);
|
||||||
|
b.Property(x => x.Location).HasMaxLength(150);
|
||||||
|
b.Property(x => x.TaxNumber).HasMaxLength(50);
|
||||||
|
|
||||||
|
// JSON alanlar nvarchar(max) olarak
|
||||||
|
b.Property(x => x.BankJson).HasColumnType("nvarchar(max)");
|
||||||
|
b.Property(x => x.WorkHoursJson).HasColumnType("nvarchar(max)");
|
||||||
|
b.Property(x => x.MapJson).HasColumnType("nvarchar(max)");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Kurs.Platform.Migrations
|
namespace Kurs.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20250820190627_GenelDuzenleme")]
|
[Migration("20250821081019_GenelDuzenleme")]
|
||||||
partial class GenelDuzenleme
|
partial class GenelDuzenleme
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -650,6 +650,59 @@ namespace Kurs.Platform.Migrations
|
||||||
b.ToTable("PNotificationRule", (string)null);
|
b.ToTable("PNotificationRule", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Kurs.Platform.Entities.About", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("CreationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatorId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("CreatorId");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DeleterId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("DeleterId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletionTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
|
b.Property<string>("DescriptionsJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false)
|
||||||
|
.HasColumnName("IsDeleted");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("LastModificationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("LastModifierId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
|
b.Property<string>("SectionsJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StatsJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("TenantId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("TenantId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PAbout", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b =>
|
modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
@ -1506,6 +1559,79 @@ namespace Kurs.Platform.Migrations
|
||||||
b.ToTable("PCity", (string)null);
|
b.ToTable("PCity", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Kurs.Platform.Entities.Contact", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Address")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<string>("BankJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("CreationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatorId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("CreatorId");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DeleterId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("DeleterId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletionTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(150)
|
||||||
|
.HasColumnType("nvarchar(150)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false)
|
||||||
|
.HasColumnName("IsDeleted");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("LastModificationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("LastModifierId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasMaxLength(150)
|
||||||
|
.HasColumnType("nvarchar(150)");
|
||||||
|
|
||||||
|
b.Property<string>("MapJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("TaxNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("TenantId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("TenantId");
|
||||||
|
|
||||||
|
b.Property<string>("WorkHoursJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PContact", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Kurs.Platform.Entities.Country", b =>
|
modelBuilder.Entity("Kurs.Platform.Entities.Country", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
@ -173,6 +173,55 @@ namespace Kurs.Platform.Migrations
|
||||||
table: "PCity",
|
table: "PCity",
|
||||||
columns: new[] { "CountryCode", "Code" });
|
columns: new[] { "CountryCode", "Code" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PAbout",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
StatsJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
DescriptionsJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
SectionsJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PAbout", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PContact",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
Address = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
Phone = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||||
|
Email = table.Column<string>(type: "nvarchar(150)", maxLength: 150, nullable: true),
|
||||||
|
Location = table.Column<string>(type: "nvarchar(150)", maxLength: 150, nullable: true),
|
||||||
|
TaxNumber = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
|
||||||
|
BankJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
WorkHoursJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
MapJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||||
|
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PContact", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "PDemo",
|
name: "PDemo",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
|
|
@ -598,6 +647,12 @@ namespace Kurs.Platform.Migrations
|
||||||
name: "FK_PUom_PUomCategory_UomCategoryId",
|
name: "FK_PUom_PUomCategory_UomCategoryId",
|
||||||
table: "PUom");
|
table: "PUom");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PAbout");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PContact");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "PDemo");
|
name: "PDemo");
|
||||||
|
|
||||||
|
|
@ -647,6 +647,59 @@ namespace Kurs.Platform.Migrations
|
||||||
b.ToTable("PNotificationRule", (string)null);
|
b.ToTable("PNotificationRule", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Kurs.Platform.Entities.About", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("CreationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatorId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("CreatorId");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DeleterId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("DeleterId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletionTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
|
b.Property<string>("DescriptionsJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false)
|
||||||
|
.HasColumnName("IsDeleted");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("LastModificationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("LastModifierId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
|
b.Property<string>("SectionsJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StatsJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("TenantId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("TenantId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PAbout", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b =>
|
modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
@ -1503,6 +1556,79 @@ namespace Kurs.Platform.Migrations
|
||||||
b.ToTable("PCity", (string)null);
|
b.ToTable("PCity", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Kurs.Platform.Entities.Contact", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Address")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<string>("BankJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("CreationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatorId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("CreatorId");
|
||||||
|
|
||||||
|
b.Property<Guid?>("DeleterId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("DeleterId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletionTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(150)
|
||||||
|
.HasColumnType("nvarchar(150)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false)
|
||||||
|
.HasColumnName("IsDeleted");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("LastModificationTime");
|
||||||
|
|
||||||
|
b.Property<Guid?>("LastModifierId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasMaxLength(150)
|
||||||
|
.HasColumnType("nvarchar(150)");
|
||||||
|
|
||||||
|
b.Property<string>("MapJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("TaxNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("TenantId")
|
||||||
|
.HasColumnType("uniqueidentifier")
|
||||||
|
.HasColumnName("TenantId");
|
||||||
|
|
||||||
|
b.Property<string>("WorkHoursJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PContact", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Kurs.Platform.Entities.Country", b =>
|
modelBuilder.Entity("Kurs.Platform.Entities.Country", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
|
||||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
}, {
|
}, {
|
||||||
"url": "index.html",
|
"url": "index.html",
|
||||||
"revision": "0.vt922hqbnl8"
|
"revision": "0.2cgibr83seg"
|
||||||
}], {});
|
}], {});
|
||||||
workbox.cleanupOutdatedCaches();
|
workbox.cleanupOutdatedCaches();
|
||||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
|
|
||||||
22
ui/src/proxy/about/models.ts
Normal file
22
ui/src/proxy/about/models.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
export interface AboutDto {
|
||||||
|
id: string
|
||||||
|
tenantId?: string
|
||||||
|
statsDto: StatDto[]
|
||||||
|
descriptionsDto: string[]
|
||||||
|
sectionsDto: SectionDto[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatDto {
|
||||||
|
icon: string
|
||||||
|
value: string
|
||||||
|
labelKey: string
|
||||||
|
useCounter?: boolean
|
||||||
|
counterEnd?: number
|
||||||
|
counterSuffix?: string
|
||||||
|
counterDuration?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SectionDto {
|
||||||
|
key: string
|
||||||
|
descKey: string
|
||||||
|
}
|
||||||
34
ui/src/proxy/contact/models.ts
Normal file
34
ui/src/proxy/contact/models.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
export interface ContactDto {
|
||||||
|
id: string
|
||||||
|
address: string
|
||||||
|
phone: string
|
||||||
|
email: string
|
||||||
|
location: string
|
||||||
|
taxNumber: string
|
||||||
|
bankDto: BankDto
|
||||||
|
workHoursDto: WorkHoursDto
|
||||||
|
mapDto: MapDto
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BankDto {
|
||||||
|
accountHolder: string
|
||||||
|
branch: string
|
||||||
|
accountNumber: string
|
||||||
|
iban: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkHoursDto {
|
||||||
|
weekday: string
|
||||||
|
weekend: string
|
||||||
|
whatsapp: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MapDto {
|
||||||
|
title: string
|
||||||
|
src: string
|
||||||
|
width: string
|
||||||
|
height: string
|
||||||
|
allowFullScreen?: boolean
|
||||||
|
loading: string
|
||||||
|
referrerPolicy: string
|
||||||
|
}
|
||||||
12
ui/src/services/about.ts
Normal file
12
ui/src/services/about.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { AboutDto } from '@/proxy/about/models'
|
||||||
|
import apiService from './api.service'
|
||||||
|
|
||||||
|
export function getAbout() {
|
||||||
|
return apiService.fetchData<AboutDto>(
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/app/public/about',
|
||||||
|
},
|
||||||
|
{ apiName: 'Default' },
|
||||||
|
)
|
||||||
|
}
|
||||||
12
ui/src/services/contact.ts
Normal file
12
ui/src/services/contact.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import apiService from './api.service'
|
||||||
|
import { ContactDto } from '@/proxy/contact/models'
|
||||||
|
|
||||||
|
export function getContact() {
|
||||||
|
return apiService.fetchData<ContactDto>(
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/app/public/contact',
|
||||||
|
},
|
||||||
|
{ apiName: 'Default' },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
import { useState, useEffect, useRef } from 'react'
|
|
||||||
|
|
||||||
interface UseCountUpOptions {
|
|
||||||
start?: number
|
|
||||||
end: number
|
|
||||||
duration?: number
|
|
||||||
suffix?: string
|
|
||||||
prefix?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useCountUp = ({
|
|
||||||
start = 0,
|
|
||||||
end,
|
|
||||||
duration = 2000,
|
|
||||||
suffix = '',
|
|
||||||
prefix = '',
|
|
||||||
}: UseCountUpOptions) => {
|
|
||||||
const [count, setCount] = useState(start)
|
|
||||||
const [isInView, setIsInView] = useState(false)
|
|
||||||
const elementRef = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
([entry]) => {
|
|
||||||
if (entry.isIntersecting && !isInView) {
|
|
||||||
setIsInView(true)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ threshold: 0.1 },
|
|
||||||
)
|
|
||||||
|
|
||||||
if (elementRef.current) {
|
|
||||||
observer.observe(elementRef.current)
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => observer.disconnect()
|
|
||||||
}, [isInView])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isInView) return
|
|
||||||
|
|
||||||
let startTime: number
|
|
||||||
const animateCount = (timestamp: number) => {
|
|
||||||
if (!startTime) startTime = timestamp
|
|
||||||
|
|
||||||
const progress = Math.min((timestamp - startTime) / duration, 1)
|
|
||||||
const currentCount = Math.floor(progress * (end - start) + start)
|
|
||||||
|
|
||||||
setCount(currentCount)
|
|
||||||
|
|
||||||
if (progress < 1) {
|
|
||||||
requestAnimationFrame(animateCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(animateCount)
|
|
||||||
}, [isInView, start, end, duration])
|
|
||||||
|
|
||||||
const displayValue = `${prefix}${count}${suffix}`
|
|
||||||
|
|
||||||
return { count, displayValue, elementRef }
|
|
||||||
}
|
|
||||||
|
|
@ -1,108 +1,44 @@
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import {
|
|
||||||
FaUsers,
|
|
||||||
FaAward,
|
|
||||||
FaClock,
|
|
||||||
FaGlobe
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { useCountUp } from '@/utils/hooks/useCountUp'
|
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { IconType } from 'react-icons'
|
import navigationIcon from '@/configs/navigation-icon.config'
|
||||||
|
import { AboutDto } from '@/proxy/about/models'
|
||||||
interface StatItem {
|
import { getAbout } from '@/services/about'
|
||||||
icon: IconType
|
|
||||||
value: string | number
|
|
||||||
labelKey: string
|
|
||||||
useCounter?: boolean
|
|
||||||
counterEnd?: number
|
|
||||||
counterSuffix?: string
|
|
||||||
counterDuration?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ContentSection {
|
|
||||||
key: string
|
|
||||||
descKey: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AboutPageData {
|
|
||||||
stats: StatItem[]
|
|
||||||
descriptions: string[]
|
|
||||||
sections: ContentSection[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const About: React.FC = () => {
|
const About: React.FC = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [about, setAbout] = useState<AboutDto>()
|
||||||
|
|
||||||
// About page data configuration
|
const iconColors = [
|
||||||
const aboutPageData: AboutPageData = {
|
'text-blue-600',
|
||||||
stats: [
|
'text-red-600',
|
||||||
{
|
'text-green-600',
|
||||||
icon: FaUsers,
|
'text-purple-600',
|
||||||
value: 300,
|
'text-yellow-600',
|
||||||
labelKey: '::Public.about.stats.clients',
|
'text-indigo-600',
|
||||||
useCounter: true,
|
]
|
||||||
counterEnd: 300,
|
|
||||||
counterSuffix: '+',
|
function getRandomColor() {
|
||||||
counterDuration: 2500
|
return iconColors[Math.floor(Math.random() * iconColors.length)]
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: FaAward,
|
|
||||||
value: 20,
|
|
||||||
labelKey: '::Public.about.stats.experience',
|
|
||||||
useCounter: true,
|
|
||||||
counterEnd: 20,
|
|
||||||
counterSuffix: '+',
|
|
||||||
counterDuration: 2000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: FaClock,
|
|
||||||
value: '7/24',
|
|
||||||
labelKey: '::Public.about.stats.support',
|
|
||||||
useCounter: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: FaGlobe,
|
|
||||||
value: 3,
|
|
||||||
labelKey: '::Public.about.stats.countries',
|
|
||||||
useCounter: true,
|
|
||||||
counterEnd: 3,
|
|
||||||
counterDuration: 1500
|
|
||||||
}
|
|
||||||
],
|
|
||||||
descriptions: [
|
|
||||||
'::Public.about.description.part1',
|
|
||||||
'::Public.about.description.motto',
|
|
||||||
'::Public.about.description.part2',
|
|
||||||
'::Public.about.description.closing'
|
|
||||||
],
|
|
||||||
sections: [
|
|
||||||
{
|
|
||||||
key: '::Public.about.mission',
|
|
||||||
descKey: '::Public.about.mission.desc'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '::Public.about.vision',
|
|
||||||
descKey: '::Public.about.vision.desc'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counter animations based on data
|
useEffect(() => {
|
||||||
const clientsCount = useCountUp({
|
setLoading(true)
|
||||||
end: aboutPageData.stats[0].counterEnd!,
|
|
||||||
suffix: aboutPageData.stats[0].counterSuffix,
|
const fetchServices = async () => {
|
||||||
duration: aboutPageData.stats[0].counterDuration!
|
try {
|
||||||
})
|
const result = await getAbout()
|
||||||
const experienceCount = useCountUp({
|
setAbout(result.data)
|
||||||
end: aboutPageData.stats[1].counterEnd!,
|
} catch (error) {
|
||||||
suffix: aboutPageData.stats[1].counterSuffix,
|
console.error('About alınırken hata oluştu:', error)
|
||||||
duration: aboutPageData.stats[1].counterDuration!
|
} finally {
|
||||||
})
|
setLoading(false)
|
||||||
const countriesCount = useCountUp({
|
}
|
||||||
end: aboutPageData.stats[3].counterEnd!,
|
}
|
||||||
duration: aboutPageData.stats[3].counterDuration!
|
|
||||||
})
|
fetchServices()
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -136,32 +72,19 @@ const About: React.FC = () => {
|
||||||
<div className="py-10 bg-white">
|
<div className="py-10 bg-white">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||||
{aboutPageData.stats.map((stat, index) => {
|
{about?.statsDto.map((stat, index) => {
|
||||||
const IconComponent = stat.icon
|
const IconComponent = navigationIcon[stat.icon || '']
|
||||||
|
|
||||||
let displayValue = stat.value
|
let displayValue = stat.value
|
||||||
let elementRef = undefined
|
let elementRef = undefined
|
||||||
|
|
||||||
// Handle counter values
|
|
||||||
if (stat.useCounter) {
|
|
||||||
if (index === 0) {
|
|
||||||
displayValue = clientsCount.displayValue
|
|
||||||
elementRef = clientsCount.elementRef
|
|
||||||
} else if (index === 1) {
|
|
||||||
displayValue = experienceCount.displayValue
|
|
||||||
elementRef = experienceCount.elementRef
|
|
||||||
} else if (index === 3) {
|
|
||||||
displayValue = countriesCount.displayValue
|
|
||||||
elementRef = countriesCount.elementRef
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className="text-center" ref={elementRef}>
|
<div key={index} className="text-center">
|
||||||
<IconComponent className="w-12 h-12 text-blue-600 mx-auto mb-4" />
|
{IconComponent && (
|
||||||
<div className="text-4xl font-bold text-gray-900 mb-2">
|
<IconComponent className={`w-12 h-12 mx-auto mb-4 ${getRandomColor()}`} />
|
||||||
{displayValue}
|
)}
|
||||||
</div>
|
<div className="text-4xl font-bold text-gray-900 mb-2">{displayValue}</div>
|
||||||
<div className="text-gray-600">{translate(stat.labelKey)}</div>
|
<div className="text-gray-600">{translate('::' + stat.labelKey)}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
@ -174,28 +97,24 @@ const About: React.FC = () => {
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="p-5 mx-auto mx-auto text-gray-800 text-lg leading-relaxed shadow-md bg-white border-l-4 border-blue-600">
|
<div className="p-5 mx-auto mx-auto text-gray-800 text-lg leading-relaxed shadow-md bg-white border-l-4 border-blue-600">
|
||||||
<p>
|
<p>{translate('::' + about?.descriptionsDto[0])}</p>
|
||||||
{translate(aboutPageData.descriptions[0])}
|
|
||||||
</p>
|
|
||||||
<p className="text-center p-5 text-blue-800">
|
<p className="text-center p-5 text-blue-800">
|
||||||
{translate(aboutPageData.descriptions[1])}
|
{translate('::' + about?.descriptionsDto[1])}
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{translate(aboutPageData.descriptions[2])}
|
|
||||||
</p>
|
</p>
|
||||||
|
<p>{translate('::' + about?.descriptionsDto[2])}</p>
|
||||||
<p className="text-center p-5 text-blue-800">
|
<p className="text-center p-5 text-blue-800">
|
||||||
{translate(aboutPageData.descriptions[3])}
|
{translate('::' + about?.descriptionsDto[3])}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||||
{aboutPageData.sections.map((section, index) => (
|
{about?.sectionsDto.map((section, index) => (
|
||||||
<div key={index} className="bg-white p-8 rounded-xl shadow-lg">
|
<div key={index} className="bg-white p-8 rounded-xl shadow-lg">
|
||||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">
|
<h3 className="text-2xl font-bold text-gray-900 mb-4">
|
||||||
{translate(section.key)}
|
{translate('::' + section.key)}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-700">{translate(section.descKey)}</p>
|
<p className="text-gray-700">{translate('::' + section.descKey)}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
FaMailBulk,
|
FaMailBulk,
|
||||||
FaPhone,
|
FaPhone,
|
||||||
|
|
@ -7,13 +7,35 @@ import {
|
||||||
FaBuilding,
|
FaBuilding,
|
||||||
FaCalendarAlt,
|
FaCalendarAlt,
|
||||||
FaCalendarCheck,
|
FaCalendarCheck,
|
||||||
FaRegComment
|
FaRegComment,
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { ContactDto } from '@/proxy/contact/models'
|
||||||
|
import { getContact } from '@/services/contact'
|
||||||
|
|
||||||
const Contact: React.FC = () => {
|
const Contact: React.FC = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [contact, setContact] = useState<ContactDto>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
const fetchServices = async () => {
|
||||||
|
try {
|
||||||
|
const result = await getContact()
|
||||||
|
|
||||||
|
setContact(result.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('About alınırken hata oluştu:', error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchServices()
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className="min-h-screen bg-gray-50">
|
||||||
|
|
@ -42,51 +64,51 @@ const Contact: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Stats Section */}
|
||||||
<div className="py-16">
|
<div className="py-16">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||||
{/* Contact Information */}
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="bg-white rounded-xl shadow-lg p-8">
|
<div className="bg-white rounded-xl shadow-lg p-8">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
{translate('::Public.contact.info.title')}
|
{translate('::Public.contact.info.title')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-6">
|
<div className="space-y-4">
|
||||||
<div className="flex items-start space-x-4">
|
<div className="flex items-start space-x-2">
|
||||||
<FaMapPin className="w-6 h-6 text-blue-600 flex-shrink-0 mt-1" />
|
<FaMapPin className="w-5 h-5 text-blue-600 flex-shrink-0 mt-1" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-600">{translate('::Public.contact.address.full')}</p>
|
<p className="text-gray-600">{translate('::' + contact?.address)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start space-x-4">
|
<div className="flex items-start space-x-2">
|
||||||
<FaPhone className="w-6 h-6 text-blue-600 flex-shrink-0" />
|
<FaPhone className="w-5 h-5 text-blue-600 flex-shrink-0" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-600">+90 (544) 769 7 638</p>
|
<p className="text-gray-600">{contact?.phone}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start space-x-4">
|
<div className="flex items-start space-x-2">
|
||||||
<FaMailBulk className="w-6 h-6 text-blue-600 flex-shrink-0" />
|
<FaMailBulk className="w-5 h-5 text-blue-600 flex-shrink-0" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
<a
|
<a
|
||||||
href="mailto:destek@sozsoft.com"
|
href={`mailto:${contact?.email}`}
|
||||||
className="hover:underline text-blue-600"
|
className="hover:underline text-blue-600"
|
||||||
>
|
>
|
||||||
destek@sozsoft.com
|
{contact?.email}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start space-x-4">
|
<div className="flex items-start space-x-2">
|
||||||
<FaBuilding className="w-6 h-6 text-blue-600 flex-shrink-0" />
|
<FaBuilding className="w-5 h-5 text-blue-600 flex-shrink-0" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-600">Kozyatağı</p>
|
<p className="text-gray-600">{contact?.location}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start space-x-4">
|
<div className="flex items-start space-x-2">
|
||||||
<FaFileAlt className="w-6 h-6 text-blue-600 flex-shrink-0" />
|
<FaFileAlt className="w-5 h-5 text-blue-600 flex-shrink-0" />
|
||||||
<div>
|
<div>
|
||||||
<p className="text-gray-600">32374982750</p>
|
<p className="text-gray-600">{contact?.taxNumber}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -96,21 +118,19 @@ const Contact: React.FC = () => {
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
{translate('::Public.contact.bank.title')}
|
{translate('::Public.contact.bank.title')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-4">
|
<div className="mb-2">
|
||||||
<img
|
<img
|
||||||
src="/img/enpara.svg"
|
src="/img/enpara.svg"
|
||||||
alt="Enpara Logo"
|
alt="Enpara Logo"
|
||||||
className="w-24 object-contain mt-1 flex-shrink-0"
|
className="w-24 object-contain mt-1 flex-shrink-0"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-gray-900">Özlem Öztürk</h3>
|
<h3 className="font-semibold text-gray-900 mb-1">
|
||||||
<p className="text-gray-600">
|
{contact?.bankDto.accountHolder}
|
||||||
03663 / Enpara
|
</h3>
|
||||||
<br />
|
<p className="text-gray-600 mb-1 ml-1">{contact?.bankDto.branch}</p>
|
||||||
73941177
|
<p className="text-gray-600 mb-1 ml-1">{contact?.bankDto.accountNumber}</p>
|
||||||
<br />
|
<p className="text-gray-600 mb-1 ml-1">{contact?.bankDto.iban}</p>
|
||||||
TR65 0011 1000 0000 0073 9411 77
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -120,28 +140,24 @@ const Contact: React.FC = () => {
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
{translate('::Public.contact.workHours')}
|
{translate('::Public.contact.workHours')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-4">
|
<div className="space-y-2">
|
||||||
<div className="flex items-start space-x-4">
|
<div className="flex items-center space-x-2">
|
||||||
<div className="space-y-2">
|
<FaCalendarAlt className="w-5 h-5 text-blue-500" />
|
||||||
<div className="flex items-center space-x-2">
|
<p className="text-gray-600">
|
||||||
<FaCalendarAlt className="w-5 h-5 text-blue-500" />
|
{translate('::' + contact?.workHoursDto.weekday)}
|
||||||
<p className="text-gray-600">
|
</p>
|
||||||
{translate('::Public.contact.workHours.weekday')}
|
</div>
|
||||||
</p>
|
<div className="flex items-center space-x-2">
|
||||||
</div>
|
<FaCalendarCheck className="w-5 h-5 text-blue-500" />
|
||||||
<div className="flex items-center space-x-2">
|
<p className="text-gray-600">
|
||||||
<FaCalendarCheck className="w-5 h-5 text-blue-500" />
|
{translate('::' + contact?.workHoursDto.weekend)}
|
||||||
<p className="text-gray-600">
|
</p>
|
||||||
{translate('::Public.contact.workHours.weekend')}
|
</div>
|
||||||
</p>
|
<div className="flex items-center space-x-2">
|
||||||
</div>
|
<FaRegComment className="w-5 h-5 text-green-500" />
|
||||||
<div className="flex items-center space-x-2">
|
<p className="text-gray-600">
|
||||||
<FaRegComment className="w-5 h-5 text-green-500" />
|
{translate('::' + contact?.workHoursDto.whatsapp)}
|
||||||
<p className="text-gray-600">
|
</p>
|
||||||
{translate('::Public.contact.workHours.whatsapp')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -150,17 +166,19 @@ const Contact: React.FC = () => {
|
||||||
{/* Map Section */}
|
{/* Map Section */}
|
||||||
<div className="bg-white rounded-xl shadow-lg p-2">
|
<div className="bg-white rounded-xl shadow-lg p-2">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center">
|
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center">
|
||||||
{translate('::Public.contact.location')}
|
{translate('::' + contact?.mapDto.title)}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="aspect-w-16 aspect-h-9 bg-gray-200 rounded-xl overflow-hidden">
|
<div className="aspect-w-16 aspect-h-9 bg-gray-200 rounded-xl overflow-hidden">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3006.209566407676!2d28.757000999999992!3d41.10811400000001!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x8a0bbbdfcfd3fd24!2zU8O2enNvZnQ!5e0!3m2!1str!2str!4v1450816303558"
|
src={contact?.mapDto.src}
|
||||||
width="100%"
|
width={contact?.mapDto.width}
|
||||||
height="740"
|
|
||||||
style={{ border: 0 }}
|
style={{ border: 0 }}
|
||||||
allowFullScreen
|
height={contact?.mapDto.height}
|
||||||
loading="lazy"
|
allowFullScreen={contact?.mapDto.allowFullScreen}
|
||||||
referrerPolicy="no-referrer-when-downgrade"
|
loading={contact?.mapDto.loading as 'lazy' | 'eager' | undefined}
|
||||||
|
referrerPolicy={
|
||||||
|
contact?.mapDto.referrerPolicy as React.HTMLAttributeReferrerPolicy | undefined
|
||||||
|
}
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -19,128 +19,7 @@ import navigationIcon from '@/configs/navigation-icon.config'
|
||||||
const Services: React.FC = () => {
|
const Services: React.FC = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const [services, setServices] = useState<ServiceDto[]>([])
|
const [services, setServices] = useState<ServiceDto[]>([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
// const services: ServiceDto[] = [
|
|
||||||
// {
|
|
||||||
// icon: <FaCode className="w-12 h-12 text-blue-600" />,
|
|
||||||
// title: translate('::Public.services.software.title'),
|
|
||||||
// description: translate('::Public.services.software.desc'),
|
|
||||||
// type: 'service',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.software.features.analysis'),
|
|
||||||
// translate('::Public.services.software.features.design'),
|
|
||||||
// translate('::Public.services.software.features.development'),
|
|
||||||
// translate('::Public.services.software.features.testing'),
|
|
||||||
// translate('::Public.services.software.features.maintenance'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaUsers className="w-12 h-12 text-purple-600" />,
|
|
||||||
// title: translate('::Public.services.web.title'),
|
|
||||||
// description: translate('::Public.services.web.desc'),
|
|
||||||
// type: 'service',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.web.features.frontend'),
|
|
||||||
// translate('::Public.services.web.features.backend'),
|
|
||||||
// translate('::Public.services.web.features.api'),
|
|
||||||
// translate('::Public.services.web.features.seo'),
|
|
||||||
// translate('::Public.services.web.features.performance'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaShieldAlt className="w-12 h-12 text-green-600" />,
|
|
||||||
// title: translate('::Public.services.mobile.title'),
|
|
||||||
// description: translate('::Public.services.mobile.desc'),
|
|
||||||
// type: 'service',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.mobile.features.design'),
|
|
||||||
// translate('::Public.services.mobile.features.native'),
|
|
||||||
// translate('::Public.services.mobile.features.cross'),
|
|
||||||
// translate('::Public.services.mobile.features.push'),
|
|
||||||
// translate('::Public.services.mobile.features.store'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaServer className="w-12 h-12 text-red-600" />,
|
|
||||||
// title: translate('::Public.services.database.title'),
|
|
||||||
// description: translate('::Public.services.database.desc'),
|
|
||||||
// type: 'service',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.database.features.design'),
|
|
||||||
// translate('::Public.services.database.features.optimization'),
|
|
||||||
// translate('::Public.services.database.features.migration'),
|
|
||||||
// translate('::Public.services.database.features.backup'),
|
|
||||||
// translate('::Public.services.database.features.recovery'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaGlobe className="w-12 h-12 text-yellow-600" />,
|
|
||||||
// title: translate('::Public.services.integration.title'),
|
|
||||||
// description: translate('::Public.services.integration.desc'),
|
|
||||||
// type: 'service',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.integration.features.api'),
|
|
||||||
// translate('::Public.services.integration.features.middleware'),
|
|
||||||
// translate('::Public.services.integration.features.legacy'),
|
|
||||||
// translate('::Public.services.integration.features.realtime'),
|
|
||||||
// translate('::Public.services.integration.features.monitoring'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaCog className="w-12 h-12 text-indigo-600" />,
|
|
||||||
// title: translate('::Public.services.consulting.title'),
|
|
||||||
// description: translate('::Public.services.consulting.desc'),
|
|
||||||
// type: 'service',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.consulting.features.tech'),
|
|
||||||
// translate('::Public.services.consulting.features.project'),
|
|
||||||
// translate('::Public.services.consulting.features.digital'),
|
|
||||||
// translate('::Public.services.consulting.features.risk'),
|
|
||||||
// translate('::Public.services.consulting.features.training'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaUsers className="w-12 h-12 text-pink-600" />, // Remote Branch Support
|
|
||||||
// title: translate('::Public.services.support.branchRemote.title'),
|
|
||||||
// description: '',
|
|
||||||
// type: 'support',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.support.branchRemote.features.priority'),
|
|
||||||
// translate('::Public.services.support.branchRemote.features.remote'),
|
|
||||||
// translate('::Public.services.support.branchRemote.features.optimization'),
|
|
||||||
// translate('::Public.services.support.branchRemote.features.maintenance'),
|
|
||||||
// translate('::Public.services.support.branchRemote.features.consulting'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaServer className="w-12 h-12 text-orange-600" />, // Backup Support
|
|
||||||
// title: translate('::Public.services.support.backup.title'),
|
|
||||||
// description: '',
|
|
||||||
// type: 'support',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.support.backup.features.daily'),
|
|
||||||
// translate('::Public.services.support.backup.features.encrypted'),
|
|
||||||
// translate('::Public.services.support.backup.features.recovery'),
|
|
||||||
// translate('::Public.services.support.backup.features.verification'),
|
|
||||||
// translate('::Public.services.support.backup.features.access'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: <FaGlobe className="w-12 h-12 text-cyan-600" />, // SMS Support
|
|
||||||
// title: translate('::Public.services.support.sms.title'),
|
|
||||||
// description: '',
|
|
||||||
// type: 'support',
|
|
||||||
// features: [
|
|
||||||
// translate('::Public.services.support.sms.features.packages'),
|
|
||||||
// translate('::Public.services.support.sms.features.bulk'),
|
|
||||||
// translate('::Public.services.support.sms.features.template'),
|
|
||||||
// translate('::Public.services.support.sms.features.reporting'),
|
|
||||||
// translate('::Public.services.support.sms.features.api'),
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
|
|
||||||
// Botları çek
|
|
||||||
|
|
||||||
const iconColors = [
|
const iconColors = [
|
||||||
'text-blue-600',
|
'text-blue-600',
|
||||||
|
|
@ -156,6 +35,8 @@ const Services: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
const fetchServices = async () => {
|
const fetchServices = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await getServices()
|
const result = await getServices()
|
||||||
|
|
@ -170,6 +51,8 @@ const Services: React.FC = () => {
|
||||||
setServices(items)
|
setServices(items)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Service listesi alınırken hata oluştu:', error)
|
console.error('Service listesi alınırken hata oluştu:', error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue