row click & filters + ...
This commit is contained in:
@@ -15,7 +15,7 @@ interface ButtonProps {
|
|||||||
const Button: FC<ButtonProps> = (props) => {
|
const Button: FC<ButtonProps> = (props) => {
|
||||||
|
|
||||||
const buttonClass = clx(
|
const buttonClass = clx(
|
||||||
'w-full bg-primary text-white rounded-xl font-normal text-xs md:text-sm h-10 px-3 md:px-4 transition-all duration-200 hover:bg-opacity-90 disabled:opacity-50 disabled:cursor-not-allowed',
|
'w-full bg-primary text-white cursor-pointer rounded-xl font-normal text-xs md:text-sm h-10 px-3 md:px-4 transition-all duration-200 hover:bg-opacity-90 disabled:opacity-50 disabled:cursor-not-allowed',
|
||||||
props.variant === 'secondary' && 'bg-[#ECEEF5] text-black',
|
props.variant === 'secondary' && 'bg-[#ECEEF5] text-black',
|
||||||
props.className
|
props.className
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { FC, useEffect, useState, ChangeEvent } from 'react';
|
import { FC, useEffect, useState, ChangeEvent } from 'react';
|
||||||
|
import { Filter, CloseCircle } from 'iconsax-react';
|
||||||
import DatePicker from './DatePicker';
|
import DatePicker from './DatePicker';
|
||||||
import Input from './Input';
|
import Input from './Input';
|
||||||
import Select, { ItemsSelectType } from './Select';
|
import Select, { ItemsSelectType } from './Select';
|
||||||
@@ -49,6 +50,7 @@ const Filters: FC<FiltersProps> = ({
|
|||||||
searchField = "search" // پیشفرض فیلد سرچ با نام search
|
searchField = "search" // پیشفرض فیلد سرچ با نام search
|
||||||
}) => {
|
}) => {
|
||||||
const [filters, setFilters] = useState<FilterValues>({});
|
const [filters, setFilters] = useState<FilterValues>({});
|
||||||
|
const [isFiltersOpen, setIsFiltersOpen] = useState(false);
|
||||||
|
|
||||||
// تنظیم مقادیر اولیه
|
// تنظیم مقادیر اولیه
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -125,12 +127,61 @@ const Filters: FC<FiltersProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div className={fieldClassName}>
|
{/* آیکون فیلتر برای موبایل */}
|
||||||
{otherFields.map(renderField)}
|
<div className="flex md:hidden items-center justify-between w-full mb-2">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsFiltersOpen(!isFiltersOpen)}
|
||||||
|
className="flex items-center gap-2 px-3 py-2 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors"
|
||||||
|
>
|
||||||
|
<Filter size={18} color="#000" />
|
||||||
|
<span className="text-sm">فیلترها</span>
|
||||||
|
</button>
|
||||||
|
{searchFieldObj && (
|
||||||
|
<div className="flex-1 mr-4">
|
||||||
|
{renderField(searchFieldObj)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{searchFieldObj && (
|
|
||||||
<div className="w-full md:w-auto">
|
{/* فیلترها برای دسکتاپ (همیشه نمایش داده میشوند) */}
|
||||||
{renderField(searchFieldObj)}
|
<div className="hidden md:flex md:flex-row justify-between items-center gap-4 w-full">
|
||||||
|
<div className={fieldClassName}>
|
||||||
|
{otherFields.map(renderField)}
|
||||||
|
</div>
|
||||||
|
{searchFieldObj && (
|
||||||
|
<div className="w-full md:w-auto">
|
||||||
|
{renderField(searchFieldObj)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* فیلترها برای موبایل (قابل باز و بسته شدن) */}
|
||||||
|
{isFiltersOpen && (
|
||||||
|
<div className="md:hidden w-full animate-fadeInDown">
|
||||||
|
<div className="bg-gradient-to-br from-white to-gray-50 border border-gray-200 rounded-xl p-5 space-y-4">
|
||||||
|
<div className="flex items-center justify-between pb-3 border-b border-gray-100">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Filter size={16} color="#6B7280" />
|
||||||
|
<span className="text-sm font-medium text-gray-700">فیلتر کردن نتایج</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsFiltersOpen(false)}
|
||||||
|
className="p-1 hover:bg-gray-100 rounded-full transition-colors"
|
||||||
|
>
|
||||||
|
<CloseCircle size={18} color="#6B7280" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{otherFields.map(field => (
|
||||||
|
<div key={field.name} className="w-full">
|
||||||
|
<label className="block text-xs font-medium text-gray-600 mb-2">
|
||||||
|
{field.placeholder}
|
||||||
|
</label>
|
||||||
|
<div className="transform hover:scale-[1.02] transition-transform duration-200">
|
||||||
|
{renderField(field)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -227,3 +227,19 @@ textarea::placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* End Generation Here */
|
/* End Generation Here */
|
||||||
|
|
||||||
|
/* انیمیشن برای فیلترهای موبایل */
|
||||||
|
@keyframes fadeInDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fadeInDown {
|
||||||
|
animation: fadeInDown 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Table from '../../components/Table';
|
|||||||
import { ColumnType } from '../../components/types/TableTypes';
|
import { ColumnType } from '../../components/types/TableTypes';
|
||||||
import { InfoCircle, More, Refresh2, Trash, Edit, Archive, ArchiveSlash } from 'iconsax-react';
|
import { InfoCircle, More, Refresh2, Trash, Edit, Archive, ArchiveSlash } from 'iconsax-react';
|
||||||
import { RowActionItem } from '../../components/RowActionsDropdown';
|
import { RowActionItem } from '../../components/RowActionsDropdown';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
interface Message extends Record<string, unknown> {
|
interface Message extends Record<string, unknown> {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -15,6 +16,7 @@ interface Message extends Record<string, unknown> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const List: FC = () => {
|
const List: FC = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isLoading] = useState(false);
|
const [isLoading] = useState(false);
|
||||||
const [selectedMessages, setSelectedMessages] = useState<Message[]>([]);
|
const [selectedMessages, setSelectedMessages] = useState<Message[]>([]);
|
||||||
@@ -141,7 +143,7 @@ const List: FC = () => {
|
|||||||
actions={tableActions}
|
actions={tableActions}
|
||||||
selectedActions={selectedActions}
|
selectedActions={selectedActions}
|
||||||
onSelectionChange={handleSelectionChange}
|
onSelectionChange={handleSelectionChange}
|
||||||
onRowClick={(id) => console.log(`کلیک روی پیام با شناسه ${id}`)}
|
onRowClick={(id) => navigate(`/mail/${id}`)}
|
||||||
noDataMessage={
|
noDataMessage={
|
||||||
<div className="flex flex-col items-center py-6">
|
<div className="flex flex-col items-center py-6">
|
||||||
<InfoCircle size={32} className="text-gray-300 mb-2" />
|
<InfoCircle size={32} className="text-gray-300 mb-2" />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { Paths } from '@/utils/Paths'
|
import { Paths } from '@/utils/Paths'
|
||||||
import { Routes, Route } from 'react-router-dom'
|
import { Routes, Route } from 'react-router-dom'
|
||||||
import Home from '@/pages/home/Home'
|
|
||||||
import ReceivedList from '@/pages/received/List'
|
import ReceivedList from '@/pages/received/List'
|
||||||
import Setting from '@/pages/setting/Setting'
|
import Setting from '@/pages/setting/Setting'
|
||||||
import SentList from '@/pages/sent/List'
|
import SentList from '@/pages/sent/List'
|
||||||
@@ -12,7 +11,7 @@ import DetailEmail from '@/pages/received/Detail'
|
|||||||
const AppRouter: FC = () => {
|
const AppRouter: FC = () => {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={<Home />} />
|
<Route path='/' element={<ReceivedList />} />
|
||||||
<Route path={Paths.received} element={<ReceivedList />} />
|
<Route path={Paths.received} element={<ReceivedList />} />
|
||||||
<Route path={Paths.setting} element={<Setting />} />
|
<Route path={Paths.setting} element={<Setting />} />
|
||||||
<Route path={Paths.sent} element={<SentList />} />
|
<Route path={Paths.sent} element={<SentList />} />
|
||||||
|
|||||||
@@ -2,8 +2,16 @@ import { FC } from 'react'
|
|||||||
import SideBar from './SideBar'
|
import SideBar from './SideBar'
|
||||||
import AppRouter from '@/router/AppRouter'
|
import AppRouter from '@/router/AppRouter'
|
||||||
import Header from './Header'
|
import Header from './Header'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
import { Add } from 'iconsax-react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSharedStore } from './store/sharedStore'
|
||||||
|
|
||||||
const Main: FC = () => {
|
const Main: FC = () => {
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { setOpenSidebar, setOpenNewMessage } = useSharedStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='p-2 md:p-4 overflow-hidden'>
|
<div className='p-2 md:p-4 overflow-hidden'>
|
||||||
<SideBar />
|
<SideBar />
|
||||||
@@ -15,6 +23,22 @@ const Main: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='flex fixed bottom-4 xl:hidden right-0 justify-center mt-10 md:mt-14 px-4'>
|
||||||
|
<Button
|
||||||
|
className='bg-secondary font-normal text-primary w-fit px-4 md:px-6 text-sm'
|
||||||
|
onClick={() => {
|
||||||
|
setOpenSidebar(false)
|
||||||
|
setOpenNewMessage(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='flex gap-2 items-center'>
|
||||||
|
<Add size={18} color='black' />
|
||||||
|
<div>{t('sidebar.new_message')}</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user