diff --git a/.env b/.env index c09abcb..3c55071 100644 --- a/.env +++ b/.env @@ -8,4 +8,6 @@ VITE_SERVICE_ID = 'e51afdc3-ea0b-49cf-8f49-2a6f131b024e' VITE_LOGIN_URL = 'https://console.danakcorp.com/auth/login' VITE_CONSOLE_URL = 'https://console.danakcorp.com' -VITE_WORKSPACE_ID = 'workspace_id' \ No newline at end of file +VITE_WORKSPACE_ID = 'workspace_id' + +VITE_SERVICE_ID = 'e51afdc3-ea0b-49cf-8f49-2a6f131b024e' diff --git a/src/App.tsx b/src/App.tsx index 423e0a5..c4e1731 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,6 +16,7 @@ import { refreshToken } from './pages/auth/service/Service'; import BuySpace from './shared/components/BuySpace' import useConvertNumbers from './hooks/useConvertNumbers' import CheckWorkSpace from './components/CheckWorkSpace' +import ReportBug from './pages/guide/ReportBug'; declare global { interface Window { @@ -105,6 +106,7 @@ const App: FC = () => {
+ diff --git a/src/assets/images/sad1.svg b/src/assets/images/sad1.svg new file mode 100644 index 0000000..e3d10d2 --- /dev/null +++ b/src/assets/images/sad1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/sad2.svg b/src/assets/images/sad2.svg new file mode 100644 index 0000000..8c188a5 --- /dev/null +++ b/src/assets/images/sad2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/sad3.svg b/src/assets/images/sad3.svg new file mode 100644 index 0000000..3a26e73 --- /dev/null +++ b/src/assets/images/sad3.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/smile.svg b/src/assets/images/smile.svg new file mode 100644 index 0000000..762d076 --- /dev/null +++ b/src/assets/images/smile.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/smile2.svg b/src/assets/images/smile2.svg new file mode 100644 index 0000000..baf2691 --- /dev/null +++ b/src/assets/images/smile2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/smile3.svg b/src/assets/images/smile3.svg new file mode 100644 index 0000000..3c53e8b --- /dev/null +++ b/src/assets/images/smile3.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/RowActionsDropdown.tsx b/src/components/RowActionsDropdown.tsx index 249cfff..2485924 100644 --- a/src/components/RowActionsDropdown.tsx +++ b/src/components/RowActionsDropdown.tsx @@ -1,6 +1,6 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { createPortal } from 'react-dom'; -import { More } from 'iconsax-react'; +import React, { useState, useRef, useEffect } from 'react' +import { createPortal } from 'react-dom' +import { More } from 'iconsax-react' export interface RowActionItem { label: string; @@ -12,72 +12,70 @@ export interface RowActionItem { interface RowActionsDropdownProps { actions: RowActionItem[]; className?: string; + trigger?: React.ReactNode; } -const RowActionsDropdown: React.FC = ({ actions, className = '' }) => { - const [isOpen, setIsOpen] = useState(false); - const [position, setPosition] = useState({ top: 0, left: 0 }); - const dropdownRef = useRef(null); - const buttonRef = useRef(null); +const RowActionsDropdown: React.FC = ({ actions, className = '', trigger }) => { + const [isOpen, setIsOpen] = useState(false) + const [position, setPosition] = useState({ top: 0, left: 0 }) + const dropdownRef = useRef(null) + const buttonRef = useRef(null) useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { - setIsOpen(false); + setIsOpen(false) } - }; - - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, []); - - const handleToggle = (e: React.MouseEvent) => { - e.stopPropagation(); - - if (!isOpen && buttonRef.current) { - const rect = buttonRef.current.getBoundingClientRect(); - const isMobile = window.innerWidth < 768; - const dropdownWidth = isMobile ? 140 : 150; - const dropdownHeight = actions.length * (isMobile ? 36 : 40); // تخمین ارتفاع منو - - let left = rect.left + window.scrollX - dropdownWidth + rect.width; - let top = rect.bottom + window.scrollY; - - // بررسی اینکه منو از سمت راست خارج نشود - if (left + dropdownWidth > window.innerWidth) { - left = rect.left + window.scrollX - dropdownWidth; - } - - // بررسی اینکه منو از سمت چپ خارج نشود - if (left < 0) { - left = isMobile ? 20 : 40; - } - - // بررسی اینکه منو از پایین خارج نشود - if (top + dropdownHeight > window.innerHeight + window.scrollY) { - top = rect.top + window.scrollY - dropdownHeight; - } - - setPosition({ top, left }); } - setIsOpen(!isOpen); - }; + document.addEventListener('mousedown', handleClickOutside) + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, []) + + const handleToggle = (e: React.MouseEvent) => { + e.stopPropagation() + + if (!isOpen && buttonRef.current) { + const rect = buttonRef.current.getBoundingClientRect() + const isMobile = window.innerWidth < 768 + const dropdownWidth = isMobile ? 140 : 150 + const dropdownHeight = actions.length * (isMobile ? 36 : 40) + + let left = rect.left + window.scrollX - dropdownWidth + rect.width + let top = rect.bottom + window.scrollY + + if (left + dropdownWidth > window.innerWidth) { + left = rect.left + window.scrollX - dropdownWidth + } + + if (left < 0) { + left = isMobile ? 20 : 40 + } + + if (top + dropdownHeight > window.innerHeight + window.scrollY) { + top = rect.top + window.scrollY - dropdownHeight + } + + setPosition({ top, left }) + } + + setIsOpen(!isOpen) + } const handleActionClick = (action: RowActionItem) => { - action.onClick(); - setIsOpen(false); - }; + action.onClick() + setIsOpen(false) + } const renderDropdown = () => { - if (!isOpen) return null; + if (!isOpen) return null return createPortal(
= ({ actions, classN ))}
, document.body - ); - }; + ) + } return (
{renderDropdown()}
- ); -}; + ) +} -export default RowActionsDropdown; \ No newline at end of file +export default RowActionsDropdown \ No newline at end of file diff --git a/src/index.css b/src/index.css index 2bf995b..1bf59c9 100644 --- a/src/index.css +++ b/src/index.css @@ -267,3 +267,8 @@ textarea::placeholder { backdrop-filter: blur(44px); } + +.filterBlue { + filter: brightness(0) saturate(100%) invert(9%) sepia(100%) saturate(7246%) + hue-rotate(236deg) brightness(111%) contrast(112%); +} diff --git a/src/pages/guide/ReportBug.tsx b/src/pages/guide/ReportBug.tsx new file mode 100644 index 0000000..1695d4f --- /dev/null +++ b/src/pages/guide/ReportBug.tsx @@ -0,0 +1,126 @@ +import React, { useMemo, useState } from 'react' +import DefaulModal from '@/components/DefaulModal' +import Textarea from '@/components/Textarea' +import Button from '@/components/Button' +import { useSharedStore } from '@/shared/store/sharedStore' +import { useSendFeedback } from './hooks/useFeedbackData' +import { toast } from '@/components/Toast' +import { ErrorType } from '@/helpers/types' + + +// Import images +import sad1 from '@/assets/images/sad1.svg' +import sad2 from '@/assets/images/sad2.svg' +import sad3 from '@/assets/images/sad3.svg' +import smile from '@/assets/images/smile.svg' +import smile2 from '@/assets/images/smile2.svg' +import smile3 from '@/assets/images/smile3.svg' +import { useGetProfile } from '../profile/hooks/useProfileData' + +const ReportBug: React.FC = () => { + const { openReportBug, setOpenReportBug } = useSharedStore() + const [description, setDescription] = useState('') + const [feeling, setFeeling] = useState(null) + const { data } = useGetProfile() + const { mutate: sendReportBug, isPending } = useSendFeedback() + + const isValid = useMemo(() => description.trim().length > 0, [description]) + + const getRatingFromFeeling = (feeling: string | null): number => { + if (!feeling) return 0 + + const feelingMap: Record = { + 'angry': 1, // sad1 + 'crying': 2, // sad2 + 'sad': 3, // sad3 + 'neutral': 4, // smile + 'smile': 6, // smile2 + 'love': 5, // smile3 + } + + return feelingMap[feeling] || 0 + } + + const handleSubmit = async () => { + if (!isValid) return + + sendReportBug({ + content: description, + rating: getRatingFromFeeling(feeling), + serviceId: import.meta.env.VITE_SERVICE_ID, + email: data?.data?.user?.emailAddress, + }, { + onSuccess: () => { + toast('گزارش با موفقیت ارسال شد', 'success') + setDescription('') + setFeeling(null) + setOpenReportBug(false) + }, + onError: (error: ErrorType) => { + toast(error.response?.data?.error?.message[0], 'error') + } + }) + } + + const feelings = [ + { id: 'neutral', image: smile, alt: 'خنثی' }, + { id: 'smile', image: smile2, alt: 'راضی' }, + { id: 'love', image: smile3, alt: 'راضی' }, + { id: 'angry', image: sad1, alt: 'ناراضی' }, + { id: 'crying', image: sad2, alt: 'ناراضی' }, + { id: 'sad', image: sad3, alt: 'ناراضی' }, + ] + + return ( + setOpenReportBug(false)} isHeader title_header='گزارش اشکالات' width={500}> +
+
+ لطفاً در صورت مشاهده هرگونه اشکال یا باگ در سایت، از طریق این فرم گزارش دهید. همچنین می‌توانید بازخورد، نظر یا پیشنهادات خود را درباره سایت با ما به اشتراک بگذارید. اطلاعات دقیق شما به ما کمک می‌کند مشکلات را سریع‌تر برطرف کنیم و کیفیت سایت را بهبود بخشیم. +
+ +