diff --git a/frontend/biome.json.old b/frontend/biome.json.old new file mode 100644 index 0000000..2eb0751 --- /dev/null +++ b/frontend/biome.json.old @@ -0,0 +1,30 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/frontend/package.json b/frontend/package.json index 5fc70f6..855e693 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,17 +1,17 @@ { "name": "frontend", "private": true, - "version": "0.0.0", + "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", - "format:check": "prettier --check .", - "format": "prettier --write .", "knip": "knip", - "generate-client": "openapi-ts" + "generate-client": "openapi-ts", + "format:write": "biome format ./src/ --write", + "format:check": "biome check ./src/" }, "dependencies": { "@hookform/resolvers": "^3.9.1", @@ -62,6 +62,7 @@ "zustand": "^5.0.3" }, "devDependencies": { + "@biomejs/biome": "^1.9.4", "@eslint/js": "^9.16.0", "@faker-js/faker": "^9.3.0", "@hey-api/client-axios": "^0.6.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 54ba0f2..089e609 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -147,6 +147,9 @@ importers: specifier: ^5.0.3 version: 5.0.3(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) devDependencies: + '@biomejs/biome': + specifier: ^1.9.4 + version: 1.9.4 '@eslint/js': specifier: ^9.16.0 version: 9.24.0 @@ -365,6 +368,59 @@ packages: resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} + '@biomejs/biome@1.9.4': + resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@1.9.4': + resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@1.9.4': + resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@1.9.4': + resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@1.9.4': + resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@1.9.4': + resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@1.9.4': + resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@1.9.4': + resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@1.9.4': + resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@date-fns/tz@1.2.0': resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==} @@ -3379,6 +3435,41 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@biomejs/biome@1.9.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.9.4 + '@biomejs/cli-darwin-x64': 1.9.4 + '@biomejs/cli-linux-arm64': 1.9.4 + '@biomejs/cli-linux-arm64-musl': 1.9.4 + '@biomejs/cli-linux-x64': 1.9.4 + '@biomejs/cli-linux-x64-musl': 1.9.4 + '@biomejs/cli-win32-arm64': 1.9.4 + '@biomejs/cli-win32-x64': 1.9.4 + + '@biomejs/cli-darwin-arm64@1.9.4': + optional: true + + '@biomejs/cli-darwin-x64@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64@1.9.4': + optional: true + + '@biomejs/cli-linux-x64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-x64@1.9.4': + optional: true + + '@biomejs/cli-win32-arm64@1.9.4': + optional: true + + '@biomejs/cli-win32-x64@1.9.4': + optional: true + '@date-fns/tz@1.2.0': {} '@esbuild/aix-ppc64@0.25.2': diff --git a/frontend/src/api/api-definition.ts b/frontend/src/api/api-definition.ts index cb04c44..4a9c65f 100644 --- a/frontend/src/api/api-definition.ts +++ b/frontend/src/api/api-definition.ts @@ -2,7 +2,7 @@ import { UserPublic, UserRegister, UserUpdate, - ShopLoginAccessTokenData + ShopLoginAccessTokenData, } from "@/client"; import { Shop } from "./mock/models"; diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index a1b8f7b..ff1e89f 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -3,10 +3,19 @@ import { MockAuthAPI } from "./mock/auth-mock-api"; import { AuthAPI, ShopAPI } from "./api-definition"; import { MockShopAPI } from "./mock/shop-mock-api"; import { MockProductAPI } from "./mock/products-mock-api"; +import { MockUserAPI } from "./mock/user-mock-api"; +import { MockCouponAPI } from "./mock/coupon-mock-api"; +import { MockPurchaseAPI } from "./mock/purchase-mock-api"; export const authAPI: AuthAPI = import.meta.env.VITE_USE_MOCK_API === "true" ? MockAuthAPI : RealAuthAPI; export const shopAPI: ShopAPI = MockShopAPI; -export const productsAPI = MockProductAPI; \ No newline at end of file +export const productsAPI = MockProductAPI; + +export const usersAPI = MockUserAPI; + +export const couponAPI = MockCouponAPI; + +export const purchaseAPI = MockPurchaseAPI; diff --git a/frontend/src/api/mock/auth-mock-api.ts b/frontend/src/api/mock/auth-mock-api.ts index 7eaef29..1211b6d 100644 --- a/frontend/src/api/mock/auth-mock-api.ts +++ b/frontend/src/api/mock/auth-mock-api.ts @@ -5,7 +5,6 @@ import { extractSubFromJWT, generateFakeJWT } from "@/utils/jwt"; import { mockDB } from "./db"; import { MockUser, Shop } from "./models"; - const db = mockDB; export const MockAuthAPI: AuthAPI = { @@ -23,7 +22,7 @@ export const MockAuthAPI: AuthAPI = { ...user, first_name: user.first_name ?? null, last_name: user.last_name ?? null, - profile_picture: user.profile_picture ?? null + profile_picture: user.profile_picture ?? null, }; return publicUser; }, @@ -37,8 +36,8 @@ export const MockAuthAPI: AuthAPI = { const newUser: MockUser = { id: userId, uuid: userUUID, - user_role: "customer", - status: "customer", + user_role: "owner", + status: "owner", shop_id: null, username: data.username, email: data.email, @@ -49,7 +48,7 @@ export const MockAuthAPI: AuthAPI = { profile_picture: undefined, created_at: now, updated_at: now, - last_login: now + last_login: now, }; const newShop: Shop = { @@ -70,16 +69,23 @@ export const MockAuthAPI: AuthAPI = { city: "", state: null, postal_code: "", - country: "" - } + country: "", + }, }; - await mockDB.transaction('rw', mockDB.users, mockDB.shops, mockDB.preferences, mockDB.statistics, async () => { - await mockDB.users.add(newUser); - await mockDB.preferences.add({ user_id: userId }); - await mockDB.statistics.add({ user_id: userId, total_spend: 0 }); - await mockDB.shops.add(newShop); - }); + await mockDB.transaction( + "rw", + mockDB.users, + mockDB.shops, + mockDB.preferences, + mockDB.statistics, + async () => { + await mockDB.users.add(newUser); + await mockDB.preferences.add({ user_id: userId }); + await mockDB.statistics.add({ user_id: userId, total_spend: 0 }); + await mockDB.shops.add(newShop); + }, + ); }, async loginUser(data: ShopLoginAccessTokenData) { @@ -115,7 +121,7 @@ export const MockAuthAPI: AuthAPI = { phone_number: data.phone_number ?? "", username: data.username ?? "", first_name: data.first_name ?? undefined, - last_name: data.last_name ?? undefined + last_name: data.last_name ?? undefined, }); - } + }, }; diff --git a/frontend/src/api/mock/coupon-mock-api.ts b/frontend/src/api/mock/coupon-mock-api.ts new file mode 100644 index 0000000..7fdb931 --- /dev/null +++ b/frontend/src/api/mock/coupon-mock-api.ts @@ -0,0 +1,45 @@ +import { mockDB } from "./db"; +import { Coupon } from "./models"; + +export const MockCouponAPI = { + async getAllCoupons(): Promise { + return mockDB.coupons.toArray(); + }, + + async getCouponById(id: number): Promise { + return (await mockDB.coupons.get(id)) ?? null; + }, + + async createCoupon(couponData: Omit): Promise { + const id = Date.now(); + + const newCoupon: Coupon = { + ...couponData, + id, + }; + + await mockDB.coupons.add(newCoupon); + return newCoupon; + }, + + async updateCoupon( + id: number, + data: Partial>, + ): Promise { + const existing = await mockDB.coupons.get(id); + if (!existing) return null; + + const updated: Coupon = { + ...existing, + ...data, + }; + + await mockDB.coupons.put(updated); + return updated; + }, + + async deleteCoupon(id: number): Promise { + await mockDB.coupons.delete(id); + return true; + }, +}; diff --git a/frontend/src/api/mock/db.ts b/frontend/src/api/mock/db.ts index 78c50fd..2d83939 100644 --- a/frontend/src/api/mock/db.ts +++ b/frontend/src/api/mock/db.ts @@ -1,5 +1,6 @@ import Dexie, { Table } from "dexie"; import { + Coupon, MockUser, MockUserPreferences, MockUserStatistics, @@ -8,7 +9,9 @@ import { ProductCategoryJunction, ProductImage, ProductVariant, - Shop + Purchase, + PurchaseEntry, + Shop, } from "./models"; class MockDB extends Dexie { @@ -21,6 +24,9 @@ class MockDB extends Dexie { product_images!: Table; product_categories!: Table; product_category_junctions!: Table; + coupons!: Table; + purchases!: Table; + purchase_entries!: Table; constructor() { super("MockDB"); @@ -33,7 +39,10 @@ class MockDB extends Dexie { product_variants: "++id,product_id", product_images: "++id,product_id", product_categories: "++id,parent_category_id", - product_category_junctions: "[product_id+category_id]" + product_category_junctions: "[product_id+category_id]", + coupons: "++id,valid_due,name", + purchases: "++id,user_id,date_purchased", + purchase_entries: "++id,purchase_id,product_id,product_variant_id", }); } } diff --git a/frontend/src/api/mock/models.ts b/frontend/src/api/mock/models.ts index 5540342..4351033 100644 --- a/frontend/src/api/mock/models.ts +++ b/frontend/src/api/mock/models.ts @@ -81,10 +81,23 @@ export interface ProductImage { alt_text: string; } +export interface ProductCreate { + name: string; + description: string; + price: number; + stock_quantity: number; + image_data: string | undefined; +} + +export interface ProductWithDetails extends Product { + images: ProductImage[]; + categories: ProductCategory[]; +} + export interface ProductVariant { id: number; product_id: number; - index: number; // used for ordering + index: number; name: string; price: number; comment?: string; @@ -92,16 +105,36 @@ export interface ProductVariant { updated_at: string; } -export interface ProductCreate { +export interface Coupon { + id: number; name: string; - description: string; - price: number; - stock_quantity: number; - image_data?: string | undefined; + text: string; + valid_due: string; + discount_amount: number; } -export interface ProductWithDetails extends Product { - images: ProductImage[]; - variants: ProductVariant[]; - categories: ProductCategory[]; +export interface Purchase { + id: number; + user_id: number; + used_coupon_id: number | null; + date_purchased: string; + total: number; +} + +export interface PurchaseEntry { + id: number; + purchase_id: number; + product_id: number; + product_variant_id: number; + quantity: number; +} + +export interface PurchaseWithDetails extends Purchase { + user: MockUser; + coupon?: Coupon | null; + entries: PurchaseEntryWithProduct[]; +} + +export interface PurchaseEntryWithProduct extends PurchaseEntry { + product: Product; } diff --git a/frontend/src/api/mock/products-mock-api.ts b/frontend/src/api/mock/products-mock-api.ts index bf8e7de..0de9dd8 100644 --- a/frontend/src/api/mock/products-mock-api.ts +++ b/frontend/src/api/mock/products-mock-api.ts @@ -28,20 +28,20 @@ export const MockProductAPI = { .toArray(); const rawCategories = await Promise.all( categoryLinks.map((link) => - mockDB.product_categories.get(link.category_id) - ) + mockDB.product_categories.get(link.category_id), + ), ); const categories = rawCategories.filter( - (c): c is ProductCategory => c !== undefined + (c): c is ProductCategory => c !== undefined, ); return { ...product, images, variants, - categories + categories, }; - }) + }), ); return detailedProducts; @@ -66,18 +66,18 @@ export const MockProductAPI = { const rawCategories = await Promise.all( categoryLinks.map((link) => - mockDB.product_categories.get(link.category_id) - ) + mockDB.product_categories.get(link.category_id), + ), ); const categories = rawCategories.filter( - (c): c is ProductCategory => c !== undefined + (c): c is ProductCategory => c !== undefined, ); return { ...product, images, variants, - categories + categories, }; }, @@ -93,7 +93,7 @@ export const MockProductAPI = { ...productData, shop_id: user.id, created_at: now, - updated_at: now + updated_at: now, }); if ("image_data" in productData && productData.image_data) { @@ -103,7 +103,7 @@ export const MockProductAPI = { product_id: productId, image_id: image_id, image_url: productData.image_data, - alt_text: `${productData.name} image` + alt_text: `${productData.name} image`, }); } @@ -117,7 +117,7 @@ export const MockProductAPI = { const updatedProduct = { ...product, ...data, - updated_at: new Date().toISOString() + updated_at: new Date().toISOString(), }; await mockDB.products.put(updatedProduct); @@ -130,7 +130,7 @@ export const MockProductAPI = { await mockDB.product_images.put({ ...existingImage, image_url: data.image_data, - alt_text: `${data.name ?? product.name} image` + alt_text: `${data.name ?? product.name} image`, }); } else { const image_id = Date.now(); @@ -139,7 +139,7 @@ export const MockProductAPI = { product_id: productId, image_id: image_id, image_url: data.image_data, - alt_text: `${data.name ?? product.name} image` + alt_text: `${data.name ?? product.name} image`, }); } } @@ -159,5 +159,5 @@ export const MockProductAPI = { .delete(); await mockDB.products.delete(productId); return true; - } + }, }; diff --git a/frontend/src/api/mock/purchase-mock-api.ts b/frontend/src/api/mock/purchase-mock-api.ts new file mode 100644 index 0000000..604e2a5 --- /dev/null +++ b/frontend/src/api/mock/purchase-mock-api.ts @@ -0,0 +1,85 @@ +import { mockDB } from "./db"; +import { + Purchase, + PurchaseWithDetails, + PurchaseEntry, + PurchaseEntryWithProduct, +} from "./models"; + +export const MockPurchaseAPI = { + async getAllPurchases(): Promise { + return mockDB.purchases.toArray(); + }, + + async getPurchaseById(purchaseId: number): Promise { + return (await mockDB.purchases.get(purchaseId)) ?? null; + }, + + async getPurchaseWithDetails( + purchaseId: number, + ): Promise { + const purchase = await mockDB.purchases.get(purchaseId); + if (!purchase) return null; + + const [user, coupon, entries] = await Promise.all([ + mockDB.users.get(purchase.user_id), + purchase.used_coupon_id + ? mockDB.coupons.get(purchase.used_coupon_id) + : null, + mockDB.purchase_entries.where("purchase_id").equals(purchaseId).toArray(), + ]); + + if (!user) return null; + + const entriesWithProducts: PurchaseEntryWithProduct[] = await Promise.all( + entries.map(async (entry) => { + const product = await mockDB.products.get(entry.product_id); + if (!product) throw new Error("Product not found"); + return { ...entry, product }; + }), + ); + + return { + ...purchase, + user, + coupon, + entries: entriesWithProducts, + }; + }, + + async getPurchasesByUser(userId: number): Promise { + return mockDB.purchases.where("user_id").equals(userId).toArray(); + }, + + async createPurchase( + purchase: Omit, + entries: Omit[], + ): Promise { + const id = Date.now(); + const newPurchase: Purchase = { + ...purchase, + id, + }; + + await mockDB.purchases.add(newPurchase); + + await mockDB.purchase_entries.bulkAdd( + entries.map((entry) => ({ + ...entry, + purchase_id: id, + id: Date.now() + Math.random(), + })), + ); + + return newPurchase; + }, + + async deletePurchase(purchaseId: number): Promise { + await mockDB.purchase_entries + .where("purchase_id") + .equals(purchaseId) + .delete(); + await mockDB.purchases.delete(purchaseId); + return true; + }, +}; diff --git a/frontend/src/api/mock/shop-mock-api.ts b/frontend/src/api/mock/shop-mock-api.ts index e6ff7fe..99667cb 100644 --- a/frontend/src/api/mock/shop-mock-api.ts +++ b/frontend/src/api/mock/shop-mock-api.ts @@ -35,7 +35,7 @@ export const MockShopAPI: ShopAPI = { console.log("Saving data ->", data); await db.shops.update(user.id, { ...data, - updated_at: new Date().toISOString() + updated_at: new Date().toISOString(), }); - } + }, }; diff --git a/frontend/src/api/mock/user-mock-api.ts b/frontend/src/api/mock/user-mock-api.ts new file mode 100644 index 0000000..0b70275 --- /dev/null +++ b/frontend/src/api/mock/user-mock-api.ts @@ -0,0 +1,60 @@ +import { mockDB } from "./db"; +import { MockUser } from "./models"; +import { getCurrentUserDirect } from "./utils/currentUser"; + +export const MockUserAPI = { + async getAllUsers(): Promise { + return mockDB.users.toArray(); + }, + + async getUserById(userId: number): Promise { + return (await mockDB.users.get(userId)) ?? null; + }, + + async createUser( + userData: Omit, + ): Promise { + const now = new Date().toISOString(); + const id = Date.now(); + + const newUser: MockUser = { + ...userData, + id, + created_at: now, + updated_at: now, + }; + + await mockDB.users.add(newUser); + return newUser; + }, + + async updateUser( + userId: number, + data: Partial>, + ): Promise { + const existingUser = await mockDB.users.get(userId); + if (!existingUser) return null; + + const updatedUser: MockUser = { + ...existingUser, + ...data, + updated_at: new Date().toISOString(), + }; + + await mockDB.users.put(updatedUser); + return updatedUser; + }, + + async deleteUser(userId: number): Promise { + await mockDB.preferences.where("user_id").equals(userId).delete(); + await mockDB.statistics.where("user_id").equals(userId).delete(); + await mockDB.users.delete(userId); + return true; + }, + + async getCurrentUser(): Promise { + const user = await getCurrentUserDirect(); + if (!user?.id) return null; + return (await mockDB.users.get(user.id)) ?? null; + }, +}; diff --git a/frontend/src/api/mock/utils/currentUser.ts b/frontend/src/api/mock/utils/currentUser.ts index 1baa0bc..0939b0d 100644 --- a/frontend/src/api/mock/utils/currentUser.ts +++ b/frontend/src/api/mock/utils/currentUser.ts @@ -10,4 +10,4 @@ export async function getCurrentUserDirect() { const user = await mockDB.users.where("uuid").equals(uuid).first(); return user ?? null; -} \ No newline at end of file +} diff --git a/frontend/src/api/real/auth-real-api.ts b/frontend/src/api/real/auth-real-api.ts index 443506e..86591c5 100644 --- a/frontend/src/api/real/auth-real-api.ts +++ b/frontend/src/api/real/auth-real-api.ts @@ -10,10 +10,10 @@ export const RealAuthAPI: AuthAPI = { }, async loginUser(data) { return LoginService.dashboardLoginAccessToken({ - formData: data.formData + formData: data.formData, }); }, async updateUser(data) { await UserService.userUpdateUser({ requestBody: data }); - } + }, }; diff --git a/frontend/src/client/core/ApiError.ts b/frontend/src/client/core/ApiError.ts index 36675d2..b5a0214 100644 --- a/frontend/src/client/core/ApiError.ts +++ b/frontend/src/client/core/ApiError.ts @@ -1,5 +1,5 @@ -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; +import type { ApiRequestOptions } from "./ApiRequestOptions"; +import type { ApiResult } from "./ApiResult"; export class ApiError extends Error { public readonly url: string; @@ -8,14 +8,18 @@ export class ApiError extends Error { public readonly body: unknown; public readonly request: ApiRequestOptions; - constructor(request: ApiRequestOptions, response: ApiResult, message: string) { + constructor( + request: ApiRequestOptions, + response: ApiResult, + message: string, + ) { super(message); - this.name = 'ApiError'; + this.name = "ApiError"; this.url = response.url; this.status = response.status; this.statusText = response.statusText; this.body = response.body; this.request = request; } -} \ No newline at end of file +} diff --git a/frontend/src/client/core/ApiRequestOptions.ts b/frontend/src/client/core/ApiRequestOptions.ts index 939a0aa..89951fd 100644 --- a/frontend/src/client/core/ApiRequestOptions.ts +++ b/frontend/src/client/core/ApiRequestOptions.ts @@ -6,16 +6,16 @@ export type ApiRequestOptions = { readonly headers?: Record; readonly mediaType?: string; readonly method: - | 'DELETE' - | 'GET' - | 'HEAD' - | 'OPTIONS' - | 'PATCH' - | 'POST' - | 'PUT'; + | "DELETE" + | "GET" + | "HEAD" + | "OPTIONS" + | "PATCH" + | "POST" + | "PUT"; readonly path?: Record; readonly query?: Record; readonly responseHeader?: string; readonly responseTransformer?: (data: unknown) => Promise; readonly url: string; -}; \ No newline at end of file +}; diff --git a/frontend/src/client/core/ApiResult.ts b/frontend/src/client/core/ApiResult.ts index 4c58e39..84b9f9d 100644 --- a/frontend/src/client/core/ApiResult.ts +++ b/frontend/src/client/core/ApiResult.ts @@ -4,4 +4,4 @@ export type ApiResult = { readonly status: number; readonly statusText: string; readonly url: string; -}; \ No newline at end of file +}; diff --git a/frontend/src/client/core/CancelablePromise.ts b/frontend/src/client/core/CancelablePromise.ts index ccc082e..4d9f819 100644 --- a/frontend/src/client/core/CancelablePromise.ts +++ b/frontend/src/client/core/CancelablePromise.ts @@ -1,7 +1,7 @@ export class CancelError extends Error { constructor(message: string) { super(message); - this.name = 'CancelError'; + this.name = "CancelError"; } public get isCancelled(): boolean { @@ -30,8 +30,8 @@ export class CancelablePromise implements Promise { executor: ( resolve: (value: T | PromiseLike) => void, reject: (reason?: unknown) => void, - onCancel: OnCancel - ) => void + onCancel: OnCancel, + ) => void, ) { this._isResolved = false; this._isRejected = false; @@ -64,15 +64,15 @@ export class CancelablePromise implements Promise { this.cancelHandlers.push(cancelHandler); }; - Object.defineProperty(onCancel, 'isResolved', { + Object.defineProperty(onCancel, "isResolved", { get: (): boolean => this._isResolved, }); - Object.defineProperty(onCancel, 'isRejected', { + Object.defineProperty(onCancel, "isRejected", { get: (): boolean => this._isRejected, }); - Object.defineProperty(onCancel, 'isCancelled', { + Object.defineProperty(onCancel, "isCancelled", { get: (): boolean => this._isCancelled, }); @@ -86,13 +86,13 @@ export class CancelablePromise implements Promise { public then( onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, ): Promise { return this.promise.then(onFulfilled, onRejected); } public catch( - onRejected?: ((reason: unknown) => TResult | PromiseLike) | null + onRejected?: ((reason: unknown) => TResult | PromiseLike) | null, ): Promise { return this.promise.catch(onRejected); } @@ -112,15 +112,15 @@ export class CancelablePromise implements Promise { cancelHandler(); } } catch (error) { - console.warn('Cancellation threw an error', error); + console.warn("Cancellation threw an error", error); return; } } this.cancelHandlers.length = 0; - if (this._reject) this._reject(new CancelError('Request aborted')); + if (this._reject) this._reject(new CancelError("Request aborted")); } public get isCancelled(): boolean { return this._isCancelled; } -} \ No newline at end of file +} diff --git a/frontend/src/client/core/OpenAPI.ts b/frontend/src/client/core/OpenAPI.ts index 8e48f3c..2505a43 100644 --- a/frontend/src/client/core/OpenAPI.ts +++ b/frontend/src/client/core/OpenAPI.ts @@ -1,32 +1,32 @@ -import type { AxiosRequestConfig, AxiosResponse } from 'axios'; -import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { AxiosRequestConfig, AxiosResponse } from "axios"; +import type { ApiRequestOptions } from "./ApiRequestOptions"; type Headers = Record; type Middleware = (value: T) => T | Promise; type Resolver = (options: ApiRequestOptions) => Promise; export class Interceptors { - _fns: Middleware[]; + _fns: Middleware[]; - constructor() { - this._fns = []; - } + constructor() { + this._fns = []; + } - eject(fn: Middleware): void { - const index = this._fns.indexOf(fn); - if (index !== -1) { - this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; - } - } + eject(fn: Middleware): void { + const index = this._fns.indexOf(fn); + if (index !== -1) { + this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; + } + } - use(fn: Middleware): void { - this._fns = [...this._fns, fn]; - } + use(fn: Middleware): void { + this._fns = [...this._fns, fn]; + } } export type OpenAPIConfig = { BASE: string; - CREDENTIALS: 'include' | 'omit' | 'same-origin'; + CREDENTIALS: "include" | "omit" | "same-origin"; ENCODE_PATH?: ((path: string) => string) | undefined; HEADERS?: Headers | Resolver | undefined; PASSWORD?: string | Resolver | undefined; @@ -41,17 +41,17 @@ export type OpenAPIConfig = { }; export const OpenAPI: OpenAPIConfig = { - BASE: '', - CREDENTIALS: 'include', + BASE: "", + CREDENTIALS: "include", ENCODE_PATH: undefined, HEADERS: undefined, PASSWORD: undefined, TOKEN: undefined, USERNAME: undefined, - VERSION: '0.0.1', + VERSION: "0.0.1", WITH_CREDENTIALS: false, interceptors: { request: new Interceptors(), response: new Interceptors(), }, -}; \ No newline at end of file +}; diff --git a/frontend/src/client/core/request.ts b/frontend/src/client/core/request.ts index ecc2e39..625bf24 100644 --- a/frontend/src/client/core/request.ts +++ b/frontend/src/client/core/request.ts @@ -1,19 +1,24 @@ -import axios from 'axios'; -import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'; +import axios from "axios"; +import type { + AxiosError, + AxiosRequestConfig, + AxiosResponse, + AxiosInstance, +} from "axios"; -import { ApiError } from './ApiError'; -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; -import { CancelablePromise } from './CancelablePromise'; -import type { OnCancel } from './CancelablePromise'; -import type { OpenAPIConfig } from './OpenAPI'; +import { ApiError } from "./ApiError"; +import type { ApiRequestOptions } from "./ApiRequestOptions"; +import type { ApiResult } from "./ApiResult"; +import { CancelablePromise } from "./CancelablePromise"; +import type { OnCancel } from "./CancelablePromise"; +import type { OpenAPIConfig } from "./OpenAPI"; export const isString = (value: unknown): value is string => { - return typeof value === 'string'; + return typeof value === "string"; }; export const isStringWithValue = (value: unknown): value is string => { - return isString(value) && value !== ''; + return isString(value) && value !== ""; }; export const isBlob = (value: any): value is Blob => { @@ -33,7 +38,7 @@ export const base64 = (str: string): string => { return btoa(str); } catch (err) { // @ts-ignore - return Buffer.from(str).toString('base64'); + return Buffer.from(str).toString("base64"); } }; @@ -52,8 +57,8 @@ export const getQueryString = (params: Record): string => { if (value instanceof Date) { append(key, value.toISOString()); } else if (Array.isArray(value)) { - value.forEach(v => encodePair(key, v)); - } else if (typeof value === 'object') { + value.forEach((v) => encodePair(key, v)); + } else if (typeof value === "object") { Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v)); } else { append(key, value); @@ -62,14 +67,14 @@ export const getQueryString = (params: Record): string => { Object.entries(params).forEach(([key, value]) => encodePair(key, value)); - return qs.length ? `?${qs.join('&')}` : ''; + return qs.length ? `?${qs.join("&")}` : ""; }; const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { const encoder = config.ENCODE_PATH || encodeURI; const path = options.url - .replace('{api-version}', config.VERSION) + .replace("{api-version}", config.VERSION) .replace(/{(.*?)}/g, (substring: string, group: string) => { if (options.path?.hasOwnProperty(group)) { return encoder(String(options.path[group])); @@ -81,7 +86,9 @@ const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { return options.query ? url + getQueryString(options.query) : url; }; -export const getFormData = (options: ApiRequestOptions): FormData | undefined => { +export const getFormData = ( + options: ApiRequestOptions, +): FormData | undefined => { if (options.formData) { const formData = new FormData(); @@ -97,7 +104,7 @@ export const getFormData = (options: ApiRequestOptions): FormData | undefined => .filter(([, value]) => value !== undefined && value !== null) .forEach(([key, value]) => { if (Array.isArray(value)) { - value.forEach(v => process(key, v)); + value.forEach((v) => process(key, v)); } else { process(key, value); } @@ -110,14 +117,20 @@ export const getFormData = (options: ApiRequestOptions): FormData | undefined => type Resolver = (options: ApiRequestOptions) => Promise; -export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { - if (typeof resolver === 'function') { +export const resolve = async ( + options: ApiRequestOptions, + resolver?: T | Resolver, +): Promise => { + if (typeof resolver === "function") { return (resolver as Resolver)(options); } return resolver; }; -export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise> => { +export const getHeaders = async ( + config: OpenAPIConfig, + options: ApiRequestOptions, +): Promise> => { const [token, username, password, additionalHeaders] = await Promise.all([ // @ts-ignore resolve(options, config.TOKEN), @@ -130,38 +143,41 @@ export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOp ]); const headers = Object.entries({ - Accept: 'application/json', + Accept: "application/json", ...additionalHeaders, ...options.headers, }) - .filter(([, value]) => value !== undefined && value !== null) - .reduce((headers, [key, value]) => ({ - ...headers, - [key]: String(value), - }), {} as Record); + .filter(([, value]) => value !== undefined && value !== null) + .reduce( + (headers, [key, value]) => ({ + ...headers, + [key]: String(value), + }), + {} as Record, + ); if (isStringWithValue(token)) { - headers['Authorization'] = `Bearer ${token}`; + headers["Authorization"] = `Bearer ${token}`; } if (isStringWithValue(username) && isStringWithValue(password)) { const credentials = base64(`${username}:${password}`); - headers['Authorization'] = `Basic ${credentials}`; + headers["Authorization"] = `Basic ${credentials}`; } if (options.body !== undefined) { if (options.mediaType) { - headers['Content-Type'] = options.mediaType; + headers["Content-Type"] = options.mediaType; } else if (isBlob(options.body)) { - headers['Content-Type'] = options.body.type || 'application/octet-stream'; + headers["Content-Type"] = options.body.type || "application/octet-stream"; } else if (isString(options.body)) { - headers['Content-Type'] = 'text/plain'; + headers["Content-Type"] = "text/plain"; } else if (!isFormData(options.body)) { - headers['Content-Type'] = 'application/json'; + headers["Content-Type"] = "application/json"; } } else if (options.formData !== undefined) { if (options.mediaType) { - headers['Content-Type'] = options.mediaType; + headers["Content-Type"] = options.mediaType; } } @@ -183,7 +199,7 @@ export const sendRequest = async ( formData: FormData | undefined, headers: Record, onCancel: OnCancel, - axiosClient: AxiosInstance + axiosClient: AxiosInstance, ): Promise> => { const controller = new AbortController(); @@ -213,7 +229,10 @@ export const sendRequest = async ( } }; -export const getResponseHeader = (response: AxiosResponse, responseHeader?: string): string | undefined => { +export const getResponseHeader = ( + response: AxiosResponse, + responseHeader?: string, +): string | undefined => { if (responseHeader) { const content = response.headers[responseHeader]; if (isString(content)) { @@ -230,50 +249,53 @@ export const getResponseBody = (response: AxiosResponse): unknown => { return undefined; }; -export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { +export const catchErrorCodes = ( + options: ApiRequestOptions, + result: ApiResult, +): void => { const errors: Record = { - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Payload Too Large', - 414: 'URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Range Not Satisfiable', - 417: 'Expectation Failed', - 418: 'Im a teapot', - 421: 'Misdirected Request', - 422: 'Unprocessable Content', - 423: 'Locked', - 424: 'Failed Dependency', - 425: 'Too Early', - 426: 'Upgrade Required', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - 451: 'Unavailable For Legal Reasons', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 506: 'Variant Also Negotiates', - 507: 'Insufficient Storage', - 508: 'Loop Detected', - 510: 'Not Extended', - 511: 'Network Authentication Required', + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Payload Too Large", + 414: "URI Too Long", + 415: "Unsupported Media Type", + 416: "Range Not Satisfiable", + 417: "Expectation Failed", + 418: "Im a teapot", + 421: "Misdirected Request", + 422: "Unprocessable Content", + 423: "Locked", + 424: "Failed Dependency", + 425: "Too Early", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 510: "Not Extended", + 511: "Network Authentication Required", ...options.errors, - } + }; const error = errors[result.status]; if (error) { @@ -281,8 +303,8 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): } if (!result.ok) { - const errorStatus = result.status ?? 'unknown'; - const errorStatusText = result.statusText ?? 'unknown'; + const errorStatus = result.status ?? "unknown"; + const errorStatusText = result.statusText ?? "unknown"; const errorBody = (() => { try { return JSON.stringify(result.body, null, 2); @@ -291,8 +313,10 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): } })(); - throw new ApiError(options, result, - `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` + throw new ApiError( + options, + result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`, ); } }; @@ -305,7 +329,11 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): * @returns CancelablePromise * @throws ApiError */ -export const request = (config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise => { +export const request = ( + config: OpenAPIConfig, + options: ApiRequestOptions, + axiosClient: AxiosInstance = axios, +): CancelablePromise => { return new CancelablePromise(async (resolve, reject, onCancel) => { try { const url = getUrl(config, options); @@ -314,18 +342,30 @@ export const request = (config: OpenAPIConfig, options: ApiRequestOptions, const headers = await getHeaders(config, options); if (!onCancel.isCancelled) { - let response = await sendRequest(config, options, url, body, formData, headers, onCancel, axiosClient); + let response = await sendRequest( + config, + options, + url, + body, + formData, + headers, + onCancel, + axiosClient, + ); for (const fn of config.interceptors.response._fns) { response = await fn(response); } const responseBody = getResponseBody(response); - const responseHeader = getResponseHeader(response, options.responseHeader); + const responseHeader = getResponseHeader( + response, + options.responseHeader, + ); let transformedBody = responseBody; if (options.responseTransformer && isSuccess(response.status)) { - transformedBody = await options.responseTransformer(responseBody) + transformedBody = await options.responseTransformer(responseBody); } const result: ApiResult = { @@ -344,4 +384,4 @@ export const request = (config: OpenAPIConfig, options: ApiRequestOptions, reject(error); } }); -}; \ No newline at end of file +}; diff --git a/frontend/src/client/index.ts b/frontend/src/client/index.ts index 50a1dd7..0739a12 100644 --- a/frontend/src/client/index.ts +++ b/frontend/src/client/index.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -export { ApiError } from './core/ApiError'; -export { CancelablePromise, CancelError } from './core/CancelablePromise'; -export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI'; -export * from './sdk.gen'; -export * from './types.gen'; \ No newline at end of file +export { ApiError } from "./core/ApiError"; +export { CancelablePromise, CancelError } from "./core/CancelablePromise"; +export { OpenAPI, type OpenAPIConfig } from "./core/OpenAPI"; +export * from "./sdk.gen"; +export * from "./types.gen"; diff --git a/frontend/src/client/sdk.gen.ts b/frontend/src/client/sdk.gen.ts index 464264e..3df1758 100644 --- a/frontend/src/client/sdk.gen.ts +++ b/frontend/src/client/sdk.gen.ts @@ -1,438 +1,485 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { CancelablePromise } from './core/CancelablePromise'; -import { OpenAPI } from './core/OpenAPI'; -import { request as __request } from './core/request'; -import type { UserGetUserResponse, UserUpdateUserData, UserUpdateUserResponse, UserRegisterData, UserRegisterResponse, UserDeleteUserResponse, DashboardLoginAccessTokenData, DashboardLoginAccessTokenResponse, DashboardRegisterNewShopData, DashboardRegisterNewShopResponse, ShopLoginAccessTokenData, ShopLoginAccessTokenResponse, ShopDeleteUserData, ShopDeleteUserResponse, ShopLogoutResponse, ShopRegisterData, ShopRegisterResponse, ShopUpdateUserData, ShopUpdateUserResponse, UtilsHealthCheckResponse, UtilsTestDbResponse } from './types.gen'; +import type { CancelablePromise } from "./core/CancelablePromise"; +import { OpenAPI } from "./core/OpenAPI"; +import { request as __request } from "./core/request"; +import type { + UserGetUserResponse, + UserUpdateUserData, + UserUpdateUserResponse, + UserRegisterData, + UserRegisterResponse, + UserDeleteUserResponse, + DashboardLoginAccessTokenData, + DashboardLoginAccessTokenResponse, + DashboardRegisterNewShopData, + DashboardRegisterNewShopResponse, + ShopLoginAccessTokenData, + ShopLoginAccessTokenResponse, + ShopDeleteUserData, + ShopDeleteUserResponse, + ShopLogoutResponse, + ShopRegisterData, + ShopRegisterResponse, + ShopUpdateUserData, + ShopUpdateUserResponse, + UtilsHealthCheckResponse, + UtilsTestDbResponse, +} from "./types.gen"; export class DashboardService { - /** - * Get information about currently logged in user - * @returns UserPublic Successful Response - * @throws ApiError - */ - public static userGetUser(): CancelablePromise { - return __request(OpenAPI, { - method: 'GET', - url: '/user' - }); - } - - /** - * Update user details - * @param data The data for the request. - * @param data.requestBody - * @returns boolean Successful Response - * @throws ApiError - */ - public static userUpdateUser(data: UserUpdateUserData): CancelablePromise { - return __request(OpenAPI, { - method: 'PUT', - url: '/user', - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Register new user - * @param data The data for the request. - * @param data.requestBody - * @returns boolean Successful Response - * @throws ApiError - */ - public static userRegister(data: UserRegisterData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/user', - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Delete user - * @returns boolean Successful Response - * @throws ApiError - */ - public static userDeleteUser(): CancelablePromise { - return __request(OpenAPI, { - method: 'DELETE', - url: '/user' - }); - } - - /** - * Login Access Token - * OAuth2 compatible token login for the dashboard. - * - * This endpoint generates an access token required for authenticating future - * requests to the dashboard section of the application. The token is valid for - * a predefined expiration period. - * - * - **username**: User's email - * - **password**: User's password - * - * **Note:** This login is restricted to dashboard access only and cannot be - * used for tenant accounts access to shops - * @param data The data for the request. - * @param data.formData - * @returns Token Successful Response - * @throws ApiError - */ - public static dashboardLoginAccessToken(data: DashboardLoginAccessTokenData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/login/access-token', - formData: data.formData, - mediaType: 'application/x-www-form-urlencoded', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Register New Shop - * @param data The data for the request. - * @param data.requestBody - * @returns boolean Successful Response - * @throws ApiError - */ - public static dashboardRegisterNewShop(data: DashboardRegisterNewShopData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/shop', - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - + /** + * Get information about currently logged in user + * @returns UserPublic Successful Response + * @throws ApiError + */ + public static userGetUser(): CancelablePromise { + return __request(OpenAPI, { + method: "GET", + url: "/user", + }); + } + + /** + * Update user details + * @param data The data for the request. + * @param data.requestBody + * @returns boolean Successful Response + * @throws ApiError + */ + public static userUpdateUser( + data: UserUpdateUserData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "PUT", + url: "/user", + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Register new user + * @param data The data for the request. + * @param data.requestBody + * @returns boolean Successful Response + * @throws ApiError + */ + public static userRegister( + data: UserRegisterData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/user", + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Delete user + * @returns boolean Successful Response + * @throws ApiError + */ + public static userDeleteUser(): CancelablePromise { + return __request(OpenAPI, { + method: "DELETE", + url: "/user", + }); + } + + /** + * Login Access Token + * OAuth2 compatible token login for the dashboard. + * + * This endpoint generates an access token required for authenticating future + * requests to the dashboard section of the application. The token is valid for + * a predefined expiration period. + * + * - **username**: User's email + * - **password**: User's password + * + * **Note:** This login is restricted to dashboard access only and cannot be + * used for tenant accounts access to shops + * @param data The data for the request. + * @param data.formData + * @returns Token Successful Response + * @throws ApiError + */ + public static dashboardLoginAccessToken( + data: DashboardLoginAccessTokenData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/login/access-token", + formData: data.formData, + mediaType: "application/x-www-form-urlencoded", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Register New Shop + * @param data The data for the request. + * @param data.requestBody + * @returns boolean Successful Response + * @throws ApiError + */ + public static dashboardRegisterNewShop( + data: DashboardRegisterNewShopData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/shop", + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } } export class LoginService { - /** - * Login Access Token - * OAuth2 compatible token login for the dashboard. - * - * This endpoint generates an access token required for authenticating future - * requests to the dashboard section of the application. The token is valid for - * a predefined expiration period. - * - * - **username**: User's email - * - **password**: User's password - * - * **Note:** This login is restricted to dashboard access only and cannot be - * used for tenant accounts access to shops - * @param data The data for the request. - * @param data.formData - * @returns Token Successful Response - * @throws ApiError - */ - public static dashboardLoginAccessToken(data: DashboardLoginAccessTokenData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/login/access-token', - formData: data.formData, - mediaType: 'application/x-www-form-urlencoded', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Login Access Token - * OAuth2 compatible token login, get an access token for future requests - * @param data The data for the request. - * @param data.formData - * @returns Token Successful Response - * @throws ApiError - */ - public static shopLoginAccessToken(data: ShopLoginAccessTokenData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/shop/{shop_uuid}/login/access-token', - formData: data.formData, - mediaType: 'application/x-www-form-urlencoded', - errors: { - 422: 'Validation Error' - } - }); - } - + /** + * Login Access Token + * OAuth2 compatible token login for the dashboard. + * + * This endpoint generates an access token required for authenticating future + * requests to the dashboard section of the application. The token is valid for + * a predefined expiration period. + * + * - **username**: User's email + * - **password**: User's password + * + * **Note:** This login is restricted to dashboard access only and cannot be + * used for tenant accounts access to shops + * @param data The data for the request. + * @param data.formData + * @returns Token Successful Response + * @throws ApiError + */ + public static dashboardLoginAccessToken( + data: DashboardLoginAccessTokenData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/login/access-token", + formData: data.formData, + mediaType: "application/x-www-form-urlencoded", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Login Access Token + * OAuth2 compatible token login, get an access token for future requests + * @param data The data for the request. + * @param data.formData + * @returns Token Successful Response + * @throws ApiError + */ + public static shopLoginAccessToken( + data: ShopLoginAccessTokenData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/shop/{shop_uuid}/login/access-token", + formData: data.formData, + mediaType: "application/x-www-form-urlencoded", + errors: { + 422: "Validation Error", + }, + }); + } } export class ShopService { - /** - * Login Access Token - * OAuth2 compatible token login, get an access token for future requests - * @param data The data for the request. - * @param data.formData - * @returns Token Successful Response - * @throws ApiError - */ - public static shopLoginAccessToken(data: ShopLoginAccessTokenData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/shop/{shop_uuid}/login/access-token', - formData: data.formData, - mediaType: 'application/x-www-form-urlencoded', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Delete user - * @param data The data for the request. - * @param data.shopUuid - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopDeleteUser(data: ShopDeleteUserData): CancelablePromise { - return __request(OpenAPI, { - method: 'DELETE', - url: '/shop/{shop_uuid}/user/delete', - path: { - shop_uuid: data.shopUuid - }, - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * User logout - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopLogout(): CancelablePromise { - return __request(OpenAPI, { - method: 'DELETE', - url: '/shop/{shop_uuid}/user/logout' - }); - } - - /** - * Register new user - * @param data The data for the request. - * @param data.shopUuid - * @param data.requestBody - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopRegister(data: ShopRegisterData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/shop/{shop_uuid}/user/register', - path: { - shop_uuid: data.shopUuid - }, - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Update user details - * @param data The data for the request. - * @param data.requestBody - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopUpdateUser(data: ShopUpdateUserData): CancelablePromise { - return __request(OpenAPI, { - method: 'PUT', - url: '/shop/{shop_uuid}/user/update', - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - + /** + * Login Access Token + * OAuth2 compatible token login, get an access token for future requests + * @param data The data for the request. + * @param data.formData + * @returns Token Successful Response + * @throws ApiError + */ + public static shopLoginAccessToken( + data: ShopLoginAccessTokenData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/shop/{shop_uuid}/login/access-token", + formData: data.formData, + mediaType: "application/x-www-form-urlencoded", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Delete user + * @param data The data for the request. + * @param data.shopUuid + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopDeleteUser( + data: ShopDeleteUserData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "DELETE", + url: "/shop/{shop_uuid}/user/delete", + path: { + shop_uuid: data.shopUuid, + }, + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * User logout + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopLogout(): CancelablePromise { + return __request(OpenAPI, { + method: "DELETE", + url: "/shop/{shop_uuid}/user/logout", + }); + } + + /** + * Register new user + * @param data The data for the request. + * @param data.shopUuid + * @param data.requestBody + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopRegister( + data: ShopRegisterData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/shop/{shop_uuid}/user/register", + path: { + shop_uuid: data.shopUuid, + }, + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Update user details + * @param data The data for the request. + * @param data.requestBody + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopUpdateUser( + data: ShopUpdateUserData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "PUT", + url: "/shop/{shop_uuid}/user/update", + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } } export class UserService { - /** - * Get information about currently logged in user - * @returns UserPublic Successful Response - * @throws ApiError - */ - public static userGetUser(): CancelablePromise { - return __request(OpenAPI, { - method: 'GET', - url: '/user' - }); - } - - /** - * Update user details - * @param data The data for the request. - * @param data.requestBody - * @returns boolean Successful Response - * @throws ApiError - */ - public static userUpdateUser(data: UserUpdateUserData): CancelablePromise { - return __request(OpenAPI, { - method: 'PUT', - url: '/user', - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Register new user - * @param data The data for the request. - * @param data.requestBody - * @returns boolean Successful Response - * @throws ApiError - */ - public static userRegister(data: UserRegisterData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/user', - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Delete user - * @returns boolean Successful Response - * @throws ApiError - */ - public static userDeleteUser(): CancelablePromise { - return __request(OpenAPI, { - method: 'DELETE', - url: '/user' - }); - } - - /** - * Delete user - * @param data The data for the request. - * @param data.shopUuid - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopDeleteUser(data: ShopDeleteUserData): CancelablePromise { - return __request(OpenAPI, { - method: 'DELETE', - url: '/shop/{shop_uuid}/user/delete', - path: { - shop_uuid: data.shopUuid - }, - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * User logout - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopLogout(): CancelablePromise { - return __request(OpenAPI, { - method: 'DELETE', - url: '/shop/{shop_uuid}/user/logout' - }); - } - - /** - * Register new user - * @param data The data for the request. - * @param data.shopUuid - * @param data.requestBody - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopRegister(data: ShopRegisterData): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/shop/{shop_uuid}/user/register', - path: { - shop_uuid: data.shopUuid - }, - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - - /** - * Update user details - * @param data The data for the request. - * @param data.requestBody - * @returns unknown Successful Response - * @throws ApiError - */ - public static shopUpdateUser(data: ShopUpdateUserData): CancelablePromise { - return __request(OpenAPI, { - method: 'PUT', - url: '/shop/{shop_uuid}/user/update', - body: data.requestBody, - mediaType: 'application/json', - errors: { - 422: 'Validation Error' - } - }); - } - + /** + * Get information about currently logged in user + * @returns UserPublic Successful Response + * @throws ApiError + */ + public static userGetUser(): CancelablePromise { + return __request(OpenAPI, { + method: "GET", + url: "/user", + }); + } + + /** + * Update user details + * @param data The data for the request. + * @param data.requestBody + * @returns boolean Successful Response + * @throws ApiError + */ + public static userUpdateUser( + data: UserUpdateUserData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "PUT", + url: "/user", + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Register new user + * @param data The data for the request. + * @param data.requestBody + * @returns boolean Successful Response + * @throws ApiError + */ + public static userRegister( + data: UserRegisterData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/user", + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Delete user + * @returns boolean Successful Response + * @throws ApiError + */ + public static userDeleteUser(): CancelablePromise { + return __request(OpenAPI, { + method: "DELETE", + url: "/user", + }); + } + + /** + * Delete user + * @param data The data for the request. + * @param data.shopUuid + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopDeleteUser( + data: ShopDeleteUserData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "DELETE", + url: "/shop/{shop_uuid}/user/delete", + path: { + shop_uuid: data.shopUuid, + }, + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * User logout + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopLogout(): CancelablePromise { + return __request(OpenAPI, { + method: "DELETE", + url: "/shop/{shop_uuid}/user/logout", + }); + } + + /** + * Register new user + * @param data The data for the request. + * @param data.shopUuid + * @param data.requestBody + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopRegister( + data: ShopRegisterData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/shop/{shop_uuid}/user/register", + path: { + shop_uuid: data.shopUuid, + }, + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } + + /** + * Update user details + * @param data The data for the request. + * @param data.requestBody + * @returns unknown Successful Response + * @throws ApiError + */ + public static shopUpdateUser( + data: ShopUpdateUserData, + ): CancelablePromise { + return __request(OpenAPI, { + method: "PUT", + url: "/shop/{shop_uuid}/user/update", + body: data.requestBody, + mediaType: "application/json", + errors: { + 422: "Validation Error", + }, + }); + } } export class UtilsService { - /** - * Health Check - * Ping the API whether it's alive or not - * @returns boolean Successful Response - * @throws ApiError - */ - public static utilsHealthCheck(): CancelablePromise { - return __request(OpenAPI, { - method: 'GET', - url: '/utils/health-check/' - }); - } - - /** - * Test Db - * Ping database using select 1 to see if connection works - * @returns boolean Successful Response - * @throws ApiError - */ - public static utilsTestDb(): CancelablePromise { - return __request(OpenAPI, { - method: 'GET', - url: '/utils/test-db/' - }); - } - -} \ No newline at end of file + /** + * Health Check + * Ping the API whether it's alive or not + * @returns boolean Successful Response + * @throws ApiError + */ + public static utilsHealthCheck(): CancelablePromise { + return __request(OpenAPI, { + method: "GET", + url: "/utils/health-check/", + }); + } + + /** + * Test Db + * Ping database using select 1 to see if connection works + * @returns boolean Successful Response + * @throws ApiError + */ + public static utilsTestDb(): CancelablePromise { + return __request(OpenAPI, { + method: "GET", + url: "/utils/test-db/", + }); + } +} diff --git a/frontend/src/client/types.gen.ts b/frontend/src/client/types.gen.ts index 44979a4..b0c68c6 100644 --- a/frontend/src/client/types.gen.ts +++ b/frontend/src/client/types.gen.ts @@ -1,142 +1,142 @@ // This file is auto-generated by @hey-api/openapi-ts export type Body_Dashboard_login_access_token = { - grant_type?: (string | null); - username: string; - password: string; - scope?: string; - client_id?: (string | null); - client_secret?: (string | null); + grant_type?: string | null; + username: string; + password: string; + scope?: string; + client_id?: string | null; + client_secret?: string | null; }; export type Body_Shop_login_access_token = { - grant_type?: (string | null); - username: string; - password: string; - scope?: string; - client_id?: (string | null); - client_secret?: (string | null); + grant_type?: string | null; + username: string; + password: string; + scope?: string; + client_id?: string | null; + client_secret?: string | null; }; export type HTTPValidationError = { - detail?: Array; + detail?: Array; }; export type ShopAddress = { - street: string; - city: string; - state: string; - postal_code: string; - country: string; + street: string; + city: string; + state: string; + postal_code: string; + country: string; }; export type ShopCreate = { - name: string; - description: string; - currency: string; - contact_email: string; - contact_phone_number: string; - address: ShopAddress; + name: string; + description: string; + currency: string; + contact_email: string; + contact_phone_number: string; + address: ShopAddress; }; export type Token = { - access_token: string; - token_type?: string; + access_token: string; + token_type?: string; }; export type UserPublic = { - uuid: string; - username: string; - email: string; - first_name: (string | null); - last_name: (string | null); - phone_number: string; + uuid: string; + username: string; + email: string; + first_name: string | null; + last_name: string | null; + phone_number: string; }; export type UserRegister = { - username: string; - email: string; - phone_number: string; - /** - * Password must conform to this regex: - * ``` - * ^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$ - * ``` - */ - password: string; + username: string; + email: string; + phone_number: string; + /** + * Password must conform to this regex: + * ``` + * ^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$ + * ``` + */ + password: string; }; export type UserUpdate = { - email: (string | null); - phone_number: (string | null); - username: (string | null); - first_name?: (string | null); - last_name?: (string | null); + email: string | null; + phone_number: string | null; + username: string | null; + first_name?: string | null; + last_name?: string | null; }; export type ValidationError = { - loc: Array<(string | number)>; - msg: string; - type: string; + loc: Array; + msg: string; + type: string; }; -export type UserGetUserResponse = (UserPublic); +export type UserGetUserResponse = UserPublic; export type UserUpdateUserData = { - requestBody: UserUpdate; + requestBody: UserUpdate; }; -export type UserUpdateUserResponse = (boolean); +export type UserUpdateUserResponse = boolean; export type UserRegisterData = { - requestBody: UserRegister; + requestBody: UserRegister; }; -export type UserRegisterResponse = (boolean); +export type UserRegisterResponse = boolean; -export type UserDeleteUserResponse = (boolean); +export type UserDeleteUserResponse = boolean; export type DashboardLoginAccessTokenData = { - formData: Body_Dashboard_login_access_token; + formData: Body_Dashboard_login_access_token; }; -export type DashboardLoginAccessTokenResponse = (Token); +export type DashboardLoginAccessTokenResponse = Token; export type DashboardRegisterNewShopData = { - requestBody: ShopCreate; + requestBody: ShopCreate; }; -export type DashboardRegisterNewShopResponse = (boolean); +export type DashboardRegisterNewShopResponse = boolean; export type ShopLoginAccessTokenData = { - formData: Body_Shop_login_access_token; + formData: Body_Shop_login_access_token; }; -export type ShopLoginAccessTokenResponse = (Token); +export type ShopLoginAccessTokenResponse = Token; export type ShopDeleteUserData = { - shopUuid: unknown; + shopUuid: unknown; }; -export type ShopDeleteUserResponse = (unknown); +export type ShopDeleteUserResponse = unknown; -export type ShopLogoutResponse = (unknown); +export type ShopLogoutResponse = unknown; export type ShopRegisterData = { - requestBody: UserRegister; - shopUuid: unknown; + requestBody: UserRegister; + shopUuid: unknown; }; -export type ShopRegisterResponse = (unknown); +export type ShopRegisterResponse = unknown; export type ShopUpdateUserData = { - requestBody: { - [key: string]: unknown; - }; + requestBody: { + [key: string]: unknown; + }; }; -export type ShopUpdateUserResponse = (unknown); +export type ShopUpdateUserResponse = unknown; -export type UtilsHealthCheckResponse = (boolean); +export type UtilsHealthCheckResponse = boolean; -export type UtilsTestDbResponse = (boolean); \ No newline at end of file +export type UtilsTestDbResponse = boolean; diff --git a/frontend/src/components/command-menu.tsx b/frontend/src/components/command-menu.tsx index fb0c476..65153dc 100644 --- a/frontend/src/components/command-menu.tsx +++ b/frontend/src/components/command-menu.tsx @@ -4,7 +4,7 @@ import { IconArrowRightDashed, IconDeviceLaptop, IconMoon, - IconSun + IconSun, } from "@tabler/icons-react"; import { useSearch } from "@/context/search-context"; import { useTheme } from "@/context/theme-context"; @@ -15,7 +15,7 @@ import { CommandInput, CommandItem, CommandList, - CommandSeparator + CommandSeparator, } from "@/components/ui/command"; import { sidebarData } from "./layout/data/sidebar-data"; import { ScrollArea } from "./ui/scroll-area"; @@ -30,7 +30,7 @@ export function CommandMenu() { setOpen(false); command(); }, - [setOpen] + [setOpen], ); return ( @@ -49,7 +49,8 @@ export function CommandMenu() { value={navItem.title} onSelect={() => { runCommand(() => navigate({ to: navItem.url })); - }}> + }} + >
@@ -63,7 +64,8 @@ export function CommandMenu() { value={subItem.title} onSelect={() => { runCommand(() => navigate({ to: subItem.url })); - }}> + }} + >
diff --git a/frontend/src/components/confirm-dialog.tsx b/frontend/src/components/confirm-dialog.tsx index e72fd12..da531d9 100644 --- a/frontend/src/components/confirm-dialog.tsx +++ b/frontend/src/components/confirm-dialog.tsx @@ -6,7 +6,7 @@ import { AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, - AlertDialogTitle + AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; @@ -56,7 +56,8 @@ export function ConfirmDialog(props: ConfirmDialogProps) { diff --git a/frontend/src/components/currency-input.tsx b/frontend/src/components/currency-input.tsx index 30fb3e9..a30e12e 100644 --- a/frontend/src/components/currency-input.tsx +++ b/frontend/src/components/currency-input.tsx @@ -153,5 +153,5 @@ const currencies = [ "YER", "ZAR", "ZMW", - "ZWG" + "ZWG", ]; diff --git a/frontend/src/components/layout/app-sidebar.tsx b/frontend/src/components/layout/app-sidebar.tsx index 6187e77..114b83d 100644 --- a/frontend/src/components/layout/app-sidebar.tsx +++ b/frontend/src/components/layout/app-sidebar.tsx @@ -2,11 +2,12 @@ import { Sidebar, SidebarContent, SidebarHeader, - SidebarRail + SidebarRail, } from "@/components/ui/sidebar"; import { NavGroup } from "@/components/layout/nav-group"; import { sidebarData } from "./data/sidebar-data"; import { cn } from "@/lib/utils"; +import { IconCoin } from "@tabler/icons-react"; export function AppSidebar({ ...props }: React.ComponentProps) { return ( @@ -14,10 +15,11 @@ export function AppSidebar({ ...props }: React.ComponentProps) {

- SwagShop + {...props} + > +

@@ -25,9 +27,6 @@ export function AppSidebar({ ...props }: React.ComponentProps) { ))} - {/* - - */} ); diff --git a/frontend/src/components/layout/data/sidebar-data.ts b/frontend/src/components/layout/data/sidebar-data.ts index cf906db..6c5097e 100644 --- a/frontend/src/components/layout/data/sidebar-data.ts +++ b/frontend/src/components/layout/data/sidebar-data.ts @@ -1,6 +1,7 @@ import { IconBrowserCheck, IconBuildingStore, + IconClipboardCheckFilled, IconCoin, IconForklift, IconHelp, @@ -8,14 +9,12 @@ import { IconNotification, IconPackage, IconPalette, - IconPercentage, IconSettings, IconTag, IconTool, IconUserCog, - IconUsers + IconUsers, } from "@tabler/icons-react"; -import { AudioWaveform, Command, GalleryVerticalEnd } from "lucide-react"; import { type SidebarData } from "../types"; export const sidebarData: SidebarData = { @@ -26,45 +25,45 @@ export const sidebarData: SidebarData = { { title: "Dashboard", url: "/dashboard", - icon: IconLayoutDashboard + icon: IconLayoutDashboard, }, { title: "Shop", url: "/dashboard/shop", - icon: IconBuildingStore + icon: IconBuildingStore, }, { title: "Products", url: "/dashboard/products", - icon: IconPackage + icon: IconPackage, }, { title: "Inventory", url: "/dashboard/tasks", - icon: IconForklift + icon: IconForklift, }, { title: "Sales", icon: IconCoin, items: [ { - title: "Discounts", - url: "/dashboard/sales/discounts", - icon: IconPercentage + title: "Recent sales", + url: "/dashboard/sales/recent-sales", + icon: IconClipboardCheckFilled, }, { title: "Coupons", url: "/dashboard/sales/coupons", - icon: IconTag - } - ] + icon: IconTag, + }, + ], }, { title: "Customers", url: "/dashboard/users", - icon: IconUsers - } - ] + icon: IconUsers, + }, + ], }, { title: "Other", @@ -76,36 +75,36 @@ export const sidebarData: SidebarData = { { title: "Profile", url: "/dashboard/settings", - icon: IconUserCog + icon: IconUserCog, }, { title: "Account", url: "/dashboard/settings/account", - icon: IconTool + icon: IconTool, }, { title: "Appearance", url: "/dashboard/settings/appearance", - icon: IconPalette + icon: IconPalette, }, { title: "Notifications", url: "/dashboard/settings/notifications", - icon: IconNotification + icon: IconNotification, }, { title: "Display", url: "/dashboard/settings/display", - icon: IconBrowserCheck - } - ] + icon: IconBrowserCheck, + }, + ], }, { title: "Help Center", url: "/help-center", - icon: IconHelp - } - ] - } - ] + icon: IconHelp, + }, + ], + }, + ], }; diff --git a/frontend/src/components/layout/header.tsx b/frontend/src/components/layout/header.tsx index 12a84b2..64d13aa 100644 --- a/frontend/src/components/layout/header.tsx +++ b/frontend/src/components/layout/header.tsx @@ -34,9 +34,10 @@ export const Header = ({ "flex h-16 items-center gap-3 bg-background p-4 sm:gap-4", fixed && "header-fixed peer/header fixed z-50 w-[inherit] rounded-md", offset > 10 && fixed ? "shadow" : "shadow-none", - className + className, )} - {...props}> + {...props} + > {children} diff --git a/frontend/src/components/layout/main.tsx b/frontend/src/components/layout/main.tsx index cd7ed34..ec99856 100644 --- a/frontend/src/components/layout/main.tsx +++ b/frontend/src/components/layout/main.tsx @@ -12,7 +12,7 @@ export const Main = ({ fixed, ...props }: MainProps) => { className={cn( "peer-[.header-fixed]/header:mt-16", "px-4 py-6", - fixed && "fixed-main flex flex-grow flex-col overflow-hidden" + fixed && "fixed-main flex flex-grow flex-col overflow-hidden", )} {...props} /> diff --git a/frontend/src/components/layout/nav-group.tsx b/frontend/src/components/layout/nav-group.tsx index 40fd93e..a6415bc 100644 --- a/frontend/src/components/layout/nav-group.tsx +++ b/frontend/src/components/layout/nav-group.tsx @@ -4,7 +4,7 @@ import { ChevronRight } from "lucide-react"; import { Collapsible, CollapsibleContent, - CollapsibleTrigger + CollapsibleTrigger, } from "@/components/ui/collapsible"; import { SidebarGroup, @@ -15,7 +15,7 @@ import { SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, - useSidebar + useSidebar, } from "@/components/ui/sidebar"; import { Badge } from "../ui/badge"; import { @@ -24,7 +24,7 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, - DropdownMenuTrigger + DropdownMenuTrigger, } from "../ui/dropdown-menu"; import { NavCollapsible, NavItem, NavLink, type NavGroup } from "./types"; @@ -64,7 +64,8 @@ const SidebarMenuLink = ({ item, href }: { item: NavLink; href: string }) => { + tooltip={item.title} + > setOpenMobile(false)}> {item.icon && } {item.title} @@ -77,7 +78,7 @@ const SidebarMenuLink = ({ item, href }: { item: NavLink; href: string }) => { const SidebarMenuCollapsible = ({ item, - href + href, }: { item: NavCollapsible; href: string; @@ -87,7 +88,8 @@ const SidebarMenuCollapsible = ({ + className="group/collapsible" + > @@ -103,7 +105,8 @@ const SidebarMenuCollapsible = ({ + isActive={checkIsActive(href, subItem)} + > setOpenMobile(false)}> {subItem.icon && } {subItem.title} @@ -121,7 +124,7 @@ const SidebarMenuCollapsible = ({ const SidebarMenuCollapsedDropdown = ({ item, - href + href, }: { item: NavCollapsible; href: string; @@ -132,7 +135,8 @@ const SidebarMenuCollapsedDropdown = ({ + isActive={checkIsActive(href, item)} + > {item.icon && } {item.title} {item.badge && {item.badge}} @@ -148,7 +152,8 @@ const SidebarMenuCollapsedDropdown = ({ + className={`${checkIsActive(href, sub) ? "bg-secondary" : ""}`} + > {sub.icon && } {sub.title} {sub.badge && ( diff --git a/frontend/src/components/layout/nav-user.tsx b/frontend/src/components/layout/nav-user.tsx index d340699..3545632 100644 --- a/frontend/src/components/layout/nav-user.tsx +++ b/frontend/src/components/layout/nav-user.tsx @@ -5,7 +5,7 @@ import { ChevronsUpDown, CreditCard, LogOut, - Sparkles + Sparkles, } from "lucide-react"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { @@ -15,17 +15,17 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, - DropdownMenuTrigger + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, - useSidebar + useSidebar, } from "@/components/ui/sidebar"; export function NavUser({ - user + user, }: { user: { name: string; @@ -42,7 +42,8 @@ export function NavUser({ + className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" + > SN @@ -58,7 +59,8 @@ export function NavUser({ className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg" side={isMobile ? "bottom" : "right"} align="end" - sideOffset={4}> + sideOffset={4} + >
diff --git a/frontend/src/components/layout/team-switcher.tsx b/frontend/src/components/layout/team-switcher.tsx index 1eb944e..523e04a 100644 --- a/frontend/src/components/layout/team-switcher.tsx +++ b/frontend/src/components/layout/team-switcher.tsx @@ -7,17 +7,17 @@ import { DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, - DropdownMenuTrigger + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, - useSidebar + useSidebar, } from "@/components/ui/sidebar"; export function TeamSwitcher({ - teams + teams, }: { teams: { name: string; @@ -35,7 +35,8 @@ export function TeamSwitcher({ + className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" + >
@@ -52,7 +53,8 @@ export function TeamSwitcher({ className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg" align="start" side={isMobile ? "bottom" : "right"} - sideOffset={4}> + sideOffset={4} + > Teams @@ -60,7 +62,8 @@ export function TeamSwitcher({ setActiveTeam(team)} - className="gap-2 p-2"> + className="gap-2 p-2" + >
diff --git a/frontend/src/components/layout/top-nav.tsx b/frontend/src/components/layout/top-nav.tsx index bda0805..351d4e5 100644 --- a/frontend/src/components/layout/top-nav.tsx +++ b/frontend/src/components/layout/top-nav.tsx @@ -6,7 +6,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuTrigger + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; interface TopNavProps extends React.HTMLAttributes { @@ -34,7 +34,8 @@ export function TopNav({ className, links, ...props }: TopNavProps) { + disabled={disabled} + > {title}
@@ -46,15 +47,17 @@ export function TopNav({ className, links, ...props }: TopNavProps) {