Added customer list, coupon list and started working on purchase list
This commit is contained in:
parent
2fe5dbcb21
commit
f2af9dc566
30
frontend/biome.json.old
Normal file
30
frontend/biome.json.old
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"format:check": "prettier --check .",
|
|
||||||
"format": "prettier --write .",
|
|
||||||
"knip": "knip",
|
"knip": "knip",
|
||||||
"generate-client": "openapi-ts"
|
"generate-client": "openapi-ts",
|
||||||
|
"format:write": "biome format ./src/ --write",
|
||||||
|
"format:check": "biome check ./src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.9.1",
|
"@hookform/resolvers": "^3.9.1",
|
||||||
@ -62,6 +62,7 @@
|
|||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@eslint/js": "^9.16.0",
|
"@eslint/js": "^9.16.0",
|
||||||
"@faker-js/faker": "^9.3.0",
|
"@faker-js/faker": "^9.3.0",
|
||||||
"@hey-api/client-axios": "^0.6.2",
|
"@hey-api/client-axios": "^0.6.2",
|
||||||
|
91
frontend/pnpm-lock.yaml
generated
91
frontend/pnpm-lock.yaml
generated
@ -147,6 +147,9 @@ importers:
|
|||||||
specifier: ^5.0.3
|
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))
|
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:
|
devDependencies:
|
||||||
|
'@biomejs/biome':
|
||||||
|
specifier: ^1.9.4
|
||||||
|
version: 1.9.4
|
||||||
'@eslint/js':
|
'@eslint/js':
|
||||||
specifier: ^9.16.0
|
specifier: ^9.16.0
|
||||||
version: 9.24.0
|
version: 9.24.0
|
||||||
@ -365,6 +368,59 @@ packages:
|
|||||||
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
|
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
|
||||||
engines: {node: '>=6.9.0'}
|
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':
|
'@date-fns/tz@1.2.0':
|
||||||
resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==}
|
resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==}
|
||||||
|
|
||||||
@ -3379,6 +3435,41 @@ snapshots:
|
|||||||
'@babel/helper-string-parser': 7.25.9
|
'@babel/helper-string-parser': 7.25.9
|
||||||
'@babel/helper-validator-identifier': 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': {}
|
'@date-fns/tz@1.2.0': {}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.25.2':
|
'@esbuild/aix-ppc64@0.25.2':
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
UserPublic,
|
UserPublic,
|
||||||
UserRegister,
|
UserRegister,
|
||||||
UserUpdate,
|
UserUpdate,
|
||||||
ShopLoginAccessTokenData
|
ShopLoginAccessTokenData,
|
||||||
} from "@/client";
|
} from "@/client";
|
||||||
import { Shop } from "./mock/models";
|
import { Shop } from "./mock/models";
|
||||||
|
|
||||||
|
@ -3,10 +3,19 @@ import { MockAuthAPI } from "./mock/auth-mock-api";
|
|||||||
import { AuthAPI, ShopAPI } from "./api-definition";
|
import { AuthAPI, ShopAPI } from "./api-definition";
|
||||||
import { MockShopAPI } from "./mock/shop-mock-api";
|
import { MockShopAPI } from "./mock/shop-mock-api";
|
||||||
import { MockProductAPI } from "./mock/products-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 =
|
export const authAPI: AuthAPI =
|
||||||
import.meta.env.VITE_USE_MOCK_API === "true" ? MockAuthAPI : RealAuthAPI;
|
import.meta.env.VITE_USE_MOCK_API === "true" ? MockAuthAPI : RealAuthAPI;
|
||||||
|
|
||||||
export const shopAPI: ShopAPI = MockShopAPI;
|
export const shopAPI: ShopAPI = MockShopAPI;
|
||||||
|
|
||||||
export const productsAPI = MockProductAPI;
|
export const productsAPI = MockProductAPI;
|
||||||
|
|
||||||
|
export const usersAPI = MockUserAPI;
|
||||||
|
|
||||||
|
export const couponAPI = MockCouponAPI;
|
||||||
|
|
||||||
|
export const purchaseAPI = MockPurchaseAPI;
|
||||||
|
@ -5,7 +5,6 @@ import { extractSubFromJWT, generateFakeJWT } from "@/utils/jwt";
|
|||||||
import { mockDB } from "./db";
|
import { mockDB } from "./db";
|
||||||
import { MockUser, Shop } from "./models";
|
import { MockUser, Shop } from "./models";
|
||||||
|
|
||||||
|
|
||||||
const db = mockDB;
|
const db = mockDB;
|
||||||
|
|
||||||
export const MockAuthAPI: AuthAPI = {
|
export const MockAuthAPI: AuthAPI = {
|
||||||
@ -23,7 +22,7 @@ export const MockAuthAPI: AuthAPI = {
|
|||||||
...user,
|
...user,
|
||||||
first_name: user.first_name ?? null,
|
first_name: user.first_name ?? null,
|
||||||
last_name: user.last_name ?? null,
|
last_name: user.last_name ?? null,
|
||||||
profile_picture: user.profile_picture ?? null
|
profile_picture: user.profile_picture ?? null,
|
||||||
};
|
};
|
||||||
return publicUser;
|
return publicUser;
|
||||||
},
|
},
|
||||||
@ -37,8 +36,8 @@ export const MockAuthAPI: AuthAPI = {
|
|||||||
const newUser: MockUser = {
|
const newUser: MockUser = {
|
||||||
id: userId,
|
id: userId,
|
||||||
uuid: userUUID,
|
uuid: userUUID,
|
||||||
user_role: "customer",
|
user_role: "owner",
|
||||||
status: "customer",
|
status: "owner",
|
||||||
shop_id: null,
|
shop_id: null,
|
||||||
username: data.username,
|
username: data.username,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
@ -49,7 +48,7 @@ export const MockAuthAPI: AuthAPI = {
|
|||||||
profile_picture: undefined,
|
profile_picture: undefined,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
last_login: now
|
last_login: now,
|
||||||
};
|
};
|
||||||
|
|
||||||
const newShop: Shop = {
|
const newShop: Shop = {
|
||||||
@ -70,16 +69,23 @@ export const MockAuthAPI: AuthAPI = {
|
|||||||
city: "",
|
city: "",
|
||||||
state: null,
|
state: null,
|
||||||
postal_code: "",
|
postal_code: "",
|
||||||
country: ""
|
country: "",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await mockDB.transaction('rw', mockDB.users, mockDB.shops, mockDB.preferences, mockDB.statistics, async () => {
|
await mockDB.transaction(
|
||||||
await mockDB.users.add(newUser);
|
"rw",
|
||||||
await mockDB.preferences.add({ user_id: userId });
|
mockDB.users,
|
||||||
await mockDB.statistics.add({ user_id: userId, total_spend: 0 });
|
mockDB.shops,
|
||||||
await mockDB.shops.add(newShop);
|
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) {
|
async loginUser(data: ShopLoginAccessTokenData) {
|
||||||
@ -115,7 +121,7 @@ export const MockAuthAPI: AuthAPI = {
|
|||||||
phone_number: data.phone_number ?? "",
|
phone_number: data.phone_number ?? "",
|
||||||
username: data.username ?? "",
|
username: data.username ?? "",
|
||||||
first_name: data.first_name ?? undefined,
|
first_name: data.first_name ?? undefined,
|
||||||
last_name: data.last_name ?? undefined
|
last_name: data.last_name ?? undefined,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
45
frontend/src/api/mock/coupon-mock-api.ts
Normal file
45
frontend/src/api/mock/coupon-mock-api.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { mockDB } from "./db";
|
||||||
|
import { Coupon } from "./models";
|
||||||
|
|
||||||
|
export const MockCouponAPI = {
|
||||||
|
async getAllCoupons(): Promise<Coupon[]> {
|
||||||
|
return mockDB.coupons.toArray();
|
||||||
|
},
|
||||||
|
|
||||||
|
async getCouponById(id: number): Promise<Coupon | null> {
|
||||||
|
return (await mockDB.coupons.get(id)) ?? null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async createCoupon(couponData: Omit<Coupon, "id">): Promise<Coupon> {
|
||||||
|
const id = Date.now();
|
||||||
|
|
||||||
|
const newCoupon: Coupon = {
|
||||||
|
...couponData,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
|
||||||
|
await mockDB.coupons.add(newCoupon);
|
||||||
|
return newCoupon;
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateCoupon(
|
||||||
|
id: number,
|
||||||
|
data: Partial<Omit<Coupon, "id">>,
|
||||||
|
): Promise<Coupon | null> {
|
||||||
|
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<boolean> {
|
||||||
|
await mockDB.coupons.delete(id);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
import Dexie, { Table } from "dexie";
|
import Dexie, { Table } from "dexie";
|
||||||
import {
|
import {
|
||||||
|
Coupon,
|
||||||
MockUser,
|
MockUser,
|
||||||
MockUserPreferences,
|
MockUserPreferences,
|
||||||
MockUserStatistics,
|
MockUserStatistics,
|
||||||
@ -8,7 +9,9 @@ import {
|
|||||||
ProductCategoryJunction,
|
ProductCategoryJunction,
|
||||||
ProductImage,
|
ProductImage,
|
||||||
ProductVariant,
|
ProductVariant,
|
||||||
Shop
|
Purchase,
|
||||||
|
PurchaseEntry,
|
||||||
|
Shop,
|
||||||
} from "./models";
|
} from "./models";
|
||||||
|
|
||||||
class MockDB extends Dexie {
|
class MockDB extends Dexie {
|
||||||
@ -21,6 +24,9 @@ class MockDB extends Dexie {
|
|||||||
product_images!: Table<ProductImage, number>;
|
product_images!: Table<ProductImage, number>;
|
||||||
product_categories!: Table<ProductCategory, number>;
|
product_categories!: Table<ProductCategory, number>;
|
||||||
product_category_junctions!: Table<ProductCategoryJunction, [number, number]>;
|
product_category_junctions!: Table<ProductCategoryJunction, [number, number]>;
|
||||||
|
coupons!: Table<Coupon, number>;
|
||||||
|
purchases!: Table<Purchase, number>;
|
||||||
|
purchase_entries!: Table<PurchaseEntry, number>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("MockDB");
|
super("MockDB");
|
||||||
@ -33,7 +39,10 @@ class MockDB extends Dexie {
|
|||||||
product_variants: "++id,product_id",
|
product_variants: "++id,product_id",
|
||||||
product_images: "++id,product_id",
|
product_images: "++id,product_id",
|
||||||
product_categories: "++id,parent_category_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",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,10 +81,23 @@ export interface ProductImage {
|
|||||||
alt_text: string;
|
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 {
|
export interface ProductVariant {
|
||||||
id: number;
|
id: number;
|
||||||
product_id: number;
|
product_id: number;
|
||||||
index: number; // used for ordering
|
index: number;
|
||||||
name: string;
|
name: string;
|
||||||
price: number;
|
price: number;
|
||||||
comment?: string;
|
comment?: string;
|
||||||
@ -92,16 +105,36 @@ export interface ProductVariant {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductCreate {
|
export interface Coupon {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
text: string;
|
||||||
price: number;
|
valid_due: string;
|
||||||
stock_quantity: number;
|
discount_amount: number;
|
||||||
image_data?: string | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductWithDetails extends Product {
|
export interface Purchase {
|
||||||
images: ProductImage[];
|
id: number;
|
||||||
variants: ProductVariant[];
|
user_id: number;
|
||||||
categories: ProductCategory[];
|
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;
|
||||||
}
|
}
|
||||||
|
@ -28,20 +28,20 @@ export const MockProductAPI = {
|
|||||||
.toArray();
|
.toArray();
|
||||||
const rawCategories = await Promise.all(
|
const rawCategories = await Promise.all(
|
||||||
categoryLinks.map((link) =>
|
categoryLinks.map((link) =>
|
||||||
mockDB.product_categories.get(link.category_id)
|
mockDB.product_categories.get(link.category_id),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
const categories = rawCategories.filter(
|
const categories = rawCategories.filter(
|
||||||
(c): c is ProductCategory => c !== undefined
|
(c): c is ProductCategory => c !== undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...product,
|
...product,
|
||||||
images,
|
images,
|
||||||
variants,
|
variants,
|
||||||
categories
|
categories,
|
||||||
};
|
};
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return detailedProducts;
|
return detailedProducts;
|
||||||
@ -66,18 +66,18 @@ export const MockProductAPI = {
|
|||||||
|
|
||||||
const rawCategories = await Promise.all(
|
const rawCategories = await Promise.all(
|
||||||
categoryLinks.map((link) =>
|
categoryLinks.map((link) =>
|
||||||
mockDB.product_categories.get(link.category_id)
|
mockDB.product_categories.get(link.category_id),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
const categories = rawCategories.filter(
|
const categories = rawCategories.filter(
|
||||||
(c): c is ProductCategory => c !== undefined
|
(c): c is ProductCategory => c !== undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...product,
|
...product,
|
||||||
images,
|
images,
|
||||||
variants,
|
variants,
|
||||||
categories
|
categories,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ export const MockProductAPI = {
|
|||||||
...productData,
|
...productData,
|
||||||
shop_id: user.id,
|
shop_id: user.id,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
updated_at: now
|
updated_at: now,
|
||||||
});
|
});
|
||||||
|
|
||||||
if ("image_data" in productData && productData.image_data) {
|
if ("image_data" in productData && productData.image_data) {
|
||||||
@ -103,7 +103,7 @@ export const MockProductAPI = {
|
|||||||
product_id: productId,
|
product_id: productId,
|
||||||
image_id: image_id,
|
image_id: image_id,
|
||||||
image_url: productData.image_data,
|
image_url: productData.image_data,
|
||||||
alt_text: `${productData.name} image`
|
alt_text: `${productData.name} image`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ export const MockProductAPI = {
|
|||||||
const updatedProduct = {
|
const updatedProduct = {
|
||||||
...product,
|
...product,
|
||||||
...data,
|
...data,
|
||||||
updated_at: new Date().toISOString()
|
updated_at: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
await mockDB.products.put(updatedProduct);
|
await mockDB.products.put(updatedProduct);
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ export const MockProductAPI = {
|
|||||||
await mockDB.product_images.put({
|
await mockDB.product_images.put({
|
||||||
...existingImage,
|
...existingImage,
|
||||||
image_url: data.image_data,
|
image_url: data.image_data,
|
||||||
alt_text: `${data.name ?? product.name} image`
|
alt_text: `${data.name ?? product.name} image`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const image_id = Date.now();
|
const image_id = Date.now();
|
||||||
@ -139,7 +139,7 @@ export const MockProductAPI = {
|
|||||||
product_id: productId,
|
product_id: productId,
|
||||||
image_id: image_id,
|
image_id: image_id,
|
||||||
image_url: data.image_data,
|
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();
|
.delete();
|
||||||
await mockDB.products.delete(productId);
|
await mockDB.products.delete(productId);
|
||||||
return true;
|
return true;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
85
frontend/src/api/mock/purchase-mock-api.ts
Normal file
85
frontend/src/api/mock/purchase-mock-api.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { mockDB } from "./db";
|
||||||
|
import {
|
||||||
|
Purchase,
|
||||||
|
PurchaseWithDetails,
|
||||||
|
PurchaseEntry,
|
||||||
|
PurchaseEntryWithProduct,
|
||||||
|
} from "./models";
|
||||||
|
|
||||||
|
export const MockPurchaseAPI = {
|
||||||
|
async getAllPurchases(): Promise<Purchase[]> {
|
||||||
|
return mockDB.purchases.toArray();
|
||||||
|
},
|
||||||
|
|
||||||
|
async getPurchaseById(purchaseId: number): Promise<Purchase | null> {
|
||||||
|
return (await mockDB.purchases.get(purchaseId)) ?? null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async getPurchaseWithDetails(
|
||||||
|
purchaseId: number,
|
||||||
|
): Promise<PurchaseWithDetails | null> {
|
||||||
|
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<Purchase[]> {
|
||||||
|
return mockDB.purchases.where("user_id").equals(userId).toArray();
|
||||||
|
},
|
||||||
|
|
||||||
|
async createPurchase(
|
||||||
|
purchase: Omit<Purchase, "id">,
|
||||||
|
entries: Omit<PurchaseEntry, "id">[],
|
||||||
|
): Promise<Purchase> {
|
||||||
|
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<boolean> {
|
||||||
|
await mockDB.purchase_entries
|
||||||
|
.where("purchase_id")
|
||||||
|
.equals(purchaseId)
|
||||||
|
.delete();
|
||||||
|
await mockDB.purchases.delete(purchaseId);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
@ -35,7 +35,7 @@ export const MockShopAPI: ShopAPI = {
|
|||||||
console.log("Saving data ->", data);
|
console.log("Saving data ->", data);
|
||||||
await db.shops.update(user.id, {
|
await db.shops.update(user.id, {
|
||||||
...data,
|
...data,
|
||||||
updated_at: new Date().toISOString()
|
updated_at: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
60
frontend/src/api/mock/user-mock-api.ts
Normal file
60
frontend/src/api/mock/user-mock-api.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { mockDB } from "./db";
|
||||||
|
import { MockUser } from "./models";
|
||||||
|
import { getCurrentUserDirect } from "./utils/currentUser";
|
||||||
|
|
||||||
|
export const MockUserAPI = {
|
||||||
|
async getAllUsers(): Promise<MockUser[]> {
|
||||||
|
return mockDB.users.toArray();
|
||||||
|
},
|
||||||
|
|
||||||
|
async getUserById(userId: number): Promise<MockUser | null> {
|
||||||
|
return (await mockDB.users.get(userId)) ?? null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async createUser(
|
||||||
|
userData: Omit<MockUser, "id" | "created_at" | "updated_at">,
|
||||||
|
): Promise<MockUser> {
|
||||||
|
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<Omit<MockUser, "id" | "created_at">>,
|
||||||
|
): Promise<MockUser | null> {
|
||||||
|
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<boolean> {
|
||||||
|
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<MockUser | null> {
|
||||||
|
const user = await getCurrentUserDirect();
|
||||||
|
if (!user?.id) return null;
|
||||||
|
return (await mockDB.users.get(user.id)) ?? null;
|
||||||
|
},
|
||||||
|
};
|
@ -10,4 +10,4 @@ export async function getCurrentUserDirect() {
|
|||||||
|
|
||||||
const user = await mockDB.users.where("uuid").equals(uuid).first();
|
const user = await mockDB.users.where("uuid").equals(uuid).first();
|
||||||
return user ?? null;
|
return user ?? null;
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ export const RealAuthAPI: AuthAPI = {
|
|||||||
},
|
},
|
||||||
async loginUser(data) {
|
async loginUser(data) {
|
||||||
return LoginService.dashboardLoginAccessToken({
|
return LoginService.dashboardLoginAccessToken({
|
||||||
formData: data.formData
|
formData: data.formData,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async updateUser(data) {
|
async updateUser(data) {
|
||||||
await UserService.userUpdateUser({ requestBody: data });
|
await UserService.userUpdateUser({ requestBody: data });
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
import type { ApiRequestOptions } from "./ApiRequestOptions";
|
||||||
import type { ApiResult } from './ApiResult';
|
import type { ApiResult } from "./ApiResult";
|
||||||
|
|
||||||
export class ApiError extends Error {
|
export class ApiError extends Error {
|
||||||
public readonly url: string;
|
public readonly url: string;
|
||||||
@ -8,14 +8,18 @@ export class ApiError extends Error {
|
|||||||
public readonly body: unknown;
|
public readonly body: unknown;
|
||||||
public readonly request: ApiRequestOptions;
|
public readonly request: ApiRequestOptions;
|
||||||
|
|
||||||
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
|
constructor(
|
||||||
|
request: ApiRequestOptions,
|
||||||
|
response: ApiResult,
|
||||||
|
message: string,
|
||||||
|
) {
|
||||||
super(message);
|
super(message);
|
||||||
|
|
||||||
this.name = 'ApiError';
|
this.name = "ApiError";
|
||||||
this.url = response.url;
|
this.url = response.url;
|
||||||
this.status = response.status;
|
this.status = response.status;
|
||||||
this.statusText = response.statusText;
|
this.statusText = response.statusText;
|
||||||
this.body = response.body;
|
this.body = response.body;
|
||||||
this.request = request;
|
this.request = request;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,16 @@ export type ApiRequestOptions<T = unknown> = {
|
|||||||
readonly headers?: Record<string, unknown>;
|
readonly headers?: Record<string, unknown>;
|
||||||
readonly mediaType?: string;
|
readonly mediaType?: string;
|
||||||
readonly method:
|
readonly method:
|
||||||
| 'DELETE'
|
| "DELETE"
|
||||||
| 'GET'
|
| "GET"
|
||||||
| 'HEAD'
|
| "HEAD"
|
||||||
| 'OPTIONS'
|
| "OPTIONS"
|
||||||
| 'PATCH'
|
| "PATCH"
|
||||||
| 'POST'
|
| "POST"
|
||||||
| 'PUT';
|
| "PUT";
|
||||||
readonly path?: Record<string, unknown>;
|
readonly path?: Record<string, unknown>;
|
||||||
readonly query?: Record<string, unknown>;
|
readonly query?: Record<string, unknown>;
|
||||||
readonly responseHeader?: string;
|
readonly responseHeader?: string;
|
||||||
readonly responseTransformer?: (data: unknown) => Promise<T>;
|
readonly responseTransformer?: (data: unknown) => Promise<T>;
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
};
|
};
|
||||||
|
@ -4,4 +4,4 @@ export type ApiResult<TData = any> = {
|
|||||||
readonly status: number;
|
readonly status: number;
|
||||||
readonly statusText: string;
|
readonly statusText: string;
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export class CancelError extends Error {
|
export class CancelError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = 'CancelError';
|
this.name = "CancelError";
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isCancelled(): boolean {
|
public get isCancelled(): boolean {
|
||||||
@ -30,8 +30,8 @@ export class CancelablePromise<T> implements Promise<T> {
|
|||||||
executor: (
|
executor: (
|
||||||
resolve: (value: T | PromiseLike<T>) => void,
|
resolve: (value: T | PromiseLike<T>) => void,
|
||||||
reject: (reason?: unknown) => void,
|
reject: (reason?: unknown) => void,
|
||||||
onCancel: OnCancel
|
onCancel: OnCancel,
|
||||||
) => void
|
) => void,
|
||||||
) {
|
) {
|
||||||
this._isResolved = false;
|
this._isResolved = false;
|
||||||
this._isRejected = false;
|
this._isRejected = false;
|
||||||
@ -64,15 +64,15 @@ export class CancelablePromise<T> implements Promise<T> {
|
|||||||
this.cancelHandlers.push(cancelHandler);
|
this.cancelHandlers.push(cancelHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperty(onCancel, 'isResolved', {
|
Object.defineProperty(onCancel, "isResolved", {
|
||||||
get: (): boolean => this._isResolved,
|
get: (): boolean => this._isResolved,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(onCancel, 'isRejected', {
|
Object.defineProperty(onCancel, "isRejected", {
|
||||||
get: (): boolean => this._isRejected,
|
get: (): boolean => this._isRejected,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(onCancel, 'isCancelled', {
|
Object.defineProperty(onCancel, "isCancelled", {
|
||||||
get: (): boolean => this._isCancelled,
|
get: (): boolean => this._isCancelled,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,13 +86,13 @@ export class CancelablePromise<T> implements Promise<T> {
|
|||||||
|
|
||||||
public then<TResult1 = T, TResult2 = never>(
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||||
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
|
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
|
||||||
): Promise<TResult1 | TResult2> {
|
): Promise<TResult1 | TResult2> {
|
||||||
return this.promise.then(onFulfilled, onRejected);
|
return this.promise.then(onFulfilled, onRejected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public catch<TResult = never>(
|
public catch<TResult = never>(
|
||||||
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
|
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
|
||||||
): Promise<T | TResult> {
|
): Promise<T | TResult> {
|
||||||
return this.promise.catch(onRejected);
|
return this.promise.catch(onRejected);
|
||||||
}
|
}
|
||||||
@ -112,15 +112,15 @@ export class CancelablePromise<T> implements Promise<T> {
|
|||||||
cancelHandler();
|
cancelHandler();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Cancellation threw an error', error);
|
console.warn("Cancellation threw an error", error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.cancelHandlers.length = 0;
|
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 {
|
public get isCancelled(): boolean {
|
||||||
return this._isCancelled;
|
return this._isCancelled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
|
import type { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
import type { ApiRequestOptions } from "./ApiRequestOptions";
|
||||||
|
|
||||||
type Headers = Record<string, string>;
|
type Headers = Record<string, string>;
|
||||||
type Middleware<T> = (value: T) => T | Promise<T>;
|
type Middleware<T> = (value: T) => T | Promise<T>;
|
||||||
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
||||||
|
|
||||||
export class Interceptors<T> {
|
export class Interceptors<T> {
|
||||||
_fns: Middleware<T>[];
|
_fns: Middleware<T>[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._fns = [];
|
this._fns = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
eject(fn: Middleware<T>): void {
|
eject(fn: Middleware<T>): void {
|
||||||
const index = this._fns.indexOf(fn);
|
const index = this._fns.indexOf(fn);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
|
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use(fn: Middleware<T>): void {
|
use(fn: Middleware<T>): void {
|
||||||
this._fns = [...this._fns, fn];
|
this._fns = [...this._fns, fn];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OpenAPIConfig = {
|
export type OpenAPIConfig = {
|
||||||
BASE: string;
|
BASE: string;
|
||||||
CREDENTIALS: 'include' | 'omit' | 'same-origin';
|
CREDENTIALS: "include" | "omit" | "same-origin";
|
||||||
ENCODE_PATH?: ((path: string) => string) | undefined;
|
ENCODE_PATH?: ((path: string) => string) | undefined;
|
||||||
HEADERS?: Headers | Resolver<Headers> | undefined;
|
HEADERS?: Headers | Resolver<Headers> | undefined;
|
||||||
PASSWORD?: string | Resolver<string> | undefined;
|
PASSWORD?: string | Resolver<string> | undefined;
|
||||||
@ -41,17 +41,17 @@ export type OpenAPIConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const OpenAPI: OpenAPIConfig = {
|
export const OpenAPI: OpenAPIConfig = {
|
||||||
BASE: '',
|
BASE: "",
|
||||||
CREDENTIALS: 'include',
|
CREDENTIALS: "include",
|
||||||
ENCODE_PATH: undefined,
|
ENCODE_PATH: undefined,
|
||||||
HEADERS: undefined,
|
HEADERS: undefined,
|
||||||
PASSWORD: undefined,
|
PASSWORD: undefined,
|
||||||
TOKEN: undefined,
|
TOKEN: undefined,
|
||||||
USERNAME: undefined,
|
USERNAME: undefined,
|
||||||
VERSION: '0.0.1',
|
VERSION: "0.0.1",
|
||||||
WITH_CREDENTIALS: false,
|
WITH_CREDENTIALS: false,
|
||||||
interceptors: {
|
interceptors: {
|
||||||
request: new Interceptors(),
|
request: new Interceptors(),
|
||||||
response: new Interceptors(),
|
response: new Interceptors(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
|
import type {
|
||||||
|
AxiosError,
|
||||||
|
AxiosRequestConfig,
|
||||||
|
AxiosResponse,
|
||||||
|
AxiosInstance,
|
||||||
|
} from "axios";
|
||||||
|
|
||||||
import { ApiError } from './ApiError';
|
import { ApiError } from "./ApiError";
|
||||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
import type { ApiRequestOptions } from "./ApiRequestOptions";
|
||||||
import type { ApiResult } from './ApiResult';
|
import type { ApiResult } from "./ApiResult";
|
||||||
import { CancelablePromise } from './CancelablePromise';
|
import { CancelablePromise } from "./CancelablePromise";
|
||||||
import type { OnCancel } from './CancelablePromise';
|
import type { OnCancel } from "./CancelablePromise";
|
||||||
import type { OpenAPIConfig } from './OpenAPI';
|
import type { OpenAPIConfig } from "./OpenAPI";
|
||||||
|
|
||||||
export const isString = (value: unknown): value is string => {
|
export const isString = (value: unknown): value is string => {
|
||||||
return typeof value === 'string';
|
return typeof value === "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isStringWithValue = (value: unknown): value is 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 => {
|
export const isBlob = (value: any): value is Blob => {
|
||||||
@ -33,7 +38,7 @@ export const base64 = (str: string): string => {
|
|||||||
return btoa(str);
|
return btoa(str);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return Buffer.from(str).toString('base64');
|
return Buffer.from(str).toString("base64");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,8 +57,8 @@ export const getQueryString = (params: Record<string, unknown>): string => {
|
|||||||
if (value instanceof Date) {
|
if (value instanceof Date) {
|
||||||
append(key, value.toISOString());
|
append(key, value.toISOString());
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
value.forEach(v => encodePair(key, v));
|
value.forEach((v) => encodePair(key, v));
|
||||||
} else if (typeof value === 'object') {
|
} else if (typeof value === "object") {
|
||||||
Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v));
|
Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v));
|
||||||
} else {
|
} else {
|
||||||
append(key, value);
|
append(key, value);
|
||||||
@ -62,14 +67,14 @@ export const getQueryString = (params: Record<string, unknown>): string => {
|
|||||||
|
|
||||||
Object.entries(params).forEach(([key, value]) => encodePair(key, value));
|
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 getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
|
||||||
const encoder = config.ENCODE_PATH || encodeURI;
|
const encoder = config.ENCODE_PATH || encodeURI;
|
||||||
|
|
||||||
const path = options.url
|
const path = options.url
|
||||||
.replace('{api-version}', config.VERSION)
|
.replace("{api-version}", config.VERSION)
|
||||||
.replace(/{(.*?)}/g, (substring: string, group: string) => {
|
.replace(/{(.*?)}/g, (substring: string, group: string) => {
|
||||||
if (options.path?.hasOwnProperty(group)) {
|
if (options.path?.hasOwnProperty(group)) {
|
||||||
return encoder(String(options.path[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;
|
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) {
|
if (options.formData) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
@ -97,7 +104,7 @@ export const getFormData = (options: ApiRequestOptions): FormData | undefined =>
|
|||||||
.filter(([, value]) => value !== undefined && value !== null)
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
.forEach(([key, value]) => {
|
.forEach(([key, value]) => {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
value.forEach(v => process(key, v));
|
value.forEach((v) => process(key, v));
|
||||||
} else {
|
} else {
|
||||||
process(key, value);
|
process(key, value);
|
||||||
}
|
}
|
||||||
@ -110,14 +117,20 @@ export const getFormData = (options: ApiRequestOptions): FormData | undefined =>
|
|||||||
|
|
||||||
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
||||||
|
|
||||||
export const resolve = async <T>(options: ApiRequestOptions<T>, resolver?: T | Resolver<T>): Promise<T | undefined> => {
|
export const resolve = async <T>(
|
||||||
if (typeof resolver === 'function') {
|
options: ApiRequestOptions<T>,
|
||||||
|
resolver?: T | Resolver<T>,
|
||||||
|
): Promise<T | undefined> => {
|
||||||
|
if (typeof resolver === "function") {
|
||||||
return (resolver as Resolver<T>)(options);
|
return (resolver as Resolver<T>)(options);
|
||||||
}
|
}
|
||||||
return resolver;
|
return resolver;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getHeaders = async <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>): Promise<Record<string, string>> => {
|
export const getHeaders = async <T>(
|
||||||
|
config: OpenAPIConfig,
|
||||||
|
options: ApiRequestOptions<T>,
|
||||||
|
): Promise<Record<string, string>> => {
|
||||||
const [token, username, password, additionalHeaders] = await Promise.all([
|
const [token, username, password, additionalHeaders] = await Promise.all([
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
resolve(options, config.TOKEN),
|
resolve(options, config.TOKEN),
|
||||||
@ -130,38 +143,41 @@ export const getHeaders = async <T>(config: OpenAPIConfig, options: ApiRequestOp
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const headers = Object.entries({
|
const headers = Object.entries({
|
||||||
Accept: 'application/json',
|
Accept: "application/json",
|
||||||
...additionalHeaders,
|
...additionalHeaders,
|
||||||
...options.headers,
|
...options.headers,
|
||||||
})
|
})
|
||||||
.filter(([, value]) => value !== undefined && value !== null)
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
.reduce((headers, [key, value]) => ({
|
.reduce(
|
||||||
...headers,
|
(headers, [key, value]) => ({
|
||||||
[key]: String(value),
|
...headers,
|
||||||
}), {} as Record<string, string>);
|
[key]: String(value),
|
||||||
|
}),
|
||||||
|
{} as Record<string, string>,
|
||||||
|
);
|
||||||
|
|
||||||
if (isStringWithValue(token)) {
|
if (isStringWithValue(token)) {
|
||||||
headers['Authorization'] = `Bearer ${token}`;
|
headers["Authorization"] = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||||
const credentials = base64(`${username}:${password}`);
|
const credentials = base64(`${username}:${password}`);
|
||||||
headers['Authorization'] = `Basic ${credentials}`;
|
headers["Authorization"] = `Basic ${credentials}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.body !== undefined) {
|
if (options.body !== undefined) {
|
||||||
if (options.mediaType) {
|
if (options.mediaType) {
|
||||||
headers['Content-Type'] = options.mediaType;
|
headers["Content-Type"] = options.mediaType;
|
||||||
} else if (isBlob(options.body)) {
|
} 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)) {
|
} else if (isString(options.body)) {
|
||||||
headers['Content-Type'] = 'text/plain';
|
headers["Content-Type"] = "text/plain";
|
||||||
} else if (!isFormData(options.body)) {
|
} else if (!isFormData(options.body)) {
|
||||||
headers['Content-Type'] = 'application/json';
|
headers["Content-Type"] = "application/json";
|
||||||
}
|
}
|
||||||
} else if (options.formData !== undefined) {
|
} else if (options.formData !== undefined) {
|
||||||
if (options.mediaType) {
|
if (options.mediaType) {
|
||||||
headers['Content-Type'] = options.mediaType;
|
headers["Content-Type"] = options.mediaType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +199,7 @@ export const sendRequest = async <T>(
|
|||||||
formData: FormData | undefined,
|
formData: FormData | undefined,
|
||||||
headers: Record<string, string>,
|
headers: Record<string, string>,
|
||||||
onCancel: OnCancel,
|
onCancel: OnCancel,
|
||||||
axiosClient: AxiosInstance
|
axiosClient: AxiosInstance,
|
||||||
): Promise<AxiosResponse<T>> => {
|
): Promise<AxiosResponse<T>> => {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
|
|
||||||
@ -213,7 +229,10 @@ export const sendRequest = async <T>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getResponseHeader = (response: AxiosResponse<unknown>, responseHeader?: string): string | undefined => {
|
export const getResponseHeader = (
|
||||||
|
response: AxiosResponse<unknown>,
|
||||||
|
responseHeader?: string,
|
||||||
|
): string | undefined => {
|
||||||
if (responseHeader) {
|
if (responseHeader) {
|
||||||
const content = response.headers[responseHeader];
|
const content = response.headers[responseHeader];
|
||||||
if (isString(content)) {
|
if (isString(content)) {
|
||||||
@ -230,50 +249,53 @@ export const getResponseBody = (response: AxiosResponse<unknown>): unknown => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
|
export const catchErrorCodes = (
|
||||||
|
options: ApiRequestOptions,
|
||||||
|
result: ApiResult,
|
||||||
|
): void => {
|
||||||
const errors: Record<number, string> = {
|
const errors: Record<number, string> = {
|
||||||
400: 'Bad Request',
|
400: "Bad Request",
|
||||||
401: 'Unauthorized',
|
401: "Unauthorized",
|
||||||
402: 'Payment Required',
|
402: "Payment Required",
|
||||||
403: 'Forbidden',
|
403: "Forbidden",
|
||||||
404: 'Not Found',
|
404: "Not Found",
|
||||||
405: 'Method Not Allowed',
|
405: "Method Not Allowed",
|
||||||
406: 'Not Acceptable',
|
406: "Not Acceptable",
|
||||||
407: 'Proxy Authentication Required',
|
407: "Proxy Authentication Required",
|
||||||
408: 'Request Timeout',
|
408: "Request Timeout",
|
||||||
409: 'Conflict',
|
409: "Conflict",
|
||||||
410: 'Gone',
|
410: "Gone",
|
||||||
411: 'Length Required',
|
411: "Length Required",
|
||||||
412: 'Precondition Failed',
|
412: "Precondition Failed",
|
||||||
413: 'Payload Too Large',
|
413: "Payload Too Large",
|
||||||
414: 'URI Too Long',
|
414: "URI Too Long",
|
||||||
415: 'Unsupported Media Type',
|
415: "Unsupported Media Type",
|
||||||
416: 'Range Not Satisfiable',
|
416: "Range Not Satisfiable",
|
||||||
417: 'Expectation Failed',
|
417: "Expectation Failed",
|
||||||
418: 'Im a teapot',
|
418: "Im a teapot",
|
||||||
421: 'Misdirected Request',
|
421: "Misdirected Request",
|
||||||
422: 'Unprocessable Content',
|
422: "Unprocessable Content",
|
||||||
423: 'Locked',
|
423: "Locked",
|
||||||
424: 'Failed Dependency',
|
424: "Failed Dependency",
|
||||||
425: 'Too Early',
|
425: "Too Early",
|
||||||
426: 'Upgrade Required',
|
426: "Upgrade Required",
|
||||||
428: 'Precondition Required',
|
428: "Precondition Required",
|
||||||
429: 'Too Many Requests',
|
429: "Too Many Requests",
|
||||||
431: 'Request Header Fields Too Large',
|
431: "Request Header Fields Too Large",
|
||||||
451: 'Unavailable For Legal Reasons',
|
451: "Unavailable For Legal Reasons",
|
||||||
500: 'Internal Server Error',
|
500: "Internal Server Error",
|
||||||
501: 'Not Implemented',
|
501: "Not Implemented",
|
||||||
502: 'Bad Gateway',
|
502: "Bad Gateway",
|
||||||
503: 'Service Unavailable',
|
503: "Service Unavailable",
|
||||||
504: 'Gateway Timeout',
|
504: "Gateway Timeout",
|
||||||
505: 'HTTP Version Not Supported',
|
505: "HTTP Version Not Supported",
|
||||||
506: 'Variant Also Negotiates',
|
506: "Variant Also Negotiates",
|
||||||
507: 'Insufficient Storage',
|
507: "Insufficient Storage",
|
||||||
508: 'Loop Detected',
|
508: "Loop Detected",
|
||||||
510: 'Not Extended',
|
510: "Not Extended",
|
||||||
511: 'Network Authentication Required',
|
511: "Network Authentication Required",
|
||||||
...options.errors,
|
...options.errors,
|
||||||
}
|
};
|
||||||
|
|
||||||
const error = errors[result.status];
|
const error = errors[result.status];
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -281,8 +303,8 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
const errorStatus = result.status ?? 'unknown';
|
const errorStatus = result.status ?? "unknown";
|
||||||
const errorStatusText = result.statusText ?? 'unknown';
|
const errorStatusText = result.statusText ?? "unknown";
|
||||||
const errorBody = (() => {
|
const errorBody = (() => {
|
||||||
try {
|
try {
|
||||||
return JSON.stringify(result.body, null, 2);
|
return JSON.stringify(result.body, null, 2);
|
||||||
@ -291,8 +313,10 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult):
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
throw new ApiError(options, result,
|
throw new ApiError(
|
||||||
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
|
options,
|
||||||
|
result,
|
||||||
|
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -305,7 +329,11 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult):
|
|||||||
* @returns CancelablePromise<T>
|
* @returns CancelablePromise<T>
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>, axiosClient: AxiosInstance = axios): CancelablePromise<T> => {
|
export const request = <T>(
|
||||||
|
config: OpenAPIConfig,
|
||||||
|
options: ApiRequestOptions<T>,
|
||||||
|
axiosClient: AxiosInstance = axios,
|
||||||
|
): CancelablePromise<T> => {
|
||||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||||
try {
|
try {
|
||||||
const url = getUrl(config, options);
|
const url = getUrl(config, options);
|
||||||
@ -314,18 +342,30 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>,
|
|||||||
const headers = await getHeaders(config, options);
|
const headers = await getHeaders(config, options);
|
||||||
|
|
||||||
if (!onCancel.isCancelled) {
|
if (!onCancel.isCancelled) {
|
||||||
let response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient);
|
let response = await sendRequest<T>(
|
||||||
|
config,
|
||||||
|
options,
|
||||||
|
url,
|
||||||
|
body,
|
||||||
|
formData,
|
||||||
|
headers,
|
||||||
|
onCancel,
|
||||||
|
axiosClient,
|
||||||
|
);
|
||||||
|
|
||||||
for (const fn of config.interceptors.response._fns) {
|
for (const fn of config.interceptors.response._fns) {
|
||||||
response = await fn(response);
|
response = await fn(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseBody = getResponseBody(response);
|
const responseBody = getResponseBody(response);
|
||||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
const responseHeader = getResponseHeader(
|
||||||
|
response,
|
||||||
|
options.responseHeader,
|
||||||
|
);
|
||||||
|
|
||||||
let transformedBody = responseBody;
|
let transformedBody = responseBody;
|
||||||
if (options.responseTransformer && isSuccess(response.status)) {
|
if (options.responseTransformer && isSuccess(response.status)) {
|
||||||
transformedBody = await options.responseTransformer(responseBody)
|
transformedBody = await options.responseTransformer(responseBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: ApiResult = {
|
const result: ApiResult = {
|
||||||
@ -344,4 +384,4 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>,
|
|||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
export { ApiError } from './core/ApiError';
|
export { ApiError } from "./core/ApiError";
|
||||||
export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
export { CancelablePromise, CancelError } from "./core/CancelablePromise";
|
||||||
export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI';
|
export { OpenAPI, type OpenAPIConfig } from "./core/OpenAPI";
|
||||||
export * from './sdk.gen';
|
export * from "./sdk.gen";
|
||||||
export * from './types.gen';
|
export * from "./types.gen";
|
||||||
|
@ -1,438 +1,485 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
import type { CancelablePromise } from './core/CancelablePromise';
|
import type { CancelablePromise } from "./core/CancelablePromise";
|
||||||
import { OpenAPI } from './core/OpenAPI';
|
import { OpenAPI } from "./core/OpenAPI";
|
||||||
import { request as __request } from './core/request';
|
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 {
|
||||||
|
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 {
|
export class DashboardService {
|
||||||
/**
|
/**
|
||||||
* Get information about currently logged in user
|
* Get information about currently logged in user
|
||||||
* @returns UserPublic Successful Response
|
* @returns UserPublic Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static userGetUser(): CancelablePromise<UserGetUserResponse> {
|
public static userGetUser(): CancelablePromise<UserGetUserResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
url: '/user'
|
url: "/user",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update user details
|
* Update user details
|
||||||
* @param data The data for the request.
|
* @param data The data for the request.
|
||||||
* @param data.requestBody
|
* @param data.requestBody
|
||||||
* @returns boolean Successful Response
|
* @returns boolean Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static userUpdateUser(data: UserUpdateUserData): CancelablePromise<UserUpdateUserResponse> {
|
public static userUpdateUser(
|
||||||
return __request(OpenAPI, {
|
data: UserUpdateUserData,
|
||||||
method: 'PUT',
|
): CancelablePromise<UserUpdateUserResponse> {
|
||||||
url: '/user',
|
return __request(OpenAPI, {
|
||||||
body: data.requestBody,
|
method: "PUT",
|
||||||
mediaType: 'application/json',
|
url: "/user",
|
||||||
errors: {
|
body: data.requestBody,
|
||||||
422: 'Validation Error'
|
mediaType: "application/json",
|
||||||
}
|
errors: {
|
||||||
});
|
422: "Validation Error",
|
||||||
}
|
},
|
||||||
|
});
|
||||||
/**
|
}
|
||||||
* Register new user
|
|
||||||
* @param data The data for the request.
|
/**
|
||||||
* @param data.requestBody
|
* Register new user
|
||||||
* @returns boolean Successful Response
|
* @param data The data for the request.
|
||||||
* @throws ApiError
|
* @param data.requestBody
|
||||||
*/
|
* @returns boolean Successful Response
|
||||||
public static userRegister(data: UserRegisterData): CancelablePromise<UserRegisterResponse> {
|
* @throws ApiError
|
||||||
return __request(OpenAPI, {
|
*/
|
||||||
method: 'POST',
|
public static userRegister(
|
||||||
url: '/user',
|
data: UserRegisterData,
|
||||||
body: data.requestBody,
|
): CancelablePromise<UserRegisterResponse> {
|
||||||
mediaType: 'application/json',
|
return __request(OpenAPI, {
|
||||||
errors: {
|
method: "POST",
|
||||||
422: 'Validation Error'
|
url: "/user",
|
||||||
}
|
body: data.requestBody,
|
||||||
});
|
mediaType: "application/json",
|
||||||
}
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
/**
|
},
|
||||||
* Delete user
|
});
|
||||||
* @returns boolean Successful Response
|
}
|
||||||
* @throws ApiError
|
|
||||||
*/
|
/**
|
||||||
public static userDeleteUser(): CancelablePromise<UserDeleteUserResponse> {
|
* Delete user
|
||||||
return __request(OpenAPI, {
|
* @returns boolean Successful Response
|
||||||
method: 'DELETE',
|
* @throws ApiError
|
||||||
url: '/user'
|
*/
|
||||||
});
|
public static userDeleteUser(): CancelablePromise<UserDeleteUserResponse> {
|
||||||
}
|
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
|
* Login Access Token
|
||||||
* a predefined expiration period.
|
* OAuth2 compatible token login for the dashboard.
|
||||||
*
|
*
|
||||||
* - **username**: User's email
|
* This endpoint generates an access token required for authenticating future
|
||||||
* - **password**: User's password
|
* requests to the dashboard section of the application. The token is valid for
|
||||||
*
|
* a predefined expiration period.
|
||||||
* **Note:** This login is restricted to dashboard access only and cannot be
|
*
|
||||||
* used for tenant accounts access to shops
|
* - **username**: User's email
|
||||||
* @param data The data for the request.
|
* - **password**: User's password
|
||||||
* @param data.formData
|
*
|
||||||
* @returns Token Successful Response
|
* **Note:** This login is restricted to dashboard access only and cannot be
|
||||||
* @throws ApiError
|
* used for tenant accounts access to shops
|
||||||
*/
|
* @param data The data for the request.
|
||||||
public static dashboardLoginAccessToken(data: DashboardLoginAccessTokenData): CancelablePromise<DashboardLoginAccessTokenResponse> {
|
* @param data.formData
|
||||||
return __request(OpenAPI, {
|
* @returns Token Successful Response
|
||||||
method: 'POST',
|
* @throws ApiError
|
||||||
url: '/login/access-token',
|
*/
|
||||||
formData: data.formData,
|
public static dashboardLoginAccessToken(
|
||||||
mediaType: 'application/x-www-form-urlencoded',
|
data: DashboardLoginAccessTokenData,
|
||||||
errors: {
|
): CancelablePromise<DashboardLoginAccessTokenResponse> {
|
||||||
422: 'Validation Error'
|
return __request(OpenAPI, {
|
||||||
}
|
method: "POST",
|
||||||
});
|
url: "/login/access-token",
|
||||||
}
|
formData: data.formData,
|
||||||
|
mediaType: "application/x-www-form-urlencoded",
|
||||||
/**
|
errors: {
|
||||||
* Register New Shop
|
422: "Validation Error",
|
||||||
* @param data The data for the request.
|
},
|
||||||
* @param data.requestBody
|
});
|
||||||
* @returns boolean Successful Response
|
}
|
||||||
* @throws ApiError
|
|
||||||
*/
|
/**
|
||||||
public static dashboardRegisterNewShop(data: DashboardRegisterNewShopData): CancelablePromise<DashboardRegisterNewShopResponse> {
|
* Register New Shop
|
||||||
return __request(OpenAPI, {
|
* @param data The data for the request.
|
||||||
method: 'POST',
|
* @param data.requestBody
|
||||||
url: '/shop',
|
* @returns boolean Successful Response
|
||||||
body: data.requestBody,
|
* @throws ApiError
|
||||||
mediaType: 'application/json',
|
*/
|
||||||
errors: {
|
public static dashboardRegisterNewShop(
|
||||||
422: 'Validation Error'
|
data: DashboardRegisterNewShopData,
|
||||||
}
|
): CancelablePromise<DashboardRegisterNewShopResponse> {
|
||||||
});
|
return __request(OpenAPI, {
|
||||||
}
|
method: "POST",
|
||||||
|
url: "/shop",
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: "application/json",
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LoginService {
|
export class LoginService {
|
||||||
/**
|
/**
|
||||||
* Login Access Token
|
* Login Access Token
|
||||||
* OAuth2 compatible token login for the dashboard.
|
* OAuth2 compatible token login for the dashboard.
|
||||||
*
|
*
|
||||||
* This endpoint generates an access token required for authenticating future
|
* This endpoint generates an access token required for authenticating future
|
||||||
* requests to the dashboard section of the application. The token is valid for
|
* requests to the dashboard section of the application. The token is valid for
|
||||||
* a predefined expiration period.
|
* a predefined expiration period.
|
||||||
*
|
*
|
||||||
* - **username**: User's email
|
* - **username**: User's email
|
||||||
* - **password**: User's password
|
* - **password**: User's password
|
||||||
*
|
*
|
||||||
* **Note:** This login is restricted to dashboard access only and cannot be
|
* **Note:** This login is restricted to dashboard access only and cannot be
|
||||||
* used for tenant accounts access to shops
|
* used for tenant accounts access to shops
|
||||||
* @param data The data for the request.
|
* @param data The data for the request.
|
||||||
* @param data.formData
|
* @param data.formData
|
||||||
* @returns Token Successful Response
|
* @returns Token Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static dashboardLoginAccessToken(data: DashboardLoginAccessTokenData): CancelablePromise<DashboardLoginAccessTokenResponse> {
|
public static dashboardLoginAccessToken(
|
||||||
return __request(OpenAPI, {
|
data: DashboardLoginAccessTokenData,
|
||||||
method: 'POST',
|
): CancelablePromise<DashboardLoginAccessTokenResponse> {
|
||||||
url: '/login/access-token',
|
return __request(OpenAPI, {
|
||||||
formData: data.formData,
|
method: "POST",
|
||||||
mediaType: 'application/x-www-form-urlencoded',
|
url: "/login/access-token",
|
||||||
errors: {
|
formData: data.formData,
|
||||||
422: 'Validation Error'
|
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.
|
* Login Access Token
|
||||||
* @param data.formData
|
* OAuth2 compatible token login, get an access token for future requests
|
||||||
* @returns Token Successful Response
|
* @param data The data for the request.
|
||||||
* @throws ApiError
|
* @param data.formData
|
||||||
*/
|
* @returns Token Successful Response
|
||||||
public static shopLoginAccessToken(data: ShopLoginAccessTokenData): CancelablePromise<ShopLoginAccessTokenResponse> {
|
* @throws ApiError
|
||||||
return __request(OpenAPI, {
|
*/
|
||||||
method: 'POST',
|
public static shopLoginAccessToken(
|
||||||
url: '/shop/{shop_uuid}/login/access-token',
|
data: ShopLoginAccessTokenData,
|
||||||
formData: data.formData,
|
): CancelablePromise<ShopLoginAccessTokenResponse> {
|
||||||
mediaType: 'application/x-www-form-urlencoded',
|
return __request(OpenAPI, {
|
||||||
errors: {
|
method: "POST",
|
||||||
422: 'Validation Error'
|
url: "/shop/{shop_uuid}/login/access-token",
|
||||||
}
|
formData: data.formData,
|
||||||
});
|
mediaType: "application/x-www-form-urlencoded",
|
||||||
}
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShopService {
|
export class ShopService {
|
||||||
/**
|
/**
|
||||||
* Login Access Token
|
* Login Access Token
|
||||||
* OAuth2 compatible token login, get an access token for future requests
|
* OAuth2 compatible token login, get an access token for future requests
|
||||||
* @param data The data for the request.
|
* @param data The data for the request.
|
||||||
* @param data.formData
|
* @param data.formData
|
||||||
* @returns Token Successful Response
|
* @returns Token Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static shopLoginAccessToken(data: ShopLoginAccessTokenData): CancelablePromise<ShopLoginAccessTokenResponse> {
|
public static shopLoginAccessToken(
|
||||||
return __request(OpenAPI, {
|
data: ShopLoginAccessTokenData,
|
||||||
method: 'POST',
|
): CancelablePromise<ShopLoginAccessTokenResponse> {
|
||||||
url: '/shop/{shop_uuid}/login/access-token',
|
return __request(OpenAPI, {
|
||||||
formData: data.formData,
|
method: "POST",
|
||||||
mediaType: 'application/x-www-form-urlencoded',
|
url: "/shop/{shop_uuid}/login/access-token",
|
||||||
errors: {
|
formData: data.formData,
|
||||||
422: 'Validation Error'
|
mediaType: "application/x-www-form-urlencoded",
|
||||||
}
|
errors: {
|
||||||
});
|
422: "Validation Error",
|
||||||
}
|
},
|
||||||
|
});
|
||||||
/**
|
}
|
||||||
* Delete user
|
|
||||||
* @param data The data for the request.
|
/**
|
||||||
* @param data.shopUuid
|
* Delete user
|
||||||
* @returns unknown Successful Response
|
* @param data The data for the request.
|
||||||
* @throws ApiError
|
* @param data.shopUuid
|
||||||
*/
|
* @returns unknown Successful Response
|
||||||
public static shopDeleteUser(data: ShopDeleteUserData): CancelablePromise<ShopDeleteUserResponse> {
|
* @throws ApiError
|
||||||
return __request(OpenAPI, {
|
*/
|
||||||
method: 'DELETE',
|
public static shopDeleteUser(
|
||||||
url: '/shop/{shop_uuid}/user/delete',
|
data: ShopDeleteUserData,
|
||||||
path: {
|
): CancelablePromise<ShopDeleteUserResponse> {
|
||||||
shop_uuid: data.shopUuid
|
return __request(OpenAPI, {
|
||||||
},
|
method: "DELETE",
|
||||||
errors: {
|
url: "/shop/{shop_uuid}/user/delete",
|
||||||
422: 'Validation Error'
|
path: {
|
||||||
}
|
shop_uuid: data.shopUuid,
|
||||||
});
|
},
|
||||||
}
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
/**
|
},
|
||||||
* User logout
|
});
|
||||||
* @returns unknown Successful Response
|
}
|
||||||
* @throws ApiError
|
|
||||||
*/
|
/**
|
||||||
public static shopLogout(): CancelablePromise<ShopLogoutResponse> {
|
* User logout
|
||||||
return __request(OpenAPI, {
|
* @returns unknown Successful Response
|
||||||
method: 'DELETE',
|
* @throws ApiError
|
||||||
url: '/shop/{shop_uuid}/user/logout'
|
*/
|
||||||
});
|
public static shopLogout(): CancelablePromise<ShopLogoutResponse> {
|
||||||
}
|
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
|
* Register new user
|
||||||
* @throws ApiError
|
* @param data The data for the request.
|
||||||
*/
|
* @param data.shopUuid
|
||||||
public static shopRegister(data: ShopRegisterData): CancelablePromise<ShopRegisterResponse> {
|
* @param data.requestBody
|
||||||
return __request(OpenAPI, {
|
* @returns unknown Successful Response
|
||||||
method: 'POST',
|
* @throws ApiError
|
||||||
url: '/shop/{shop_uuid}/user/register',
|
*/
|
||||||
path: {
|
public static shopRegister(
|
||||||
shop_uuid: data.shopUuid
|
data: ShopRegisterData,
|
||||||
},
|
): CancelablePromise<ShopRegisterResponse> {
|
||||||
body: data.requestBody,
|
return __request(OpenAPI, {
|
||||||
mediaType: 'application/json',
|
method: "POST",
|
||||||
errors: {
|
url: "/shop/{shop_uuid}/user/register",
|
||||||
422: 'Validation Error'
|
path: {
|
||||||
}
|
shop_uuid: data.shopUuid,
|
||||||
});
|
},
|
||||||
}
|
body: data.requestBody,
|
||||||
|
mediaType: "application/json",
|
||||||
/**
|
errors: {
|
||||||
* Update user details
|
422: "Validation Error",
|
||||||
* @param data The data for the request.
|
},
|
||||||
* @param data.requestBody
|
});
|
||||||
* @returns unknown Successful Response
|
}
|
||||||
* @throws ApiError
|
|
||||||
*/
|
/**
|
||||||
public static shopUpdateUser(data: ShopUpdateUserData): CancelablePromise<ShopUpdateUserResponse> {
|
* Update user details
|
||||||
return __request(OpenAPI, {
|
* @param data The data for the request.
|
||||||
method: 'PUT',
|
* @param data.requestBody
|
||||||
url: '/shop/{shop_uuid}/user/update',
|
* @returns unknown Successful Response
|
||||||
body: data.requestBody,
|
* @throws ApiError
|
||||||
mediaType: 'application/json',
|
*/
|
||||||
errors: {
|
public static shopUpdateUser(
|
||||||
422: 'Validation Error'
|
data: ShopUpdateUserData,
|
||||||
}
|
): CancelablePromise<ShopUpdateUserResponse> {
|
||||||
});
|
return __request(OpenAPI, {
|
||||||
}
|
method: "PUT",
|
||||||
|
url: "/shop/{shop_uuid}/user/update",
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: "application/json",
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserService {
|
export class UserService {
|
||||||
/**
|
/**
|
||||||
* Get information about currently logged in user
|
* Get information about currently logged in user
|
||||||
* @returns UserPublic Successful Response
|
* @returns UserPublic Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static userGetUser(): CancelablePromise<UserGetUserResponse> {
|
public static userGetUser(): CancelablePromise<UserGetUserResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
url: '/user'
|
url: "/user",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update user details
|
* Update user details
|
||||||
* @param data The data for the request.
|
* @param data The data for the request.
|
||||||
* @param data.requestBody
|
* @param data.requestBody
|
||||||
* @returns boolean Successful Response
|
* @returns boolean Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static userUpdateUser(data: UserUpdateUserData): CancelablePromise<UserUpdateUserResponse> {
|
public static userUpdateUser(
|
||||||
return __request(OpenAPI, {
|
data: UserUpdateUserData,
|
||||||
method: 'PUT',
|
): CancelablePromise<UserUpdateUserResponse> {
|
||||||
url: '/user',
|
return __request(OpenAPI, {
|
||||||
body: data.requestBody,
|
method: "PUT",
|
||||||
mediaType: 'application/json',
|
url: "/user",
|
||||||
errors: {
|
body: data.requestBody,
|
||||||
422: 'Validation Error'
|
mediaType: "application/json",
|
||||||
}
|
errors: {
|
||||||
});
|
422: "Validation Error",
|
||||||
}
|
},
|
||||||
|
});
|
||||||
/**
|
}
|
||||||
* Register new user
|
|
||||||
* @param data The data for the request.
|
/**
|
||||||
* @param data.requestBody
|
* Register new user
|
||||||
* @returns boolean Successful Response
|
* @param data The data for the request.
|
||||||
* @throws ApiError
|
* @param data.requestBody
|
||||||
*/
|
* @returns boolean Successful Response
|
||||||
public static userRegister(data: UserRegisterData): CancelablePromise<UserRegisterResponse> {
|
* @throws ApiError
|
||||||
return __request(OpenAPI, {
|
*/
|
||||||
method: 'POST',
|
public static userRegister(
|
||||||
url: '/user',
|
data: UserRegisterData,
|
||||||
body: data.requestBody,
|
): CancelablePromise<UserRegisterResponse> {
|
||||||
mediaType: 'application/json',
|
return __request(OpenAPI, {
|
||||||
errors: {
|
method: "POST",
|
||||||
422: 'Validation Error'
|
url: "/user",
|
||||||
}
|
body: data.requestBody,
|
||||||
});
|
mediaType: "application/json",
|
||||||
}
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
/**
|
},
|
||||||
* Delete user
|
});
|
||||||
* @returns boolean Successful Response
|
}
|
||||||
* @throws ApiError
|
|
||||||
*/
|
/**
|
||||||
public static userDeleteUser(): CancelablePromise<UserDeleteUserResponse> {
|
* Delete user
|
||||||
return __request(OpenAPI, {
|
* @returns boolean Successful Response
|
||||||
method: 'DELETE',
|
* @throws ApiError
|
||||||
url: '/user'
|
*/
|
||||||
});
|
public static userDeleteUser(): CancelablePromise<UserDeleteUserResponse> {
|
||||||
}
|
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
|
* Delete user
|
||||||
*/
|
* @param data The data for the request.
|
||||||
public static shopDeleteUser(data: ShopDeleteUserData): CancelablePromise<ShopDeleteUserResponse> {
|
* @param data.shopUuid
|
||||||
return __request(OpenAPI, {
|
* @returns unknown Successful Response
|
||||||
method: 'DELETE',
|
* @throws ApiError
|
||||||
url: '/shop/{shop_uuid}/user/delete',
|
*/
|
||||||
path: {
|
public static shopDeleteUser(
|
||||||
shop_uuid: data.shopUuid
|
data: ShopDeleteUserData,
|
||||||
},
|
): CancelablePromise<ShopDeleteUserResponse> {
|
||||||
errors: {
|
return __request(OpenAPI, {
|
||||||
422: 'Validation Error'
|
method: "DELETE",
|
||||||
}
|
url: "/shop/{shop_uuid}/user/delete",
|
||||||
});
|
path: {
|
||||||
}
|
shop_uuid: data.shopUuid,
|
||||||
|
},
|
||||||
/**
|
errors: {
|
||||||
* User logout
|
422: "Validation Error",
|
||||||
* @returns unknown Successful Response
|
},
|
||||||
* @throws ApiError
|
});
|
||||||
*/
|
}
|
||||||
public static shopLogout(): CancelablePromise<ShopLogoutResponse> {
|
|
||||||
return __request(OpenAPI, {
|
/**
|
||||||
method: 'DELETE',
|
* User logout
|
||||||
url: '/shop/{shop_uuid}/user/logout'
|
* @returns unknown Successful Response
|
||||||
});
|
* @throws ApiError
|
||||||
}
|
*/
|
||||||
|
public static shopLogout(): CancelablePromise<ShopLogoutResponse> {
|
||||||
/**
|
return __request(OpenAPI, {
|
||||||
* Register new user
|
method: "DELETE",
|
||||||
* @param data The data for the request.
|
url: "/shop/{shop_uuid}/user/logout",
|
||||||
* @param data.shopUuid
|
});
|
||||||
* @param data.requestBody
|
}
|
||||||
* @returns unknown Successful Response
|
|
||||||
* @throws ApiError
|
/**
|
||||||
*/
|
* Register new user
|
||||||
public static shopRegister(data: ShopRegisterData): CancelablePromise<ShopRegisterResponse> {
|
* @param data The data for the request.
|
||||||
return __request(OpenAPI, {
|
* @param data.shopUuid
|
||||||
method: 'POST',
|
* @param data.requestBody
|
||||||
url: '/shop/{shop_uuid}/user/register',
|
* @returns unknown Successful Response
|
||||||
path: {
|
* @throws ApiError
|
||||||
shop_uuid: data.shopUuid
|
*/
|
||||||
},
|
public static shopRegister(
|
||||||
body: data.requestBody,
|
data: ShopRegisterData,
|
||||||
mediaType: 'application/json',
|
): CancelablePromise<ShopRegisterResponse> {
|
||||||
errors: {
|
return __request(OpenAPI, {
|
||||||
422: 'Validation Error'
|
method: "POST",
|
||||||
}
|
url: "/shop/{shop_uuid}/user/register",
|
||||||
});
|
path: {
|
||||||
}
|
shop_uuid: data.shopUuid,
|
||||||
|
},
|
||||||
/**
|
body: data.requestBody,
|
||||||
* Update user details
|
mediaType: "application/json",
|
||||||
* @param data The data for the request.
|
errors: {
|
||||||
* @param data.requestBody
|
422: "Validation Error",
|
||||||
* @returns unknown Successful Response
|
},
|
||||||
* @throws ApiError
|
});
|
||||||
*/
|
}
|
||||||
public static shopUpdateUser(data: ShopUpdateUserData): CancelablePromise<ShopUpdateUserResponse> {
|
|
||||||
return __request(OpenAPI, {
|
/**
|
||||||
method: 'PUT',
|
* Update user details
|
||||||
url: '/shop/{shop_uuid}/user/update',
|
* @param data The data for the request.
|
||||||
body: data.requestBody,
|
* @param data.requestBody
|
||||||
mediaType: 'application/json',
|
* @returns unknown Successful Response
|
||||||
errors: {
|
* @throws ApiError
|
||||||
422: 'Validation Error'
|
*/
|
||||||
}
|
public static shopUpdateUser(
|
||||||
});
|
data: ShopUpdateUserData,
|
||||||
}
|
): CancelablePromise<ShopUpdateUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: "PUT",
|
||||||
|
url: "/shop/{shop_uuid}/user/update",
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: "application/json",
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UtilsService {
|
export class UtilsService {
|
||||||
/**
|
/**
|
||||||
* Health Check
|
* Health Check
|
||||||
* Ping the API whether it's alive or not
|
* Ping the API whether it's alive or not
|
||||||
* @returns boolean Successful Response
|
* @returns boolean Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static utilsHealthCheck(): CancelablePromise<UtilsHealthCheckResponse> {
|
public static utilsHealthCheck(): CancelablePromise<UtilsHealthCheckResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
url: '/utils/health-check/'
|
url: "/utils/health-check/",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test Db
|
* Test Db
|
||||||
* Ping database using select 1 to see if connection works
|
* Ping database using select 1 to see if connection works
|
||||||
* @returns boolean Successful Response
|
* @returns boolean Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static utilsTestDb(): CancelablePromise<UtilsTestDbResponse> {
|
public static utilsTestDb(): CancelablePromise<UtilsTestDbResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
url: '/utils/test-db/'
|
url: "/utils/test-db/",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -1,142 +1,142 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
export type Body_Dashboard_login_access_token = {
|
export type Body_Dashboard_login_access_token = {
|
||||||
grant_type?: (string | null);
|
grant_type?: string | null;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
scope?: string;
|
scope?: string;
|
||||||
client_id?: (string | null);
|
client_id?: string | null;
|
||||||
client_secret?: (string | null);
|
client_secret?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Body_Shop_login_access_token = {
|
export type Body_Shop_login_access_token = {
|
||||||
grant_type?: (string | null);
|
grant_type?: string | null;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
scope?: string;
|
scope?: string;
|
||||||
client_id?: (string | null);
|
client_id?: string | null;
|
||||||
client_secret?: (string | null);
|
client_secret?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HTTPValidationError = {
|
export type HTTPValidationError = {
|
||||||
detail?: Array<ValidationError>;
|
detail?: Array<ValidationError>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ShopAddress = {
|
export type ShopAddress = {
|
||||||
street: string;
|
street: string;
|
||||||
city: string;
|
city: string;
|
||||||
state: string;
|
state: string;
|
||||||
postal_code: string;
|
postal_code: string;
|
||||||
country: string;
|
country: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ShopCreate = {
|
export type ShopCreate = {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
currency: string;
|
currency: string;
|
||||||
contact_email: string;
|
contact_email: string;
|
||||||
contact_phone_number: string;
|
contact_phone_number: string;
|
||||||
address: ShopAddress;
|
address: ShopAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Token = {
|
export type Token = {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
token_type?: string;
|
token_type?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserPublic = {
|
export type UserPublic = {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
first_name: (string | null);
|
first_name: string | null;
|
||||||
last_name: (string | null);
|
last_name: string | null;
|
||||||
phone_number: string;
|
phone_number: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserRegister = {
|
export type UserRegister = {
|
||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone_number: string;
|
phone_number: string;
|
||||||
/**
|
/**
|
||||||
* Password must conform to this regex:
|
* Password must conform to this regex:
|
||||||
* ```
|
* ```
|
||||||
* ^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$
|
* ^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserUpdate = {
|
export type UserUpdate = {
|
||||||
email: (string | null);
|
email: string | null;
|
||||||
phone_number: (string | null);
|
phone_number: string | null;
|
||||||
username: (string | null);
|
username: string | null;
|
||||||
first_name?: (string | null);
|
first_name?: string | null;
|
||||||
last_name?: (string | null);
|
last_name?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ValidationError = {
|
export type ValidationError = {
|
||||||
loc: Array<(string | number)>;
|
loc: Array<string | number>;
|
||||||
msg: string;
|
msg: string;
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserGetUserResponse = (UserPublic);
|
export type UserGetUserResponse = UserPublic;
|
||||||
|
|
||||||
export type UserUpdateUserData = {
|
export type UserUpdateUserData = {
|
||||||
requestBody: UserUpdate;
|
requestBody: UserUpdate;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserUpdateUserResponse = (boolean);
|
export type UserUpdateUserResponse = boolean;
|
||||||
|
|
||||||
export type UserRegisterData = {
|
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 = {
|
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 = {
|
export type DashboardRegisterNewShopData = {
|
||||||
requestBody: ShopCreate;
|
requestBody: ShopCreate;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DashboardRegisterNewShopResponse = (boolean);
|
export type DashboardRegisterNewShopResponse = boolean;
|
||||||
|
|
||||||
export type ShopLoginAccessTokenData = {
|
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 = {
|
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 = {
|
export type ShopRegisterData = {
|
||||||
requestBody: UserRegister;
|
requestBody: UserRegister;
|
||||||
shopUuid: unknown;
|
shopUuid: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ShopRegisterResponse = (unknown);
|
export type ShopRegisterResponse = unknown;
|
||||||
|
|
||||||
export type ShopUpdateUserData = {
|
export type ShopUpdateUserData = {
|
||||||
requestBody: {
|
requestBody: {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ShopUpdateUserResponse = (unknown);
|
export type ShopUpdateUserResponse = unknown;
|
||||||
|
|
||||||
export type UtilsHealthCheckResponse = (boolean);
|
export type UtilsHealthCheckResponse = boolean;
|
||||||
|
|
||||||
export type UtilsTestDbResponse = (boolean);
|
export type UtilsTestDbResponse = boolean;
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
IconArrowRightDashed,
|
IconArrowRightDashed,
|
||||||
IconDeviceLaptop,
|
IconDeviceLaptop,
|
||||||
IconMoon,
|
IconMoon,
|
||||||
IconSun
|
IconSun,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { useSearch } from "@/context/search-context";
|
import { useSearch } from "@/context/search-context";
|
||||||
import { useTheme } from "@/context/theme-context";
|
import { useTheme } from "@/context/theme-context";
|
||||||
@ -15,7 +15,7 @@ import {
|
|||||||
CommandInput,
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
CommandSeparator
|
CommandSeparator,
|
||||||
} from "@/components/ui/command";
|
} from "@/components/ui/command";
|
||||||
import { sidebarData } from "./layout/data/sidebar-data";
|
import { sidebarData } from "./layout/data/sidebar-data";
|
||||||
import { ScrollArea } from "./ui/scroll-area";
|
import { ScrollArea } from "./ui/scroll-area";
|
||||||
@ -30,7 +30,7 @@ export function CommandMenu() {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
command();
|
command();
|
||||||
},
|
},
|
||||||
[setOpen]
|
[setOpen],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -49,7 +49,8 @@ export function CommandMenu() {
|
|||||||
value={navItem.title}
|
value={navItem.title}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
runCommand(() => navigate({ to: navItem.url }));
|
runCommand(() => navigate({ to: navItem.url }));
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<div className="mr-2 flex h-4 w-4 items-center justify-center">
|
<div className="mr-2 flex h-4 w-4 items-center justify-center">
|
||||||
<IconArrowRightDashed className="size-2 text-muted-foreground/80" />
|
<IconArrowRightDashed className="size-2 text-muted-foreground/80" />
|
||||||
</div>
|
</div>
|
||||||
@ -63,7 +64,8 @@ export function CommandMenu() {
|
|||||||
value={subItem.title}
|
value={subItem.title}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
runCommand(() => navigate({ to: subItem.url }));
|
runCommand(() => navigate({ to: subItem.url }));
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<div className="mr-2 flex h-4 w-4 items-center justify-center">
|
<div className="mr-2 flex h-4 w-4 items-center justify-center">
|
||||||
<IconArrowRightDashed className="size-2 text-muted-foreground/80" />
|
<IconArrowRightDashed className="size-2 text-muted-foreground/80" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
AlertDialogDescription,
|
AlertDialogDescription,
|
||||||
AlertDialogFooter,
|
AlertDialogFooter,
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle
|
AlertDialogTitle,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
@ -56,7 +56,8 @@ export function ConfirmDialog(props: ConfirmDialogProps) {
|
|||||||
<Button
|
<Button
|
||||||
variant={destructive ? "destructive" : "default"}
|
variant={destructive ? "destructive" : "default"}
|
||||||
onClick={handleConfirm}
|
onClick={handleConfirm}
|
||||||
disabled={disabled || isLoading}>
|
disabled={disabled || isLoading}
|
||||||
|
>
|
||||||
{confirmText ?? "Continue"}
|
{confirmText ?? "Continue"}
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
|
@ -153,5 +153,5 @@ const currencies = [
|
|||||||
"YER",
|
"YER",
|
||||||
"ZAR",
|
"ZAR",
|
||||||
"ZMW",
|
"ZMW",
|
||||||
"ZWG"
|
"ZWG",
|
||||||
];
|
];
|
||||||
|
@ -2,11 +2,12 @@ import {
|
|||||||
Sidebar,
|
Sidebar,
|
||||||
SidebarContent,
|
SidebarContent,
|
||||||
SidebarHeader,
|
SidebarHeader,
|
||||||
SidebarRail
|
SidebarRail,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { NavGroup } from "@/components/layout/nav-group";
|
import { NavGroup } from "@/components/layout/nav-group";
|
||||||
import { sidebarData } from "./data/sidebar-data";
|
import { sidebarData } from "./data/sidebar-data";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { IconCoin } from "@tabler/icons-react";
|
||||||
|
|
||||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
return (
|
return (
|
||||||
@ -14,10 +15,11 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
<h1
|
<h1
|
||||||
className={cn(
|
className={cn(
|
||||||
"header-fixed peer/header flex h-16 w-[inherit] items-center gap-3 rounded-md bg-background p-4 text-xl font-bold sm:gap-4"
|
"header-fixed peer/header flex h-16 w-[inherit] items-center gap-3 rounded-md bg-background p-4 text-xl font-bold sm:gap-4",
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
SwagShop
|
>
|
||||||
|
<IconCoin />
|
||||||
</h1>
|
</h1>
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
@ -25,9 +27,6 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|||||||
<NavGroup key={props.title} {...props} />
|
<NavGroup key={props.title} {...props} />
|
||||||
))}
|
))}
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
{/* <SidebarFooter>
|
|
||||||
<NavUser user={sidebarData.user} />
|
|
||||||
</SidebarFooter> */}
|
|
||||||
<SidebarRail />
|
<SidebarRail />
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
IconBrowserCheck,
|
IconBrowserCheck,
|
||||||
IconBuildingStore,
|
IconBuildingStore,
|
||||||
|
IconClipboardCheckFilled,
|
||||||
IconCoin,
|
IconCoin,
|
||||||
IconForklift,
|
IconForklift,
|
||||||
IconHelp,
|
IconHelp,
|
||||||
@ -8,14 +9,12 @@ import {
|
|||||||
IconNotification,
|
IconNotification,
|
||||||
IconPackage,
|
IconPackage,
|
||||||
IconPalette,
|
IconPalette,
|
||||||
IconPercentage,
|
|
||||||
IconSettings,
|
IconSettings,
|
||||||
IconTag,
|
IconTag,
|
||||||
IconTool,
|
IconTool,
|
||||||
IconUserCog,
|
IconUserCog,
|
||||||
IconUsers
|
IconUsers,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { AudioWaveform, Command, GalleryVerticalEnd } from "lucide-react";
|
|
||||||
import { type SidebarData } from "../types";
|
import { type SidebarData } from "../types";
|
||||||
|
|
||||||
export const sidebarData: SidebarData = {
|
export const sidebarData: SidebarData = {
|
||||||
@ -26,45 +25,45 @@ export const sidebarData: SidebarData = {
|
|||||||
{
|
{
|
||||||
title: "Dashboard",
|
title: "Dashboard",
|
||||||
url: "/dashboard",
|
url: "/dashboard",
|
||||||
icon: IconLayoutDashboard
|
icon: IconLayoutDashboard,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Shop",
|
title: "Shop",
|
||||||
url: "/dashboard/shop",
|
url: "/dashboard/shop",
|
||||||
icon: IconBuildingStore
|
icon: IconBuildingStore,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Products",
|
title: "Products",
|
||||||
url: "/dashboard/products",
|
url: "/dashboard/products",
|
||||||
icon: IconPackage
|
icon: IconPackage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Inventory",
|
title: "Inventory",
|
||||||
url: "/dashboard/tasks",
|
url: "/dashboard/tasks",
|
||||||
icon: IconForklift
|
icon: IconForklift,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Sales",
|
title: "Sales",
|
||||||
icon: IconCoin,
|
icon: IconCoin,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "Discounts",
|
title: "Recent sales",
|
||||||
url: "/dashboard/sales/discounts",
|
url: "/dashboard/sales/recent-sales",
|
||||||
icon: IconPercentage
|
icon: IconClipboardCheckFilled,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Coupons",
|
title: "Coupons",
|
||||||
url: "/dashboard/sales/coupons",
|
url: "/dashboard/sales/coupons",
|
||||||
icon: IconTag
|
icon: IconTag,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Customers",
|
title: "Customers",
|
||||||
url: "/dashboard/users",
|
url: "/dashboard/users",
|
||||||
icon: IconUsers
|
icon: IconUsers,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Other",
|
title: "Other",
|
||||||
@ -76,36 +75,36 @@ export const sidebarData: SidebarData = {
|
|||||||
{
|
{
|
||||||
title: "Profile",
|
title: "Profile",
|
||||||
url: "/dashboard/settings",
|
url: "/dashboard/settings",
|
||||||
icon: IconUserCog
|
icon: IconUserCog,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Account",
|
title: "Account",
|
||||||
url: "/dashboard/settings/account",
|
url: "/dashboard/settings/account",
|
||||||
icon: IconTool
|
icon: IconTool,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Appearance",
|
title: "Appearance",
|
||||||
url: "/dashboard/settings/appearance",
|
url: "/dashboard/settings/appearance",
|
||||||
icon: IconPalette
|
icon: IconPalette,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Notifications",
|
title: "Notifications",
|
||||||
url: "/dashboard/settings/notifications",
|
url: "/dashboard/settings/notifications",
|
||||||
icon: IconNotification
|
icon: IconNotification,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Display",
|
title: "Display",
|
||||||
url: "/dashboard/settings/display",
|
url: "/dashboard/settings/display",
|
||||||
icon: IconBrowserCheck
|
icon: IconBrowserCheck,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Help Center",
|
title: "Help Center",
|
||||||
url: "/help-center",
|
url: "/help-center",
|
||||||
icon: IconHelp
|
icon: IconHelp,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
@ -34,9 +34,10 @@ export const Header = ({
|
|||||||
"flex h-16 items-center gap-3 bg-background p-4 sm:gap-4",
|
"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",
|
fixed && "header-fixed peer/header fixed z-50 w-[inherit] rounded-md",
|
||||||
offset > 10 && fixed ? "shadow" : "shadow-none",
|
offset > 10 && fixed ? "shadow" : "shadow-none",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<SidebarTrigger variant="outline" className="scale-125 sm:scale-100" />
|
<SidebarTrigger variant="outline" className="scale-125 sm:scale-100" />
|
||||||
<Separator orientation="vertical" className="h-6" />
|
<Separator orientation="vertical" className="h-6" />
|
||||||
{children}
|
{children}
|
||||||
|
@ -12,7 +12,7 @@ export const Main = ({ fixed, ...props }: MainProps) => {
|
|||||||
className={cn(
|
className={cn(
|
||||||
"peer-[.header-fixed]/header:mt-16",
|
"peer-[.header-fixed]/header:mt-16",
|
||||||
"px-4 py-6",
|
"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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@ -4,7 +4,7 @@ import { ChevronRight } from "lucide-react";
|
|||||||
import {
|
import {
|
||||||
Collapsible,
|
Collapsible,
|
||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible";
|
} from "@/components/ui/collapsible";
|
||||||
import {
|
import {
|
||||||
SidebarGroup,
|
SidebarGroup,
|
||||||
@ -15,7 +15,7 @@ import {
|
|||||||
SidebarMenuSub,
|
SidebarMenuSub,
|
||||||
SidebarMenuSubButton,
|
SidebarMenuSubButton,
|
||||||
SidebarMenuSubItem,
|
SidebarMenuSubItem,
|
||||||
useSidebar
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { Badge } from "../ui/badge";
|
import { Badge } from "../ui/badge";
|
||||||
import {
|
import {
|
||||||
@ -24,7 +24,7 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "../ui/dropdown-menu";
|
} from "../ui/dropdown-menu";
|
||||||
import { NavCollapsible, NavItem, NavLink, type NavGroup } from "./types";
|
import { NavCollapsible, NavItem, NavLink, type NavGroup } from "./types";
|
||||||
|
|
||||||
@ -64,7 +64,8 @@ const SidebarMenuLink = ({ item, href }: { item: NavLink; href: string }) => {
|
|||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
asChild
|
asChild
|
||||||
isActive={checkIsActive(href, item)}
|
isActive={checkIsActive(href, item)}
|
||||||
tooltip={item.title}>
|
tooltip={item.title}
|
||||||
|
>
|
||||||
<Link to={item.url} onClick={() => setOpenMobile(false)}>
|
<Link to={item.url} onClick={() => setOpenMobile(false)}>
|
||||||
{item.icon && <item.icon />}
|
{item.icon && <item.icon />}
|
||||||
<span>{item.title}</span>
|
<span>{item.title}</span>
|
||||||
@ -77,7 +78,7 @@ const SidebarMenuLink = ({ item, href }: { item: NavLink; href: string }) => {
|
|||||||
|
|
||||||
const SidebarMenuCollapsible = ({
|
const SidebarMenuCollapsible = ({
|
||||||
item,
|
item,
|
||||||
href
|
href,
|
||||||
}: {
|
}: {
|
||||||
item: NavCollapsible;
|
item: NavCollapsible;
|
||||||
href: string;
|
href: string;
|
||||||
@ -87,7 +88,8 @@ const SidebarMenuCollapsible = ({
|
|||||||
<Collapsible
|
<Collapsible
|
||||||
asChild
|
asChild
|
||||||
defaultOpen={checkIsActive(href, item, true)}
|
defaultOpen={checkIsActive(href, item, true)}
|
||||||
className="group/collapsible">
|
className="group/collapsible"
|
||||||
|
>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<SidebarMenuButton tooltip={item.title}>
|
<SidebarMenuButton tooltip={item.title}>
|
||||||
@ -103,7 +105,8 @@ const SidebarMenuCollapsible = ({
|
|||||||
<SidebarMenuSubItem key={subItem.title}>
|
<SidebarMenuSubItem key={subItem.title}>
|
||||||
<SidebarMenuSubButton
|
<SidebarMenuSubButton
|
||||||
asChild
|
asChild
|
||||||
isActive={checkIsActive(href, subItem)}>
|
isActive={checkIsActive(href, subItem)}
|
||||||
|
>
|
||||||
<Link to={subItem.url} onClick={() => setOpenMobile(false)}>
|
<Link to={subItem.url} onClick={() => setOpenMobile(false)}>
|
||||||
{subItem.icon && <subItem.icon />}
|
{subItem.icon && <subItem.icon />}
|
||||||
<span>{subItem.title}</span>
|
<span>{subItem.title}</span>
|
||||||
@ -121,7 +124,7 @@ const SidebarMenuCollapsible = ({
|
|||||||
|
|
||||||
const SidebarMenuCollapsedDropdown = ({
|
const SidebarMenuCollapsedDropdown = ({
|
||||||
item,
|
item,
|
||||||
href
|
href,
|
||||||
}: {
|
}: {
|
||||||
item: NavCollapsible;
|
item: NavCollapsible;
|
||||||
href: string;
|
href: string;
|
||||||
@ -132,7 +135,8 @@ const SidebarMenuCollapsedDropdown = ({
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
tooltip={item.title}
|
tooltip={item.title}
|
||||||
isActive={checkIsActive(href, item)}>
|
isActive={checkIsActive(href, item)}
|
||||||
|
>
|
||||||
{item.icon && <item.icon />}
|
{item.icon && <item.icon />}
|
||||||
<span>{item.title}</span>
|
<span>{item.title}</span>
|
||||||
{item.badge && <NavBadge>{item.badge}</NavBadge>}
|
{item.badge && <NavBadge>{item.badge}</NavBadge>}
|
||||||
@ -148,7 +152,8 @@ const SidebarMenuCollapsedDropdown = ({
|
|||||||
<DropdownMenuItem key={`${sub.title}-${sub.url}`} asChild>
|
<DropdownMenuItem key={`${sub.title}-${sub.url}`} asChild>
|
||||||
<Link
|
<Link
|
||||||
to={sub.url}
|
to={sub.url}
|
||||||
className={`${checkIsActive(href, sub) ? "bg-secondary" : ""}`}>
|
className={`${checkIsActive(href, sub) ? "bg-secondary" : ""}`}
|
||||||
|
>
|
||||||
{sub.icon && <sub.icon />}
|
{sub.icon && <sub.icon />}
|
||||||
<span className="max-w-52 text-wrap">{sub.title}</span>
|
<span className="max-w-52 text-wrap">{sub.title}</span>
|
||||||
{sub.badge && (
|
{sub.badge && (
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
ChevronsUpDown,
|
ChevronsUpDown,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
LogOut,
|
LogOut,
|
||||||
Sparkles
|
Sparkles,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
@ -15,17 +15,17 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
useSidebar
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
export function NavUser({
|
export function NavUser({
|
||||||
user
|
user,
|
||||||
}: {
|
}: {
|
||||||
user: {
|
user: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -42,7 +42,8 @@ export function NavUser({
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
size="lg"
|
size="lg"
|
||||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||||
|
>
|
||||||
<Avatar className="h-8 w-8 rounded-lg">
|
<Avatar className="h-8 w-8 rounded-lg">
|
||||||
<AvatarImage src={user.avatar} alt={user.name} />
|
<AvatarImage src={user.avatar} alt={user.name} />
|
||||||
<AvatarFallback className="rounded-lg">SN</AvatarFallback>
|
<AvatarFallback className="rounded-lg">SN</AvatarFallback>
|
||||||
@ -58,7 +59,8 @@ export function NavUser({
|
|||||||
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
||||||
side={isMobile ? "bottom" : "right"}
|
side={isMobile ? "bottom" : "right"}
|
||||||
align="end"
|
align="end"
|
||||||
sideOffset={4}>
|
sideOffset={4}
|
||||||
|
>
|
||||||
<DropdownMenuLabel className="p-0 font-normal">
|
<DropdownMenuLabel className="p-0 font-normal">
|
||||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||||
<Avatar className="h-8 w-8 rounded-lg">
|
<Avatar className="h-8 w-8 rounded-lg">
|
||||||
|
@ -7,17 +7,17 @@ import {
|
|||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
useSidebar
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
export function TeamSwitcher({
|
export function TeamSwitcher({
|
||||||
teams
|
teams,
|
||||||
}: {
|
}: {
|
||||||
teams: {
|
teams: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -35,7 +35,8 @@ export function TeamSwitcher({
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
size="lg"
|
size="lg"
|
||||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||||
|
>
|
||||||
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||||
<activeTeam.logo className="size-4" />
|
<activeTeam.logo className="size-4" />
|
||||||
</div>
|
</div>
|
||||||
@ -52,7 +53,8 @@ export function TeamSwitcher({
|
|||||||
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
||||||
align="start"
|
align="start"
|
||||||
side={isMobile ? "bottom" : "right"}
|
side={isMobile ? "bottom" : "right"}
|
||||||
sideOffset={4}>
|
sideOffset={4}
|
||||||
|
>
|
||||||
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
||||||
Teams
|
Teams
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
@ -60,7 +62,8 @@ export function TeamSwitcher({
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={team.name}
|
key={team.name}
|
||||||
onClick={() => setActiveTeam(team)}
|
onClick={() => setActiveTeam(team)}
|
||||||
className="gap-2 p-2">
|
className="gap-2 p-2"
|
||||||
|
>
|
||||||
<div className="flex size-6 items-center justify-center rounded-sm border">
|
<div className="flex size-6 items-center justify-center rounded-sm border">
|
||||||
<team.logo className="size-4 shrink-0" />
|
<team.logo className="size-4 shrink-0" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
interface TopNavProps extends React.HTMLAttributes<HTMLElement> {
|
interface TopNavProps extends React.HTMLAttributes<HTMLElement> {
|
||||||
@ -34,7 +34,8 @@ export function TopNav({ className, links, ...props }: TopNavProps) {
|
|||||||
<Link
|
<Link
|
||||||
to={href}
|
to={href}
|
||||||
className={!isActive ? "text-muted-foreground" : ""}
|
className={!isActive ? "text-muted-foreground" : ""}
|
||||||
disabled={disabled}>
|
disabled={disabled}
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
@ -46,15 +47,17 @@ export function TopNav({ className, links, ...props }: TopNavProps) {
|
|||||||
<nav
|
<nav
|
||||||
className={cn(
|
className={cn(
|
||||||
"hidden items-center space-x-4 md:flex lg:space-x-6",
|
"hidden items-center space-x-4 md:flex lg:space-x-6",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{links.map(({ title, href, isActive, disabled }) => (
|
{links.map(({ title, href, isActive, disabled }) => (
|
||||||
<Link
|
<Link
|
||||||
key={`${title}-${href}`}
|
key={`${title}-${href}`}
|
||||||
to={href}
|
to={href}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={`text-sm font-medium transition-colors hover:text-primary ${isActive ? "" : "text-muted-foreground"}`}>
|
className={`text-sm font-medium transition-colors hover:text-primary ${isActive ? "" : "text-muted-foreground"}`}
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
@ -3,13 +3,13 @@ import { cn } from "@/lib/utils";
|
|||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -21,7 +21,7 @@ interface Props {
|
|||||||
export default function LongText({
|
export default function LongText({
|
||||||
children,
|
children,
|
||||||
className = "",
|
className = "",
|
||||||
contentClassName = ""
|
contentClassName = "",
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const [isOverflown, setIsOverflown] = useState(false);
|
const [isOverflown, setIsOverflown] = useState(false);
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Menu } from "lucide-react";
|
import { Menu } from "lucide-react";
|
||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
@ -29,7 +29,6 @@ const MainNavbar = () => {
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<DynamicLoginButton />
|
<DynamicLoginButton />
|
||||||
<div className="mr-2 flex items-center gap-2 md:hidden">
|
<div className="mr-2 flex items-center gap-2 md:hidden">
|
||||||
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" size="icon">
|
<Button variant="outline" size="icon">
|
||||||
|
@ -5,7 +5,7 @@ import { ChevronRight, type LucideIcon } from "lucide-react";
|
|||||||
import {
|
import {
|
||||||
Collapsible,
|
Collapsible,
|
||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible";
|
} from "@/components/ui/collapsible";
|
||||||
import {
|
import {
|
||||||
SidebarGroup,
|
SidebarGroup,
|
||||||
@ -15,11 +15,11 @@ import {
|
|||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
SidebarMenuSub,
|
SidebarMenuSub,
|
||||||
SidebarMenuSubButton,
|
SidebarMenuSubButton,
|
||||||
SidebarMenuSubItem
|
SidebarMenuSubItem,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
export function NavMain({
|
export function NavMain({
|
||||||
items
|
items,
|
||||||
}: {
|
}: {
|
||||||
items: {
|
items: {
|
||||||
title: string;
|
title: string;
|
||||||
@ -41,7 +41,8 @@ export function NavMain({
|
|||||||
key={item.title}
|
key={item.title}
|
||||||
asChild
|
asChild
|
||||||
defaultOpen={item.isActive}
|
defaultOpen={item.isActive}
|
||||||
className="group/collapsible">
|
className="group/collapsible"
|
||||||
|
>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<SidebarMenuButton tooltip={item.title}>
|
<SidebarMenuButton tooltip={item.title}>
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
Forward,
|
Forward,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
Trash2,
|
Trash2,
|
||||||
type LucideIcon
|
type LucideIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -13,7 +13,7 @@ import {
|
|||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
SidebarGroup,
|
SidebarGroup,
|
||||||
@ -22,11 +22,11 @@ import {
|
|||||||
SidebarMenuAction,
|
SidebarMenuAction,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
useSidebar
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
export function NavProjects({
|
export function NavProjects({
|
||||||
projects
|
projects,
|
||||||
}: {
|
}: {
|
||||||
projects: {
|
projects: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -58,7 +58,8 @@ export function NavProjects({
|
|||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
className="w-48 rounded-lg"
|
className="w-48 rounded-lg"
|
||||||
side={isMobile ? "bottom" : "right"}
|
side={isMobile ? "bottom" : "right"}
|
||||||
align={isMobile ? "end" : "start"}>
|
align={isMobile ? "end" : "start"}
|
||||||
|
>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<Folder className="text-muted-foreground" />
|
<Folder className="text-muted-foreground" />
|
||||||
<span>View Project</span>
|
<span>View Project</span>
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
ChevronsUpDown,
|
ChevronsUpDown,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
LogOut,
|
LogOut,
|
||||||
Sparkles
|
Sparkles,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
@ -17,17 +17,17 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
useSidebar
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
export function NavUser({
|
export function NavUser({
|
||||||
user
|
user,
|
||||||
}: {
|
}: {
|
||||||
user: {
|
user: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -44,7 +44,8 @@ export function NavUser({
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
size="lg"
|
size="lg"
|
||||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||||
|
>
|
||||||
<Avatar className="h-8 w-8 rounded-lg">
|
<Avatar className="h-8 w-8 rounded-lg">
|
||||||
<AvatarImage src={user.avatar} alt={user.name} />
|
<AvatarImage src={user.avatar} alt={user.name} />
|
||||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||||
@ -60,7 +61,8 @@ export function NavUser({
|
|||||||
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
||||||
side={isMobile ? "bottom" : "right"}
|
side={isMobile ? "bottom" : "right"}
|
||||||
align="end"
|
align="end"
|
||||||
sideOffset={4}>
|
sideOffset={4}
|
||||||
|
>
|
||||||
<DropdownMenuLabel className="p-0 font-normal">
|
<DropdownMenuLabel className="p-0 font-normal">
|
||||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||||
<Avatar className="h-8 w-8 rounded-lg">
|
<Avatar className="h-8 w-8 rounded-lg">
|
||||||
|
@ -26,12 +26,13 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className="absolute right-1 top-1/2 h-6 w-6 -translate-y-1/2 rounded-md text-muted-foreground"
|
className="absolute right-1 top-1/2 h-6 w-6 -translate-y-1/2 rounded-md text-muted-foreground"
|
||||||
onClick={() => setShowPassword((prev) => !prev)}>
|
onClick={() => setShowPassword((prev) => !prev)}
|
||||||
|
>
|
||||||
{showPassword ? <IconEye size={18} /> : <IconEyeOff size={18} />}
|
{showPassword ? <IconEye size={18} /> : <IconEyeOff size={18} />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
PasswordInput.displayName = "PasswordInput";
|
PasswordInput.displayName = "PasswordInput";
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ const PinInput = ({ className, children, ref, ...props }: PinInputProps) => {
|
|||||||
placeholder,
|
placeholder,
|
||||||
type,
|
type,
|
||||||
length,
|
length,
|
||||||
readOnly
|
readOnly,
|
||||||
});
|
});
|
||||||
|
|
||||||
/* call onChange func if pinValue changes */
|
/* call onChange func if pinValue changes */
|
||||||
@ -171,7 +171,7 @@ const PinInput = ({ className, children, ref, ...props }: PinInputProps) => {
|
|||||||
} else {
|
} else {
|
||||||
refMap?.delete(pinIndex);
|
refMap?.delete(pinIndex);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
skipRef.current = skipRef.current + 1;
|
skipRef.current = skipRef.current + 1;
|
||||||
@ -220,7 +220,7 @@ const PinInputField = <T extends React.ElementType = "input">({
|
|||||||
const isInsidePinInput = React.useContext(PinInputContext);
|
const isInsidePinInput = React.useContext(PinInputContext);
|
||||||
if (!isInsidePinInput) {
|
if (!isInsidePinInput) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`PinInputField must be used within ${PinInput.displayName}.`
|
`PinInputField must be used within ${PinInput.displayName}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +254,7 @@ const usePinInput = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
type,
|
type,
|
||||||
length,
|
length,
|
||||||
readOnly
|
readOnly,
|
||||||
}: UsePinInputProps) => {
|
}: UsePinInputProps) => {
|
||||||
const pinInputs = React.useMemo(
|
const pinInputs = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -263,9 +263,9 @@ const usePinInput = ({
|
|||||||
? defaultValue.charAt(index)
|
? defaultValue.charAt(index)
|
||||||
: value
|
: value
|
||||||
? value.charAt(index)
|
? value.charAt(index)
|
||||||
: ""
|
: "",
|
||||||
),
|
),
|
||||||
[defaultValue, length, value]
|
[defaultValue, length, value],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [pins, setPins] = React.useState(pinInputs);
|
const [pins, setPins] = React.useState(pinInputs);
|
||||||
@ -305,7 +305,7 @@ const usePinInput = ({
|
|||||||
|
|
||||||
function handleFocus(
|
function handleFocus(
|
||||||
event: React.FocusEvent<HTMLInputElement>,
|
event: React.FocusEvent<HTMLInputElement>,
|
||||||
index: number
|
index: number,
|
||||||
) {
|
) {
|
||||||
event.target.select();
|
event.target.select();
|
||||||
focusInput(index);
|
focusInput(index);
|
||||||
@ -332,7 +332,7 @@ const usePinInput = ({
|
|||||||
} else {
|
} else {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ const usePinInput = ({
|
|||||||
|
|
||||||
function handleKeyDown(
|
function handleKeyDown(
|
||||||
event: React.KeyboardEvent<HTMLInputElement>,
|
event: React.KeyboardEvent<HTMLInputElement>,
|
||||||
index: number
|
index: number,
|
||||||
) {
|
) {
|
||||||
const { ctrlKey, key, shiftKey, metaKey } = event;
|
const { ctrlKey, key, shiftKey, metaKey } = event;
|
||||||
|
|
||||||
@ -429,7 +429,7 @@ const usePinInput = ({
|
|||||||
handleBlur,
|
handleBlur,
|
||||||
handleChange,
|
handleChange,
|
||||||
handlePaste,
|
handlePaste,
|
||||||
handleKeyDown
|
handleKeyDown,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import useAuth from "@/hooks/useAuth";
|
import useAuth from "@/hooks/useAuth";
|
||||||
|
|
||||||
|
@ -16,9 +16,10 @@ export function Search({ className = "", placeholder = "Search" }: Props) {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative h-8 w-full flex-1 justify-start rounded-md bg-muted/25 text-sm font-normal text-muted-foreground shadow-none hover:bg-muted/50 sm:pr-12 md:w-40 md:flex-none lg:w-56 xl:w-64",
|
"relative h-8 w-full flex-1 justify-start rounded-md bg-muted/25 text-sm font-normal text-muted-foreground shadow-none hover:bg-muted/50 sm:pr-12 md:w-40 md:flex-none lg:w-56 xl:w-64",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
onClick={() => setOpen(true)}>
|
onClick={() => setOpen(true)}
|
||||||
|
>
|
||||||
<IconSearch
|
<IconSearch
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="absolute left-1.5 top-1/2 -translate-y-1/2"
|
className="absolute left-1.5 top-1/2 -translate-y-1/2"
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
interface SelectDropdownProps {
|
interface SelectDropdownProps {
|
||||||
@ -28,7 +28,7 @@ export function SelectDropdown({
|
|||||||
placeholder,
|
placeholder,
|
||||||
disabled,
|
disabled,
|
||||||
className = "",
|
className = "",
|
||||||
isControlled = false
|
isControlled = false,
|
||||||
}: SelectDropdownProps) {
|
}: SelectDropdownProps) {
|
||||||
const defaultState = isControlled
|
const defaultState = isControlled
|
||||||
? { value: defaultValue, onValueChange }
|
? { value: defaultValue, onValueChange }
|
||||||
|
@ -2,7 +2,8 @@ const SkipToMain = () => {
|
|||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className={`fixed left-44 z-[999] -translate-y-52 whitespace-nowrap bg-primary px-4 py-2 text-sm font-medium text-primary-foreground opacity-95 shadow transition hover:bg-primary/90 focus:translate-y-3 focus:transform focus-visible:ring-1 focus-visible:ring-ring`}
|
className={`fixed left-44 z-[999] -translate-y-52 whitespace-nowrap bg-primary px-4 py-2 text-sm font-medium text-primary-foreground opacity-95 shadow transition hover:bg-primary/90 focus:translate-y-3 focus:transform focus-visible:ring-1 focus-visible:ring-ring`}
|
||||||
href="#content">
|
href="#content"
|
||||||
|
>
|
||||||
Skip to Main
|
Skip to Main
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
@ -10,17 +10,17 @@ import {
|
|||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
useSidebar
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
export function TeamSwitcher({
|
export function TeamSwitcher({
|
||||||
teams
|
teams,
|
||||||
}: {
|
}: {
|
||||||
teams: {
|
teams: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -38,7 +38,8 @@ export function TeamSwitcher({
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
size="lg"
|
size="lg"
|
||||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||||
|
>
|
||||||
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||||
<activeTeam.logo className="size-4" />
|
<activeTeam.logo className="size-4" />
|
||||||
</div>
|
</div>
|
||||||
@ -55,7 +56,8 @@ export function TeamSwitcher({
|
|||||||
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
||||||
align="start"
|
align="start"
|
||||||
side={isMobile ? "bottom" : "right"}
|
side={isMobile ? "bottom" : "right"}
|
||||||
sideOffset={4}>
|
sideOffset={4}
|
||||||
|
>
|
||||||
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
||||||
Teams
|
Teams
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
@ -63,7 +65,8 @@ export function TeamSwitcher({
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={team.name}
|
key={team.name}
|
||||||
onClick={() => setActiveTeam(team)}
|
onClick={() => setActiveTeam(team)}
|
||||||
className="gap-2 p-2">
|
className="gap-2 p-2"
|
||||||
|
>
|
||||||
<div className="flex size-6 items-center justify-center rounded-sm border">
|
<div className="flex size-6 items-center justify-center rounded-sm border">
|
||||||
<team.logo className="size-4 shrink-0" />
|
<team.logo className="size-4 shrink-0" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
export function ThemeSwitch() {
|
export function ThemeSwitch() {
|
||||||
|
@ -1,56 +1,56 @@
|
|||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||||
import { ChevronDown } from "lucide-react"
|
import { ChevronDown } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const Accordion = AccordionPrimitive.Root
|
const Accordion = AccordionPrimitive.Root;
|
||||||
|
|
||||||
const AccordionItem = React.forwardRef<
|
const AccordionItem = React.forwardRef<
|
||||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<AccordionPrimitive.Item
|
<AccordionPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("border-b", className)}
|
className={cn("border-b", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
AccordionItem.displayName = "AccordionItem"
|
AccordionItem.displayName = "AccordionItem";
|
||||||
|
|
||||||
const AccordionTrigger = React.forwardRef<
|
const AccordionTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, ...props }, ref) => (
|
||||||
<AccordionPrimitive.Header className="flex">
|
<AccordionPrimitive.Header className="flex">
|
||||||
<AccordionPrimitive.Trigger
|
<AccordionPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||||
</AccordionPrimitive.Trigger>
|
</AccordionPrimitive.Trigger>
|
||||||
</AccordionPrimitive.Header>
|
</AccordionPrimitive.Header>
|
||||||
))
|
));
|
||||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
const AccordionContent = React.forwardRef<
|
const AccordionContent = React.forwardRef<
|
||||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, ...props }, ref) => (
|
||||||
<AccordionPrimitive.Content
|
<AccordionPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||||
</AccordionPrimitive.Content>
|
</AccordionPrimitive.Content>
|
||||||
))
|
));
|
||||||
|
|
||||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
||||||
|
@ -16,7 +16,7 @@ const AlertDialogOverlay = React.forwardRef<
|
|||||||
<AlertDialogPrimitive.Overlay
|
<AlertDialogPrimitive.Overlay
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -34,7 +34,7 @@ const AlertDialogContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -49,7 +49,7 @@ const AlertDialogHeader = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col space-y-2 text-center sm:text-left",
|
"flex flex-col space-y-2 text-center sm:text-left",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -63,7 +63,7 @@ const AlertDialogFooter = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -116,7 +116,7 @@ const AlertDialogCancel = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
buttonVariants({ variant: "outline" }),
|
buttonVariants({ variant: "outline" }),
|
||||||
"mt-2 sm:mt-0",
|
"mt-2 sm:mt-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -134,5 +134,5 @@ export {
|
|||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogDescription,
|
AlertDialogDescription,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel
|
AlertDialogCancel,
|
||||||
};
|
};
|
||||||
|
@ -9,13 +9,13 @@ const alertVariants = cva(
|
|||||||
variant: {
|
variant: {
|
||||||
default: "bg-background text-foreground",
|
default: "bg-background text-foreground",
|
||||||
destructive:
|
destructive:
|
||||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive"
|
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default"
|
variant: "default",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const Alert = React.forwardRef<
|
const Alert = React.forwardRef<
|
||||||
|
@ -10,7 +10,7 @@ const Avatar = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -37,7 +37,7 @@ const AvatarFallback = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@ -13,13 +13,13 @@ const badgeVariants = cva(
|
|||||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
destructive:
|
destructive:
|
||||||
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
||||||
outline: "text-foreground"
|
outline: "text-foreground",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default"
|
variant: "default",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface BadgeProps
|
export interface BadgeProps
|
||||||
|
@ -20,7 +20,7 @@ const BreadcrumbList = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
|
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -81,7 +81,8 @@ const BreadcrumbSeparator = ({
|
|||||||
role="presentation"
|
role="presentation"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={cn("[&>svg]:h-3.5 [&>svg]:w-3.5", className)}
|
className={cn("[&>svg]:h-3.5 [&>svg]:w-3.5", className)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{children ?? <ChevronRight />}
|
{children ?? <ChevronRight />}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@ -95,7 +96,8 @@ const BreadcrumbEllipsis = ({
|
|||||||
role="presentation"
|
role="presentation"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
<span className="sr-only">More</span>
|
<span className="sr-only">More</span>
|
||||||
</span>
|
</span>
|
||||||
@ -109,5 +111,5 @@ export {
|
|||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
BreadcrumbEllipsis
|
BreadcrumbEllipsis,
|
||||||
};
|
};
|
||||||
|
@ -17,20 +17,20 @@ const buttonVariants = cva(
|
|||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
link: "text-primary underline-offset-4 hover:underline"
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-10 px-4 py-2",
|
default: "h-10 px-4 py-2",
|
||||||
sm: "h-9 rounded-md px-3",
|
sm: "h-9 rounded-md px-3",
|
||||||
lg: "h-11 rounded-md px-8",
|
lg: "h-11 rounded-md px-8",
|
||||||
icon: "h-10 w-10"
|
icon: "h-10 w-10",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: "default",
|
||||||
size: "default"
|
size: "default",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface ButtonProps
|
export interface ButtonProps
|
||||||
@ -49,7 +49,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
Button.displayName = "Button";
|
Button.displayName = "Button";
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ function Calendar({
|
|||||||
nav: "space-x-1 flex items-center",
|
nav: "space-x-1 flex items-center",
|
||||||
nav_button: cn(
|
nav_button: cn(
|
||||||
buttonVariants({ variant: "outline" }),
|
buttonVariants({ variant: "outline" }),
|
||||||
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
|
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
|
||||||
),
|
),
|
||||||
nav_button_previous: "absolute left-1",
|
nav_button_previous: "absolute left-1",
|
||||||
nav_button_next: "absolute right-1",
|
nav_button_next: "absolute right-1",
|
||||||
@ -37,7 +37,7 @@ function Calendar({
|
|||||||
cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
|
cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
|
||||||
day: cn(
|
day: cn(
|
||||||
buttonVariants({ variant: "ghost" }),
|
buttonVariants({ variant: "ghost" }),
|
||||||
"h-9 w-9 p-0 font-normal aria-selected:opacity-100"
|
"h-9 w-9 p-0 font-normal aria-selected:opacity-100",
|
||||||
),
|
),
|
||||||
day_range_end: "day-range-end",
|
day_range_end: "day-range-end",
|
||||||
day_selected:
|
day_selected:
|
||||||
@ -49,7 +49,7 @@ function Calendar({
|
|||||||
day_range_middle:
|
day_range_middle:
|
||||||
"aria-selected:bg-accent aria-selected:text-accent-foreground",
|
"aria-selected:bg-accent aria-selected:text-accent-foreground",
|
||||||
day_hidden: "invisible",
|
day_hidden: "invisible",
|
||||||
...classNames
|
...classNames,
|
||||||
}}
|
}}
|
||||||
components={{
|
components={{
|
||||||
IconLeft: ({ className, ...props }) => (
|
IconLeft: ({ className, ...props }) => (
|
||||||
@ -57,7 +57,7 @@ function Calendar({
|
|||||||
),
|
),
|
||||||
IconRight: ({ className, ...props }) => (
|
IconRight: ({ className, ...props }) => (
|
||||||
<ChevronRight className={cn("h-4 w-4", className)} {...props} />
|
<ChevronRight className={cn("h-4 w-4", className)} {...props} />
|
||||||
)
|
),
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@ -9,7 +9,7 @@ const Card = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-xl border bg-card text-card-foreground shadow",
|
"rounded-xl border bg-card text-card-foreground shadow",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -78,5 +78,5 @@ export {
|
|||||||
CardFooter,
|
CardFooter,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardContent
|
CardContent,
|
||||||
};
|
};
|
||||||
|
@ -11,11 +11,13 @@ const Checkbox = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<CheckboxPrimitive.Indicator
|
<CheckboxPrimitive.Indicator
|
||||||
className={cn("flex items-center justify-center text-current")}>
|
className={cn("flex items-center justify-center text-current")}
|
||||||
|
>
|
||||||
<Check className="h-4 w-4" />
|
<Check className="h-4 w-4" />
|
||||||
</CheckboxPrimitive.Indicator>
|
</CheckboxPrimitive.Indicator>
|
||||||
</CheckboxPrimitive.Root>
|
</CheckboxPrimitive.Root>
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogTitle
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
|
|
||||||
const Command = React.forwardRef<
|
const Command = React.forwardRef<
|
||||||
@ -19,7 +19,7 @@ const Command = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -54,7 +54,7 @@ const CommandInput = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -97,7 +97,7 @@ const CommandGroup = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -125,7 +125,7 @@ const CommandItem = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -141,7 +141,7 @@ const CommandShortcut = ({
|
|||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"ml-auto text-xs tracking-widest text-muted-foreground",
|
"ml-auto text-xs tracking-widest text-muted-foreground",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -158,5 +158,5 @@ export {
|
|||||||
CommandGroup,
|
CommandGroup,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandShortcut,
|
CommandShortcut,
|
||||||
CommandSeparator
|
CommandSeparator,
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -38,9 +38,10 @@ const DialogContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
@ -58,7 +59,7 @@ const DialogHeader = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -72,7 +73,7 @@ const DialogFooter = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -87,7 +88,7 @@ const DialogTitle = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-lg font-semibold leading-none tracking-tight",
|
"text-lg font-semibold leading-none tracking-tight",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -116,5 +117,5 @@ export {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogDescription
|
DialogDescription,
|
||||||
};
|
};
|
||||||
|
@ -26,9 +26,10 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||||
inset && "pl-8",
|
inset && "pl-8",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
<ChevronRight className="ml-auto" />
|
<ChevronRight className="ml-auto" />
|
||||||
</DropdownMenuPrimitive.SubTrigger>
|
</DropdownMenuPrimitive.SubTrigger>
|
||||||
@ -44,7 +45,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -63,7 +64,7 @@ const DropdownMenuContent = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
||||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -82,7 +83,7 @@ const DropdownMenuItem = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
inset && "pl-8",
|
inset && "pl-8",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -97,10 +98,11 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
<DropdownMenuPrimitive.ItemIndicator>
|
<DropdownMenuPrimitive.ItemIndicator>
|
||||||
<Check className="h-4 w-4" />
|
<Check className="h-4 w-4" />
|
||||||
@ -120,9 +122,10 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
<DropdownMenuPrimitive.ItemIndicator>
|
<DropdownMenuPrimitive.ItemIndicator>
|
||||||
<Circle className="h-2 w-2 fill-current" />
|
<Circle className="h-2 w-2 fill-current" />
|
||||||
@ -144,7 +147,7 @@ const DropdownMenuLabel = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"px-2 py-1.5 text-sm font-semibold",
|
"px-2 py-1.5 text-sm font-semibold",
|
||||||
inset && "pl-8",
|
inset && "pl-8",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -191,5 +194,5 @@ export {
|
|||||||
DropdownMenuSub,
|
DropdownMenuSub,
|
||||||
DropdownMenuSubContent,
|
DropdownMenuSubContent,
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuRadioGroup
|
DropdownMenuRadioGroup,
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
FieldPath,
|
FieldPath,
|
||||||
FieldValues,
|
FieldValues,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
useFormContext
|
useFormContext,
|
||||||
} from "react-hook-form";
|
} from "react-hook-form";
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||||
import { Slot } from "@radix-ui/react-slot";
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
@ -16,18 +16,18 @@ const Form = FormProvider;
|
|||||||
|
|
||||||
type FormFieldContextValue<
|
type FormFieldContextValue<
|
||||||
TFieldValues extends FieldValues = FieldValues,
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||||
> = {
|
> = {
|
||||||
name: TName;
|
name: TName;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||||
{} as FormFieldContextValue
|
{} as FormFieldContextValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
const FormField = <
|
const FormField = <
|
||||||
TFieldValues extends FieldValues = FieldValues,
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||||
>({
|
>({
|
||||||
...props
|
...props
|
||||||
}: ControllerProps<TFieldValues, TName>) => {
|
}: ControllerProps<TFieldValues, TName>) => {
|
||||||
@ -57,7 +57,7 @@ const useFormField = () => {
|
|||||||
formItemId: `${id}-form-item`,
|
formItemId: `${id}-form-item`,
|
||||||
formDescriptionId: `${id}-form-item-description`,
|
formDescriptionId: `${id}-form-item-description`,
|
||||||
formMessageId: `${id}-form-item-message`,
|
formMessageId: `${id}-form-item-message`,
|
||||||
...fieldState
|
...fieldState,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ type FormItemContextValue = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FormItemContext = React.createContext<FormItemContextValue>(
|
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||||
{} as FormItemContextValue
|
{} as FormItemContextValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
const FormItem = React.forwardRef<
|
const FormItem = React.forwardRef<
|
||||||
@ -156,7 +156,8 @@ const FormMessage = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
id={formMessageId}
|
id={formMessageId}
|
||||||
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{body}
|
{body}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
@ -171,5 +172,5 @@ export {
|
|||||||
FormControl,
|
FormControl,
|
||||||
FormDescription,
|
FormDescription,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
FormField
|
FormField,
|
||||||
};
|
};
|
||||||
|
@ -8,13 +8,13 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
Input.displayName = "Input";
|
Input.displayName = "Input";
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const labelVariants = cva(
|
const labelVariants = cva(
|
||||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||||
);
|
);
|
||||||
|
|
||||||
const Label = React.forwardRef<
|
const Label = React.forwardRef<
|
||||||
|
@ -10,13 +10,13 @@ import {
|
|||||||
CommandGroup,
|
CommandGroup,
|
||||||
CommandInput,
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList
|
CommandList,
|
||||||
} from "@/components/ui/command";
|
} from "@/components/ui/command";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@ -53,7 +53,7 @@ const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> =
|
|||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
PhoneInput.displayName = "PhoneInput";
|
PhoneInput.displayName = "PhoneInput";
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ const CountrySelect = ({
|
|||||||
disabled,
|
disabled,
|
||||||
value: selectedCountry,
|
value: selectedCountry,
|
||||||
options: countryList,
|
options: countryList,
|
||||||
onChange
|
onChange,
|
||||||
}: CountrySelectProps) => {
|
}: CountrySelectProps) => {
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
@ -91,7 +91,8 @@ const CountrySelect = ({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="flex gap-1 rounded-e-none rounded-s-lg border-r-0 px-3 focus:z-10"
|
className="flex gap-1 rounded-e-none rounded-s-lg border-r-0 px-3 focus:z-10"
|
||||||
disabled={disabled}>
|
disabled={disabled}
|
||||||
|
>
|
||||||
<FlagComponent
|
<FlagComponent
|
||||||
country={selectedCountry}
|
country={selectedCountry}
|
||||||
countryName={selectedCountry}
|
countryName={selectedCountry}
|
||||||
@ -99,7 +100,7 @@ const CountrySelect = ({
|
|||||||
<ChevronsUpDown
|
<ChevronsUpDown
|
||||||
className={cn(
|
className={cn(
|
||||||
"-mr-2 size-4 opacity-50",
|
"-mr-2 size-4 opacity-50",
|
||||||
disabled ? "hidden" : "opacity-100"
|
disabled ? "hidden" : "opacity-100",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
@ -120,7 +121,7 @@ const CountrySelect = ({
|
|||||||
selectedCountry={selectedCountry}
|
selectedCountry={selectedCountry}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null,
|
||||||
)}
|
)}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
@ -140,7 +141,7 @@ const CountrySelectOption = ({
|
|||||||
country,
|
country,
|
||||||
countryName,
|
countryName,
|
||||||
selectedCountry,
|
selectedCountry,
|
||||||
onChange
|
onChange,
|
||||||
}: CountrySelectOptionProps) => {
|
}: CountrySelectOptionProps) => {
|
||||||
return (
|
return (
|
||||||
<CommandItem className="gap-2" onSelect={() => onChange(country)}>
|
<CommandItem className="gap-2" onSelect={() => onChange(country)}>
|
||||||
|
@ -19,7 +19,7 @@ const PopoverContent = React.forwardRef<
|
|||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@ -26,9 +26,10 @@ const RadioGroupItem = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
"aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||||
<Circle className="h-3.5 w-3.5 fill-primary" />
|
<Circle className="h-3.5 w-3.5 fill-primary" />
|
||||||
</RadioGroupPrimitive.Indicator>
|
</RadioGroupPrimitive.Indicator>
|
||||||
|
@ -14,12 +14,14 @@ const ScrollArea = React.forwardRef<
|
|||||||
<ScrollAreaPrimitive.Root
|
<ScrollAreaPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("relative overflow-hidden", className)}
|
className={cn("relative overflow-hidden", className)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<ScrollAreaPrimitive.Viewport
|
<ScrollAreaPrimitive.Viewport
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-full w-full rounded-[inherit]",
|
"h-full w-full rounded-[inherit]",
|
||||||
orientation === "horizontal" && "!overflow-x-auto"
|
orientation === "horizontal" && "!overflow-x-auto",
|
||||||
)}>
|
)}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</ScrollAreaPrimitive.Viewport>
|
</ScrollAreaPrimitive.Viewport>
|
||||||
<ScrollBar orientation={orientation} />
|
<ScrollBar orientation={orientation} />
|
||||||
@ -41,9 +43,10 @@ const ScrollBar = React.forwardRef<
|
|||||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||||
orientation === "horizontal" &&
|
orientation === "horizontal" &&
|
||||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||||
));
|
));
|
||||||
|
@ -17,9 +17,10 @@ const SelectTrigger = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
<SelectPrimitive.Icon asChild>
|
<SelectPrimitive.Icon asChild>
|
||||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||||
@ -36,9 +37,10 @@ const SelectScrollUpButton = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-default items-center justify-center py-1",
|
"flex cursor-default items-center justify-center py-1",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<ChevronUp className="h-4 w-4" />
|
<ChevronUp className="h-4 w-4" />
|
||||||
</SelectPrimitive.ScrollUpButton>
|
</SelectPrimitive.ScrollUpButton>
|
||||||
));
|
));
|
||||||
@ -52,9 +54,10 @@ const SelectScrollDownButton = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-default items-center justify-center py-1",
|
"flex cursor-default items-center justify-center py-1",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<ChevronDown className="h-4 w-4" />
|
<ChevronDown className="h-4 w-4" />
|
||||||
</SelectPrimitive.ScrollDownButton>
|
</SelectPrimitive.ScrollDownButton>
|
||||||
));
|
));
|
||||||
@ -72,17 +75,19 @@ const SelectContent = React.forwardRef<
|
|||||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
position={position}
|
position={position}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<SelectScrollUpButton />
|
<SelectScrollUpButton />
|
||||||
<SelectPrimitive.Viewport
|
<SelectPrimitive.Viewport
|
||||||
className={cn(
|
className={cn(
|
||||||
"p-1",
|
"p-1",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
||||||
)}>
|
)}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</SelectPrimitive.Viewport>
|
</SelectPrimitive.Viewport>
|
||||||
<SelectScrollDownButton />
|
<SelectScrollDownButton />
|
||||||
@ -111,9 +116,10 @@ const SelectItem = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
<SelectPrimitive.ItemIndicator>
|
<SelectPrimitive.ItemIndicator>
|
||||||
<Check className="h-4 w-4" />
|
<Check className="h-4 w-4" />
|
||||||
@ -146,5 +152,5 @@ export {
|
|||||||
SelectItem,
|
SelectItem,
|
||||||
SelectSeparator,
|
SelectSeparator,
|
||||||
SelectScrollUpButton,
|
SelectScrollUpButton,
|
||||||
SelectScrollDownButton
|
SelectScrollDownButton,
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ const Separator = React.forwardRef<
|
|||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{ className, orientation = "horizontal", decorative = true, ...props },
|
{ className, orientation = "horizontal", decorative = true, ...props },
|
||||||
ref
|
ref,
|
||||||
) => (
|
) => (
|
||||||
<SeparatorPrimitive.Root
|
<SeparatorPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -17,11 +17,11 @@ const Separator = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 bg-border",
|
"shrink-0 bg-border",
|
||||||
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ const SheetOverlay = React.forwardRef<
|
|||||||
<SheetPrimitive.Overlay
|
<SheetPrimitive.Overlay
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -39,13 +39,13 @@ const sheetVariants = cva(
|
|||||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||||
right:
|
right:
|
||||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm"
|
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
side: "right"
|
side: "right",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
interface SheetContentProps
|
interface SheetContentProps
|
||||||
@ -61,7 +61,8 @@ const SheetContent = React.forwardRef<
|
|||||||
<SheetPrimitive.Content
|
<SheetPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(sheetVariants({ side }), className)}
|
className={cn(sheetVariants({ side }), className)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
@ -79,7 +80,7 @@ const SheetHeader = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col space-y-2 text-center sm:text-left",
|
"flex flex-col space-y-2 text-center sm:text-left",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -93,7 +94,7 @@ const SheetFooter = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -134,5 +135,5 @@ export {
|
|||||||
SheetHeader,
|
SheetHeader,
|
||||||
SheetFooter,
|
SheetFooter,
|
||||||
SheetTitle,
|
SheetTitle,
|
||||||
SheetDescription
|
SheetDescription,
|
||||||
};
|
};
|
||||||
|
@ -12,14 +12,14 @@ import {
|
|||||||
Sheet,
|
Sheet,
|
||||||
SheetContent,
|
SheetContent,
|
||||||
SheetDescription,
|
SheetDescription,
|
||||||
SheetTitle
|
SheetTitle,
|
||||||
} from "@/components/ui/sheet";
|
} from "@/components/ui/sheet";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
|
|
||||||
const SIDEBAR_COOKIE_NAME = "sidebar:state";
|
const SIDEBAR_COOKIE_NAME = "sidebar:state";
|
||||||
@ -68,7 +68,7 @@ const SidebarProvider = React.forwardRef<
|
|||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const [openMobile, setOpenMobile] = React.useState(false);
|
const [openMobile, setOpenMobile] = React.useState(false);
|
||||||
@ -89,7 +89,7 @@ const SidebarProvider = React.forwardRef<
|
|||||||
// This sets the cookie to keep the sidebar state.
|
// This sets the cookie to keep the sidebar state.
|
||||||
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
||||||
},
|
},
|
||||||
[setOpenProp, open]
|
[setOpenProp, open],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Helper to toggle the sidebar.
|
// Helper to toggle the sidebar.
|
||||||
@ -127,9 +127,17 @@ const SidebarProvider = React.forwardRef<
|
|||||||
isMobile,
|
isMobile,
|
||||||
openMobile,
|
openMobile,
|
||||||
setOpenMobile,
|
setOpenMobile,
|
||||||
toggleSidebar
|
toggleSidebar,
|
||||||
}),
|
}),
|
||||||
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
|
[
|
||||||
|
state,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
isMobile,
|
||||||
|
openMobile,
|
||||||
|
setOpenMobile,
|
||||||
|
toggleSidebar,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -140,21 +148,22 @@ const SidebarProvider = React.forwardRef<
|
|||||||
{
|
{
|
||||||
"--sidebar-width": SIDEBAR_WIDTH,
|
"--sidebar-width": SIDEBAR_WIDTH,
|
||||||
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
||||||
...style
|
...style,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group/sidebar-wrapper has-[[data-variant=inset]]:bg-sidebar flex min-h-svh w-full",
|
"group/sidebar-wrapper has-[[data-variant=inset]]:bg-sidebar flex min-h-svh w-full",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</SidebarContext.Provider>
|
</SidebarContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
SidebarProvider.displayName = "SidebarProvider";
|
SidebarProvider.displayName = "SidebarProvider";
|
||||||
|
|
||||||
@ -175,7 +184,7 @@ const Sidebar = React.forwardRef<
|
|||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
||||||
|
|
||||||
@ -184,10 +193,11 @@ const Sidebar = React.forwardRef<
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-sidebar text-sidebar-foreground flex h-full w-[--sidebar-width] flex-col",
|
"bg-sidebar text-sidebar-foreground flex h-full w-[--sidebar-width] flex-col",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -205,10 +215,11 @@ const Sidebar = React.forwardRef<
|
|||||||
className="bg-sidebar text-sidebar-foreground w-[--sidebar-width] p-0 [&>button]:hidden"
|
className="bg-sidebar text-sidebar-foreground w-[--sidebar-width] p-0 [&>button]:hidden"
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
"--sidebar-width": SIDEBAR_WIDTH_MOBILE
|
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
side={side}>
|
side={side}
|
||||||
|
>
|
||||||
<VisuallyHidden asChild>
|
<VisuallyHidden asChild>
|
||||||
<SheetDescription />
|
<SheetDescription />
|
||||||
</VisuallyHidden>
|
</VisuallyHidden>
|
||||||
@ -225,7 +236,8 @@ const Sidebar = React.forwardRef<
|
|||||||
data-state={state}
|
data-state={state}
|
||||||
data-collapsible={state === "collapsed" ? collapsible : ""}
|
data-collapsible={state === "collapsed" ? collapsible : ""}
|
||||||
data-variant={variant}
|
data-variant={variant}
|
||||||
data-side={side}>
|
data-side={side}
|
||||||
|
>
|
||||||
{/* This is what handles the sidebar gap on desktop */}
|
{/* This is what handles the sidebar gap on desktop */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -234,7 +246,7 @@ const Sidebar = React.forwardRef<
|
|||||||
"group-data-[side=right]:rotate-180",
|
"group-data-[side=right]:rotate-180",
|
||||||
variant === "floating" || variant === "inset"
|
variant === "floating" || variant === "inset"
|
||||||
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
|
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
|
||||||
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]"
|
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
@ -247,18 +259,20 @@ const Sidebar = React.forwardRef<
|
|||||||
variant === "floating" || variant === "inset"
|
variant === "floating" || variant === "inset"
|
||||||
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
|
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
|
||||||
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
data-sidebar="sidebar"
|
data-sidebar="sidebar"
|
||||||
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow">
|
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow"
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
Sidebar.displayName = "Sidebar";
|
Sidebar.displayName = "Sidebar";
|
||||||
|
|
||||||
@ -279,7 +293,8 @@ const SidebarTrigger = React.forwardRef<
|
|||||||
onClick?.(event);
|
onClick?.(event);
|
||||||
toggleSidebar();
|
toggleSidebar();
|
||||||
}}
|
}}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<PanelLeft />
|
<PanelLeft />
|
||||||
<span className="sr-only">Toggle Sidebar</span>
|
<span className="sr-only">Toggle Sidebar</span>
|
||||||
</Button>
|
</Button>
|
||||||
@ -308,7 +323,7 @@ const SidebarRail = React.forwardRef<
|
|||||||
"group-data-[collapsible=offcanvas]:hover:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
|
"group-data-[collapsible=offcanvas]:hover:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
|
||||||
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
||||||
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -326,7 +341,7 @@ const SidebarInset = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"relative flex min-h-svh flex-1 flex-col bg-background",
|
"relative flex min-h-svh flex-1 flex-col bg-background",
|
||||||
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -344,7 +359,7 @@ const SidebarInput = React.forwardRef<
|
|||||||
data-sidebar="input"
|
data-sidebar="input"
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus-visible:ring-sidebar-ring h-8 w-full bg-background shadow-none focus-visible:ring-2",
|
"focus-visible:ring-sidebar-ring h-8 w-full bg-background shadow-none focus-visible:ring-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -407,7 +422,7 @@ const SidebarContent = React.forwardRef<
|
|||||||
data-sidebar="content"
|
data-sidebar="content"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
|
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -443,7 +458,7 @@ const SidebarGroupLabel = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-none transition-[margin,opa] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-none transition-[margin,opa] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -466,7 +481,7 @@ const SidebarGroupAction = React.forwardRef<
|
|||||||
// Increases the hit area of the button on mobile.
|
// Increases the hit area of the button on mobile.
|
||||||
"after:absolute after:-inset-2 after:md:hidden",
|
"after:absolute after:-inset-2 after:md:hidden",
|
||||||
"group-data-[collapsible=icon]:hidden",
|
"group-data-[collapsible=icon]:hidden",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -520,19 +535,19 @@ const sidebarMenuButtonVariants = cva(
|
|||||||
variant: {
|
variant: {
|
||||||
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||||
outline:
|
outline:
|
||||||
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
|
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-8 text-sm",
|
default: "h-8 text-sm",
|
||||||
sm: "h-7 text-xs",
|
sm: "h-7 text-xs",
|
||||||
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0"
|
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: "default",
|
||||||
size: "default"
|
size: "default",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const SidebarMenuButton = React.forwardRef<
|
const SidebarMenuButton = React.forwardRef<
|
||||||
@ -553,7 +568,7 @@ const SidebarMenuButton = React.forwardRef<
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const Comp = asChild ? Slot : "button";
|
const Comp = asChild ? Slot : "button";
|
||||||
const { isMobile, state } = useSidebar();
|
const { isMobile, state } = useSidebar();
|
||||||
@ -575,7 +590,7 @@ const SidebarMenuButton = React.forwardRef<
|
|||||||
|
|
||||||
if (typeof tooltip === "string") {
|
if (typeof tooltip === "string") {
|
||||||
tooltip = {
|
tooltip = {
|
||||||
children: tooltip
|
children: tooltip,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +605,7 @@ const SidebarMenuButton = React.forwardRef<
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
SidebarMenuButton.displayName = "SidebarMenuButton";
|
SidebarMenuButton.displayName = "SidebarMenuButton";
|
||||||
|
|
||||||
@ -617,7 +632,7 @@ const SidebarMenuAction = React.forwardRef<
|
|||||||
"group-data-[collapsible=icon]:hidden",
|
"group-data-[collapsible=icon]:hidden",
|
||||||
showOnHover &&
|
showOnHover &&
|
||||||
"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
|
"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -639,7 +654,7 @@ const SidebarMenuBadge = React.forwardRef<
|
|||||||
"peer-data-[size=default]/menu-button:top-1.5",
|
"peer-data-[size=default]/menu-button:top-1.5",
|
||||||
"peer-data-[size=lg]/menu-button:top-2.5",
|
"peer-data-[size=lg]/menu-button:top-2.5",
|
||||||
"group-data-[collapsible=icon]:hidden",
|
"group-data-[collapsible=icon]:hidden",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -662,7 +677,8 @@ const SidebarMenuSkeleton = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
data-sidebar="menu-skeleton"
|
data-sidebar="menu-skeleton"
|
||||||
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
{showIcon && (
|
{showIcon && (
|
||||||
<Skeleton
|
<Skeleton
|
||||||
className="size-4 rounded-md"
|
className="size-4 rounded-md"
|
||||||
@ -674,7 +690,7 @@ const SidebarMenuSkeleton = React.forwardRef<
|
|||||||
data-sidebar="menu-skeleton-text"
|
data-sidebar="menu-skeleton-text"
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
"--skeleton-width": width
|
"--skeleton-width": width,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -693,7 +709,7 @@ const SidebarMenuSub = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
|
"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
|
||||||
"group-data-[collapsible=icon]:hidden",
|
"group-data-[collapsible=icon]:hidden",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -728,7 +744,7 @@ const SidebarMenuSubButton = React.forwardRef<
|
|||||||
size === "sm" && "text-xs",
|
size === "sm" && "text-xs",
|
||||||
size === "md" && "text-sm",
|
size === "md" && "text-sm",
|
||||||
"group-data-[collapsible=icon]:hidden",
|
"group-data-[collapsible=icon]:hidden",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -760,5 +776,5 @@ export {
|
|||||||
SidebarRail,
|
SidebarRail,
|
||||||
SidebarSeparator,
|
SidebarSeparator,
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
useSidebar
|
useSidebar,
|
||||||
};
|
};
|
||||||
|
@ -9,13 +9,14 @@ const Switch = React.forwardRef<
|
|||||||
<SwitchPrimitives.Root
|
<SwitchPrimitives.Root
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}>
|
ref={ref}
|
||||||
|
>
|
||||||
<SwitchPrimitives.Thumb
|
<SwitchPrimitives.Thumb
|
||||||
className={cn(
|
className={cn(
|
||||||
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</SwitchPrimitives.Root>
|
</SwitchPrimitives.Root>
|
||||||
|
@ -43,7 +43,7 @@ const TableFooter = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -58,7 +58,7 @@ const TableRow = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -73,7 +73,7 @@ const TableHead = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -88,7 +88,7 @@ const TableCell = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -115,5 +115,5 @@ export {
|
|||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableCaption
|
TableCaption,
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@ const TabsList = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -27,7 +27,7 @@ const TabsTrigger = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -42,7 +42,7 @@ const TabsContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@ -9,7 +9,7 @@ const Textarea = React.forwardRef<
|
|||||||
<textarea
|
<textarea
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -28,13 +28,13 @@ const toastVariants = cva(
|
|||||||
variant: {
|
variant: {
|
||||||
default: "border bg-background text-foreground",
|
default: "border bg-background text-foreground",
|
||||||
destructive:
|
destructive:
|
||||||
"destructive group border-destructive bg-destructive text-destructive-foreground"
|
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default"
|
variant: "default",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const Toast = React.forwardRef<
|
const Toast = React.forwardRef<
|
||||||
@ -60,7 +60,7 @@ const ToastAction = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -75,10 +75,11 @@ const ToastClose = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
toast-close=""
|
toast-close=""
|
||||||
{...props}>
|
{...props}
|
||||||
|
>
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
</ToastPrimitives.Close>
|
</ToastPrimitives.Close>
|
||||||
));
|
));
|
||||||
@ -121,5 +122,5 @@ export {
|
|||||||
ToastTitle,
|
ToastTitle,
|
||||||
ToastDescription,
|
ToastDescription,
|
||||||
ToastClose,
|
ToastClose,
|
||||||
ToastAction
|
ToastAction,
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
ToastDescription,
|
ToastDescription,
|
||||||
ToastProvider,
|
ToastProvider,
|
||||||
ToastTitle,
|
ToastTitle,
|
||||||
ToastViewport
|
ToastViewport,
|
||||||
} from "@/components/ui/toast";
|
} from "@/components/ui/toast";
|
||||||
|
|
||||||
export function Toaster() {
|
export function Toaster() {
|
||||||
|
@ -18,7 +18,7 @@ const TooltipContent = React.forwardRef<
|
|||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
export const AppName = "SwagShop"
|
export const AppName = "SwagShop";
|
||||||
export const AdminAppName = "SwagShop Admin"
|
export const AdminAppName = "SwagShop Admin";
|
||||||
|
@ -11,7 +11,7 @@ interface FontContextType {
|
|||||||
const FontContext = createContext<FontContextType | undefined>(undefined);
|
const FontContext = createContext<FontContextType | undefined>(undefined);
|
||||||
|
|
||||||
export const FontProvider: React.FC<{ children: React.ReactNode }> = ({
|
export const FontProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||||
children
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const [font, _setFont] = useState<Font>(() => {
|
const [font, _setFont] = useState<Font>(() => {
|
||||||
const savedFont = localStorage.getItem("font");
|
const savedFont = localStorage.getItem("font");
|
||||||
|
@ -15,7 +15,7 @@ type ThemeProviderState = {
|
|||||||
|
|
||||||
const initialState: ThemeProviderState = {
|
const initialState: ThemeProviderState = {
|
||||||
theme: "system",
|
theme: "system",
|
||||||
setTheme: () => null
|
setTheme: () => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
||||||
@ -27,7 +27,7 @@ export function ThemeProvider({
|
|||||||
...props
|
...props
|
||||||
}: ThemeProviderProps) {
|
}: ThemeProviderProps) {
|
||||||
const [theme, _setTheme] = useState<Theme>(
|
const [theme, _setTheme] = useState<Theme>(
|
||||||
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
|
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -61,7 +61,7 @@ export function ThemeProvider({
|
|||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
theme,
|
theme,
|
||||||
setTheme
|
setTheme,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -22,14 +22,14 @@ const useAuth = () => {
|
|||||||
const { data: user } = useQuery<UserPublic | null, Error>({
|
const { data: user } = useQuery<UserPublic | null, Error>({
|
||||||
queryKey: ["currentUser"],
|
queryKey: ["currentUser"],
|
||||||
queryFn: authAPI.getCurrentUser,
|
queryFn: authAPI.getCurrentUser,
|
||||||
enabled: loggedIn
|
enabled: loggedIn,
|
||||||
});
|
});
|
||||||
|
|
||||||
const signUpMutation = useMutation({
|
const signUpMutation = useMutation({
|
||||||
mutationFn: authAPI.registerUser,
|
mutationFn: authAPI.registerUser,
|
||||||
onSuccess: () => navigate({ to: "/sign-in" }),
|
onSuccess: () => navigate({ to: "/sign-in" }),
|
||||||
onError: (err: ApiError) => handleServerError(err),
|
onError: (err: ApiError) => handleServerError(err),
|
||||||
onSettled: () => queryClient.invalidateQueries({ queryKey: ["users"] })
|
onSettled: () => queryClient.invalidateQueries({ queryKey: ["users"] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const login = async (data: ShopLoginAccessTokenData) => {
|
const login = async (data: ShopLoginAccessTokenData) => {
|
||||||
@ -42,7 +42,7 @@ const useAuth = () => {
|
|||||||
const loginMutation = useMutation({
|
const loginMutation = useMutation({
|
||||||
mutationFn: login,
|
mutationFn: login,
|
||||||
onSuccess: () => navigate({ to: "/" }),
|
onSuccess: () => navigate({ to: "/" }),
|
||||||
onError: (err: ApiError) => handleServerError(err)
|
onError: (err: ApiError) => handleServerError(err),
|
||||||
});
|
});
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
@ -58,7 +58,7 @@ const useAuth = () => {
|
|||||||
toast({ title: "Account updated successfully" });
|
toast({ title: "Account updated successfully" });
|
||||||
queryClient.invalidateQueries({ queryKey: ["currentUser"] });
|
queryClient.invalidateQueries({ queryKey: ["currentUser"] });
|
||||||
},
|
},
|
||||||
onError: (err: ApiError) => handleServerError(err)
|
onError: (err: ApiError) => handleServerError(err),
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -97,7 +97,7 @@ const useAuth = () => {
|
|||||||
logout,
|
logout,
|
||||||
user,
|
user,
|
||||||
error,
|
error,
|
||||||
resetError: () => setError(null)
|
resetError: () => setError(null),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
39
frontend/src/hooks/useCoupon.ts
Normal file
39
frontend/src/hooks/useCoupon.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { Coupon } from "@/api/mock/models";
|
||||||
|
import { couponAPI } from "@/api/api";
|
||||||
|
|
||||||
|
export function useCoupon(couponId?: number) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const coupon = useQuery<Coupon | null>({
|
||||||
|
queryKey: ["coupon", couponId],
|
||||||
|
queryFn: () => couponAPI.getCouponById(couponId!),
|
||||||
|
enabled: !!couponId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createCoupon = useMutation({
|
||||||
|
mutationFn: (data: Omit<Coupon, "id">) => couponAPI.createCoupon(data),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["coupons"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateCoupon = useMutation({
|
||||||
|
mutationFn: (data: Partial<Omit<Coupon, "id">>) =>
|
||||||
|
couponAPI.updateCoupon(couponId!, data),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["coupons"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["coupon", couponId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteCoupon = useMutation({
|
||||||
|
mutationFn: (id: number) => couponAPI.deleteCoupon(id),
|
||||||
|
onSuccess: (_, id) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["coupons"] });
|
||||||
|
queryClient.removeQueries({ queryKey: ["coupon", id] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { coupon, createCoupon, updateCoupon, deleteCoupon };
|
||||||
|
}
|
10
frontend/src/hooks/useCoupons.ts
Normal file
10
frontend/src/hooks/useCoupons.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { couponAPI } from "@/api/api";
|
||||||
|
import { Coupon } from "@/api/mock/models";
|
||||||
|
|
||||||
|
export function useCoupons() {
|
||||||
|
return useQuery<Coupon[]>({
|
||||||
|
queryKey: ["coupons"],
|
||||||
|
queryFn: couponAPI.getAllCoupons,
|
||||||
|
});
|
||||||
|
}
|
@ -7,7 +7,7 @@ import { useState } from "react";
|
|||||||
* @example const [open, setOpen] = useDialogState<"approve" | "reject">()
|
* @example const [open, setOpen] = useDialogState<"approve" | "reject">()
|
||||||
*/
|
*/
|
||||||
export default function useDialogState<T extends string | boolean>(
|
export default function useDialogState<T extends string | boolean>(
|
||||||
initialState: T | null = null
|
initialState: T | null = null,
|
||||||
) {
|
) {
|
||||||
const [open, _setOpen] = useState<T | null>(initialState);
|
const [open, _setOpen] = useState<T | null>(initialState);
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ const MOBILE_BREAKPOINT = 768;
|
|||||||
|
|
||||||
export function useIsMobile() {
|
export function useIsMobile() {
|
||||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
||||||
undefined
|
undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -8,22 +8,23 @@ export function useProduct(productId?: number) {
|
|||||||
const product = useQuery<ProductWithDetails | null>({
|
const product = useQuery<ProductWithDetails | null>({
|
||||||
queryKey: ["product", productId],
|
queryKey: ["product", productId],
|
||||||
queryFn: () => productsAPI.getProductById(productId!),
|
queryFn: () => productsAPI.getProductById(productId!),
|
||||||
enabled: !!productId
|
enabled: !!productId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const createProduct = useMutation({
|
const createProduct = useMutation({
|
||||||
mutationFn: (data: ProductCreate) => productsAPI.createProduct(data),
|
mutationFn: (data: ProductCreate) => productsAPI.createProduct(data),
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["products"] })
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["products"] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateProduct = useMutation({
|
const updateProduct = useMutation({
|
||||||
mutationFn: (data: Partial<ProductCreate>) => productsAPI.updateProduct(productId!, data),
|
mutationFn: (data: Partial<ProductCreate>) =>
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["products"] })
|
productsAPI.updateProduct(productId!, data),
|
||||||
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["products"] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteProduct = useMutation({
|
const deleteProduct = useMutation({
|
||||||
mutationFn: () => productsAPI.deleteProduct(productId!),
|
mutationFn: () => productsAPI.deleteProduct(productId!),
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["products"] })
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["products"] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return { product, createProduct, updateProduct, deleteProduct };
|
return { product, createProduct, updateProduct, deleteProduct };
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// src/hooks/useProducts.ts
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { productsAPI } from "@/api/api";
|
import { productsAPI } from "@/api/api";
|
||||||
import { ProductWithDetails } from "@/api/mock/models";
|
import { ProductWithDetails } from "@/api/mock/models";
|
||||||
@ -6,7 +5,7 @@ import { ProductWithDetails } from "@/api/mock/models";
|
|||||||
export function useProducts() {
|
export function useProducts() {
|
||||||
const query = useQuery<ProductWithDetails[]>({
|
const query = useQuery<ProductWithDetails[]>({
|
||||||
queryKey: ["products"],
|
queryKey: ["products"],
|
||||||
queryFn: productsAPI.getProductsForShop
|
queryFn: productsAPI.getProductsForShop,
|
||||||
});
|
});
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
|
14
frontend/src/hooks/usePurchase.ts
Normal file
14
frontend/src/hooks/usePurchase.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { PurchaseWithDetails } from "@/api/mock/models";
|
||||||
|
import { purchaseAPI } from "@/api/api";
|
||||||
|
|
||||||
|
export function usePurchase(purchaseId?: number) {
|
||||||
|
return useQuery<PurchaseWithDetails | null>({
|
||||||
|
queryKey: ["purchase", purchaseId],
|
||||||
|
queryFn: () => {
|
||||||
|
if (purchaseId === undefined) return Promise.resolve(null);
|
||||||
|
return purchaseAPI.getPurchaseWithDetails(purchaseId);
|
||||||
|
},
|
||||||
|
enabled: purchaseId !== undefined,
|
||||||
|
});
|
||||||
|
}
|
10
frontend/src/hooks/usePurchases.ts
Normal file
10
frontend/src/hooks/usePurchases.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { Purchase } from "@/api/mock/models";
|
||||||
|
import { purchaseAPI } from "@/api/api";
|
||||||
|
|
||||||
|
export function usePurchases() {
|
||||||
|
return useQuery<Purchase[]>({
|
||||||
|
queryKey: ["purchases"],
|
||||||
|
queryFn: purchaseAPI.getAllPurchases,
|
||||||
|
});
|
||||||
|
}
|
@ -8,15 +8,15 @@ export function useShop() {
|
|||||||
const {
|
const {
|
||||||
data: shop,
|
data: shop,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError
|
isError,
|
||||||
} = useQuery<Shop | null>({
|
} = useQuery<Shop | null>({
|
||||||
queryKey: ["shop"],
|
queryKey: ["shop"],
|
||||||
queryFn: shopAPI.getShop
|
queryFn: shopAPI.getShop,
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateShopMutation = useMutation({
|
const updateShopMutation = useMutation({
|
||||||
mutationFn: shopAPI.updateShop,
|
mutationFn: shopAPI.updateShop,
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["shop"] })
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["shop"] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -24,6 +24,6 @@ export function useShop() {
|
|||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
updateShop: updateShopMutation.mutate,
|
updateShop: updateShopMutation.mutate,
|
||||||
updateStatus: updateShopMutation.status
|
updateStatus: updateShopMutation.status,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ const actionTypes = {
|
|||||||
ADD_TOAST: "ADD_TOAST",
|
ADD_TOAST: "ADD_TOAST",
|
||||||
UPDATE_TOAST: "UPDATE_TOAST",
|
UPDATE_TOAST: "UPDATE_TOAST",
|
||||||
DISMISS_TOAST: "DISMISS_TOAST",
|
DISMISS_TOAST: "DISMISS_TOAST",
|
||||||
REMOVE_TOAST: "REMOVE_TOAST"
|
REMOVE_TOAST: "REMOVE_TOAST",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@ -62,7 +62,7 @@ const addToRemoveQueue = (toastId: string) => {
|
|||||||
toastTimeouts.delete(toastId);
|
toastTimeouts.delete(toastId);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "REMOVE_TOAST",
|
type: "REMOVE_TOAST",
|
||||||
toastId: toastId
|
toastId: toastId,
|
||||||
});
|
});
|
||||||
}, TOAST_REMOVE_DELAY);
|
}, TOAST_REMOVE_DELAY);
|
||||||
|
|
||||||
@ -74,15 +74,15 @@ const reducer = (state: State, action: Action): State => {
|
|||||||
case "ADD_TOAST":
|
case "ADD_TOAST":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
|
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||||
};
|
};
|
||||||
|
|
||||||
case "UPDATE_TOAST":
|
case "UPDATE_TOAST":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: state.toasts.map((t) =>
|
toasts: state.toasts.map((t) =>
|
||||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
case "DISMISS_TOAST": {
|
case "DISMISS_TOAST": {
|
||||||
@ -104,22 +104,22 @@ const reducer = (state: State, action: Action): State => {
|
|||||||
t.id === toastId || toastId === undefined
|
t.id === toastId || toastId === undefined
|
||||||
? {
|
? {
|
||||||
...t,
|
...t,
|
||||||
open: false
|
open: false,
|
||||||
}
|
}
|
||||||
: t
|
: t,
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "REMOVE_TOAST":
|
case "REMOVE_TOAST":
|
||||||
if (action.toastId === undefined) {
|
if (action.toastId === undefined) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: []
|
toasts: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toasts: state.toasts.filter((t) => t.id !== action.toastId)
|
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -143,7 +143,7 @@ function toast({ ...props }: Toast) {
|
|||||||
const update = (props: ToasterToast) =>
|
const update = (props: ToasterToast) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "UPDATE_TOAST",
|
type: "UPDATE_TOAST",
|
||||||
toast: { ...props, id }
|
toast: { ...props, id },
|
||||||
});
|
});
|
||||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
||||||
|
|
||||||
@ -155,14 +155,14 @@ function toast({ ...props }: Toast) {
|
|||||||
open: true,
|
open: true,
|
||||||
onOpenChange: (open) => {
|
onOpenChange: (open) => {
|
||||||
if (!open) dismiss();
|
if (!open) dismiss();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
dismiss,
|
dismiss,
|
||||||
update
|
update,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ function useToast() {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
toast,
|
toast,
|
||||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId })
|
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
38
frontend/src/hooks/useUser.ts
Normal file
38
frontend/src/hooks/useUser.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { MockUser } from "@/api/mock/models";
|
||||||
|
import { usersAPI } from "@/api/api";
|
||||||
|
|
||||||
|
export function useUser(userId?: number) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const user = useQuery<MockUser | null>({
|
||||||
|
queryKey: ["user", userId],
|
||||||
|
queryFn: () => usersAPI.getUserById(userId!),
|
||||||
|
enabled: !!userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createUser = useMutation({
|
||||||
|
mutationFn: (data: Omit<MockUser, "id" | "created_at" | "updated_at">) =>
|
||||||
|
usersAPI.createUser(data),
|
||||||
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["users"] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateUser = useMutation({
|
||||||
|
mutationFn: (data: Partial<Omit<MockUser, "id" | "created_at">>) =>
|
||||||
|
usersAPI.updateUser(userId!, data),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["user", userId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteUser = useMutation({
|
||||||
|
mutationFn: () => usersAPI.deleteUser(userId!),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||||
|
queryClient.removeQueries({ queryKey: ["user", userId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { user, createUser, updateUser, deleteUser };
|
||||||
|
}
|
10
frontend/src/hooks/useUsers.ts
Normal file
10
frontend/src/hooks/useUsers.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { usersAPI } from "@/api/api";
|
||||||
|
import { MockUser } from "@/api/mock/models";
|
||||||
|
|
||||||
|
export function useUsers() {
|
||||||
|
return useQuery<MockUser[]>({
|
||||||
|
queryKey: ["users"],
|
||||||
|
queryFn: usersAPI.getAllUsers,
|
||||||
|
});
|
||||||
|
}
|
@ -3,152 +3,150 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 222.2 84% 4.9%;
|
--foreground: 222.2 84% 4.9%;
|
||||||
--card: 0 0% 100%;
|
--card: 0 0% 100%;
|
||||||
--card-foreground: 222.2 84% 4.9%;
|
--card-foreground: 222.2 84% 4.9%;
|
||||||
--popover: 0 0% 100%;
|
--popover: 0 0% 100%;
|
||||||
--popover-foreground: 222.2 84% 4.9%;
|
--popover-foreground: 222.2 84% 4.9%;
|
||||||
--primary: 222.2 47.4% 11.2%;
|
--primary: 222.2 47.4% 11.2%;
|
||||||
--primary-foreground: 210 40% 98%;
|
--primary-foreground: 210 40% 98%;
|
||||||
--secondary: 210 40% 96.1%;
|
--secondary: 210 40% 96.1%;
|
||||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
--muted: 210 40% 96.1%;
|
--muted: 210 40% 96.1%;
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
--accent: 210 40% 96.1%;
|
--accent: 210 40% 96.1%;
|
||||||
--accent-foreground: 222.2 47.4% 11.2%;
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
--destructive: 0 84.2% 60.2%;
|
--destructive: 0 84.2% 60.2%;
|
||||||
--destructive-foreground: 210 40% 98%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
--border: 214.3 31.8% 91.4%;
|
--border: 214.3 31.8% 91.4%;
|
||||||
--input: 214.3 31.8% 91.4%;
|
--input: 214.3 31.8% 91.4%;
|
||||||
--ring: 222.2 84% 4.9%;
|
--ring: 222.2 84% 4.9%;
|
||||||
--chart-1: 12 76% 61%;
|
--chart-1: 12 76% 61%;
|
||||||
--chart-2: 173 58% 39%;
|
--chart-2: 173 58% 39%;
|
||||||
--chart-3: 197 37% 24%;
|
--chart-3: 197 37% 24%;
|
||||||
--chart-4: 43 74% 66%;
|
--chart-4: 43 74% 66%;
|
||||||
--chart-5: 27 87% 67%;
|
--chart-5: 27 87% 67%;
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
--sidebar-background: 0 0% 98%;
|
--sidebar-background: 0 0% 98%;
|
||||||
--sidebar-foreground: 240 5.3% 26.1%;
|
--sidebar-foreground: 240 5.3% 26.1%;
|
||||||
--sidebar-primary: 240 5.9% 10%;
|
--sidebar-primary: 240 5.9% 10%;
|
||||||
--sidebar-primary-foreground: 0 0% 98%;
|
--sidebar-primary-foreground: 0 0% 98%;
|
||||||
--sidebar-accent: 240 4.8% 95.9%;
|
--sidebar-accent: 240 4.8% 95.9%;
|
||||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||||
--sidebar-border: 220 13% 91%;
|
--sidebar-border: 220 13% 91%;
|
||||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
}
|
}
|
||||||
.dark {
|
.dark {
|
||||||
--background: 222.2 84% 4.9%;
|
--background: 222.2 84% 4.9%;
|
||||||
--foreground: 210 40% 98%;
|
--foreground: 210 40% 98%;
|
||||||
--card: 222.2 84% 4.9%;
|
--card: 222.2 84% 4.9%;
|
||||||
--card-foreground: 210 40% 98%;
|
--card-foreground: 210 40% 98%;
|
||||||
--popover: 222.2 84% 4.9%;
|
--popover: 222.2 84% 4.9%;
|
||||||
--popover-foreground: 210 40% 98%;
|
--popover-foreground: 210 40% 98%;
|
||||||
--primary: 210 40% 98%;
|
--primary: 210 40% 98%;
|
||||||
--primary-foreground: 222.2 47.4% 11.2%;
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
--secondary: 217.2 32.6% 17.5%;
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
--secondary-foreground: 210 40% 98%;
|
--secondary-foreground: 210 40% 98%;
|
||||||
--muted: 217.2 32.6% 17.5%;
|
--muted: 217.2 32.6% 17.5%;
|
||||||
--muted-foreground: 215 20.2% 65.1%;
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
--accent: 217.2 32.6% 17.5%;
|
--accent: 217.2 32.6% 17.5%;
|
||||||
--accent-foreground: 210 40% 98%;
|
--accent-foreground: 210 40% 98%;
|
||||||
--destructive: 0 62.8% 30.6%;
|
--destructive: 0 62.8% 30.6%;
|
||||||
--destructive-foreground: 210 40% 98%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
--border: 217.2 32.6% 17.5%;
|
--border: 217.2 32.6% 17.5%;
|
||||||
--input: 217.2 32.6% 17.5%;
|
--input: 217.2 32.6% 17.5%;
|
||||||
--ring: 212.7 26.8% 83.9%;
|
--ring: 212.7 26.8% 83.9%;
|
||||||
--chart-1: 220 70% 50%;
|
--chart-1: 220 70% 50%;
|
||||||
--chart-2: 160 60% 45%;
|
--chart-2: 160 60% 45%;
|
||||||
--chart-3: 30 80% 55%;
|
--chart-3: 30 80% 55%;
|
||||||
--chart-4: 280 65% 60%;
|
--chart-4: 280 65% 60%;
|
||||||
--chart-5: 340 75% 55%;
|
--chart-5: 340 75% 55%;
|
||||||
--sidebar-background: var(--background);
|
--sidebar-background: var(--background);
|
||||||
--sidebar-foreground: var(--foreground);
|
--sidebar-foreground: var(--foreground);
|
||||||
--sidebar-primary: var(--primary);
|
--sidebar-primary: var(--primary);
|
||||||
--sidebar-primary-foreground: var(--primary-foreground);
|
--sidebar-primary-foreground: var(--primary-foreground);
|
||||||
--sidebar-accent: var(--accent);
|
--sidebar-accent: var(--accent);
|
||||||
--sidebar-accent-foreground: var(--accent-foreground);
|
--sidebar-accent-foreground: var(--accent-foreground);
|
||||||
--sidebar-border: var(--border);
|
--sidebar-border: var(--border);
|
||||||
--sidebar-ring: var(--ring);
|
--sidebar-ring: var(--ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* styles.css */
|
/* styles.css */
|
||||||
.CollapsibleContent {
|
.CollapsibleContent {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.CollapsibleContent[data-state='open'] {
|
.CollapsibleContent[data-state="open"] {
|
||||||
animation: slideDown 300ms ease-out;
|
animation: slideDown 300ms ease-out;
|
||||||
}
|
}
|
||||||
.CollapsibleContent[data-state='closed'] {
|
.CollapsibleContent[data-state="closed"] {
|
||||||
animation: slideUp 300ms ease-out;
|
animation: slideUp 300ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideDown {
|
@keyframes slideDown {
|
||||||
from {
|
from {
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
height: var(--radix-collapsible-content-height);
|
height: var(--radix-collapsible-content-height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideUp {
|
@keyframes slideUp {
|
||||||
from {
|
from {
|
||||||
height: var(--radix-collapsible-content-height);
|
height: var(--radix-collapsible-content-height);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prevent focus zoom on mobile devices */
|
/* Prevent focus zoom on mobile devices */
|
||||||
@media screen and (max-width: 767px) {
|
@media screen and (max-width: 767px) {
|
||||||
input,
|
input,
|
||||||
select,
|
select,
|
||||||
textarea {
|
textarea {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||||
.no-scrollbar::-webkit-scrollbar {
|
.no-scrollbar::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
/* Hide scrollbar for IE, Edge and Firefox */
|
/* Hide scrollbar for IE, Edge and Firefox */
|
||||||
.no-scrollbar {
|
.no-scrollbar {
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none; /* Firefox */
|
||||||
}
|
}
|
||||||
|
|
||||||
.faded-bottom {
|
.faded-bottom {
|
||||||
@apply after:pointer-events-none after:absolute after:bottom-0 after:left-0 after:hidden after:h-32 after:w-full after:bg-[linear-gradient(180deg,_transparent_10%,_hsl(var(--background))_70%)] after:md:block;
|
@apply after:pointer-events-none after:absolute after:bottom-0 after:left-0 after:hidden after:h-32 after:w-full after:bg-[linear-gradient(180deg,_transparent_10%,_hsl(var(--background))_70%)] after:md:block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: hsl(var(--border)) transparent;
|
scrollbar-color: hsl(var(--border)) transparent;
|
||||||
}
|
}
|
||||||
html {
|
html {
|
||||||
@apply overflow-x-hidden;
|
@apply overflow-x-hidden;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@apply min-h-svh w-full bg-background text-foreground;
|
@apply min-h-svh w-full bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,13 @@ export const registerUser = async (userData: {
|
|||||||
`${API_BASE_URL}/user/register`,
|
`${API_BASE_URL}/user/register`,
|
||||||
{
|
{
|
||||||
...userData,
|
...userData,
|
||||||
shop_id: 0
|
shop_id: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import { AxiosError } from "axios";
|
|||||||
import {
|
import {
|
||||||
QueryCache,
|
QueryCache,
|
||||||
QueryClient,
|
QueryClient,
|
||||||
QueryClientProvider
|
QueryClientProvider,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||||
import { useAuthStore } from "@/stores/authStore";
|
import { useAuthStore } from "@/stores/authStore";
|
||||||
@ -38,7 +38,7 @@ const queryClient = new QueryClient({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
refetchOnWindowFocus: import.meta.env.PROD,
|
refetchOnWindowFocus: import.meta.env.PROD,
|
||||||
staleTime: 10 * 1000 // 10s
|
staleTime: 10 * 1000, // 10s
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@ -48,12 +48,12 @@ const queryClient = new QueryClient({
|
|||||||
if (error.response?.status === 304) {
|
if (error.response?.status === 304) {
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Content not modified!"
|
title: "Content not modified!",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
queryCache: new QueryCache({
|
queryCache: new QueryCache({
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@ -61,7 +61,7 @@ const queryClient = new QueryClient({
|
|||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Session expired!"
|
title: "Session expired!",
|
||||||
});
|
});
|
||||||
useAuthStore.getState().auth.reset();
|
useAuthStore.getState().auth.reset();
|
||||||
const redirect = `${router.history.location.href}`;
|
const redirect = `${router.history.location.href}`;
|
||||||
@ -70,7 +70,7 @@ const queryClient = new QueryClient({
|
|||||||
if (error.response?.status === 500) {
|
if (error.response?.status === 500) {
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Internal Server Error!"
|
title: "Internal Server Error!",
|
||||||
});
|
});
|
||||||
router.navigate({ to: "/500" });
|
router.navigate({ to: "/500" });
|
||||||
}
|
}
|
||||||
@ -78,8 +78,8 @@ const queryClient = new QueryClient({
|
|||||||
// router.navigate("/forbidden", { replace: true });
|
// router.navigate("/forbidden", { replace: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a new router instance
|
// Create a new router instance
|
||||||
@ -87,7 +87,7 @@ const router = createRouter({
|
|||||||
routeTree,
|
routeTree,
|
||||||
context: { queryClient },
|
context: { queryClient },
|
||||||
defaultPreload: "intent",
|
defaultPreload: "intent",
|
||||||
defaultPreloadStaleTime: 0
|
defaultPreloadStaleTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register the router instance for type safety
|
// Register the router instance for type safety
|
||||||
@ -110,6 +110,6 @@ if (!rootElement.innerHTML) {
|
|||||||
</FontProvider>
|
</FontProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</StrictMode>
|
</StrictMode>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {AdminAppName} from "@/config/manifest"
|
import { AdminAppName } from "@/config/manifest";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -17,7 +17,8 @@ export default function AuthLayout({ children }: Props) {
|
|||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
className="mr-2 h-6 w-6">
|
className="mr-2 h-6 w-6"
|
||||||
|
>
|
||||||
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
|
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
|
||||||
</svg>
|
</svg>
|
||||||
<h1 className="text-xl font-medium">{AdminAppName}</h1>
|
<h1 className="text-xl font-medium">{AdminAppName}</h1>
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ const formSchema = z.object({
|
|||||||
email: z
|
email: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, { message: "Please enter your email" })
|
.min(1, { message: "Please enter your email" })
|
||||||
.email({ message: "Invalid email address" })
|
.email({ message: "Invalid email address" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ForgotForm({ className, ...props }: ForgotFormProps) {
|
export function ForgotForm({ className, ...props }: ForgotFormProps) {
|
||||||
@ -28,7 +28,7 @@ export function ForgotForm({ className, ...props }: ForgotFormProps) {
|
|||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: { email: "" }
|
defaultValues: { email: "" },
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(data: z.infer<typeof formSchema>) {
|
function onSubmit(data: z.infer<typeof formSchema>) {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user