initial commit
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
using Indotalent.ConfigBackEnd.Interfaces;
|
||||
using Indotalent.Infrastructure.AutoNumberGenerator;
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace Indotalent.ConfigBackEnd.Extensions;
|
||||
|
||||
public static class AppDbContextExtensions
|
||||
{
|
||||
public static async Task<string> GenerateAutoNumberAsync(
|
||||
this AppDbContext context,
|
||||
string entityName,
|
||||
string prefixTemplate,
|
||||
string? suffixTemplate = null,
|
||||
int paddingLength = 4,
|
||||
bool useYear = true,
|
||||
bool useMonth = false,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var currentUserService = context.GetService<ICurrentUserService>();
|
||||
var tenantId = currentUserService?.TenantId;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
throw new InvalidOperationException("TenantId cannot be null or empty for multi-tenant auto-number sequence generation. Ensure the user session context is valid.");
|
||||
}
|
||||
|
||||
var generator = context.GetService<AutoNumberGeneratorService>();
|
||||
|
||||
return await generator.GenerateNextNumberAsync(
|
||||
tenantId,
|
||||
entityName,
|
||||
prefixTemplate,
|
||||
suffixTemplate,
|
||||
paddingLength,
|
||||
useYear,
|
||||
useMonth,
|
||||
ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Indotalent.ConfigBackEnd.Extensions;
|
||||
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
private const string DefaultFormat = "dd MMM yyyy, HH:mm";
|
||||
|
||||
public static string ToString(this DateTimeOffset? dateTimeOffset, string format = DefaultFormat)
|
||||
{
|
||||
if (!dateTimeOffset.HasValue) return "-";
|
||||
|
||||
return dateTimeOffset.Value.ToLocalTime().ToString(format);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
namespace Indotalent.ConfigBackEnd.Extensions;
|
||||
|
||||
using Indotalent.Shared.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
public static class GenericExtensions
|
||||
{
|
||||
public static bool IsNullOrEmpty<T>(this IEnumerable<T>? enumerable)
|
||||
{
|
||||
return enumerable == null || !enumerable.Any();
|
||||
}
|
||||
|
||||
public static T? ToObject<T>(this string json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return default;
|
||||
|
||||
return JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
}
|
||||
|
||||
public static string ToJson<T>(this T obj)
|
||||
{
|
||||
return JsonSerializer.Serialize(obj);
|
||||
}
|
||||
|
||||
public static TDestination MapTo<TDestination>(this object source)
|
||||
where TDestination : new()
|
||||
{
|
||||
var json = JsonSerializer.Serialize(source);
|
||||
return JsonSerializer.Deserialize<TDestination>(json) ?? new TDestination();
|
||||
}
|
||||
|
||||
public static string ToCurrency(this decimal value, string culture = "id-ID")
|
||||
{
|
||||
return value.ToString("C0", new System.Globalization.CultureInfo(culture));
|
||||
}
|
||||
|
||||
public static bool IsBetween<T>(this T value, T low, T high) where T : IComparable<T>
|
||||
{
|
||||
return value.CompareTo(low) >= 0 && value.CompareTo(high) <= 0;
|
||||
}
|
||||
|
||||
public static IResult ToApiResponse<T>(this PagedList<T> result)
|
||||
{
|
||||
return Results.Ok(new ApiResponse<List<T>>
|
||||
{
|
||||
IsSuccess = true,
|
||||
StatusCode = 200,
|
||||
Value = result.Value,
|
||||
Pagination = new PaginationMetadata
|
||||
{
|
||||
Count = result.Count,
|
||||
Top = result.Top,
|
||||
Skip = result.Skip
|
||||
},
|
||||
ServerTime = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
public static IResult ToApiResponse<T>(this T? result, string? message = null, int statusCode = 200)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return Results.NotFound(new ApiResponse<T>
|
||||
{
|
||||
IsSuccess = false,
|
||||
StatusCode = 404,
|
||||
Message = message ?? "Data not found",
|
||||
ServerTime = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
return Results.Ok(new ApiResponse<T>
|
||||
{
|
||||
IsSuccess = true,
|
||||
StatusCode = statusCode,
|
||||
Message = message,
|
||||
Value = result,
|
||||
Pagination = null,
|
||||
ServerTime = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace Indotalent.ConfigBackEnd.Extensions;
|
||||
|
||||
using Indotalent.Data.Interfaces;
|
||||
using Indotalent.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
public static class IQueryableExtensions
|
||||
{
|
||||
public static IQueryable<T> WhereIf<T>(
|
||||
this IQueryable<T> query,
|
||||
bool condition,
|
||||
Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
return condition ? query.Where(predicate) : query;
|
||||
}
|
||||
|
||||
public static async Task<PagedList<T>> ToPagedListAsync<T>(
|
||||
this IQueryable<T> query,
|
||||
int skip,
|
||||
int top)
|
||||
{
|
||||
top = top <= 0 ? 5 : top;
|
||||
skip = skip < 0 ? 0 : skip;
|
||||
|
||||
var count = await query.CountAsync();
|
||||
|
||||
var items = await query
|
||||
.Skip(skip)
|
||||
.Take(top)
|
||||
.ToListAsync();
|
||||
|
||||
return new PagedList<T>(items, count, skip, top);
|
||||
}
|
||||
|
||||
public static IQueryable<T> OrderByPropertyName<T>(
|
||||
this IQueryable<T> query,
|
||||
string? propertyName,
|
||||
bool isDescending)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(propertyName)) return query;
|
||||
|
||||
var parameter = Expression.Parameter(typeof(T), "x");
|
||||
var property = Expression.Property(parameter, propertyName);
|
||||
var lambda = Expression.Lambda(property, parameter);
|
||||
|
||||
var methodName = isDescending ? "OrderByDescending" : "OrderBy";
|
||||
var resultExpression = Expression.Call(
|
||||
typeof(Queryable),
|
||||
methodName,
|
||||
new Type[] { typeof(T), property.Type },
|
||||
query.Expression,
|
||||
Expression.Quote(lambda));
|
||||
|
||||
return query.Provider.CreateQuery<T>(resultExpression);
|
||||
}
|
||||
|
||||
public static IQueryable<T> NotDeletedOnly<T>(this IQueryable<T> query)
|
||||
where T : class, IHasIsDeleted
|
||||
{
|
||||
return query.Where(x => !x.IsDeleted);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace Indotalent.ConfigBackEnd.Extensions;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public static class ListExtensions
|
||||
{
|
||||
public static void AddIf<T>(this IList<T> list, bool condition, T item)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int size)
|
||||
{
|
||||
while (source.Any())
|
||||
{
|
||||
yield return source.Take(size);
|
||||
source = source.Skip(size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Shuffle<T>(this IList<T> list)
|
||||
{
|
||||
var rng = new Random();
|
||||
int n = list.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = rng.Next(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
|
||||
{
|
||||
var seenKeys = new HashSet<TKey>();
|
||||
foreach (var element in source)
|
||||
{
|
||||
if (seenKeys.Add(keySelector(element)))
|
||||
{
|
||||
yield return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
namespace Indotalent.ConfigBackEnd.Extensions;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string ToShortNameVowel(this string? value, int length = 3)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return "GEN";
|
||||
|
||||
var vowels = new string(value.Where(c => "AEIOUaeiou".Contains(c)).ToArray());
|
||||
var baseName = vowels.Length >= length ? vowels : value;
|
||||
|
||||
return new string(baseName.Trim().Take(length).ToArray()).ToUpper();
|
||||
}
|
||||
|
||||
public static string ToShortNameConsonant(this string? value, int length = 3)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return "GEN";
|
||||
|
||||
var consonants = new string(value.Where(c => char.IsLetter(c) && !"AEIOUaeiou".Contains(c)).ToArray());
|
||||
var baseName = consonants.Length >= length ? consonants : value;
|
||||
|
||||
return new string(baseName.Trim().Take(length).ToArray()).ToUpper();
|
||||
}
|
||||
|
||||
public static string ToInitial(this string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return "XX";
|
||||
|
||||
var words = value.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (words.Length >= 2)
|
||||
{
|
||||
var firstInitial = words[0][0];
|
||||
var secondInitial = words[1][0];
|
||||
return $"{firstInitial}{secondInitial}".ToUpper();
|
||||
}
|
||||
|
||||
return new string(value.Trim().Take(2).ToArray()).ToUpper();
|
||||
}
|
||||
|
||||
public static bool IsNotNullOrWhiteSpace(this string? value)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(value);
|
||||
}
|
||||
|
||||
public static string ToTitleCase(this string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
|
||||
return System.Globalization.CultureInfo.CurrentCulture.TextInfo
|
||||
.ToTitleCase(value.ToLower().Trim());
|
||||
}
|
||||
|
||||
public static string ToSlug(this string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
|
||||
var str = value.ToLower().Trim();
|
||||
str = Regex.Replace(str, @"[^a-z0-9\s-]", "");
|
||||
str = Regex.Replace(str, @"[\s-]+", " ").Trim();
|
||||
str = str.Replace(" ", "-");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public static string MaskEmail(this string? email)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(email) || !email.Contains("@")) return "******";
|
||||
|
||||
var parts = email.Split('@');
|
||||
var name = parts[0];
|
||||
var domain = parts[1];
|
||||
|
||||
if (name.Length <= 2) return $"{name}***@{domain}";
|
||||
|
||||
return $"{name[..2]}***{name[^1..]}@{domain}";
|
||||
}
|
||||
|
||||
public static string Truncate(this string? value, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
return value.Length <= maxLength ? value : $"{value[..maxLength]}...";
|
||||
}
|
||||
|
||||
public static string RemoveNonNumeric(this string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
return Regex.Replace(value, "[^0-9]", "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user