Implemented groundwork for search configuration.
This commit is contained in:
parent
3129e5e564
commit
2719142538
@ -15,7 +15,7 @@ namespace Props.Shop.Adafruit.Api
|
|||||||
private Dictionary<string, List<ProductListing>> listings = new Dictionary<string, List<ProductListing>>();
|
private Dictionary<string, List<ProductListing>> listings = new Dictionary<string, List<ProductListing>>();
|
||||||
private bool requested = false;
|
private bool requested = false;
|
||||||
public DateTime TimeOfLastRequest { get; private set; }
|
public DateTime TimeOfLastRequest { get; private set; }
|
||||||
public bool RequestReady => DateTime.Now - TimeOfLastRequest > TimeSpan.FromMinutes(minutesPerRequest);
|
public bool RequestReady => !requested || DateTime.Now - TimeOfLastRequest > TimeSpan.FromMinutes(minutesPerRequest);
|
||||||
|
|
||||||
public ProductListingManager(int requestsPerMinute = 5)
|
public ProductListingManager(int requestsPerMinute = 5)
|
||||||
{
|
{
|
||||||
@ -27,10 +27,10 @@ namespace Props.Shop.Adafruit.Api
|
|||||||
requested = true;
|
requested = true;
|
||||||
TimeOfLastRequest = DateTime.Now;
|
TimeOfLastRequest = DateTime.Now;
|
||||||
HttpResponseMessage response = await http.GetAsync("/products");
|
HttpResponseMessage response = await http.GetAsync("/products");
|
||||||
SetListings(await response.Content.ReadAsStringAsync());
|
SetListingsData(await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetListings(string data)
|
public void SetListingsData(string data)
|
||||||
{
|
{
|
||||||
ListingsParser listingsParser = new ListingsParser(data);
|
ListingsParser listingsParser = new ListingsParser(data);
|
||||||
foreach (ProductListing listing in listingsParser.ProductListings)
|
foreach (ProductListing listing in listingsParser.ProductListings)
|
||||||
|
@ -25,7 +25,7 @@ namespace Props.Shop.Framework
|
|||||||
public int MinPurchases { get; set; }
|
public int MinPurchases { get; set; }
|
||||||
public bool KeepUnknownPurchaseCount { get; set; } = true;
|
public bool KeepUnknownPurchaseCount { get; set; } = true;
|
||||||
public int MinReviews { get; set; }
|
public int MinReviews { get; set; }
|
||||||
public bool KeepUnknownRatingCount { get; set; } = true;
|
public bool KeepUnknownReviewCount { get; set; } = true;
|
||||||
public bool EnableMaxShippingFee { get; set; }
|
public bool EnableMaxShippingFee { get; set; }
|
||||||
private int maxShippingFee;
|
private int maxShippingFee;
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ namespace Props.Shop.Framework
|
|||||||
MinPurchases == other.MinPurchases &&
|
MinPurchases == other.MinPurchases &&
|
||||||
KeepUnknownPurchaseCount == other.KeepUnknownPurchaseCount &&
|
KeepUnknownPurchaseCount == other.KeepUnknownPurchaseCount &&
|
||||||
MinReviews == other.MinReviews &&
|
MinReviews == other.MinReviews &&
|
||||||
KeepUnknownRatingCount == other.KeepUnknownRatingCount &&
|
KeepUnknownReviewCount == other.KeepUnknownReviewCount &&
|
||||||
EnableMaxShippingFee == other.EnableMaxShippingFee &&
|
EnableMaxShippingFee == other.EnableMaxShippingFee &&
|
||||||
MaxShippingFee == other.MaxShippingFee &&
|
MaxShippingFee == other.MaxShippingFee &&
|
||||||
KeepUnknownShipping == other.KeepUnknownShipping;
|
KeepUnknownShipping == other.KeepUnknownShipping;
|
||||||
@ -81,5 +81,16 @@ namespace Props.Shop.Framework
|
|||||||
{
|
{
|
||||||
return (Filters)this.MemberwiseClone();
|
return (Filters)this.MemberwiseClone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Validate(ProductListing listing)
|
||||||
|
{
|
||||||
|
if (listing.Shipping == null && !KeepUnknownShipping || (EnableMaxShippingFee && listing.Shipping > MaxShippingFee)) return false;
|
||||||
|
float shippingDifference = listing.Shipping != null ? listing.Shipping.Value : 0;
|
||||||
|
if (!(listing.LowerPrice + shippingDifference >= LowerPrice && (!EnableUpperPrice || listing.UpperPrice + shippingDifference <= UpperPrice))) return false;
|
||||||
|
if ((listing.Rating == null && !KeepUnrated) && MinRating > (listing.Rating == null ? 0 : listing.Rating)) return false;
|
||||||
|
if ((listing.PurchaseCount == null && !KeepUnknownPurchaseCount) || MinPurchases > (listing.PurchaseCount == null ? 0 : listing.PurchaseCount)) return false;
|
||||||
|
if ((listing.ReviewCount == null && !KeepUnknownReviewCount) || MinReviews > (listing.ReviewCount == null ? 0 : listing.ReviewCount)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ namespace Props.Shop.Adafruit.Tests.Api
|
|||||||
public async Task TestSearch()
|
public async Task TestSearch()
|
||||||
{
|
{
|
||||||
ProductListingManager mockProductListingManager = new ProductListingManager();
|
ProductListingManager mockProductListingManager = new ProductListingManager();
|
||||||
mockProductListingManager.SetListings(File.ReadAllText("./Assets/products.json"));
|
mockProductListingManager.SetListingsData(File.ReadAllText("./Assets/products.json"));
|
||||||
List<ProductListing> results = new List<ProductListing>();
|
List<ProductListing> results = new List<ProductListing>();
|
||||||
await foreach (ProductListing item in mockProductListingManager.Search("arduino", 0.5f))
|
await foreach (ProductListing item in mockProductListingManager.Search("arduino", 0.5f))
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ module.exports = {
|
|||||||
"node": true,
|
"node": true,
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"parser": "babel-eslint",
|
"parser": "@babel/eslint-parser",
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
@ -22,6 +22,10 @@ module.exports = {
|
|||||||
"always"
|
"always"
|
||||||
],
|
],
|
||||||
"comma-dangle": ["error", "only-multiline"],
|
"comma-dangle": ["error", "only-multiline"],
|
||||||
"space-before-function-paren": ["error", "never"]
|
"space-before-function-paren": ["error", {
|
||||||
|
"anonymous": "always",
|
||||||
|
"named": "never",
|
||||||
|
"asyncArrow": "always"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,8 +6,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div class="flex-grow-1 d-flex flex-column justify-content-center">
|
<div class="flex-grow-1 d-flex flex-column justify-content-center">
|
||||||
<div class="jumbotron border-top border-bottom">
|
<div class="jumbotron sole d-flex flex-column align-content-center">
|
||||||
<h1 class="mx-auto mt-3 mb-4 text-center">@ViewData["Title"]</h1>
|
<img alt="Props logo" src="~/images/logo-simplified.svg" class="img-fluid" style="max-height: 180px;" asp-append-version="true" />
|
||||||
|
<h1 class="mt-3 mb-4 text-center">@ViewData["Title"]</h1>
|
||||||
<div class="my-3 row justify-content-md-center">
|
<div class="my-3 row justify-content-md-center">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<form id="account" method="post">
|
<form id="account" method="post">
|
||||||
|
@ -13,3 +13,5 @@
|
|||||||
<li class="nav-item"><a class="nav-link @ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)" id="two-factor" asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
|
<li class="nav-item"><a class="nav-link @ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)" id="two-factor" asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
|
||||||
<li class="nav-item"><a class="nav-link @ManageNavPages.PersonalDataNavClass(ViewContext)" id="personal-data" asp-page="./PersonalData">Personal data</a></li>
|
<li class="nav-item"><a class="nav-link @ManageNavPages.PersonalDataNavClass(ViewContext)" id="personal-data" asp-page="./PersonalData">Personal data</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@* TODO: Finish styling this page. *@
|
@ -5,8 +5,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div class="flex-grow-1 d-flex flex-column justify-content-center">
|
<div class="flex-grow-1 d-flex flex-column justify-content-center">
|
||||||
<div class="jumbotron border-top border-bottom">
|
<div class="jumbotron sole d-flex flex-column align-content-center">
|
||||||
<h1 class="mx-auto mt-3 mb-4 text-center">@ViewData["Title"]</h1>
|
<img alt="Props logo" src="~/images/logo-simplified.svg" class="img-fluid" style="max-height: 180px;" asp-append-version="true" />
|
||||||
|
<h1 class="mt-3 mb-4 text-center">@ViewData["Title"]</h1>
|
||||||
<div class="my-3 row justify-content-md-center">
|
<div class="my-3 row justify-content-md-center">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
|
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
|
||||||
|
@ -20,4 +20,4 @@
|
|||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@* TODO: https://aka.ms/aspaccountconf *@
|
@* TODO: Do something about this. *@
|
41
Props/Controllers/SearchController.cs
Normal file
41
Props/Controllers/SearchController.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Props.Models.Search;
|
||||||
|
using Props.Services.Modules;
|
||||||
|
|
||||||
|
namespace Props.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[Controller]")]
|
||||||
|
public class SearchController : ControllerBase
|
||||||
|
{
|
||||||
|
private SearchOutline defaultOutline = new SearchOutline();
|
||||||
|
IShopManager shopManager;
|
||||||
|
|
||||||
|
public SearchController(IShopManager shopManager)
|
||||||
|
{
|
||||||
|
this.shopManager = shopManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Shops/Available")]
|
||||||
|
public IActionResult GetAvailableShops()
|
||||||
|
{
|
||||||
|
return Ok(shopManager.AvailableShops());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Default/Outline/Filters")]
|
||||||
|
public IActionResult GetDefaultFilters()
|
||||||
|
{
|
||||||
|
return Ok(defaultOutline.Filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Default/Outline/DisabledShops")]
|
||||||
|
public IActionResult GetDefaultDisabledShops()
|
||||||
|
{
|
||||||
|
return Ok(defaultOutline.Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
using Props.Models;
|
using Props.Models;
|
||||||
|
using Props.Models.Search;
|
||||||
using Props.Models.User;
|
using Props.Models.User;
|
||||||
using Props.Shop.Framework;
|
using Props.Shop.Framework;
|
||||||
|
|
||||||
@ -15,6 +16,8 @@ namespace Props.Data
|
|||||||
{
|
{
|
||||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||||
{
|
{
|
||||||
|
DbSet<SearchOutline> SearchOutlines { get; set; }
|
||||||
|
DbSet<ProductListingInfo> TrackedListings { get; set; }
|
||||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||||
: base(options)
|
: base(options)
|
||||||
{
|
{
|
||||||
@ -37,11 +40,11 @@ namespace Props.Data
|
|||||||
);
|
);
|
||||||
|
|
||||||
modelBuilder.Entity<SearchOutline>()
|
modelBuilder.Entity<SearchOutline>()
|
||||||
.Property(e => e.ShopStates)
|
.Property(e => e.Disabled)
|
||||||
.HasConversion(
|
.HasConversion(
|
||||||
v => JsonSerializer.Serialize(v, null),
|
v => JsonSerializer.Serialize(v, null),
|
||||||
v => JsonSerializer.Deserialize<SearchOutline.ShopToggler>(v, null),
|
v => JsonSerializer.Deserialize<SearchOutline.ShopsDisabled>(v, null),
|
||||||
new ValueComparer<SearchOutline.ShopToggler>(
|
new ValueComparer<SearchOutline.ShopsDisabled>(
|
||||||
(a, b) => a.Equals(b),
|
(a, b) => a.Equals(b),
|
||||||
c => c.GetHashCode(),
|
c => c.GetHashCode(),
|
||||||
c => c.Copy()
|
c => c.Copy()
|
||||||
|
@ -9,7 +9,7 @@ using Props.Data;
|
|||||||
namespace Props.Data.Migrations
|
namespace Props.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
[Migration("20210721064503_InitialCreate")]
|
[Migration("20210722180024_InitialCreate")]
|
||||||
partial class InitialCreate
|
partial class InitialCreate
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@ -150,7 +150,80 @@ namespace Props.Data.Migrations
|
|||||||
b.ToTable("AspNetUserTokens");
|
b.ToTable("AspNetUserTokens");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ApplicationUser", b =>
|
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ApplicationUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Order")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProfileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApplicationUserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ResultsPreferences");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Props.Models.Search.ProductListingInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("Hits")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProductName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProductUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("TrackedListings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Props.Models.Search.SearchOutline", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ApplicationUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Disabled")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Filters")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApplicationUserId");
|
||||||
|
|
||||||
|
b.ToTable("SearchOutlines");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Props.Models.User.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
@ -214,56 +287,6 @@ namespace Props.Data.Migrations
|
|||||||
b.ToTable("AspNetUsers");
|
b.ToTable("AspNetUsers");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("ApplicationUserId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Order")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ApplicationUserId")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("ResultsPreferences");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.SearchOutline", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("ApplicationUserId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Filters")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<int>("MaxResults")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("ShopStates")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ApplicationUserId")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("SearchOutline");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -299,7 +322,7 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -308,7 +331,7 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -323,7 +346,7 @@ namespace Props.Data.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -332,7 +355,7 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -341,32 +364,38 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", "ApplicationUser")
|
||||||
.WithOne("ResultsPreferences")
|
.WithOne("ResultsPreferences")
|
||||||
.HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId")
|
.HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ApplicationUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.SearchOutline", b =>
|
modelBuilder.Entity("Props.Models.Search.SearchOutline", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", "ApplicationUser")
|
||||||
.WithOne("SearchOutline")
|
.WithMany("SearchOutlines")
|
||||||
.HasForeignKey("Props.Models.SearchOutline", "ApplicationUserId")
|
.HasForeignKey("ApplicationUserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ApplicationUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", "ApplicationUser")
|
||||||
.WithOne("ApplicationPreferences")
|
.WithOne("ApplicationPreferences")
|
||||||
.HasForeignKey("Props.Shared.Models.User.ApplicationPreferences", "ApplicationUserId")
|
.HasForeignKey("Props.Shared.Models.User.ApplicationPreferences", "ApplicationUserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ApplicationUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ApplicationUser", b =>
|
modelBuilder.Entity("Props.Models.User.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("ApplicationPreferences")
|
b.Navigation("ApplicationPreferences")
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
@ -374,8 +403,7 @@ namespace Props.Data.Migrations
|
|||||||
b.Navigation("ResultsPreferences")
|
b.Navigation("ResultsPreferences")
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("SearchOutline")
|
b.Navigation("SearchOutlines");
|
||||||
.IsRequired();
|
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
@ -46,6 +46,22 @@ namespace Props.Data.Migrations
|
|||||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TrackedListings",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Hits = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||||
|
LastUpdated = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
ProductUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ProductName = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TrackedListings", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "AspNetRoleClaims",
|
name: "AspNetRoleClaims",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@ -180,7 +196,8 @@ namespace Props.Data.Migrations
|
|||||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
|
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Order = table.Column<string>(type: "TEXT", nullable: false)
|
Order = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
ProfileName = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@ -194,21 +211,20 @@ namespace Props.Data.Migrations
|
|||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "SearchOutline",
|
name: "SearchOutlines",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
|
ApplicationUserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Filters = table.Column<string>(type: "TEXT", nullable: true),
|
Filters = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
MaxResults = table.Column<int>(type: "INTEGER", nullable: false),
|
Disabled = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
ShopStates = table.Column<string>(type: "TEXT", nullable: false)
|
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_SearchOutline", x => x.Id);
|
table.PrimaryKey("PK_SearchOutlines", x => x.Id);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_SearchOutline_AspNetUsers_ApplicationUserId",
|
name: "FK_SearchOutlines_AspNetUsers_ApplicationUserId",
|
||||||
column: x => x.ApplicationUserId,
|
column: x => x.ApplicationUserId,
|
||||||
principalTable: "AspNetUsers",
|
principalTable: "AspNetUsers",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
@ -265,10 +281,9 @@ namespace Props.Data.Migrations
|
|||||||
unique: true);
|
unique: true);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_SearchOutline_ApplicationUserId",
|
name: "IX_SearchOutlines_ApplicationUserId",
|
||||||
table: "SearchOutline",
|
table: "SearchOutlines",
|
||||||
column: "ApplicationUserId",
|
column: "ApplicationUserId");
|
||||||
unique: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
@ -295,7 +310,10 @@ namespace Props.Data.Migrations
|
|||||||
name: "ResultsPreferences");
|
name: "ResultsPreferences");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "SearchOutline");
|
name: "SearchOutlines");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TrackedListings");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "AspNetRoles");
|
name: "AspNetRoles");
|
@ -148,7 +148,80 @@ namespace Props.Data.Migrations
|
|||||||
b.ToTable("AspNetUserTokens");
|
b.ToTable("AspNetUserTokens");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ApplicationUser", b =>
|
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ApplicationUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Order")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProfileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApplicationUserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ResultsPreferences");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Props.Models.Search.ProductListingInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("Hits")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastUpdated")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProductName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProductUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("TrackedListings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Props.Models.Search.SearchOutline", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ApplicationUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Disabled")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Filters")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApplicationUserId");
|
||||||
|
|
||||||
|
b.ToTable("SearchOutlines");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Props.Models.User.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
@ -212,56 +285,6 @@ namespace Props.Data.Migrations
|
|||||||
b.ToTable("AspNetUsers");
|
b.ToTable("AspNetUsers");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("ApplicationUserId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Order")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ApplicationUserId")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("ResultsPreferences");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.SearchOutline", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("ApplicationUserId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Filters")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<int>("MaxResults")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("ShopStates")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ApplicationUserId")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("SearchOutline");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -297,7 +320,7 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -306,7 +329,7 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -321,7 +344,7 @@ namespace Props.Data.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -330,7 +353,7 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@ -339,32 +362,38 @@ namespace Props.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
modelBuilder.Entity("Props.Models.ResultsPreferences", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", "ApplicationUser")
|
||||||
.WithOne("ResultsPreferences")
|
.WithOne("ResultsPreferences")
|
||||||
.HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId")
|
.HasForeignKey("Props.Models.ResultsPreferences", "ApplicationUserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ApplicationUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.SearchOutline", b =>
|
modelBuilder.Entity("Props.Models.Search.SearchOutline", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", "ApplicationUser")
|
||||||
.WithOne("SearchOutline")
|
.WithMany("SearchOutlines")
|
||||||
.HasForeignKey("Props.Models.SearchOutline", "ApplicationUserId")
|
.HasForeignKey("ApplicationUserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ApplicationUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
modelBuilder.Entity("Props.Shared.Models.User.ApplicationPreferences", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Props.Models.ApplicationUser", null)
|
b.HasOne("Props.Models.User.ApplicationUser", "ApplicationUser")
|
||||||
.WithOne("ApplicationPreferences")
|
.WithOne("ApplicationPreferences")
|
||||||
.HasForeignKey("Props.Shared.Models.User.ApplicationPreferences", "ApplicationUserId")
|
.HasForeignKey("Props.Shared.Models.User.ApplicationPreferences", "ApplicationUserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ApplicationUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Props.Models.ApplicationUser", b =>
|
modelBuilder.Entity("Props.Models.User.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("ApplicationPreferences")
|
b.Navigation("ApplicationPreferences")
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
@ -372,8 +401,7 @@ namespace Props.Data.Migrations
|
|||||||
b.Navigation("ResultsPreferences")
|
b.Navigation("ResultsPreferences")
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("SearchOutline")
|
b.Navigation("SearchOutlines");
|
||||||
.IsRequired();
|
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
|
18
Props/Models/Search/ProductListingInfo.cs
Normal file
18
Props/Models/Search/ProductListingInfo.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using Props.Shop.Framework;
|
||||||
|
|
||||||
|
namespace Props.Models.Search
|
||||||
|
{
|
||||||
|
public class ProductListingInfo
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public uint Hits { get; set; }
|
||||||
|
|
||||||
|
public DateTime LastUpdated { get; set; }
|
||||||
|
|
||||||
|
public string ProductUrl { get; set; }
|
||||||
|
|
||||||
|
public string ProductName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -2,23 +2,27 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Props.Models.User;
|
||||||
using Props.Shop.Framework;
|
using Props.Shop.Framework;
|
||||||
|
|
||||||
namespace Props.Models
|
namespace Props.Models.Search
|
||||||
{
|
{
|
||||||
public class SearchOutline
|
public class SearchOutline
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string ApplicationUserId { get; set; }
|
public string ApplicationUserId { get; set; }
|
||||||
|
|
||||||
public Filters Filters { get; set; }
|
[Required]
|
||||||
public int MaxResults { get; set; } = 100;
|
public virtual ApplicationUser ApplicationUser { get; set; }
|
||||||
|
|
||||||
|
public Filters Filters { get; set; } = new Filters();
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public ShopToggler ShopStates { get; set; } = new ShopToggler();
|
public ShopsDisabled Disabled { get; set; } = new ShopsDisabled();
|
||||||
|
|
||||||
public sealed class ShopToggler : HashSet<string>
|
public sealed class ShopsDisabled : HashSet<string>
|
||||||
{
|
{
|
||||||
public int TotalShops { get; set; }
|
public int TotalShops { get; set; }
|
||||||
public bool this[string name]
|
public bool this[string name]
|
||||||
@ -41,9 +45,9 @@ namespace Props.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShopToggler Copy()
|
public ShopsDisabled Copy()
|
||||||
{
|
{
|
||||||
ShopToggler copy = new ShopToggler();
|
ShopsDisabled copy = new ShopsDisabled();
|
||||||
copy.Union(this);
|
copy.Union(this);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
@ -63,9 +67,8 @@ namespace Props.Models
|
|||||||
SearchOutline other = (SearchOutline)obj;
|
SearchOutline other = (SearchOutline)obj;
|
||||||
return
|
return
|
||||||
Id == other.Id &&
|
Id == other.Id &&
|
||||||
MaxResults == other.MaxResults &&
|
|
||||||
Filters.Equals(other.Filters) &&
|
Filters.Equals(other.Filters) &&
|
||||||
ShopStates.Equals(other.ShopStates);
|
Disabled.Equals(other.Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Props.Models.User;
|
||||||
|
|
||||||
namespace Props.Shared.Models.User
|
namespace Props.Shared.Models.User
|
||||||
{
|
{
|
||||||
@ -9,6 +10,9 @@ namespace Props.Shared.Models.User
|
|||||||
[Required]
|
[Required]
|
||||||
public string ApplicationUserId { get; set; }
|
public string ApplicationUserId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public virtual ApplicationUser ApplicationUser { get; set; }
|
||||||
|
|
||||||
public bool EnableSearchHistory { get; set; } = true;
|
public bool EnableSearchHistory { get; set; } = true;
|
||||||
public bool DarkMode { get; set; } = false;
|
public bool DarkMode { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
@ -4,30 +4,30 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Props.Models.Search;
|
||||||
using Props.Shared.Models.User;
|
using Props.Shared.Models.User;
|
||||||
|
|
||||||
namespace Props.Models.User
|
namespace Props.Models.User
|
||||||
{
|
{
|
||||||
public class ApplicationUser : IdentityUser
|
public class ApplicationUser : IdentityUser
|
||||||
{
|
{
|
||||||
[Required]
|
public virtual ISet<SearchOutline> SearchOutlines { get; set; }
|
||||||
public virtual SearchOutline SearchOutline { get; private set; }
|
|
||||||
[Required]
|
[Required]
|
||||||
public virtual ResultsPreferences ResultsPreferences { get; private set; }
|
public virtual ResultsPreferences ResultsPreferences { get; private set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public virtual ApplicationPreferences ApplicationPreferences { get; private set; }
|
public virtual ApplicationPreferences ApplicationPreferences { get; private set; }
|
||||||
|
|
||||||
|
// TODO: Write project system.
|
||||||
public ApplicationUser()
|
public ApplicationUser()
|
||||||
{
|
{
|
||||||
SearchOutline = new SearchOutline();
|
|
||||||
ResultsPreferences = new ResultsPreferences();
|
ResultsPreferences = new ResultsPreferences();
|
||||||
ApplicationPreferences = new ApplicationPreferences();
|
ApplicationPreferences = new ApplicationPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApplicationUser(SearchOutline searchOutline, ResultsPreferences resultsPreferences, ApplicationPreferences applicationPreferences)
|
public ApplicationUser(ResultsPreferences resultsPreferences, ApplicationPreferences applicationPreferences)
|
||||||
{
|
{
|
||||||
this.SearchOutline = searchOutline;
|
|
||||||
this.ResultsPreferences = resultsPreferences;
|
this.ResultsPreferences = resultsPreferences;
|
||||||
this.ApplicationPreferences = applicationPreferences;
|
this.ApplicationPreferences = applicationPreferences;
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,27 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Props.Models.User;
|
||||||
|
|
||||||
namespace Props.Models
|
namespace Props.Models
|
||||||
{
|
{
|
||||||
public class ResultsPreferences
|
public class ResultsPreferences
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string ApplicationUserId { get; set; }
|
public string ApplicationUserId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public virtual ApplicationUser ApplicationUser { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public IList<Category> Order { get; set; }
|
public IList<Category> Order { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string ProfileName { get; set; }
|
||||||
|
|
||||||
public ResultsPreferences()
|
public ResultsPreferences()
|
||||||
{
|
{
|
||||||
Order = new List<Category>(Enum.GetValues<Category>().Length);
|
Order = new List<Category>(Enum.GetValues<Category>().Length);
|
||||||
|
11
Props/Options/ModulesOptions.cs
Normal file
11
Props/Options/ModulesOptions.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Props.Options
|
||||||
|
{
|
||||||
|
public class ModulesOptions
|
||||||
|
{
|
||||||
|
public const string Modules = "Modules";
|
||||||
|
public string ShopsDir { get; set; }
|
||||||
|
public bool RecursiveLoad { get; set; }
|
||||||
|
public int MaxResults { get; set; }
|
||||||
|
public string ShopRegex { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<section class="jumbotron d-flex flex-column align-items-center">
|
<section class="jumbotron d-flex flex-column align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<img alt="Props logo" src="./images/logo.svg" class="img-fluid" style="max-height: 540px;" />
|
<img alt="Props logo" src="~/images/logo.svg" class="img-fluid" style="max-height: 540px;" asp-append-version="true" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center px-3 my-2 concise">
|
<div class="text-center px-3 my-2 concise">
|
||||||
<h1 class="my-2">Props</h1>
|
<h1 class="my-2">Props</h1>
|
||||||
|
@ -4,13 +4,107 @@
|
|||||||
ViewData["Specific"] = "Search";
|
ViewData["Specific"] = "Search";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="container d-flex flex-column align-items-center">
|
<div class="container">
|
||||||
<form class="my-4" style="width: 720px;">
|
<div class="my-4 less-concise mx-auto">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" placeholder="What are you looking for?" aria-label="Search" aria-describedby="search-btn">
|
<input type="text" class="form-control" placeholder="What are you looking for?" aria-label="Search" aria-describedby="search-btn" id="search-bar">
|
||||||
<input type="checkbox" class="btn-check" id="config-check-toggle" autocomplete="off">
|
<input type="checkbox" class="btn-check" id="config-check-toggle" autocomplete="off">
|
||||||
<label class="btn btn-outline-secondary" for="config-check-toggle"><i class="bi bi-sliders"></i></label>
|
<label class="btn btn-outline-secondary" for="config-check-toggle"><i class="bi bi-sliders"></i></label>
|
||||||
<button class="btn btn-outline-primary" type="button" id="search-btn">Search</button>
|
<button class="btn btn-outline-primary" type="button" id="search-btn">Search</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tear d-none" id="configuration">
|
||||||
|
<div class="container invisible">
|
||||||
|
<h2 class="my-2">Configuration</h2>
|
||||||
|
<div class="row justify-content-md-center">
|
||||||
|
<section class="col-lg">
|
||||||
|
<h3>Price</h3>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="max-price" class="form-label">Maximum Price</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<input class="form-check-input mt-0" type="checkbox" id="max-price-enabled">
|
||||||
|
</div>
|
||||||
|
<span class="input-group-text">$</span>
|
||||||
|
<input type="number" class="form-control" min="0" id="max-price">
|
||||||
|
<span class="input-group-text">.00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="min-price" class="form-label">Minimum Price</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">$</span>
|
||||||
|
<input type="number" class="form-control" min="0" id="min-price">
|
||||||
|
<span class="input-group-text">.00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="max-shipping" class="form-label">Maximum Shipping Fee</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<input class="form-check-input mt-0" type="checkbox" id="max-shipping-enabled">
|
||||||
|
</div>
|
||||||
|
<span class="input-group-text">$</span>
|
||||||
|
<input type="number" class="form-control" min="0" id="max-shipping">
|
||||||
|
<span class="input-group-text">.00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="keep-unknown-shipping">
|
||||||
|
<label class="form-check-label" for="keep-unknown-shipping">Keep Unknown Shipping</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="col-lg">
|
||||||
|
<h3>Metrics</h3>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="min-purchases" class="form-label">Minimum Purchases</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" min="0" id="min-purchases">
|
||||||
|
<span class="input-group-text">Purchases</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="keep-unknown-purchases">
|
||||||
|
<label class="form-check-label" for="keep-unknown-purchases">Keep Unknown Purchases</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="min-reviews" class="form-label">Minimum Reviews</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" min="0" id="min-reviews">
|
||||||
|
<span class="input-group-text">Reviews</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="keep-unknown-reviews">
|
||||||
|
<label class="form-check-label" for="keep-unknown-reviews">Keep Unknown Number of Reviews</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-1">
|
||||||
|
<label for="min-rating" class="form-label">Minimum Rating</label>
|
||||||
|
<input type="range" class="form-range" id="min-rating" min="0" max="100" step="1">
|
||||||
|
<div id="min-rating-display" class="form-text"></div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="keep-unrated">
|
||||||
|
<label class="form-check-label" for="keep-unrated">Keep Minimum Rating</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="col-md">
|
||||||
|
<h3>Shops Enabled</h3>
|
||||||
|
<div class="mb-3 px-3" id="shop-checkboxes">
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@* TODO: Add results display and default results display *@
|
@ -4,6 +4,6 @@ namespace Props.Pages
|
|||||||
{
|
{
|
||||||
public class SearchModel : PageModel
|
public class SearchModel : PageModel
|
||||||
{
|
{
|
||||||
|
// TODO: Complete the search model.
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,10 +11,6 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - Props</title>
|
<title>@ViewData["Title"] - Props</title>
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
@if (!string.IsNullOrEmpty((ViewData["Specific"] as string)))
|
|
||||||
{
|
|
||||||
<script src="@($"~/js/specific/{(ViewData["Specific"])}.js")" asp-append-version="true"></script>
|
|
||||||
}
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="theme-light">
|
<body class="theme-light">
|
||||||
@ -67,6 +63,10 @@
|
|||||||
© 2021 - Props - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
© 2021 - Props - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty((ViewData["Specific"] as string)))
|
||||||
|
{
|
||||||
|
<script src="@($"~/js/specific/{(ViewData["Specific"])}.js")" asp-append-version="true"></script>
|
||||||
|
}
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace Props.Services.Content
|
namespace Props.Services.Content
|
||||||
{
|
{
|
||||||
public class ContentManager<Page> : IContentManager<Page>
|
public class CachedContentManager<TPage> : IContentManager<TPage>
|
||||||
{
|
{
|
||||||
private dynamic data;
|
private dynamic data;
|
||||||
private readonly string directory;
|
private readonly string directory;
|
||||||
private readonly string fileName;
|
private readonly string fileName;
|
||||||
|
|
||||||
dynamic IContentManager<Page>.Json
|
dynamic IContentManager<TPage>.Json
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -18,10 +18,10 @@ namespace Props.Services.Content
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentManager(string directory = "content")
|
public CachedContentManager(string directory = "content")
|
||||||
{
|
{
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
this.fileName = typeof(Page).Name.Replace("Model", "") + ".json";
|
this.fileName = typeof(TPage).Name.Replace("Model", "") + ".json";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
14
Props/Services/Modules/IShopManager.cs
Normal file
14
Props/Services/Modules/IShopManager.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Props.Models.Search;
|
||||||
|
using Props.Shop.Framework;
|
||||||
|
|
||||||
|
namespace Props.Services.Modules
|
||||||
|
{
|
||||||
|
public interface IShopManager
|
||||||
|
{
|
||||||
|
public IEnumerable<string> AvailableShops();
|
||||||
|
public Task<IList<ProductListing>> Search(string query, SearchOutline searchOutline);
|
||||||
|
}
|
||||||
|
}
|
107
Props/Services/Modules/LocalShopManager.cs
Normal file
107
Props/Services/Modules/LocalShopManager.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Props.Models.Search;
|
||||||
|
using Props.Options;
|
||||||
|
using Props.Shop.Framework;
|
||||||
|
|
||||||
|
namespace Props.Services.Modules
|
||||||
|
{
|
||||||
|
public class LocalShopManager : IShopManager
|
||||||
|
{
|
||||||
|
private ILogger<LocalShopManager> logger;
|
||||||
|
private Dictionary<string, IShop> shops;
|
||||||
|
private ModulesOptions options;
|
||||||
|
private IConfiguration configuration;
|
||||||
|
public LocalShopManager(IConfiguration configuration, ILogger<LocalShopManager> logger)
|
||||||
|
{
|
||||||
|
this.configuration = configuration;
|
||||||
|
this.logger = logger;
|
||||||
|
options = configuration.GetSection(ModulesOptions.Modules).Get<ModulesOptions>();
|
||||||
|
|
||||||
|
shops = new Dictionary<string, IShop>();
|
||||||
|
foreach (IShop shop in LoadShops(options.ShopsDir, options.ShopRegex, options.RecursiveLoad))
|
||||||
|
{
|
||||||
|
if (!shops.TryAdd(shop.ShopName, shop))
|
||||||
|
{
|
||||||
|
logger.LogWarning("Duplicate shop {0} detected. Ignoring the latter.", shop.ShopName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> AvailableShops()
|
||||||
|
{
|
||||||
|
return shops.Keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IList<ProductListing>> Search(string query, SearchOutline searchOutline)
|
||||||
|
{
|
||||||
|
List<ProductListing> results = new List<ProductListing>();
|
||||||
|
foreach (string shopName in shops.Keys)
|
||||||
|
{
|
||||||
|
if (!searchOutline.Disabled[shopName])
|
||||||
|
{
|
||||||
|
int amount = 0;
|
||||||
|
await foreach (ProductListing product in shops[shopName].Search(query, searchOutline.Filters))
|
||||||
|
{
|
||||||
|
if (searchOutline.Filters.Validate(product))
|
||||||
|
{
|
||||||
|
amount += 1;
|
||||||
|
results.Add(product);
|
||||||
|
}
|
||||||
|
if (amount >= options.MaxResults) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IShop> LoadShops(string shopsDir, string shopRegex, bool recursiveLoad)
|
||||||
|
{
|
||||||
|
Stack<string> directories = new Stack<string>();
|
||||||
|
directories.Push(shopsDir);
|
||||||
|
string currentDirectory = null;
|
||||||
|
while (directories.TryPop(out currentDirectory))
|
||||||
|
{
|
||||||
|
if (recursiveLoad)
|
||||||
|
{
|
||||||
|
foreach (string dir in Directory.EnumerateDirectories(currentDirectory))
|
||||||
|
{
|
||||||
|
directories.Push(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string file in Directory.EnumerateFiles(currentDirectory))
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(file).Equals(".dll") && Regex.IsMatch(file, shopRegex))
|
||||||
|
{
|
||||||
|
ShopAssemblyLoadContext context = new ShopAssemblyLoadContext(file);
|
||||||
|
Assembly assembly = context.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(file)));
|
||||||
|
int success = 0;
|
||||||
|
foreach (Type type in assembly.GetTypes())
|
||||||
|
{
|
||||||
|
if (typeof(IShop).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
IShop shop = Activator.CreateInstance(type) as IShop;
|
||||||
|
if (shop != null)
|
||||||
|
{
|
||||||
|
success += 1;
|
||||||
|
yield return shop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (success == 0)
|
||||||
|
{
|
||||||
|
logger.LogWarning("There were no shops found within the assembly at path \"{0}\".", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
using Microsoft.Extensions.DependencyModel;
|
using Microsoft.Extensions.DependencyModel;
|
||||||
|
using Props.Shop.Framework;
|
||||||
|
|
||||||
namespace Props.Services.Modules
|
namespace Props.Services.Modules
|
||||||
{
|
{
|
||||||
@ -16,6 +17,7 @@ namespace Props.Services.Modules
|
|||||||
|
|
||||||
protected override Assembly Load(AssemblyName assemblyName)
|
protected override Assembly Load(AssemblyName assemblyName)
|
||||||
{
|
{
|
||||||
|
if (assemblyName.FullName.Equals(typeof(IShop).Assembly.FullName)) return null;
|
||||||
string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
|
string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
|
||||||
return assemblyPath != null ? LoadFromAssemblyPath(assemblyPath) : null;
|
return assemblyPath != null ? LoadFromAssemblyPath(assemblyPath) : null;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using Microsoft.Extensions.Hosting;
|
|||||||
using Props.Data;
|
using Props.Data;
|
||||||
using Props.Models.User;
|
using Props.Models.User;
|
||||||
using Props.Services.Content;
|
using Props.Services.Content;
|
||||||
|
using Props.Services.Modules;
|
||||||
|
|
||||||
namespace Props
|
namespace Props
|
||||||
{
|
{
|
||||||
@ -53,7 +54,8 @@ namespace Props
|
|||||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||||
services.AddRazorPages();
|
services.AddRazorPages();
|
||||||
|
|
||||||
services.AddSingleton(typeof(IContentManager<>), typeof(ContentManager<>));
|
services.AddSingleton(typeof(IContentManager<>), typeof(CachedContentManager<>));
|
||||||
|
services.AddSingleton<IShopManager, LocalShopManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
@ -75,6 +77,7 @@ namespace Props
|
|||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints =>
|
||||||
{
|
{
|
||||||
|
endpoints.MapControllers();
|
||||||
endpoints.MapRazorPages();
|
endpoints.MapRazorPages();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,11 @@
|
|||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Shops": {
|
"Modules": {
|
||||||
"Path": "./shops"
|
"ShopsDir": "./shops",
|
||||||
|
"RecursiveLoad": "false",
|
||||||
|
"MaxResults": "100",
|
||||||
|
"ShopRegex": "Props\\.Shop\\.."
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
}
|
}
|
232
Props/assets/images/logo-simplified.svg
Normal file
232
Props/assets/images/logo-simplified.svg
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="130.02815mm"
|
||||||
|
height="183.73235mm"
|
||||||
|
viewBox="0 0 130.02815 183.73235"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
sodipodi:docname="logo-simplified.svg"
|
||||||
|
inkscape:export-filename="C:\Users\yunya\Documents\Props\Props\client\src\assets\images\logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:snap-global="true"
|
||||||
|
inkscape:zoom="0.4734482"
|
||||||
|
inkscape:cx="11.616899"
|
||||||
|
inkscape:cy="186.92647"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1027"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-40.717182,-65.583996)">
|
||||||
|
<rect
|
||||||
|
style="fill:#51b6bf;fill-opacity:1;stroke:none;stroke-width:0.394384"
|
||||||
|
id="rect846"
|
||||||
|
width="130.02815"
|
||||||
|
height="180.11604"
|
||||||
|
x="40.717182"
|
||||||
|
y="69.200294"
|
||||||
|
ry="7.2278728" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.355205"
|
||||||
|
id="rect6139"
|
||||||
|
width="114.73653"
|
||||||
|
height="165.58026"
|
||||||
|
x="48.362995"
|
||||||
|
y="76.468185"
|
||||||
|
ry="6.6445661" />
|
||||||
|
<g
|
||||||
|
id="g20855"
|
||||||
|
transform="translate(0,-4.5861139)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#3b3485;stroke-width:1.465;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4822-1"
|
||||||
|
width="8.1061144"
|
||||||
|
height="8.1061144"
|
||||||
|
x="56.92775"
|
||||||
|
y="113.97274"
|
||||||
|
ry="1.3718038" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:1.86051;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect5136-7"
|
||||||
|
width="42.169209"
|
||||||
|
height="3.4690557"
|
||||||
|
x="69.150803"
|
||||||
|
y="116.29127"
|
||||||
|
ry="1.7345278" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g20859"
|
||||||
|
transform="translate(0,-7.0555611)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#3b3485;stroke-width:1.465;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4822-14"
|
||||||
|
width="8.1061144"
|
||||||
|
height="8.1061144"
|
||||||
|
x="56.92775"
|
||||||
|
y="127.76641"
|
||||||
|
ry="1.3718038" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:1.43107;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect5136-7-2"
|
||||||
|
width="21.986813"
|
||||||
|
height="3.9363635"
|
||||||
|
x="68.953468"
|
||||||
|
y="129.85129"
|
||||||
|
ry="1.9681817" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g20845"
|
||||||
|
transform="translate(0,-2.1166667)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#3b3485;stroke-width:1.465;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4822"
|
||||||
|
width="8.1061144"
|
||||||
|
height="8.1061144"
|
||||||
|
x="56.92775"
|
||||||
|
y="100.17907"
|
||||||
|
ry="1.3718038" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:1.59414;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect5136"
|
||||||
|
width="28.571259"
|
||||||
|
height="3.7589138"
|
||||||
|
x="69.028404"
|
||||||
|
y="102.35267"
|
||||||
|
ry="1.8794569" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:1.50408;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect5136-5"
|
||||||
|
width="24.788029"
|
||||||
|
height="3.8569105"
|
||||||
|
x="102.89133"
|
||||||
|
y="102.30367"
|
||||||
|
ry="1.9284552" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g20863"
|
||||||
|
transform="translate(0,-9.5250005)">
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#3b3485;stroke-width:1.465;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4822-1-4"
|
||||||
|
width="8.1061144"
|
||||||
|
height="8.1061144"
|
||||||
|
x="56.92775"
|
||||||
|
y="141.56007"
|
||||||
|
ry="1.3718038" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:1.57172;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect5728"
|
||||||
|
width="27.594185"
|
||||||
|
height="3.7833092"
|
||||||
|
x="69.018089"
|
||||||
|
y="143.72148"
|
||||||
|
ry="1.8916546" />
|
||||||
|
</g>
|
||||||
|
<rect
|
||||||
|
style="fill:#202042;fill-opacity:1;stroke:none;stroke-width:0.23824"
|
||||||
|
id="rect1392"
|
||||||
|
width="53.152565"
|
||||||
|
height="18.233921"
|
||||||
|
x="-132.30754"
|
||||||
|
y="-83.817917"
|
||||||
|
ry="3.4788733"
|
||||||
|
transform="scale(-1)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:2.22336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6412"
|
||||||
|
width="73.657707"
|
||||||
|
height="2.8362327"
|
||||||
|
x="56.336582"
|
||||||
|
y="201.33577"
|
||||||
|
ry="1.4181163" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:2.23876;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6414"
|
||||||
|
width="69.712616"
|
||||||
|
height="3.0383809"
|
||||||
|
x="56.344284"
|
||||||
|
y="209.24466"
|
||||||
|
ry="1.5191904" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:2.42357;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6416"
|
||||||
|
width="86.988022"
|
||||||
|
height="2.8535743"
|
||||||
|
x="56.436687"
|
||||||
|
y="217.23825"
|
||||||
|
ry="1.4267871" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8383bc;fill-opacity:1;stroke:none;stroke-width:2.38882;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6418"
|
||||||
|
width="83.495445"
|
||||||
|
height="2.8883159"
|
||||||
|
x="56.419315"
|
||||||
|
y="225.12207"
|
||||||
|
ry="1.444158" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2f3898;fill-opacity:1;stroke:none;stroke-width:3.0459;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6295-8"
|
||||||
|
width="57.490276"
|
||||||
|
height="6.8199105"
|
||||||
|
x="76.986122"
|
||||||
|
y="87.608482"
|
||||||
|
ry="3.4099553" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2f3898;fill-opacity:1;stroke:none;stroke-width:2.58155;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6490"
|
||||||
|
width="41.297459"
|
||||||
|
height="6.8199105"
|
||||||
|
x="85.082527"
|
||||||
|
y="188.09093"
|
||||||
|
ry="3.4099553" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2f3898;fill-opacity:1;stroke:none;stroke-width:1.87563;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6490-3"
|
||||||
|
width="21.8001"
|
||||||
|
height="6.8199105"
|
||||||
|
x="94.831207"
|
||||||
|
y="151.63762"
|
||||||
|
ry="3.4099553" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#2bc8d7;fill-opacity:1;stroke:none;stroke-width:1.63579;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6492"
|
||||||
|
width="16.931116"
|
||||||
|
height="13.580167"
|
||||||
|
x="132.27409"
|
||||||
|
y="201.23309"
|
||||||
|
ry="2.6454868" />
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:#dcf8f6;fill-opacity:1;stroke:none;stroke-width:1.465;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect6932"
|
||||||
|
width="79.717339"
|
||||||
|
height="19.929335"
|
||||||
|
x="65.872589"
|
||||||
|
y="161.02196"
|
||||||
|
ry="5.6437054" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
@ -1,2 +1,2 @@
|
|||||||
import "../../node_modules/bootstrap/js/dist/collapse";
|
import "~/node_modules/bootstrap/js/dist/collapse";
|
||||||
import "simplebar";
|
import "simplebar";
|
||||||
|
@ -1 +1,115 @@
|
|||||||
console.log("abc");
|
import { apiHttp } from "~/assets/js/services/http.js";
|
||||||
|
|
||||||
|
// All input fields.
|
||||||
|
let inputs = {
|
||||||
|
maxPriceEnabled: document.getElementById("max-price-enabled"),
|
||||||
|
maxShippingEnabled: document.getElementById("max-shipping-enabled"),
|
||||||
|
minRating: document.getElementById("min-rating"),
|
||||||
|
maxPrice: document.getElementById("max-price"),
|
||||||
|
maxShipping: document.getElementById("max-shipping"),
|
||||||
|
keepUnknownPurchases: document.getElementById("keep-unknown-purchases"),
|
||||||
|
keepUnknownReviews: document.getElementById("keep-unknown-reviews"),
|
||||||
|
keepUnknownShipping: document.getElementById("keep-unknown-shipping"),
|
||||||
|
keepUnrated: document.getElementById("keep-unrated"),
|
||||||
|
minPrice: document.getElementById("min-price"),
|
||||||
|
minPurchases: document.getElementById("min-purchases"),
|
||||||
|
minReviews: document.getElementById("min-reviews"),
|
||||||
|
shopToggles: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
setupInteractiveBehavior();
|
||||||
|
await setupInitialValues((await apiHttp.get("/Search/Default/Outline/Filters")).data);
|
||||||
|
await setupShopToggles((await apiHttp.get("/Search/Shops/Available")).data);
|
||||||
|
|
||||||
|
document.getElementById("configuration").querySelector(".invisible").classList.remove("invisible"); // Load completed, show the UI.
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupInteractiveBehavior() {
|
||||||
|
document.getElementById("config-check-toggle").addEventListener("change", function () {
|
||||||
|
let configElem = document.getElementById("configuration");
|
||||||
|
if (this.checked) {
|
||||||
|
configElem.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
configElem.classList.add("d-none");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
inputs.maxPriceEnabled.addEventListener("change", function () {
|
||||||
|
inputs.maxPrice.disabled = !this.checked;
|
||||||
|
});
|
||||||
|
|
||||||
|
inputs.maxShippingEnabled.addEventListener("change", function () {
|
||||||
|
inputs.maxShipping.disabled = !this.checked;
|
||||||
|
});
|
||||||
|
|
||||||
|
inputs.minRating.addEventListener("input", function () {
|
||||||
|
document.getElementById("min-rating-display").innerHTML = `Minimum rating: ${this.value}%`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupInitialValues(filters) {
|
||||||
|
inputs.maxShippingEnabled.checked = filters.enableMaxShippingFee;
|
||||||
|
inputs.maxShippingEnabled.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.maxPriceEnabled.checked = filters.enableUpperPrice;
|
||||||
|
inputs.maxPriceEnabled.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.keepUnknownPurchases.checked = filters.keepUnknownPurchaseCount;
|
||||||
|
inputs.keepUnknownPurchases.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.keepUnknownReviews.checked = filters.keepUnknownReviewCount;
|
||||||
|
inputs.keepUnknownReviews.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.keepUnknownShipping.checked = filters.keepUnknownShipping;
|
||||||
|
inputs.keepUnknownShipping.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.keepUnrated.checked = filters.keepUnrated;
|
||||||
|
inputs.keepUnrated.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.minPrice.value = filters.lowerPrice;
|
||||||
|
inputs.minPrice.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.maxShipping.value = filters.maxShippingFee;
|
||||||
|
inputs.maxShipping.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.minPurchases.value = filters.minPurchases;
|
||||||
|
inputs.minPurchases.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.minRating.value = filters.minRating * 100;
|
||||||
|
inputs.minRating.dispatchEvent(new Event("input"));
|
||||||
|
|
||||||
|
inputs.minReviews.value = filters.minReviews;
|
||||||
|
inputs.minReviews.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
inputs.maxPrice.value = filters.upperPrice;
|
||||||
|
inputs.maxPrice.dispatchEvent(new Event("change"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupShopToggles(availableShops) {
|
||||||
|
let disabledShops = (await apiHttp.get("/Search/Default/Outline/DisabledShops")).data;
|
||||||
|
let shopsElem = document.getElementById("shop-checkboxes");
|
||||||
|
availableShops.forEach(shopName => {
|
||||||
|
let id = `${shopName}-enabled`;
|
||||||
|
let shopLabelElem = document.createElement("label");
|
||||||
|
shopLabelElem.classList.add("form-check-label");
|
||||||
|
shopLabelElem.htmlFor = id;
|
||||||
|
shopLabelElem.innerHTML = `Enable ${shopName}`;
|
||||||
|
|
||||||
|
let shopCheckboxElem = document.createElement("input");
|
||||||
|
shopCheckboxElem.classList.add("form-check-input");
|
||||||
|
shopCheckboxElem.type = "checkbox";
|
||||||
|
shopCheckboxElem.id = id;
|
||||||
|
shopCheckboxElem.checked = !disabledShops.includes(shopName);
|
||||||
|
inputs.shopToggles[shopName] = shopCheckboxElem;
|
||||||
|
|
||||||
|
let shopToggleElem = document.createElement("div");
|
||||||
|
shopToggleElem.classList.add("form-check");
|
||||||
|
shopToggleElem.appendChild(shopCheckboxElem);
|
||||||
|
shopToggleElem.appendChild(shopLabelElem);
|
||||||
|
shopsElem.appendChild(shopToggleElem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
// TODO: Animate configuration toggle.
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
$themes: (
|
$themes: (
|
||||||
"light": ("background": #f4f4f4, "navbar": #FFF8F8, "main": #BDF2D5, "footer": #F2F2F2,"sub": #F4FCFC, "bold": #647b9b, "text": #1A1A1A, "muted": #797a7e),
|
"light": (
|
||||||
|
"background": #f4f4f4,
|
||||||
|
"navbar": #FFF8F8,
|
||||||
|
"main": #BDF2D5,
|
||||||
|
"footer": #F2F2F2,
|
||||||
|
"sub": #F4FCFC,
|
||||||
|
"bold": #647b9b,
|
||||||
|
"text": #1A1A1A,
|
||||||
|
"muted": #797a7e,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@use "themer";
|
@use "themer";
|
||||||
@use "~/node_modules/bootstrap/scss/bootstrap";
|
@use "~/node_modules/bootstrap/scss/bootstrap";
|
||||||
@import "~/node_modules/bootstrap-icons/font/bootstrap-icons.css";
|
@use "sass:color";
|
||||||
@import "~/node_modules/simplebar/dist/simplebar.min.css";
|
|
||||||
|
|
||||||
header > nav {
|
header > nav {
|
||||||
@extend .navbar-expand-lg;
|
@extend .navbar-expand-lg;
|
||||||
@ -33,6 +32,19 @@ main {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.tear {
|
||||||
|
@extend .p-3;
|
||||||
|
border-top: 1px;
|
||||||
|
border-top-style: solid;
|
||||||
|
border-bottom: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
@include themer.themed {
|
||||||
|
$tear: themer.color-of("background");
|
||||||
|
border-color: adjust-color($color: $tear, $lightness: -20%, $alpha: 1.0);
|
||||||
|
background-color: adjust-color($color: $tear, $lightness: -5%, $alpha: 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
@ -65,6 +77,16 @@ footer {
|
|||||||
background-color: themer.color-of("sub");
|
background-color: themer.color-of("sub");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.sole {
|
||||||
|
@include themer.themed {
|
||||||
|
background-color: themer.color-of("main");
|
||||||
|
border-color: themer.color-of("sub");
|
||||||
|
border-top-style: solid;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
border-width: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.concise {
|
.concise {
|
||||||
@ -112,4 +134,7 @@ body {
|
|||||||
background-color: themer.color-of("background");
|
background-color: themer.color-of("background");
|
||||||
color: themer.color-of("text");
|
color: themer.color-of("text");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import "~/node_modules/bootstrap-icons/font/bootstrap-icons.css";
|
||||||
|
@import "~/node_modules/simplebar/dist/simplebar.min.css";
|
||||||
|
11
Props/babel.config.js
Normal file
11
Props/babel.config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = function (api) {
|
||||||
|
api.cache(true);
|
||||||
|
|
||||||
|
const presets = [];
|
||||||
|
const plugins = [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
presets,
|
||||||
|
plugins
|
||||||
|
};
|
||||||
|
};
|
54
Props/package-lock.json
generated
54
Props/package-lock.json
generated
@ -63,6 +63,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@babel/eslint-parser": {
|
||||||
|
"version": "7.14.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.14.7.tgz",
|
||||||
|
"integrity": "sha512-6WPwZqO5priAGIwV6msJcdc9TsEPzYeYdS/Xuoap+/ihkgN6dzHp2bcAAwyWZ5bLzk0vvjDmKvRwkqNaiJ8BiQ==",
|
||||||
|
"requires": {
|
||||||
|
"eslint-scope": "^5.1.1",
|
||||||
|
"eslint-visitor-keys": "^2.1.0",
|
||||||
|
"semver": "^6.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/generator": {
|
"@babel/generator": {
|
||||||
"version": "7.14.8",
|
"version": "7.14.8",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz",
|
||||||
@ -950,6 +967,28 @@
|
|||||||
"@babel/helper-plugin-utils": "^7.14.5"
|
"@babel/helper-plugin-utils": "^7.14.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@babel/plugin-transform-runtime": {
|
||||||
|
"version": "7.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz",
|
||||||
|
"integrity": "sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-module-imports": "^7.14.5",
|
||||||
|
"@babel/helper-plugin-utils": "^7.14.5",
|
||||||
|
"babel-plugin-polyfill-corejs2": "^0.2.2",
|
||||||
|
"babel-plugin-polyfill-corejs3": "^0.2.2",
|
||||||
|
"babel-plugin-polyfill-regenerator": "^0.2.2",
|
||||||
|
"semver": "^6.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/plugin-transform-shorthand-properties": {
|
"@babel/plugin-transform-shorthand-properties": {
|
||||||
"version": "7.14.5",
|
"version": "7.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz",
|
||||||
@ -1121,7 +1160,6 @@
|
|||||||
"version": "7.14.8",
|
"version": "7.14.8",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz",
|
||||||
"integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==",
|
"integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.4"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
@ -2136,7 +2174,6 @@
|
|||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"esrecurse": "^4.3.0",
|
"esrecurse": "^4.3.0",
|
||||||
"estraverse": "^4.1.1"
|
"estraverse": "^4.1.1"
|
||||||
@ -2162,8 +2199,7 @@
|
|||||||
"eslint-visitor-keys": {
|
"eslint-visitor-keys": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
|
||||||
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"espree": {
|
"espree": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
@ -2217,7 +2253,6 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||||
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"estraverse": "^5.2.0"
|
"estraverse": "^5.2.0"
|
||||||
},
|
},
|
||||||
@ -2225,16 +2260,14 @@
|
|||||||
"estraverse": {
|
"estraverse": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
|
||||||
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
|
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"estraverse": {
|
"estraverse": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"esutils": {
|
"esutils": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
@ -3227,8 +3260,7 @@
|
|||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.7",
|
"version": "0.13.7",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
|
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"regenerator-transform": {
|
"regenerator-transform": {
|
||||||
"version": "0.14.5",
|
"version": "0.14.5",
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.8",
|
"@babel/core": "^7.14.8",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.14.5",
|
||||||
"@babel/preset-env": "^7.14.8",
|
"@babel/preset-env": "^7.14.8",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
@ -23,6 +24,8 @@
|
|||||||
"webpack-cli": "^4.7.2"
|
"webpack-cli": "^4.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/eslint-parser": "^7.14.7",
|
||||||
|
"@babel/runtime": "^7.14.8",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"bootstrap": "^5.0.2",
|
"bootstrap": "^5.0.2",
|
||||||
"bootstrap-icons": "^1.5.0",
|
"bootstrap-icons": "^1.5.0",
|
||||||
|
BIN
Props/shops/FuzzySharp.dll
Normal file
BIN
Props/shops/FuzzySharp.dll
Normal file
Binary file not shown.
BIN
Props/shops/Newtonsoft.Json.dll
Normal file
BIN
Props/shops/Newtonsoft.Json.dll
Normal file
Binary file not shown.
BIN
Props/shops/Props.Shop.Adafruit.dll
Normal file
BIN
Props/shops/Props.Shop.Adafruit.dll
Normal file
Binary file not shown.
@ -31,7 +31,8 @@ let config = {
|
|||||||
use: {
|
use: {
|
||||||
loader: "babel-loader",
|
loader: "babel-loader",
|
||||||
options: {
|
options: {
|
||||||
presets: ["@babel/preset-env"]
|
presets: ["@babel/preset-env"],
|
||||||
|
plugins: ["@babel/plugin-transform-runtime"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user