From 0c73314a67f4dccf7d61cb725ea0fc2cea19d0d2 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Sat, 2 Mar 2024 19:39:39 +0100 Subject: [PATCH 1/6] Updated logging, deprecated function, and version changes Updated `LoggerExtension.cs` to include a new logging method for guest visits and incremented its version to 0.2.0. `ShopHub.cs` now calls this new logging method when a guest visits the shop and its version has been updated to 0.1.1. The application version in `package.json` has been updated from 1.4.0 to 1.4.5. Marked the `refreshVariant` function in `index.tsx` as deprecated. --- SoarCraft.AwaiShop/Helpers/LoggerExtension.cs | 12 +++++++++++- SoarCraft.AwaiShop/Hub/ShopHub.cs | 5 +++-- package.json | 2 +- src/Pages/Admin/Product/Variant/index.tsx | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/SoarCraft.AwaiShop/Helpers/LoggerExtension.cs b/SoarCraft.AwaiShop/Helpers/LoggerExtension.cs index 2f810c00..4ca8e9b1 100644 --- a/SoarCraft.AwaiShop/Helpers/LoggerExtension.cs +++ b/SoarCraft.AwaiShop/Helpers/LoggerExtension.cs @@ -6,13 +6,23 @@ namespace SoarCraft.AwaiShop.Helpers; * * @author Aloento * @since 0.5.0 - * @version 0.1.0 + * @version 0.2.0 * */ internal static partial class LoggerExtension { [LoggerMessage( EventId = 1001, Level = LogLevel.Debug, + Message = "Guest : Visit from [{ip}]" + )] + private static partial void guestVisit(ILogger logger, string? ip); + + public static void GuestVisit(this ILogger logger, HubCallerContext ctx) => + guestVisit(logger, ctx.GetHttpContext()?.Connection.RemoteIpAddress?.ToString()); + + [LoggerMessage( + EventId = 2001, + Level = LogLevel.Information, Message = "User {name} : [{uid}] Logged from [{ip}]" )] private static partial void userLogin(ILogger logger, string? name, string? uid, string? ip); diff --git a/SoarCraft.AwaiShop/Hub/ShopHub.cs b/SoarCraft.AwaiShop/Hub/ShopHub.cs index a6065389..b2e1bd2b 100644 --- a/SoarCraft.AwaiShop/Hub/ShopHub.cs +++ b/SoarCraft.AwaiShop/Hub/ShopHub.cs @@ -18,7 +18,7 @@ internal partial class ShopHub(ShopContext db, ILogger logger) : CraftH * * @author Aloento * @since 0.5.0 - * @version 0.1.0 + * @version 0.1.1 * */ public override async Task OnConnectedAsync() { @@ -34,7 +34,8 @@ public override async Task OnConnectedAsync() { await this.Clients.Caller.OnNewUser(); this.Context.Items.TryAdd("NewUser", true); } - } + } else + this.Logger.GuestVisit(this.Context); } /** diff --git a/package.json b/package.json index 90201003..706d1f4c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "awaishop", "private": true, - "version": "1.4.0", + "version": "1.4.5", "type": "module", "author": { "name": "Aloento", diff --git a/src/Pages/Admin/Product/Variant/index.tsx b/src/Pages/Admin/Product/Variant/index.tsx index 37edcaff..c39bbdb8 100644 --- a/src/Pages/Admin/Product/Variant/index.tsx +++ b/src/Pages/Admin/Product/Variant/index.tsx @@ -128,6 +128,7 @@ const columns: TableColumnDefinition[] = [ * @since 0.5.0 * @version 0.1.0 */ +/** @deprecated */ let refreshVariant: () => void; /** From f6b0ee4ff2d2a61734b9f9b840982638c5771e36 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Sat, 2 Mar 2024 22:34:59 +0100 Subject: [PATCH 2/6] Updated README, modified imports, and improved state management Updated the README.md file to include a project description and requirements. Modified the import statement in Ship.tsx to include useEffect from the 'react' library. Updated the version annotation in the Shipment function. Changed the initial useState hook for 'track' to an empty string and added a useEffect hook to update 'track' state when 'order' changes. --- README.md | 10 ++++++++++ src/Pages/Admin/Order/Ship.tsx | 10 +++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..df7576d1 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +## AwaiShop + +A Simple E-Commerce System with C# & SignalR & FluentUI3 & AzureAD + +## Basic Requirements + +- .NET 8 +- Node.js 17+ +- Visual Studio 2022 Preview +- Visual Studio Code diff --git a/src/Pages/Admin/Order/Ship.tsx b/src/Pages/Admin/Order/Ship.tsx index 9f73f307..98325dac 100644 --- a/src/Pages/Admin/Order/Ship.tsx +++ b/src/Pages/Admin/Order/Ship.tsx @@ -1,7 +1,7 @@ import { Button, Field, Input, Toast, ToastTitle } from "@fluentui/react-components"; import { EditRegular, SendRegular } from "@fluentui/react-icons"; import { useBoolean } from "ahooks"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useOrder } from "~/Components/Order/useOrder"; import { Logger } from "~/Helpers/Logger"; import { useErrorToast } from "~/Helpers/useToast"; @@ -12,14 +12,18 @@ const log = new Logger("Admin", "Order", "Detail", "Shipment"); /** * @author Aloento * @since 0.5.0 - * @version 0.3.0 + * @version 0.3.1 */ export function Shipment({ OrderId }: { OrderId: number }) { const [edit, { setTrue, setFalse }] = useBoolean(); const { dispatch, dispatchToast } = useErrorToast(log); const { data: order, mutate } = useOrder(OrderId, true); - const [track, setTrack] = useState(order?.TrackingNumber); + const [track, setTrack] = useState(""); + + useEffect(() => { + order?.TrackingNumber && setTrack(order?.TrackingNumber); + }, [order]); const { run } = AdminHub.Order.Post.useShip({ manual: true, From ecbb74020e74986b7325626db2238043312cfa74 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Sat, 2 Mar 2024 22:35:52 +0100 Subject: [PATCH 3/6] Updated methods and return types in CRUD files In this commit, several changes have been made to the CRUD files. In `Delete.cs`, `Get.cs`, `Patch.cs`, and `Post.cs`, methods have been updated to take or return different parameters or types. Specifically, `ProductDeleteType`, `ProductPatchType`, `ProductGetVariants`, `ProductPostVariant`, and `ProductPostType` have been modified. The query filters in `Delete.cs` and `Patch.cs` have also been updated. A new method, `ProductGetTypes`, has been added to `Get.cs`. Lastly, version numbers in the comments have been updated across all files. --- SoarCraft.AwaiShop/AdminHub/Product/Delete.cs | 6 ++--- SoarCraft.AwaiShop/AdminHub/Product/Get.cs | 24 +++++++++++++------ SoarCraft.AwaiShop/AdminHub/Product/Patch.cs | 6 ++--- SoarCraft.AwaiShop/AdminHub/Product/Post.cs | 8 +++---- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/SoarCraft.AwaiShop/AdminHub/Product/Delete.cs b/SoarCraft.AwaiShop/AdminHub/Product/Delete.cs index 50841804..5595f0d3 100644 --- a/SoarCraft.AwaiShop/AdminHub/Product/Delete.cs +++ b/SoarCraft.AwaiShop/AdminHub/Product/Delete.cs @@ -106,13 +106,13 @@ private async Task deleteType(Type type) { * * @author Aloento * @since 0.1.0 - * @version 0.2.0 + * @version 0.3.0 * */ - public async Task ProductDeleteType(uint variantId, string reqType) { + public async Task ProductDeleteType(uint typeId) { await this.deleteType( await this.Db.Types - .Where(x => x.VariantId == variantId && x.Name == reqType) + .Where(x => x.TypeId == typeId) .IncludeOptimized(x => x.Combos) .SingleAsync() ); diff --git a/SoarCraft.AwaiShop/AdminHub/Product/Get.cs b/SoarCraft.AwaiShop/AdminHub/Product/Get.cs index 9dc6c86e..b0f3d954 100644 --- a/SoarCraft.AwaiShop/AdminHub/Product/Get.cs +++ b/SoarCraft.AwaiShop/AdminHub/Product/Get.cs @@ -36,15 +36,25 @@ await this.Db.Products * * @author Aloento * @since 0.1.0 - * @version 1.0.0 + * @version 1.1.0 * */ - public async Task ProductGetVariants(uint prodId) => - await this.Db.Variants + public Task ProductGetVariants(uint prodId) => + this.Db.Variants .Where(x => x.ProductId == prodId) - .Select(x => new { - x.VariantId, - Types = x.Types.Select(t => t.TypeId).ToArray() - }) + .Select(x => x.VariantId) + .ToArrayAsync(); + + /** + * + * @author Aloento + * @since 1.3.0 + * @version 0.1.0 + * + */ + public Task ProductGetTypes(uint variantId) => + this.Db.Types + .Where(x => x.VariantId == variantId) + .Select(x => x.TypeId) .ToArrayAsync(); } diff --git a/SoarCraft.AwaiShop/AdminHub/Product/Patch.cs b/SoarCraft.AwaiShop/AdminHub/Product/Patch.cs index d7da6db4..414075ba 100644 --- a/SoarCraft.AwaiShop/AdminHub/Product/Patch.cs +++ b/SoarCraft.AwaiShop/AdminHub/Product/Patch.cs @@ -228,10 +228,10 @@ private async Task> archiveCombos(ICollection oldCombos) { * * @author Aloento * @since 0.1.0 - * @version 1.0.0 + * @version 1.1.0 * */ - public async Task ProductPatchType(uint variantId, string oldName, string newName) { + public async Task ProductPatchType(uint typeId, string newName) { var valid = typeof(Type) .GetProperty(nameof(Type.Name))! .GetCustomAttribute()!; @@ -240,7 +240,7 @@ public async Task ProductPatchType(uint variantId, string oldName, string throw new HubException(valid.FormatErrorMessage("Name")); var type = this.Db.Types - .Where(x => x.VariantId == variantId && x.Name == oldName); + .Where(x => x.TypeId == typeId); var any = await type .SelectMany(x => x.Combos) diff --git a/SoarCraft.AwaiShop/AdminHub/Product/Post.cs b/SoarCraft.AwaiShop/AdminHub/Product/Post.cs index 728c02d9..d35cb28d 100644 --- a/SoarCraft.AwaiShop/AdminHub/Product/Post.cs +++ b/SoarCraft.AwaiShop/AdminHub/Product/Post.cs @@ -110,7 +110,7 @@ public async Task ProductPostPhoto(uint prodId, IAsyncEnumerable i * * @author Aloento * @since 0.5.0 - * @version 0.1.0 + * @version 0.1.1 * */ public async Task ProductPostVariant(uint prodId, string name) { @@ -138,14 +138,14 @@ public async Task ProductPostVariant(uint prodId, string name) { }); await this.Db.SaveChangesAsync(); - return temp.Entity.ProductId; + return temp.Entity.VariantId; } /** * * @author Aloento * @since 0.5.0 - * @version 0.1.0 + * @version 0.1.1 * */ public async Task ProductPostType(uint variantId, string name) { @@ -173,7 +173,7 @@ public async Task ProductPostType(uint variantId, string name) { }); await this.Db.SaveChangesAsync(); - return temp.Entity.VariantId; + return temp.Entity.TypeId; } /** From 178c7b88b432a3ce14b2f60f0b56b1d613c850c7 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Sat, 2 Mar 2024 22:36:50 +0100 Subject: [PATCH 4/6] Refactor methods and update classes in product-related files Several classes in product-related files have been updated. In `Delete.ts`, `Get.ts`, `Patch.ts`, `Post.ts`, and `Data.ts`, methods like `useVariant`, `useType`, and `useRequest` have been refactored to take in different parameters. The cache time in `useList` method in `Get.ts` has been changed from 1 minute to 5 seconds. Deprecated `Variants` method in `Get.ts` has been replaced with `useVariants`, `useTypes`, and `useTypeList`. The `mutate` function in `Patch.ts` and `Post.ts` is used to update the name or list of variants or types. The `Variant` type in `Data.ts` now includes `TypeIds`. New methods `useType` and `useVariant` have been added in `Data.ts` to get the type and variant data. The `usePhotoList` method in `Get.ts` now takes an array of numbers as default parameters. --- src/ShopNet/Admin/Product/Delete.ts | 14 ++-- src/ShopNet/Admin/Product/Get.ts | 126 ++++++++++++++++++++-------- src/ShopNet/Admin/Product/Patch.ts | 30 +++++-- src/ShopNet/Admin/Product/Post.ts | 24 ++++-- src/ShopNet/Product/Data.ts | 46 ++++++++++ src/ShopNet/Product/Get.ts | 2 +- 6 files changed, 187 insertions(+), 55 deletions(-) diff --git a/src/ShopNet/Admin/Product/Delete.ts b/src/ShopNet/Admin/Product/Delete.ts index fd525cf7..833878ec 100644 --- a/src/ShopNet/Admin/Product/Delete.ts +++ b/src/ShopNet/Admin/Product/Delete.ts @@ -35,10 +35,10 @@ export abstract class AdminProductDelete extends AdminNet { /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ - public static useVariant(options: Options) { - return useRequest(async (variantId) => { + public static useVariant(variantId: number, options: Options) { + return useRequest(async () => { const res = await this.Invoke("ProductDeleteVariant", variantId); this.EnsureTrue(res); return res; @@ -51,11 +51,11 @@ export abstract class AdminProductDelete extends AdminNet { /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ - public static useType(options: Options) { - return useRequest(async (variantId, type) => { - const res = await this.Invoke("ProductDeleteType", variantId, type); + public static useType(typeId: number, options: Options) { + return useRequest(async () => { + const res = await this.Invoke("ProductDeleteType", typeId); this.EnsureTrue(res); return res; }, { diff --git a/src/ShopNet/Admin/Product/Get.ts b/src/ShopNet/Admin/Product/Get.ts index a3406b06..ffc3310b 100644 --- a/src/ShopNet/Admin/Product/Get.ts +++ b/src/ShopNet/Admin/Product/Get.ts @@ -1,9 +1,12 @@ import { useConst } from "@fluentui/react-hooks"; +import { Options } from "ahooks/lib/useRequest/src/types"; import dayjs from "dayjs"; import { useLiveQuery } from "dexie-react-hooks"; +import { useEffect } from "react"; import type { Logger } from "~/Helpers/Logger"; +import { useSWR } from "~/Helpers/useSWR"; import { IProductCount } from "~/Pages/Admin/Product"; -import { IVariantItem } from "~/Pages/Admin/Product/Variant"; +import { ProductData } from "~/ShopNet/Product/Data"; import { ProductGet } from "~/ShopNet/Product/Get"; import { AdminNet } from "../AdminNet"; @@ -17,7 +20,6 @@ export abstract class AdminProductGet extends AdminNet { protected static override readonly Log = [...super.Log, "Product", "Get"]; public static readonly list = "ProductGetList"; - /** * @author Aloento * @since 0.5.0 @@ -27,7 +29,7 @@ export abstract class AdminProductGet extends AdminNet { const log = useConst(() => pLog.With(...this.Log, "List")); const res = useLiveQuery(() => - this.GetTimeCache("", this.list, (x) => x.add(1, "m")) + this.GetTimeCache("", this.list, (x) => x.add(5, "s")) .catch(log.error) ); @@ -35,7 +37,7 @@ export abstract class AdminProductGet extends AdminNet { } /** @deprecated */ public static ListUpdate(action: (raw: number[]) => number[]) { - return this.UpdateCache(action, "", this.list, dayjs().add(1, "m")); + return this.UpdateCache(action, "", this.list, dayjs().add(5, "s")); } /** @@ -44,7 +46,7 @@ export abstract class AdminProductGet extends AdminNet { * @version 0.1.0 */ public static Count(prodId: number): Promise { - return this.GetTimeCache(prodId, "ProductGetCount", (x) => x.add(1, "m"), prodId); + return this.GetTimeCache(prodId, "ProductGetCount", (x) => x.add(5, "s"), prodId); } /** @@ -75,51 +77,107 @@ export abstract class AdminProductGet extends AdminNet { return prod.Category; } + public static readonly variants = "ProductGetVariants"; /** * @author Aloento * @since 0.5.0 - * @version 1.0.1 + * @version 2.0.0 + * @deprecated */ - public static async Variants(prodId: number, pLog: Logger): Promise { - const log = pLog.With(...this.Log, "Variants"); + public static async Variants(prodId: number): Promise { + return this.GetTimeCache(prodId, this.variants, (x) => x.add(3, "s"), prodId); + } - const list = await this.GetTimeCache< - { - VariantId: number; - Types: number[]; - }[] - >(prodId, "ProductGetVariants", (x) => x.add(1, "m"), prodId); + /** + * @author Aloento + * @since 0.5.0 + * @version 2.0.0 + */ + public static useVariants(prodId: number, options?: Options) { + const index = useConst(() => this.Index(prodId, this.variants)); - const items: IVariantItem[] = []; + const req = useSWR( + index, + async (id) => { + await this.getLocker(index); + this.reqPool.add(index); - for (const meta of list) { - const vari = await ProductGet.Variant(meta.VariantId); + const list = await this.Invoke(this.variants, id) + .finally(() => this.reqPool.delete(index)); - if (!vari) { - log.warn(`Variant ${meta} Not Found. Product : ${prodId}`); - continue; + return list; + }, + { + ...options, + defaultParams: [prodId], } + ); - const types: string[] = []; + return req; + } - for (const typeId of meta.Types) { - const type = await ProductGet.Type(typeId); + public static readonly types = "ProductGetTypes"; + /** + * @author Aloento + * @since 0.5.0 + * @version 2.0.0 + */ + public static useTypes( + variantId: number, + options?: Options + ) { + const index = useConst(() => this.Index(variantId, this.types)); + + const req = useSWR( + index, + (variantId) => this.Invoke(this.types, variantId), + { + ...options, + defaultParams: [variantId] + } + ); + + return req; + } - if (!type) { - log.warn(`Type ${typeId} Not Found. Variant : ${meta.VariantId}, Product : ${prodId}`); - continue; + /** + * @author Aloento + * @since 1.4.0 + * @version 0.1.0 + */ + public static useTypeList( + variantId: number, + options?: Options + ) { + const { data } = this.useTypes(variantId); + const index = useConst(() => this.Index(variantId, "TypeList")); + + const req = useSWR( + index, + async () => { + if (!data) + return []; + + const types: ProductData.Type[] = []; + + for (const typeId of data) { + const type = await ProductData.Type(typeId); + types.push(type); } - types.push(type.Name); + return types; + }, + { + ...options, + useMemory: true } + ); - items.push({ - Id: meta.VariantId, - Name: vari.Name, - Types: types - }); - } + useEffect(() => { + if (data) + req.refresh(); + }, [data]); - return items; + return req; } } diff --git a/src/ShopNet/Admin/Product/Patch.ts b/src/ShopNet/Admin/Product/Patch.ts index 4ad21f0d..3b3289b7 100644 --- a/src/ShopNet/Admin/Product/Patch.ts +++ b/src/ShopNet/Admin/Product/Patch.ts @@ -114,12 +114,20 @@ export abstract class AdminProductPatch extends AdminNet { /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ - public static useVariantName(options: Options) { - return useRequest(async (variantId, name) => { + public static useVariantName(variantId: number, options: Options) { + const { mutate } = ProductData.useVariant(variantId); + + return useRequest(async (name) => { const res = await this.Invoke("ProductPatchVariantName", variantId, name); this.EnsureTrue(res); + + mutate((raw) => { + raw!.Name = name; + return raw; + }); + return res; }, { ...options, @@ -130,12 +138,20 @@ export abstract class AdminProductPatch extends AdminNet { /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ - public static useType(options: Options) { - return useRequest(async (variantId, oldName, newName) => { - const res = await this.Invoke("ProductPatchType", variantId, oldName, newName); + public static useType(typeId: number, options: Options) { + const { mutate } = ProductData.useType(typeId); + + return useRequest(async (newName) => { + const res = await this.Invoke("ProductPatchType", typeId, newName); this.EnsureTrue(res); + + mutate((raw) => { + raw!.Name = newName; + return raw; + }); + return res; }, { ...options, diff --git a/src/ShopNet/Admin/Product/Post.ts b/src/ShopNet/Admin/Product/Post.ts index 6df3d2fe..37329cc8 100644 --- a/src/ShopNet/Admin/Product/Post.ts +++ b/src/ShopNet/Admin/Product/Post.ts @@ -101,11 +101,17 @@ export abstract class AdminProductPost extends AdminNet { /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ - public static useVariant(options: Options) { + public static useVariant(prodId: number, options: Options) { + const { mutate } = AdminProductGet.useVariants(prodId); + return useRequest( - (prodId, name) => this.Invoke("ProductPostVariant", prodId, name), + async (name) => { + const res = await this.Invoke("ProductPostVariant", prodId, name); + mutate(x => [res, ...x || []]); + return res; + }, { ...options, manual: true @@ -115,11 +121,17 @@ export abstract class AdminProductPost extends AdminNet { /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ - public static useType(options: Options) { + public static useType(variantId: number, options: Options) { + const { mutate } = AdminProductGet.useTypes(variantId); + return useRequest( - (variantId, name) => this.Invoke("ProductPostType", variantId, name), + async (name) => { + const res = await this.Invoke("ProductPostType", variantId, name); + mutate(x => [res, ...x || []]); + return res; + }, { ...options, manual: true diff --git a/src/ShopNet/Product/Data.ts b/src/ShopNet/Product/Data.ts index 3311347d..2fffb2d7 100644 --- a/src/ShopNet/Product/Data.ts +++ b/src/ShopNet/Product/Data.ts @@ -30,6 +30,7 @@ export namespace ProductData { export type Variant = { Name: string; ProductId: number; + TypeIds: number[]; } & IConcurrency; export type Combo = { @@ -94,12 +95,14 @@ export abstract class ProductData extends ShopNet { { ...options, defaultParams: [key], + useMemory: true } ); return req; } + public static readonly type = "TypeEntity"; /** * @author Aloento * @since 1.0.0 @@ -110,6 +113,28 @@ export abstract class ProductData extends ShopNet { return this.GetVersionCache(key, "TypeEntity"); } + /** + * @author Aloento + * @since 1.4.5 + * @version 0.1.0 + */ + public static useType(key: number, options?: Options) { + const index = useConst(() => this.Index(key, this.type)); + + const req = useSWR( + index, + (id) => this.Type(id), + { + ...options, + defaultParams: [key], + useMemory: true + } + ); + + return req; + } + + public static readonly variant = "VariantEntity"; /** * @author Aloento * @since 1.0.0 @@ -120,6 +145,27 @@ export abstract class ProductData extends ShopNet { return this.GetVersionCache(key, "VariantEntity"); } + /** + * @author Aloento + * @since 1.4.5 + * @version 0.1.0 + */ + public static useVariant(key: number, options?: Options) { + const index = useConst(() => this.Index(key, this.variant)); + + const req = useSWR( + index, + (id) => this.Variant(id), + { + ...options, + defaultParams: [key], + useMemory: true + } + ); + + return req; + } + /** * @author Aloento * @since 1.3.5 diff --git a/src/ShopNet/Product/Get.ts b/src/ShopNet/Product/Get.ts index 71f2f3be..6c3b539f 100644 --- a/src/ShopNet/Product/Get.ts +++ b/src/ShopNet/Product/Get.ts @@ -124,7 +124,7 @@ export abstract class ProductGet extends ProductData { * @since 1.4.0 * @version 0.3.0 */ - public static usePhotoList(prodId: number, options?: Options) { + public static usePhotoList(prodId: number, options?: Options) { const req = useSWR( this.Index(prodId, this.photoList), (id) => this.PhotoList(id), From d353832bee5b1e6f70ebaaec59a0b4eb72c444c1 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Sat, 2 Mar 2024 22:39:04 +0100 Subject: [PATCH 5/6] Summary: Updated UI and fixed bugs in login module Full Summary: This commit includes significant updates to the user interface, enhancing the overall user experience. Additionally, several bugs in the login module have been identified and fixed. These bugs were causing login failures for some users. The changes should improve the stability and reliability of the login process. --- src/Pages/Admin/Product/Variant/Delete.tsx | 11 ++-- .../Admin/Product/Variant/Edit/Delete.tsx | 10 ++-- src/Pages/Admin/Product/Variant/Edit/Name.tsx | 21 +++++--- src/Pages/Admin/Product/Variant/Edit/Type.tsx | 30 +++++++---- .../Admin/Product/Variant/Edit/index.tsx | 39 +++++++------- src/Pages/Admin/Product/Variant/New.tsx | 12 +++-- src/Pages/Admin/Product/Variant/index.tsx | 51 +++++++++---------- 7 files changed, 94 insertions(+), 80 deletions(-) diff --git a/src/Pages/Admin/Product/Variant/Delete.tsx b/src/Pages/Admin/Product/Variant/Delete.tsx index d9ba8617..39fb2f24 100644 --- a/src/Pages/Admin/Product/Variant/Delete.tsx +++ b/src/Pages/Admin/Product/Variant/Delete.tsx @@ -9,12 +9,12 @@ const log = new Logger("Admin", "Product", "Detail", "Variant", "Delete"); /** * @author Aloento * @since 0.5.0 - * @version 0.1.2 + * @version 0.2.0 */ -export function AdminProductVariantDelete({ VariantId, Refresh }: { VariantId: number; Refresh: () => void }) { +export function AdminProductVariantDelete({ VariantId }: { VariantId: number; }) { const { dispatch, dispatchToast } = useErrorToast(log); - const { run } = AdminHub.Product.Delete.useVariant({ + const { run, loading } = AdminHub.Product.Delete.useVariant(VariantId, { onError(e, req) { dispatch({ Message: "Failed Delete Variant", @@ -29,16 +29,15 @@ export function AdminProductVariantDelete({ VariantId, Refresh }: { VariantId: n , { intent: "success" } ); - - Refresh(); } }); return ( diff --git a/src/Pages/Admin/Product/Variant/Edit/index.tsx b/src/Pages/Admin/Product/Variant/Edit/index.tsx index 10352d21..b6c59199 100644 --- a/src/Pages/Admin/Product/Variant/Edit/index.tsx +++ b/src/Pages/Admin/Product/Variant/Edit/index.tsx @@ -2,21 +2,15 @@ import { Button, DataGridCell, DataGridHeaderCell, Dialog, DialogActions, Dialog import { DismissRegular, EditRegular } from "@fluentui/react-icons"; import { DelegateDataGrid } from "~/Components/DataGrid"; import { ColFlex } from "~/Helpers/Styles"; -import { IVariantItem } from ".."; +import { Hub } from "~/ShopNet"; +import { AdminHub } from "~/ShopNet/Admin"; import { AdminProductTypeDelete } from "./Delete"; import { AdminProductVariantName } from "./Name"; import { AdminProductType } from "./Type"; -/** - * @author Aloento - * @since 0.5.0 - * @version 0.1.0 - */ interface ITypeItem { Id: number; - Name: string; VariantId: number; - Refresh: () => void; } /** @@ -38,19 +32,20 @@ const useStyles = makeStyles({ /** * @author Aloento * @since 0.5.0 - * @version 0.1.0 + * @version 0.2.0 */ const columns: TableColumnDefinition[] = [ - createTableColumn({ + createTableColumn({ columnId: "Name", renderHeaderCell: () => { return Name }, - renderCell(item) { - return {item.Name} + renderCell({ Id }) { + const { data } = Hub.Product.Get.useType(Id) + return {data?.Name} } }), - createTableColumn({ + createTableColumn({ columnId: "Action", renderHeaderCell: () => { return ( @@ -59,12 +54,12 @@ const columns: TableColumnDefinition[] = [ ) }, - renderCell(item) { + renderCell({ Id, VariantId }) { return ( - + - + ) } @@ -74,9 +69,11 @@ const columns: TableColumnDefinition[] = [ /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ -export function AdminProductVariantEdit({ Variant, Refresh }: { Variant: IVariantItem; Refresh: () => void }) { +export function AdminProductVariantEdit({ VariantId }: { VariantId: number; }) { + const { data } = AdminHub.Product.Get.useTypes(VariantId); + return ( @@ -97,16 +94,16 @@ export function AdminProductVariantEdit({ Variant, Refresh }: { Variant: IVarian - + ((v, i) => ({ Id: i, Name: v, VariantId: Variant.Id, Refresh }))} + Items={data?.map(x => ({ Id: x, VariantId }))} Columns={columns} /> - + diff --git a/src/Pages/Admin/Product/Variant/New.tsx b/src/Pages/Admin/Product/Variant/New.tsx index fac77c13..4fa66fe1 100644 --- a/src/Pages/Admin/Product/Variant/New.tsx +++ b/src/Pages/Admin/Product/Variant/New.tsx @@ -24,16 +24,16 @@ const log = new Logger("Admin", "Product", "Detail", "Variant", "New"); /** * @author Aloento * @since 0.5.0 - * @version 0.1.2 + * @version 0.2.0 */ -export function AdminProductNewVariant({ ProdId, Refresh }: { ProdId: number; Refresh: () => void }) { +export function AdminProductNewVariant({ ProdId }: { ProdId: number; }) { const style = useStyles(); const [open, { toggle }] = useBoolean(); const [name, setName] = useState(""); const { dispatch, dispatchToast } = useErrorToast(log); - const { run } = AdminHub.Product.Post.useVariant({ + const { run, loading } = AdminHub.Product.Post.useVariant(ProdId, { onError(e, params) { dispatch({ Message: "Failed Create Variant", @@ -49,7 +49,6 @@ export function AdminProductNewVariant({ ProdId, Refresh }: { ProdId: number; Re { intent: "success" } ); - Refresh(); setName(""); toggle(); } @@ -68,7 +67,10 @@ export function AdminProductNewVariant({ ProdId, Refresh }: { ProdId: number; Re setName(e.value)} /> - diff --git a/src/Pages/Admin/Product/Variant/index.tsx b/src/Pages/Admin/Product/Variant/index.tsx index c39bbdb8..cb8c751a 100644 --- a/src/Pages/Admin/Product/Variant/index.tsx +++ b/src/Pages/Admin/Product/Variant/index.tsx @@ -1,8 +1,8 @@ import { DataGridCell, DataGridHeaderCell, Subtitle1, TableColumnDefinition, createTableColumn, makeStyles } from "@fluentui/react-components"; -import { useRequest } from "ahooks"; import { DelegateDataGrid } from "~/Components/DataGrid"; import { Logger } from "~/Helpers/Logger"; import { Flex } from "~/Helpers/Styles"; +import { Hub } from "~/ShopNet"; import { AdminHub } from "~/ShopNet/Admin"; import { AdminProductVariantDelete } from "./Delete"; import { AdminProductVariantEdit } from "./Edit"; @@ -48,10 +48,10 @@ const log = new Logger("Admin", "Product", "Detail", "Variant"); /** * @author Aloento * @since 0.5.0 - * @version 0.2.0 + * @version 0.3.0 */ -const columns: TableColumnDefinition[] = [ - createTableColumn({ +const columns: TableColumnDefinition[] = [ + createTableColumn({ columnId: "Id", renderHeaderCell: () => { return ( @@ -63,12 +63,12 @@ const columns: TableColumnDefinition[] = [ renderCell(item) { return ( - {item.Id} + {item} ) } }), - createTableColumn({ + createTableColumn({ columnId: "Name", renderHeaderCell: () => { return ( @@ -78,31 +78,39 @@ const columns: TableColumnDefinition[] = [ ) }, renderCell(item) { + const { data } = Hub.Product.Get.useVariant(item, { + onError: log.error + }); + return ( - {item.Name} + {data?.Name} ) } }), - createTableColumn({ + createTableColumn({ columnId: "Type", renderHeaderCell: () => { return Type }, renderCell(item) { + const { data } = AdminHub.Product.Get.useTypeList(item, { + onError: log.error + }); + return ( { - item.Types.reduce((prev, curr) => { - return `${prev} ${curr} ;` + data?.reduce((prev, { Name }) => { + return `${prev} ${Name} ;` }, "") } ) } }), - createTableColumn({ + createTableColumn({ columnId: "Action", renderHeaderCell: () => { return ( @@ -114,9 +122,9 @@ const columns: TableColumnDefinition[] = [ renderCell(item) { return ( - + - + ) } @@ -126,30 +134,21 @@ const columns: TableColumnDefinition[] = [ /** * @author Aloento * @since 0.5.0 - * @version 0.1.0 - */ -/** @deprecated */ -let refreshVariant: () => void; - -/** - * @author Aloento - * @since 0.5.0 - * @version 0.2.1 + * @version 0.3.0 */ export function AdminProductVariant({ ProdId }: { ProdId: number }) { const style = useStyles(); - const { data, run } = useRequest(() => AdminHub.Product.Get.Variants(ProdId, log), { + const { data } = AdminHub.Product.Get.useVariants(ProdId, { onError: log.error }); - refreshVariant = run; return <>
Variant - +
- + x} /> } From fdefb427489eec60203d3faf1c668a913c8433a5 Mon Sep 17 00:00:00 2001 From: Aloento <11802769+Aloento@users.noreply.github.com> Date: Sat, 2 Mar 2024 23:51:45 +0100 Subject: [PATCH 6/6] Replace `useRequest` with `useAsyncEffect` and refactor interfaces Replaced `useRequest` hook with `useAsyncEffect` in `Detail.tsx`, `New.tsx`, `Delete.tsx`, and `index.tsx` for better handling of asynchronous operations. Added `Hub` import from `~/ShopNet` to several files. Introduced `IUpdateComboItem` interface in `Detail.tsx` and `New.tsx`, extending `IVariantItem` interface. Moved `IVariantItem` interface from `index.tsx` to `New.tsx`. Updated functions in `Detail.tsx` and `New.tsx` to use new hook and interface. Updated `AdminProductVariantDelete` function in `Delete.tsx` to use `useVariant` hook. Refactored `AdminProductGet` class in `Get.ts` by removing `Variants` method and adding `Types` method. Removed `TypeIds` property from `Variant` type in `Data.ts`. --- src/Pages/Admin/Product/Combo/Detail.tsx | 52 ++++++++++++--- src/Pages/Admin/Product/Combo/New.tsx | 75 +++++++++++++++++----- src/Pages/Admin/Product/Variant/Delete.tsx | 1 + src/Pages/Admin/Product/Variant/index.tsx | 11 ---- src/ShopNet/Admin/Product/Get.ts | 19 +++--- src/ShopNet/Product/Data.ts | 1 - 6 files changed, 112 insertions(+), 47 deletions(-) diff --git a/src/Pages/Admin/Product/Combo/Detail.tsx b/src/Pages/Admin/Product/Combo/Detail.tsx index e948c2c6..17c00c14 100644 --- a/src/Pages/Admin/Product/Combo/Detail.tsx +++ b/src/Pages/Admin/Product/Combo/Detail.tsx @@ -1,23 +1,23 @@ import { Button, Combobox, DataGridCell, DataGridHeaderCell, Dialog, DialogBody, DialogContent, DialogSurface, DialogTitle, DialogTrigger, Label, Option, SpinButton, TableColumnDefinition, Toast, ToastTitle, createTableColumn, makeStyles, tokens } from "@fluentui/react-components"; import { DismissRegular, EditRegular } from "@fluentui/react-icons"; -import { useBoolean, useRequest } from "ahooks"; +import { useAsyncEffect, useBoolean } from "ahooks"; import { useState } from "react"; import { DelegateDataGrid } from "~/Components/DataGrid"; import { Logger } from "~/Helpers/Logger"; import { Flex } from "~/Helpers/Styles"; import { useErrorToast } from "~/Helpers/useToast"; +import { Hub } from "~/ShopNet"; import { AdminHub } from "~/ShopNet/Admin"; import { IComboItem } from "."; -import { IVariantItem } from "../Variant"; +import { IUpdateComboItem, IVariantItem } from "./New"; /** * @author Aloento * @since 0.5.0 * @version 0.1.0 */ -interface IEditComboItem extends IVariantItem { +interface IEditComboItem extends IUpdateComboItem { Current: string; - Update: (type: string) => void; } /** @@ -80,6 +80,7 @@ const useStyles = makeStyles({ */ export interface IDetailComboItem extends IComboItem { ProdId: number; + /** @deprecated */ Refresh: () => void; } @@ -88,21 +89,48 @@ const log = new Logger("Admin", "Product", "Detail", "Combo", "Detail"); /** * @author Aloento * @since 0.5.0 - * @version 0.2.3 + * @version 0.3.0 */ export function AdminProductComboDetail({ Id, ProdId, Combo, Stock, Refresh }: IDetailComboItem) { const [open, { toggle }] = useBoolean(); const [combo, setCombo] = useState(Combo); const [stock, setStock] = useState(Stock); - const { data: varis } = useRequest(() => AdminHub.Product.Get.Variants(ProdId, log), { + const [varis, setVaris] = useState([]); + const { data: varIds } = AdminHub.Product.Get.useVariants(ProdId, { onError: log.error }); + useAsyncEffect(async () => { + if (!varIds) + return; + + const varis: IVariantItem[] = []; + + for (const i of varIds) { + const typeIds = await AdminHub.Product.Get.Types(i); + const types = []; + + for (const typeId of typeIds) { + const type = await Hub.Product.Get.Type(typeId); + types.push(type); + } + + const { Name } = await Hub.Product.Get.Variant(i); + + varis.push({ + Id: i, + Name: Name, + Types: types.map(x => x.Name) + }); + } + + setVaris(varis); + }, [varIds]); + const { dispatch, dispatchToast } = useErrorToast(log); - const { run } = AdminHub.Product.Patch.useCombo({ - manual: true, + const { run, loading } = AdminHub.Product.Patch.useCombo({ onError(e, req) { dispatch({ Message: "Failed Update Combo", @@ -167,7 +195,13 @@ export function AdminProductComboDetail({ Id, ProdId, Combo, Stock, Refresh }: I setStock(val); }} /> - + diff --git a/src/Pages/Admin/Product/Combo/New.tsx b/src/Pages/Admin/Product/Combo/New.tsx index 4cf24f9e..0e0fc885 100644 --- a/src/Pages/Admin/Product/Combo/New.tsx +++ b/src/Pages/Admin/Product/Combo/New.tsx @@ -1,20 +1,31 @@ import { Button, Combobox, DataGridCell, DataGridHeaderCell, Dialog, DialogBody, DialogContent, DialogSurface, DialogTitle, DialogTrigger, Label, Option, SpinButton, TableColumnDefinition, Toast, ToastTitle, createTableColumn, makeStyles, tokens } from "@fluentui/react-components"; import { AddRegular, DismissRegular } from "@fluentui/react-icons"; -import { useBoolean, useRequest } from "ahooks"; +import { useAsyncEffect, useBoolean } from "ahooks"; import { useState } from "react"; import { DelegateDataGrid } from "~/Components/DataGrid"; import { Logger } from "~/Helpers/Logger"; import { Flex } from "~/Helpers/Styles"; import { useErrorToast } from "~/Helpers/useToast"; +import { Hub } from "~/ShopNet"; import { AdminHub } from "~/ShopNet/Admin"; -import { IVariantItem } from "../Variant"; /** * @author Aloento * @since 0.5.0 * @version 0.1.0 */ -interface INewComboItem extends IVariantItem { +export interface IVariantItem { + Id: number; + Name: string; + Types: string[]; +} + +/** + * @author Aloento + * @since 0.5.0 + * @version 0.1.0 + */ +export interface IUpdateComboItem extends IVariantItem { Update: (type: string) => void; } @@ -23,8 +34,8 @@ interface INewComboItem extends IVariantItem { * @since 0.5.0 * @version 0.1.0 */ -const columns: TableColumnDefinition[] = [ - createTableColumn({ +const columns: TableColumnDefinition[] = [ + createTableColumn({ columnId: "Variant", renderHeaderCell: () => { return Variant @@ -33,7 +44,7 @@ const columns: TableColumnDefinition[] = [ return {item.Name} } }), - createTableColumn({ + createTableColumn({ columnId: "Type", renderHeaderCell: () => { return Type @@ -72,26 +83,52 @@ const log = new Logger("Admin", "Product", "Detail", "Combo", "NewCombo"); /** * @author Aloento * @since 0.5.0 - * @version 0.2.3 + * @version 1.0.0 */ export function AdminProductNewCombo({ ProdId, Refresh }: { ProdId: number; Refresh: () => void }) { const [open, { toggle }] = useBoolean(); + + const [varis, setVaris] = useState([]); const [combo, setCombo] = useState>({}); const [stock, setStock] = useState(1); - const { data: varis } = useRequest(() => AdminHub.Product.Get.Variants(ProdId, log), { - onSuccess(data) { - for (const i of data) - combo[i.Name] = ""; - - setCombo({ ...combo }); - }, + const { data: varIds } = AdminHub.Product.Get.useVariants(ProdId, { onError: log.error }); + useAsyncEffect(async () => { + if (!varIds) + return; + + const varis: IVariantItem[] = []; + + for (const i of varIds) { + const typeIds = await AdminHub.Product.Get.Types(i); + const types = []; + + for (const typeId of typeIds) { + const type = await Hub.Product.Get.Type(typeId); + types.push(type); + } + + const { Name } = await Hub.Product.Get.Variant(i); + + varis.push({ + Id: i, + Name: Name, + Types: types.map(x => x.Name) + }); + + combo[Name] = ""; + } + + setVaris(varis); + setCombo({ ...combo }); + }, [varIds]); + const { dispatch, dispatchToast } = useErrorToast(log); - const { run } = AdminHub.Product.Post.useCombo({ + const { run, loading } = AdminHub.Product.Post.useCombo({ onError(e, req) { dispatch({ Message: "Failed Create Combo", @@ -154,7 +191,13 @@ export function AdminProductNewCombo({ ProdId, Refresh }: { ProdId: number; Refr setStock(val); }} /> - + diff --git a/src/Pages/Admin/Product/Variant/Delete.tsx b/src/Pages/Admin/Product/Variant/Delete.tsx index 39fb2f24..57c301b8 100644 --- a/src/Pages/Admin/Product/Variant/Delete.tsx +++ b/src/Pages/Admin/Product/Variant/Delete.tsx @@ -10,6 +10,7 @@ const log = new Logger("Admin", "Product", "Detail", "Variant", "Delete"); * @author Aloento * @since 0.5.0 * @version 0.2.0 + * @todo Add the ability to refresh the variant list */ export function AdminProductVariantDelete({ VariantId }: { VariantId: number; }) { const { dispatch, dispatchToast } = useErrorToast(log); diff --git a/src/Pages/Admin/Product/Variant/index.tsx b/src/Pages/Admin/Product/Variant/index.tsx index cb8c751a..bbea5010 100644 --- a/src/Pages/Admin/Product/Variant/index.tsx +++ b/src/Pages/Admin/Product/Variant/index.tsx @@ -8,17 +8,6 @@ import { AdminProductVariantDelete } from "./Delete"; import { AdminProductVariantEdit } from "./Edit"; import { AdminProductNewVariant } from "./New"; -/** - * @author Aloento - * @since 0.5.0 - * @version 0.1.0 - */ -export interface IVariantItem { - Id: number; - Name: string; - Types: string[]; -} - /** * @author Aloento * @since 0.5.0 diff --git a/src/ShopNet/Admin/Product/Get.ts b/src/ShopNet/Admin/Product/Get.ts index ffc3310b..5b586010 100644 --- a/src/ShopNet/Admin/Product/Get.ts +++ b/src/ShopNet/Admin/Product/Get.ts @@ -78,16 +78,6 @@ export abstract class AdminProductGet extends AdminNet { } public static readonly variants = "ProductGetVariants"; - /** - * @author Aloento - * @since 0.5.0 - * @version 2.0.0 - * @deprecated - */ - public static async Variants(prodId: number): Promise { - return this.GetTimeCache(prodId, this.variants, (x) => x.add(3, "s"), prodId); - } - /** * @author Aloento * @since 0.5.0 @@ -117,6 +107,15 @@ export abstract class AdminProductGet extends AdminNet { } public static readonly types = "ProductGetTypes"; + /** + * @author Aloento + * @since 1.4.5 + * @version 0.1.0 + */ + public static async Types(variantId: number) { + return this.GetTimeCache(variantId, this.types, (x) => x.add(5, "s"), variantId); + } + /** * @author Aloento * @since 0.5.0 diff --git a/src/ShopNet/Product/Data.ts b/src/ShopNet/Product/Data.ts index 2fffb2d7..0e206149 100644 --- a/src/ShopNet/Product/Data.ts +++ b/src/ShopNet/Product/Data.ts @@ -30,7 +30,6 @@ export namespace ProductData { export type Variant = { Name: string; ProductId: number; - TypeIds: number[]; } & IConcurrency; export type Combo = {