feat: Set up initial WebClientTpos .NET project structure, including client, server, assets, and documentation.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
namespace WebClientTpos.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Standard API response wrapper for consistent response format.
|
||||
/// VI: Wrapper response API chuẩn cho định dạng response nhất quán.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of data in the response.</typeparam>
|
||||
public class ApiResponse<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// EN: Indicates if the request was successful.
|
||||
/// VI: Cho biết request có thành công không.
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// EN: The response data.
|
||||
/// VI: Dữ liệu response.
|
||||
/// </summary>
|
||||
public T? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// EN: Error message if request failed.
|
||||
/// VI: Thông báo lỗi nếu request thất bại.
|
||||
/// </summary>
|
||||
public string? Error { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// EN: Creates a successful response with data.
|
||||
/// VI: Tạo response thành công với dữ liệu.
|
||||
/// </summary>
|
||||
public static ApiResponse<T> Ok(T data) => new() { Success = true, Data = data };
|
||||
|
||||
/// <summary>
|
||||
/// EN: Creates a failed response with error message.
|
||||
/// VI: Tạo response thất bại với thông báo lỗi.
|
||||
/// </summary>
|
||||
public static ApiResponse<T> Fail(string error) => new() { Success = false, Error = error };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EN: Non-generic API response for operations without return data.
|
||||
/// VI: API response không generic cho các operation không có dữ liệu trả về.
|
||||
/// </summary>
|
||||
public class ApiResponse : ApiResponse<object>
|
||||
{
|
||||
/// <summary>
|
||||
/// EN: Creates a successful response.
|
||||
/// VI: Tạo response thành công.
|
||||
/// </summary>
|
||||
public static new ApiResponse Ok() => new() { Success = true };
|
||||
|
||||
/// <summary>
|
||||
/// EN: Creates a failed response with error message.
|
||||
/// VI: Tạo response thất bại với thông báo lỗi.
|
||||
/// </summary>
|
||||
public static new ApiResponse Fail(string error) => new() { Success = false, Error = error };
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebClientTpos.Shared.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Change password DTO for authenticated users.
|
||||
/// VI: DTO cho đổi mật khẩu của người dùng đã xác thực.
|
||||
/// </summary>
|
||||
public class ChangePasswordDto
|
||||
{
|
||||
[Required(ErrorMessage = "Current password is required")]
|
||||
public string CurrentPassword { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "New password is required")]
|
||||
[MinLength(8, ErrorMessage = "Password must be at least 8 characters")]
|
||||
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).+$",
|
||||
ErrorMessage = "Password must contain uppercase, lowercase, digit, and special character")]
|
||||
public string NewPassword { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "Confirm password is required")]
|
||||
[Compare(nameof(NewPassword), ErrorMessage = "Passwords do not match")]
|
||||
public string ConfirmPassword { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebClientTpos.Shared.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Forgot password request DTO.
|
||||
/// VI: DTO cho yêu cầu quên mật khẩu.
|
||||
/// </summary>
|
||||
public class ForgotPasswordDto
|
||||
{
|
||||
[Required(ErrorMessage = "Email is required")]
|
||||
[EmailAddress(ErrorMessage = "Invalid email address")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebClientTpos.Shared.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Product data transfer object with validation.
|
||||
/// VI: DTO sản phẩm với validation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// EN: This DTO is shared between Client and Server for consistent validation.
|
||||
/// VI: DTO này được chia sẻ giữa Client và Server để validation nhất quán.
|
||||
/// </remarks>
|
||||
public class ProductDto
|
||||
{
|
||||
/// <summary>
|
||||
/// EN: Product name, required with length constraints.
|
||||
/// VI: Tên sản phẩm, bắt buộc với ràng buộc độ dài.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Tên sản phẩm là bắt buộc / Name is required")]
|
||||
[StringLength(100, MinimumLength = 3, ErrorMessage = "Tên phải từ 3-100 ký tự / Name must be 3-100 chars")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Product description.
|
||||
/// VI: Mô tả sản phẩm.
|
||||
/// </summary>
|
||||
[StringLength(500, ErrorMessage = "Mô tả tối đa 500 ký tự / Max 500 characters")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// EN: Product price, must be positive.
|
||||
/// VI: Giá sản phẩm, phải dương.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Giá là bắt buộc / Price is required")]
|
||||
[Range(0.01, 1_000_000_000, ErrorMessage = "Giá phải từ 0.01 đến 1 tỷ / Price must be 0.01 to 1B")]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// EN: Stock quantity.
|
||||
/// VI: Số lượng tồn kho.
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "Số lượng không âm / Quantity must be non-negative")]
|
||||
public int Quantity { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EN: Create product request DTO.
|
||||
/// VI: DTO request tạo sản phẩm.
|
||||
/// </summary>
|
||||
public class CreateProductDto : ProductDto
|
||||
{
|
||||
/// <summary>
|
||||
/// EN: Product category ID.
|
||||
/// VI: ID danh mục sản phẩm.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Danh mục là bắt buộc / Category is required")]
|
||||
public Guid CategoryId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EN: Update product request DTO.
|
||||
/// VI: DTO request cập nhật sản phẩm.
|
||||
/// </summary>
|
||||
public class UpdateProductDto : ProductDto
|
||||
{
|
||||
/// <summary>
|
||||
/// EN: Product ID.
|
||||
/// VI: ID sản phẩm.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebClientTpos.Shared.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Reset password DTO.
|
||||
/// VI: DTO cho đặt lại mật khẩu.
|
||||
/// </summary>
|
||||
public class ResetPasswordDto
|
||||
{
|
||||
[Required(ErrorMessage = "Token is required")]
|
||||
public string Token { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "Email is required")]
|
||||
[EmailAddress(ErrorMessage = "Invalid email address")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
[MinLength(8, ErrorMessage = "Password must be at least 8 characters")]
|
||||
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).+$",
|
||||
ErrorMessage = "Password must contain uppercase, lowercase, digit, and special character")]
|
||||
public string NewPassword { get; set; } = string.Empty;
|
||||
|
||||
[Required(ErrorMessage = "Confirm password is required")]
|
||||
[Compare(nameof(NewPassword), ErrorMessage = "Passwords do not match")]
|
||||
public string ConfirmPassword { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebClientTpos.Shared.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// EN: User registration DTO with validation.
|
||||
/// VI: DTO đăng ký user với validation.
|
||||
/// </summary>
|
||||
public class RegisterDto
|
||||
{
|
||||
/// <summary>
|
||||
/// EN: User email address.
|
||||
/// VI: Địa chỉ email user.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Email là bắt buộc / Email is required")]
|
||||
[EmailAddress(ErrorMessage = "Email không hợp lệ / Invalid email format")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// EN: User password with strength requirements.
|
||||
/// VI: Mật khẩu user với yêu cầu độ mạnh.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Mật khẩu là bắt buộc / Password is required")]
|
||||
[StringLength(100, MinimumLength = 8, ErrorMessage = "Mật khẩu phải từ 8-100 ký tự / Password must be 8-100 chars")]
|
||||
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$",
|
||||
ErrorMessage = "Mật khẩu phải có chữ hoa, chữ thường và số / Password must have upper, lower and digit")]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Password confirmation must match.
|
||||
/// VI: Xác nhận mật khẩu phải khớp.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Xác nhận mật khẩu là bắt buộc / Confirm password is required")]
|
||||
[Compare(nameof(Password), ErrorMessage = "Mật khẩu không khớp / Passwords do not match")]
|
||||
public string ConfirmPassword { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// EN: User display name.
|
||||
/// VI: Tên hiển thị của user.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Tên là bắt buộc / Name is required")]
|
||||
[StringLength(50, MinimumLength = 2, ErrorMessage = "Tên phải từ 2-50 ký tự / Name must be 2-50 chars")]
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Accept terms of service.
|
||||
/// VI: Chấp nhận điều khoản dịch vụ.
|
||||
/// </summary>
|
||||
[Range(typeof(bool), "true", "true", ErrorMessage = "Bạn phải chấp nhận Điều khoản dịch vụ / You must accept the Terms of Service")]
|
||||
public bool AcceptTerms { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EN: User login DTO.
|
||||
/// VI: DTO đăng nhập user.
|
||||
/// </summary>
|
||||
public class LoginDto
|
||||
{
|
||||
/// <summary>
|
||||
/// EN: User email address.
|
||||
/// VI: Địa chỉ email user.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Email là bắt buộc / Email is required")]
|
||||
[EmailAddress(ErrorMessage = "Email không hợp lệ / Invalid email format")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// EN: User password.
|
||||
/// VI: Mật khẩu user.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Mật khẩu là bắt buộc / Password is required")]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Remember me option.
|
||||
/// VI: Tùy chọn ghi nhớ đăng nhập.
|
||||
/// </summary>
|
||||
public bool RememberMe { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EN: User profile DTO.
|
||||
/// VI: DTO hồ sơ user.
|
||||
/// </summary>
|
||||
public class UserProfileDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
public string? AvatarUrl { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public bool EmailVerified { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user