aboutsummaryrefslogtreecommitdiffstats
path: root/src/Data
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-06-01 21:13:43 +0200
committerivarlovlie <git@ivarlovlie.no>2022-06-01 21:13:43 +0200
commit9383a2fb09ffb60cfe63683106945bd688affa59 (patch)
tree65b3f4b48841583e355887db5de5a16e7005fc87 /src/Data
downloadvinjesvingenhandel.no-9383a2fb09ffb60cfe63683106945bd688affa59.tar.xz
vinjesvingenhandel.no-9383a2fb09ffb60cfe63683106945bd688affa59.zip
feat: Initial commit after clean slate
Diffstat (limited to 'src/Data')
-rw-r--r--src/Data/Database/Base.cs15
-rw-r--r--src/Data/Database/Category.cs30
-rw-r--r--src/Data/Database/Document.cs9
-rw-r--r--src/Data/Database/Order.cs98
-rw-r--r--src/Data/Database/Product.cs54
-rw-r--r--src/Data/Database/User.cs18
-rw-r--r--src/Data/Dtos/AdminViewOrderDto.cs29
-rw-r--r--src/Data/Enums/AnalyticType.cs7
-rw-r--r--src/Data/Enums/CategoryVisibility.cs8
-rw-r--r--src/Data/Enums/CookieType.cs8
-rw-r--r--src/Data/Enums/DocumentType.cs10
-rw-r--r--src/Data/Enums/OrderAdminStatus.cs9
-rw-r--r--src/Data/Enums/OrderPaymentType.cs7
-rw-r--r--src/Data/Enums/OrderStatus.cs11
-rw-r--r--src/Data/Enums/PriceSuffix.cs7
-rw-r--r--src/Data/Enums/ProductVisibility.cs8
-rw-r--r--src/Data/MainDbContext.cs40
-rw-r--r--src/Data/Miscellaneous/AppCookie.cs8
-rw-r--r--src/Data/Miscellaneous/AppPath.cs7
-rw-r--r--src/Data/Miscellaneous/AppSettings.cs118
-rw-r--r--src/Data/Miscellaneous/OpenGraphData.cs12
-rw-r--r--src/Data/Payloads/CreateProductDto.cs22
-rw-r--r--src/Data/Payloads/LoginPayload.cs8
-rw-r--r--src/Data/Payloads/UpdatePasswordPayload.cs6
-rw-r--r--src/Data/Payloads/ValidateOrderPayload.cs15
-rw-r--r--src/Data/Results/AppValidationResult.cs27
-rw-r--r--src/Data/Results/ErrorResult.cs12
-rw-r--r--src/Data/Static/AppCookies.cs21
-rw-r--r--src/Data/Static/AppPaths.cs40
29 files changed, 664 insertions, 0 deletions
diff --git a/src/Data/Database/Base.cs b/src/Data/Database/Base.cs
new file mode 100644
index 0000000..4084ec8
--- /dev/null
+++ b/src/Data/Database/Base.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace VSH.Data.Database;
+
+public class Base
+{
+ public Guid Id { get; set; }
+ public DateTime Created { get; set; }
+ public DateTime? Updated { get; set; }
+
+ public void SetBaseValues() {
+ Id = Guid.NewGuid();
+ Created = DateTime.UtcNow;
+ }
+} \ No newline at end of file
diff --git a/src/Data/Database/Category.cs b/src/Data/Database/Category.cs
new file mode 100644
index 0000000..6e0d324
--- /dev/null
+++ b/src/Data/Database/Category.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using IOL.Helpers;
+using VSH.Data.Enums;
+using VSH.Utilities;
+
+namespace VSH.Data.Database;
+
+public class Category : Base
+{
+ public Category(string name = default) {
+ if (name.IsNullOrWhiteSpace())
+ return;
+ Name = name;
+ Slug = name.Slugified();
+ }
+
+ public string Name { get; set; }
+ public string Slug { get; set; }
+ public CategoryVisibility VisibilityState { get; set; }
+ public ICollection<Product> Products { get; set; }
+ public bool Disabled => VisibilityState == CategoryVisibility.DISABLED;
+ public bool Deleted => VisibilityState == CategoryVisibility.DELETED;
+
+ public void Update(Category update = default) {
+ Name = update?.Name ?? Name;
+ VisibilityState = update?.VisibilityState ?? VisibilityState;
+ Updated = DateTime.UtcNow;
+ }
+} \ No newline at end of file
diff --git a/src/Data/Database/Document.cs b/src/Data/Database/Document.cs
new file mode 100644
index 0000000..2ae40d6
--- /dev/null
+++ b/src/Data/Database/Document.cs
@@ -0,0 +1,9 @@
+using VSH.Data.Enums;
+
+namespace VSH.Data.Database;
+
+public class Document : Base
+{
+ public DocumentType Type { get; set; }
+ public string Content { get; set; }
+} \ No newline at end of file
diff --git a/src/Data/Database/Order.cs b/src/Data/Database/Order.cs
new file mode 100644
index 0000000..02869b2
--- /dev/null
+++ b/src/Data/Database/Order.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using IOL.Helpers;
+using IOL.VippsEcommerce.Models.Api;
+using VSH.Data.Enums;
+using VSH.Utilities;
+
+namespace VSH.Data.Database;
+
+public class Order : Base
+{
+ public string Comment { get; set; }
+ public string OrderReference { get; set; }
+ public OrderStatus Status { get; set; }
+ public OrderPaymentType PaymentType { get; set; }
+ public string VippsTransactionId { get; set; }
+ public ETransactionStatus VippsTransactionStatus { get; set; }
+ public EStatusEnum VippsStatus { get; set; }
+ public ContactInformation ContactInfo { get; set; }
+ public List<OrderProduct> Products { get; set; }
+
+ public class ContactInformation
+ {
+ public string Name { get; set; }
+ public string PhoneNumber { get; set; }
+ public string EmailAddress { get; set; }
+ }
+
+ public decimal Total(bool includeTax = true) {
+ if (!Products.Any())
+ return default;
+ var total = Products.Sum(product => product.PriceAtTimeOfOrder * product.NumberOfItems);
+ if (includeTax)
+ return total;
+ var taxCut = total * 25 / 100;
+ return total - taxCut;
+ }
+
+ public decimal Tax(bool includeTax = true) {
+ if (!Products.Any())
+ return default;
+ var total = Products.Sum(product => product.PriceAtTimeOfOrder * product.NumberOfItems);
+ var taxCut = total * 25 / 100;
+ return taxCut;
+ }
+
+ public string GetAdminMailContent(string hostname) => @$"
+Referanse: {OrderReference}
+Lenke: {hostname}/kontoret/bestillinger?order={OrderReference}
+Betalingsmetode: {EnumName.ForPaymentType(PaymentType)}
+Status: {EnumName.ForOrderStatus(Status)}
+Navn: {ContactInfo.Name}
+E-postadresse: {ContactInfo.EmailAddress}
+Telefon: {ContactInfo.PhoneNumber}
+{(Comment.HasValue() ? "Kommentar:" + Comment : "")}
+";
+
+ public string GetCustomerMailContent(string hostname) => @$"
+Hei {ContactInfo.Name}, tusen takk for din bestilling!
+Gå til {hostname}/status/{OrderReference} for å få full oversikt over bestillinga.
+
+Hvis du har spørsmål angående din ordre er det bare å svare på denne mailen så svarer vi så raskt vi kan.
+
+Med venleg hilsen
+
+Øystein og Bodil
+Vinjesvingen Handel AS
+915 61 900
+";
+}
+
+public class OrderProduct
+{
+ public Guid Id { get; set; }
+
+ public int NumberOfItems { get; set; }
+
+ public decimal PriceAtTimeOfOrder { get; set; }
+
+ public decimal Total(bool includeTax = true) {
+ if (NumberOfItems == default || PriceAtTimeOfOrder == default)
+ return default;
+ var total = PriceAtTimeOfOrder * NumberOfItems;
+ if (includeTax)
+ return total;
+ var taxCut = total * 25 / 100;
+ return total - taxCut;
+ }
+
+ public decimal Tax(bool includeTax = true) {
+ if (NumberOfItems == default || PriceAtTimeOfOrder == default)
+ return default;
+ var total = PriceAtTimeOfOrder * NumberOfItems;
+ var taxCut = total * 25 / 100;
+ return taxCut;
+ }
+} \ No newline at end of file
diff --git a/src/Data/Database/Product.cs b/src/Data/Database/Product.cs
new file mode 100644
index 0000000..d8a3d8b
--- /dev/null
+++ b/src/Data/Database/Product.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using VSH.Data.Enums;
+using VSH.Data.Miscellaneous;
+using VSH.Data.Static;
+using VSH.Utilities;
+
+namespace VSH.Data.Database;
+
+public class Product : Base
+{
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public decimal Price { get; set; }
+ public PriceSuffix PriceSuffix { get; set; }
+ public ProductVisibility VisibilityState { get; set; }
+ public Category Category { get; set; }
+ public List<ProductImage> Images { get; set; }
+ public int Count { get; set; }
+ public string Slug { get; set; }
+ public bool ShowOnFrontpage { get; set; }
+
+ public string ReadablePriceSuffix => EnumName.ForPriceSuffix(PriceSuffix);
+
+ public AppPath GetPrimaryImage() {
+ var productImage = Images.OrderBy(c => c.Order).FirstOrDefault();
+ return productImage != default ? productImage.GetPath() : AppPaths.DefaultProductImage;
+ }
+
+ public string WebPath() => $"/produktar/{Category?.Slug}/{Slug}";
+
+ public bool IsVisible => VisibilityState == ProductVisibility.DEFAULT;
+ public bool IsAvailable => Count == -1 || Count >= 1;
+}
+
+public class ProductImage
+{
+ public ProductImage(string fileName = default, int order = default) {
+ FileName = fileName;
+ Order = order;
+ }
+
+ public int Order { get; }
+ public string FileName { get; }
+
+ public AppPath GetPath() {
+ if (FileName == default) return AppPaths.DefaultProductImage;
+ return new AppPath {
+ WebPath = Path.Combine(AppPaths.ProductImages.WebPath, FileName),
+ HostPath = Path.Combine(AppPaths.ProductImages.HostPath, FileName),
+ };
+ }
+} \ No newline at end of file
diff --git a/src/Data/Database/User.cs b/src/Data/Database/User.cs
new file mode 100644
index 0000000..f8b083d
--- /dev/null
+++ b/src/Data/Database/User.cs
@@ -0,0 +1,18 @@
+using IOL.Helpers;
+
+namespace VSH.Data.Database;
+
+public class User : Base
+{
+ public User(string username) => Username = username;
+ public string Username { get; set; }
+ public string Password { get; set; }
+
+ public void HashAndSetPassword(string password) {
+ Password = PasswordHelper.HashPassword(password);
+ }
+
+ public bool VerifyPassword(string password) {
+ return PasswordHelper.Verify(password, Password);
+ }
+} \ No newline at end of file
diff --git a/src/Data/Dtos/AdminViewOrderDto.cs b/src/Data/Dtos/AdminViewOrderDto.cs
new file mode 100644
index 0000000..846b503
--- /dev/null
+++ b/src/Data/Dtos/AdminViewOrderDto.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using VSH.Data.Database;
+using VSH.Data.Enums;
+
+namespace VSH.Data.Dtos;
+
+public class AdminViewOrderDto
+{
+ public Guid Id { get; set; }
+ public Order.ContactInformation ContactInformation { get; set; }
+ public List<OrderDetailProduct> Products { get; set; }
+ public string OrderReference { get; set; }
+ public DateTime OrderDate { get; set; }
+ public OrderPaymentType PaymentType { get; set; }
+ public OrderStatus Status { get; set; }
+ public string VippsStatus { get; set; }
+ public string VippsId { get; set; }
+ public string VippsTransactionStatus { get; set; }
+ public string Comment { get; set; }
+
+ public class OrderDetailProduct
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ public int Count { get; set; }
+ public decimal PayedPrice { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Data/Enums/AnalyticType.cs b/src/Data/Enums/AnalyticType.cs
new file mode 100644
index 0000000..0ec8b6b
--- /dev/null
+++ b/src/Data/Enums/AnalyticType.cs
@@ -0,0 +1,7 @@
+namespace VSH.Data.Enums;
+
+public enum AnalyticType
+{
+ PAGE_LOAD,
+ ERROR
+} \ No newline at end of file
diff --git a/src/Data/Enums/CategoryVisibility.cs b/src/Data/Enums/CategoryVisibility.cs
new file mode 100644
index 0000000..b69ee56
--- /dev/null
+++ b/src/Data/Enums/CategoryVisibility.cs
@@ -0,0 +1,8 @@
+namespace VSH.Data.Enums;
+
+public enum CategoryVisibility
+{
+ DEFAULT = 0,
+ DISABLED = 1,
+ DELETED = 2
+} \ No newline at end of file
diff --git a/src/Data/Enums/CookieType.cs b/src/Data/Enums/CookieType.cs
new file mode 100644
index 0000000..74f1742
--- /dev/null
+++ b/src/Data/Enums/CookieType.cs
@@ -0,0 +1,8 @@
+namespace VSH.Data.Enums;
+
+public enum CookieType
+{
+ XSRF = 0,
+ SESSION = 1,
+ CULTURE = 2
+} \ No newline at end of file
diff --git a/src/Data/Enums/DocumentType.cs b/src/Data/Enums/DocumentType.cs
new file mode 100644
index 0000000..084e80e
--- /dev/null
+++ b/src/Data/Enums/DocumentType.cs
@@ -0,0 +1,10 @@
+namespace VSH.Data.Enums;
+
+public enum DocumentType
+{
+ PRIVACY_POLICY = 0,
+ SALES_TERMS = 1,
+ ABOUT_PAGE = 2,
+ CONTACT_PAGE = 3,
+ DEALERS_PAGE = 4,
+} \ No newline at end of file
diff --git a/src/Data/Enums/OrderAdminStatus.cs b/src/Data/Enums/OrderAdminStatus.cs
new file mode 100644
index 0000000..b3235ea
--- /dev/null
+++ b/src/Data/Enums/OrderAdminStatus.cs
@@ -0,0 +1,9 @@
+namespace VSH.Data.Enums;
+
+public enum OrderAdminStatus
+{
+ VIPPS_CAPTURED = 0,
+ INVOICE_SENT = 1,
+ PAYED = 2,
+ IN_PROGESS = 3,
+} \ No newline at end of file
diff --git a/src/Data/Enums/OrderPaymentType.cs b/src/Data/Enums/OrderPaymentType.cs
new file mode 100644
index 0000000..73df3b9
--- /dev/null
+++ b/src/Data/Enums/OrderPaymentType.cs
@@ -0,0 +1,7 @@
+namespace VSH.Data.Enums;
+
+public enum OrderPaymentType
+{
+ VIPPS = 0,
+ INVOICE_BY_EMAIL = 1
+} \ No newline at end of file
diff --git a/src/Data/Enums/OrderStatus.cs b/src/Data/Enums/OrderStatus.cs
new file mode 100644
index 0000000..9baee22
--- /dev/null
+++ b/src/Data/Enums/OrderStatus.cs
@@ -0,0 +1,11 @@
+namespace VSH.Data.Enums;
+
+public enum OrderStatus
+{
+ IN_PROGRESS = 0,
+ CANCELLED = 1,
+ FAILED = 2,
+ COMPLETED = 3,
+ AWAITING_INVOICE = 4,
+ AWAITING_VIPPS = 5,
+} \ No newline at end of file
diff --git a/src/Data/Enums/PriceSuffix.cs b/src/Data/Enums/PriceSuffix.cs
new file mode 100644
index 0000000..c421cd2
--- /dev/null
+++ b/src/Data/Enums/PriceSuffix.cs
@@ -0,0 +1,7 @@
+namespace VSH.Data.Enums;
+
+public enum PriceSuffix
+{
+ PER = 0,
+ KILOS = 1
+} \ No newline at end of file
diff --git a/src/Data/Enums/ProductVisibility.cs b/src/Data/Enums/ProductVisibility.cs
new file mode 100644
index 0000000..41ecc4d
--- /dev/null
+++ b/src/Data/Enums/ProductVisibility.cs
@@ -0,0 +1,8 @@
+namespace VSH.Data.Enums;
+
+public enum ProductVisibility
+{
+ DEFAULT = 0,
+ DISABLED = 1,
+ DELETED = 2
+} \ No newline at end of file
diff --git a/src/Data/MainDbContext.cs b/src/Data/MainDbContext.cs
new file mode 100644
index 0000000..c95a4d0
--- /dev/null
+++ b/src/Data/MainDbContext.cs
@@ -0,0 +1,40 @@
+using Microsoft.EntityFrameworkCore;
+using VSH.Data.Database;
+
+namespace VSH.Data;
+
+public class MainDbContext : DbContext
+{
+ public MainDbContext(DbContextOptions<MainDbContext> options) : base(options) { }
+
+ public DbSet<User> Users { get; set; }
+ public DbSet<Category> Categories { get; set; }
+ public DbSet<Product> Products { get; set; }
+ public DbSet<Order> Orders { get; set; }
+ public DbSet<Document> Documents { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder) {
+ modelBuilder.Entity<User>(e => {
+ e.ToTable("Users");
+ });
+ modelBuilder.Entity<Category>(e => {
+ e.ToTable("Categories");
+ });
+ modelBuilder.Entity<Order>(e => {
+ e.Property(c => c.Products).HasColumnType("jsonb");
+ e.Property(c => c.ContactInfo).HasColumnType("jsonb");
+ e.ToTable("Orders");
+ });
+
+ modelBuilder.Entity<Product>(e => {
+ e.Property(c => c.Images).HasColumnType("jsonb");
+ e.ToTable("Products");
+ });
+
+ modelBuilder.Entity<Document>(e => {
+ e.ToTable("Documents");
+ });
+
+ base.OnModelCreating(modelBuilder);
+ }
+} \ No newline at end of file
diff --git a/src/Data/Miscellaneous/AppCookie.cs b/src/Data/Miscellaneous/AppCookie.cs
new file mode 100644
index 0000000..f355bb6
--- /dev/null
+++ b/src/Data/Miscellaneous/AppCookie.cs
@@ -0,0 +1,8 @@
+namespace VSH.Data.Miscellaneous;
+
+public sealed record AppCookie
+{
+ public string Name { get; init; }
+ public string Description { get; init; }
+ public bool Required { get; init; }
+} \ No newline at end of file
diff --git a/src/Data/Miscellaneous/AppPath.cs b/src/Data/Miscellaneous/AppPath.cs
new file mode 100644
index 0000000..4e17a88
--- /dev/null
+++ b/src/Data/Miscellaneous/AppPath.cs
@@ -0,0 +1,7 @@
+namespace VSH.Data.Miscellaneous;
+
+public sealed record AppPath
+{
+ public string HostPath { get; init; }
+ public string WebPath { get; init; }
+} \ No newline at end of file
diff --git a/src/Data/Miscellaneous/AppSettings.cs b/src/Data/Miscellaneous/AppSettings.cs
new file mode 100644
index 0000000..048c67c
--- /dev/null
+++ b/src/Data/Miscellaneous/AppSettings.cs
@@ -0,0 +1,118 @@
+using System.Text.Json.Serialization;
+
+namespace VSH.Data.Miscellaneous;
+
+public class AppSettings
+{
+ [JsonPropertyName("General")]
+ public GeneralConfiguration General { get; set; }
+
+ [JsonPropertyName("Serilog")]
+ public SerilogConfiguration Serilog { get; set; }
+
+ public class GeneralConfiguration
+ {
+ public const string Name = "General";
+
+ [JsonPropertyName("StoreName")]
+ public string StoreName { get; set; }
+
+ [JsonPropertyName("LegalName")]
+ public string LegalName { get; set; }
+
+ [JsonPropertyName("ShortStoreName")]
+ public string ShortStoreName { get; set; }
+
+ [JsonPropertyName("DefaultCulture")]
+ public string DefaultCulture { get; set; }
+
+ [JsonPropertyName("GoogleMapsUrl")]
+ public string GoogleMapsUrl { get; set; }
+
+ [JsonPropertyName("DefaultDescription")]
+ public string DefaultDescription { get; set; }
+ }
+
+ public class SerilogConfiguration
+ {
+ public const string Name = "Serilog";
+
+ [JsonPropertyName("Using")]
+ public string[] Using { get; set; }
+
+ [JsonPropertyName("MinimumLevel")]
+ public MinimumLevel MinimumLevel { get; set; }
+
+ [JsonPropertyName("WriteTo")]
+ public WriteTo[] WriteTo { get; set; }
+
+ [JsonPropertyName("Enrich")]
+ public string[] Enrich { get; set; }
+
+ [JsonPropertyName("Destructure")]
+ public Destructure[] Destructure { get; set; }
+
+ [JsonPropertyName("Properties")]
+ public Properties Properties { get; set; }
+ }
+
+ public class Destructure
+ {
+ [JsonPropertyName("Name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("Args")]
+ public Args Args { get; set; }
+ }
+
+ public class Args
+ {
+ [JsonPropertyName("maximumDestructuringDepth")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public long? MaximumDestructuringDepth { get; set; }
+
+ [JsonPropertyName("maximumStringLength")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public long? MaximumStringLength { get; set; }
+
+ [JsonPropertyName("maximumCollectionCount")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public long? MaximumCollectionCount { get; set; }
+ }
+
+ public class MinimumLevel
+ {
+ [JsonPropertyName("Default")]
+ public string Default { get; set; }
+
+ [JsonPropertyName("Override")]
+ public Override Override { get; set; }
+ }
+
+ public class Override
+ {
+ [JsonPropertyName("Microsoft")]
+ public string Microsoft { get; set; }
+
+ [JsonPropertyName("System")]
+ public string System { get; set; }
+
+ [JsonPropertyName("Microsoft.AspNetCore")]
+ public string MicrosoftAspNetCore { get; set; }
+
+ [JsonPropertyName("Microsoft.Hosting")]
+ public string MicrosoftHosting { get; set; }
+ }
+
+ public class Properties
+ {
+ [JsonPropertyName("Application")]
+ public string Application { get; set; }
+ }
+
+ public class WriteTo
+ {
+ [JsonPropertyName("Name")]
+ public string Name { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Data/Miscellaneous/OpenGraphData.cs b/src/Data/Miscellaneous/OpenGraphData.cs
new file mode 100644
index 0000000..5bf33fe
--- /dev/null
+++ b/src/Data/Miscellaneous/OpenGraphData.cs
@@ -0,0 +1,12 @@
+namespace VSH.Data.Miscellaneous;
+
+public class OpenGraphData
+{
+ public string Type { get; set; }
+ public string Locale { get; set; }
+ public string Url { get; set; }
+ public string Title { get; set; }
+ public string Description { get; set; }
+ public string SiteName { get; set; }
+ public string Image { get; set; }
+} \ No newline at end of file
diff --git a/src/Data/Payloads/CreateProductDto.cs b/src/Data/Payloads/CreateProductDto.cs
new file mode 100644
index 0000000..eada16e
--- /dev/null
+++ b/src/Data/Payloads/CreateProductDto.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using VSH.Data.Enums;
+
+namespace VSH.Data.Payloads;
+
+public class CreateProductDto
+{
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public double Price { get; set; }
+ public PriceSuffix PriceSuffix { get; set; }
+ public Guid CategoryId { get; set; }
+ public List<TProductImage> Images { get; set; }
+ public int Count { get; set; }
+
+ public class TProductImage
+ {
+ public string FileName { get; set; }
+ public int Order { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Data/Payloads/LoginPayload.cs b/src/Data/Payloads/LoginPayload.cs
new file mode 100644
index 0000000..8e4f461
--- /dev/null
+++ b/src/Data/Payloads/LoginPayload.cs
@@ -0,0 +1,8 @@
+namespace VSH.Data.Payloads;
+
+public record LoginPayload
+{
+ public string Username { get; set; }
+ public string Password { get; set; }
+ public bool Persist { get; set; }
+} \ No newline at end of file
diff --git a/src/Data/Payloads/UpdatePasswordPayload.cs b/src/Data/Payloads/UpdatePasswordPayload.cs
new file mode 100644
index 0000000..4105eb4
--- /dev/null
+++ b/src/Data/Payloads/UpdatePasswordPayload.cs
@@ -0,0 +1,6 @@
+namespace VSH.Data.Payloads;
+
+public class UpdatePasswordPayload
+{
+ public string NewPassword { get; set; }
+} \ No newline at end of file
diff --git a/src/Data/Payloads/ValidateOrderPayload.cs b/src/Data/Payloads/ValidateOrderPayload.cs
new file mode 100644
index 0000000..d3aee82
--- /dev/null
+++ b/src/Data/Payloads/ValidateOrderPayload.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+
+namespace VSH.Data.Payloads;
+
+public record ValidateOrderPayload
+{
+ public List<ProductValidationDto> Products { get; set; }
+
+ public class ProductValidationDto
+ {
+ public Guid Id { get; set; }
+ public int Count { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Data/Results/AppValidationResult.cs b/src/Data/Results/AppValidationResult.cs
new file mode 100644
index 0000000..0b1c967
--- /dev/null
+++ b/src/Data/Results/AppValidationResult.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace VSH.Data.Results;
+
+public class AppValidationResult
+{
+ public AppValidationResult() {
+ Errors = new List<ValidationError>();
+ }
+
+ public bool IsValid => !Errors.Any();
+ public List<ValidationError> Errors { get; set; }
+
+
+ public class ValidationError
+ {
+ public ValidationError(Guid id = default) {
+ Id = id != default ? id : null;
+ Errors = new List<string>();
+ }
+
+ public Guid? Id { get; set; }
+ public List<string> Errors { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Data/Results/ErrorResult.cs b/src/Data/Results/ErrorResult.cs
new file mode 100644
index 0000000..60d1b1d
--- /dev/null
+++ b/src/Data/Results/ErrorResult.cs
@@ -0,0 +1,12 @@
+namespace VSH.Data.Results;
+
+public class ErrorResult
+{
+ public ErrorResult(string title = default, string message = default) {
+ Title = title;
+ Message = message;
+ }
+
+ public string Title { get; set; }
+ public string Message { get; set; }
+} \ No newline at end of file
diff --git a/src/Data/Static/AppCookies.cs b/src/Data/Static/AppCookies.cs
new file mode 100644
index 0000000..711d59e
--- /dev/null
+++ b/src/Data/Static/AppCookies.cs
@@ -0,0 +1,21 @@
+using VSH.Data.Miscellaneous;
+
+namespace VSH.Data.Static;
+
+public static class AppCookies
+{
+ public static AppCookie Xsrf => new() {
+ Name = "vsh_xsrf",
+ Required = true
+ };
+
+ public static AppCookie Session => new() {
+ Name = "vsh_session",
+ Required = true
+ };
+
+ public static AppCookie Culture => new() {
+ Name = "vsh_culture",
+ Required = false
+ };
+} \ No newline at end of file
diff --git a/src/Data/Static/AppPaths.cs b/src/Data/Static/AppPaths.cs
new file mode 100644
index 0000000..4073a75
--- /dev/null
+++ b/src/Data/Static/AppPaths.cs
@@ -0,0 +1,40 @@
+using System.IO;
+using VSH.Data.Miscellaneous;
+
+namespace VSH.Data.Static;
+
+public static class AppPaths
+{
+ public static AppPath WwwRoot => new() {
+ HostPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"),
+ WebPath = "/"
+ };
+
+ public static AppPath Assets => new() {
+ HostPath = Path.Combine(WwwRoot.HostPath, "assets"),
+ WebPath = "/assets"
+ };
+
+ public static AppPath ProductImages => new() {
+ HostPath = Path.Combine(Assets.HostPath, "images", "products"),
+ WebPath = Path.Combine(Assets.WebPath, "images", "products")
+ };
+
+ public static AppPath DocumentImages => new() {
+ HostPath = Path.Combine(Assets.HostPath, "images", "documents"),
+ WebPath = Path.Combine(Assets.WebPath, "images", "documents")
+ };
+
+ public static AppPath DataProtectionKeys => new() {
+ HostPath = Path.Combine(AppData.HostPath, "DPKeys"),
+ };
+
+ public static AppPath AppData => new() {
+ HostPath = Path.Combine(Directory.GetCurrentDirectory(), "AppData"),
+ };
+
+ public static AppPath DefaultProductImage => new() {
+ HostPath = Path.Combine(Assets.HostPath, "profile", "innrammet.svg"),
+ WebPath = Path.Combine(Assets.WebPath, "profile", "innrammet.svg")
+ };
+} \ No newline at end of file