base structure

This commit is contained in:
hamid zarghami
2025-08-03 09:12:48 +03:30
parent 866618a5bc
commit 61a934074c
9 changed files with 234 additions and 6 deletions
+27
View File
@@ -8,6 +8,7 @@
"name": "shop",
"version": "0.1.0",
"dependencies": {
"@tanstack/react-query": "^5.84.1",
"next": "15.4.5",
"react": "19.1.0",
"react-dom": "19.1.0"
@@ -1265,6 +1266,32 @@
"tailwindcss": "4.1.11"
}
},
"node_modules/@tanstack/query-core": {
"version": "5.83.1",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.1.tgz",
"integrity": "sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/react-query": {
"version": "5.84.1",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.1.tgz",
"integrity": "sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==",
"license": "MIT",
"dependencies": {
"@tanstack/query-core": "5.83.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": "^18 || ^19"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
+7 -6
View File
@@ -9,19 +9,20 @@
"lint": "next lint"
},
"dependencies": {
"@tanstack/react-query": "^5.84.1",
"next": "15.4.5",
"react": "19.1.0",
"react-dom": "19.1.0",
"next": "15.4.5"
"react-dom": "19.1.0"
},
"devDependencies": {
"typescript": "^5",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.4.5",
"@eslint/eslintrc": "^3"
"tailwindcss": "^4",
"typescript": "^5"
}
}
+35
View File
@@ -0,0 +1,35 @@
'use client'
import { IApiErrorRepsonse } from '@/types/error.types'
import { QueryClient, QueryClientProvider, HydrationBoundary, QueryCache } from '@tanstack/react-query'
import { ReactNode, useState } from 'react'
import { removeToken } from './func'
import { useSharedStore } from '@/share/store/sharedStore'
export default function QueryProvider({ children }: { children: ReactNode }) {
const { setIsLogin } = useSharedStore()
const [client] = useState(() => new QueryClient({
queryCache: new QueryCache({
onError: async (error: IApiErrorRepsonse) => {
if (error.response?.status === 401) {
removeToken()
setIsLogin(false)
}
}
}),
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // داده‌ها برای 5 دقیقه معتبر هستند
}
}
}))
return (
<QueryClientProvider client={client}>
<HydrationBoundary state={null}>
{children}
</HydrationBoundary>
</QueryClientProvider>
)
}
+2
View File
@@ -0,0 +1,2 @@
export const TOKEN_NAME = "sh_token";
export const REFRESH_TOKEN_NAME = "sh_refresh_token";
+96
View File
@@ -0,0 +1,96 @@
import { TOKEN_NAME, REFRESH_TOKEN_NAME } from "./const";
export function isEmail(input: string): boolean {
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(input);
}
export function NumberFormat(number: number): string {
return Intl.NumberFormat("fa-IR").format(number);
// return Intl.NumberFormat("fa-IR").format(number);
}
export const timeAgo = (date: string | Date): string => {
const now = new Date();
const past = new Date(date);
const diff = Math.floor((now.getTime() - past.getTime()) / 1000); // اختلاف بر حسب ثانیه
const units = [
{ name: "سال", value: 60 * 60 * 24 * 365 },
{ name: "ماه", value: 60 * 60 * 24 * 30 },
{ name: "روز", value: 60 * 60 * 24 },
{ name: "ساعت", value: 60 * 60 },
{ name: "دقیقه", value: 60 },
{ name: "ثانیه", value: 1 },
];
for (const unit of units) {
const amount = Math.floor(diff / unit.value);
if (amount >= 1) return `${amount} ${unit.name} پیش`;
}
return "همین الان";
};
export const getToken = async () => {
return localStorage.getItem(TOKEN_NAME);
};
export const getRefreshToken = () => {
return localStorage.getItem(REFRESH_TOKEN_NAME);
};
export const setToken = (token: string) => {
localStorage.setItem(TOKEN_NAME, token);
};
export const setRefreshToken = (refreshToken: string) => {
localStorage.setItem(REFRESH_TOKEN_NAME, refreshToken);
};
export const removeToken = () => {
localStorage.removeItem(TOKEN_NAME);
};
export const removeRefreshToken = () => {
localStorage.removeItem(REFRESH_TOKEN_NAME);
};
export function toJalaliDate(
date: Date | string | number,
options?: Intl.DateTimeFormatOptions,
locale: string = "fa-IR-u-ca-persian"
): string {
const parsedDate = new Date(date);
if (isNaN(parsedDate.getTime())) {
return "";
throw new Error("Invalid date provided to toJalaliDate");
}
const defaultOptions: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "numeric",
day: "numeric",
// weekday: "long",
// hour: "numeric",
// minute: "numeric",
};
const formatter = new Intl.DateTimeFormat(locale, {
...defaultOptions,
...options,
});
return formatter.format(parsedDate);
}
export function limitHtmlWords(htmlString: string, wordLimit: number): string {
// پاک کردن تگ‌ها
const plainText = htmlString.replace(/<[^>]*>/g, "");
// تقسیم و محدود کردن کلمات
const words = plainText.split(/\s+/).slice(0, wordLimit);
const result = words.join(" ") + "...";
return result;
}
+9
View File
@@ -0,0 +1,9 @@
import { create } from "zustand";
import { SharedStoreType } from "../types/SharedTypes";
export const useSharedStore = create<SharedStoreType>((set) => ({
openSidebar: false,
setOpenSidebar: (value) => set({ openSidebar: value }),
isLogin: false,
setIsLogin: (value) => set({ isLogin: value }),
}));
+37
View File
@@ -0,0 +1,37 @@
export type SharedStoreType = {
openSidebar: boolean;
setOpenSidebar: (value: boolean) => void;
isLogin: boolean;
setIsLogin: (value: boolean) => void;
};
export interface ApiResponse<T> {
statusCode: number;
success: boolean;
data: T;
}
export type UserMeResponseType = ApiResponse<{
user: {
id: string;
createdAt: string;
updatedAt: string;
firstName: string;
lastName: string;
email: string;
emailVerified: boolean;
phone: string;
birthDate: string;
nationalCode: string;
financialType: string;
profilePic: string | null;
userName: string | null;
roles: Array<{
id: string;
createdAt: string;
name: string;
// Add other role properties as needed
}>;
};
}>;
+14
View File
@@ -0,0 +1,14 @@
import { DefaultError } from "@tanstack/react-query";
import { IBaseResponse } from "./response.types";
interface IError {
message: string[];
}
export interface IErrorResponse extends IBaseResponse {
error: IError;
}
export interface IApiErrorRepsonse extends DefaultError {
response?: IErrorResponse;
}
+7
View File
@@ -0,0 +1,7 @@
export interface IBaseResponse {
status: number;
success: boolean;
}
export interface IResponse<T> extends IBaseResponse {
data: T;
}