create catalog
This commit is contained in:
+8
-1
@@ -5,6 +5,8 @@ import MainRouter from './router/MainRouter'
|
||||
import i18next from 'i18next'
|
||||
import FaJson from '@/langs/fa.json'
|
||||
import { I18nextProvider } from 'react-i18next'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import ToastContainer from './components/Toast'
|
||||
|
||||
i18next.init({
|
||||
interpolation: { escapeValue: false },
|
||||
@@ -16,11 +18,16 @@ i18next.init({
|
||||
}
|
||||
})
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
const App: FC = () => {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<I18nextProvider i18n={i18next}>
|
||||
<MainRouter />
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MainRouter />
|
||||
<ToastContainer />
|
||||
</QueryClientProvider>
|
||||
</I18nextProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
'use client'
|
||||
import { CloseCircle, InfoCircle, TickCircle } from 'iconsax-react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
interface Toast {
|
||||
id: string;
|
||||
message: string;
|
||||
type?: 'success' | 'error' | 'info';
|
||||
isExiting?: boolean;
|
||||
}
|
||||
|
||||
let addToast: (toast: Toast) => void;
|
||||
|
||||
const ToastContainer: React.FC = () => {
|
||||
const [toasts, setToasts] = useState<Toast[]>([]);
|
||||
|
||||
// ثبت toast جدید
|
||||
const showToast = (toast: Toast) => {
|
||||
setToasts((prev) => [...prev, toast]);
|
||||
|
||||
// حذف خودکار بعد از ۳ ثانیه
|
||||
setTimeout(() => {
|
||||
setToasts((prev) => prev.map(t =>
|
||||
t.id === toast.id ? { ...t, isExiting: true } : t
|
||||
));
|
||||
|
||||
// حذف از DOM بعد از اتمام انیمیشن
|
||||
setTimeout(() => {
|
||||
setToasts((prev) => prev.filter((t) => t.id !== toast.id));
|
||||
}, 300); // مدت زمان انیمیشن خروج
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
// تخصیص تابع نمایش toast به متغیر سراسری
|
||||
useEffect(() => {
|
||||
addToast = showToast;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="fixed top-4 text-sm right-0 left-0 mx-auto w-fit z-50 flex flex-col gap-2">
|
||||
{toasts.map((toast) => (
|
||||
<div
|
||||
key={toast.id}
|
||||
className={`px-4 flex items-center gap-2 backdrop-blur-2xl h-16 min-w-[300px] rounded-2xl shadow-md
|
||||
${toast.isExiting ? 'animate-slide-out-right' : 'animate-slide-in-right'} ${toast.type === 'success'
|
||||
? 'bg-white/70 text-black'
|
||||
: toast.type === 'error'
|
||||
? 'bg-white/70 text-black'
|
||||
: 'bg-white/70 text-black'
|
||||
}`}
|
||||
style={{
|
||||
animationFillMode: 'forwards',
|
||||
animationDuration: '0.3s',
|
||||
animationTimingFunction: 'ease-out'
|
||||
}}
|
||||
>
|
||||
{
|
||||
toast.type === 'success' ? <TickCircle className='size-5' color='green' /> :
|
||||
toast.type === 'error' ? <CloseCircle className='size-5' color='red' /> :
|
||||
<InfoCircle className='size-5' color='blue' />
|
||||
}
|
||||
{toast.message}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const toast = (message?: string, type: 'success' | 'error' | 'info' = 'info') => {
|
||||
addToast({ id: Date.now().toString(), message: message || '', type });
|
||||
};
|
||||
|
||||
export default ToastContainer;
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { getToken } from "./func";
|
||||
// import { Pages } from "./Pages";
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_BASE_URL,
|
||||
baseURL: import.meta.env.VITE_API_URL,
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
|
||||
+6
-14
@@ -31,35 +31,27 @@ export const timeAgo = (date: string | Date): string => {
|
||||
};
|
||||
|
||||
export const getToken = () => {
|
||||
return localStorage.get(import.meta.env.VITE_TOKEN_NAME);
|
||||
return localStorage.getItem(import.meta.env.VITE_TOKEN_NAME);
|
||||
};
|
||||
|
||||
export const getRefreshToken = () => {
|
||||
return localStorage.get(import.meta.env.VITE_REFRESH_TOKEN_NAME);
|
||||
return localStorage.getItem(import.meta.env.VITE_REFRESH_TOKEN_NAME);
|
||||
};
|
||||
|
||||
const isProduction = import.meta.env.MODE === "production";
|
||||
const domain = isProduction ? ".danakcorp.com" : window.location.hostname;
|
||||
|
||||
export const setToken = (token: string) => {
|
||||
localStorage.set(import.meta.env.VITE_TOKEN_NAME, token, { domain });
|
||||
localStorage.setItem(import.meta.env.VITE_TOKEN_NAME, token);
|
||||
};
|
||||
|
||||
export const setRefreshToken = (refreshToken: string) => {
|
||||
localStorage.set(import.meta.env.VITE_REFRESH_TOKEN_NAME, refreshToken, {
|
||||
domain,
|
||||
});
|
||||
localStorage.setItem(import.meta.env.VITE_REFRESH_TOKEN_NAME, refreshToken);
|
||||
};
|
||||
|
||||
export const removeToken = () => {
|
||||
localStorage.remove(import.meta.env.VITE_TOKEN_NAME, { domain, path: "/" });
|
||||
localStorage.removeItem(import.meta.env.VITE_TOKEN_NAME);
|
||||
};
|
||||
|
||||
export const removeRefreshToken = () => {
|
||||
localStorage.remove(import.meta.env.VITE_REFRESH_TOKEN_NAME, {
|
||||
domain,
|
||||
path: "/",
|
||||
});
|
||||
localStorage.removeItem(import.meta.env.VITE_REFRESH_TOKEN_NAME);
|
||||
};
|
||||
|
||||
export const getRedirectUrl = () => {
|
||||
|
||||
+25
-2
@@ -4,10 +4,31 @@ import { useState, type FC } from 'react'
|
||||
import PdfIcon from '@/assets/images/PDF.svg'
|
||||
import Button from '@/components/Button'
|
||||
import Input from '@/components/Input'
|
||||
import { toast } from '@/components/Toast'
|
||||
import { useCreateCatalog } from './hooks/useHomeData'
|
||||
|
||||
const Home: FC = () => {
|
||||
|
||||
const [active, setActive] = useState<string>('')
|
||||
const [active, setActive] = useState<string>('a4')
|
||||
const [name, setName] = useState<string>('')
|
||||
const { mutate: createCatalog, isPending } = useCreateCatalog()
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!name) {
|
||||
toast('نام کاتالوگ را وارد کنید', 'error')
|
||||
} else {
|
||||
createCatalog({ name: name, size: active, content: '' }, {
|
||||
onSuccess: () => {
|
||||
toast('کاتالوگ با موفقیت ساخته شد', 'success')
|
||||
},
|
||||
onError: (err) => {
|
||||
console.log(err);
|
||||
|
||||
toast('خطا در ساخت کاتالوگ', 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4 w-full'>
|
||||
@@ -69,11 +90,13 @@ const Home: FC = () => {
|
||||
<div className='mt-5'>
|
||||
<Input
|
||||
label='نام کاتالوگ'
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-center'>
|
||||
<Button className='mt-8 rounded-xl'>
|
||||
<Button disabled={isPending} onClick={handleSubmit} className='mt-8 rounded-xl'>
|
||||
<div className='flex gap-2 items-center'>
|
||||
ادامه
|
||||
<ArrowLeft size={16} color='white' />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as api from "../service/HomeService";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
export const useHomeData = () => {
|
||||
export const useCreateCatalog = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.createCatalog,
|
||||
});
|
||||
|
||||
@@ -2,6 +2,6 @@ import axios from "@/config/axios";
|
||||
import type { CreateCatalogParamsType } from "../types/Types";
|
||||
|
||||
export const createCatalog = async (params: CreateCatalogParamsType) => {
|
||||
const { data } = await axios.post("/catalogs", params);
|
||||
const { data } = await axios.post("/catalogue", params);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export type CreateCatalogParamsType = {
|
||||
name: string;
|
||||
size: string;
|
||||
content?: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user