list of pager + change status
This commit is contained in:
@@ -875,5 +875,15 @@
|
||||
"poor_quality": "کیفیت پایین",
|
||||
"overpriced": "قیمت بالا",
|
||||
"unfriendly_service": "خدمات نامناسب"
|
||||
},
|
||||
"pager": {
|
||||
"status": {
|
||||
"pending": "در انتظار",
|
||||
"acknowledged": "تایید شده",
|
||||
"rejected": "رد شده",
|
||||
"completed": "تکمیل شده"
|
||||
},
|
||||
"change_status": "تغییر وضعیت",
|
||||
"save": "ذخیره"
|
||||
}
|
||||
}
|
||||
|
||||
+29
-44
@@ -1,19 +1,21 @@
|
||||
import { type FC, useEffect, useState, useMemo } from 'react'
|
||||
import { useSocket } from '@/pages/pager/hooks/useSocket'
|
||||
import { getToken } from '@/config/func'
|
||||
import type { PagersType, Pager } from './types/Types'
|
||||
import { type FC, useMemo, useState } from 'react'
|
||||
import type { Pager } from './types/Types'
|
||||
import Table from '@/components/Table'
|
||||
import Filters from '@/components/Filters'
|
||||
import { usePagerFilters } from './hooks/usePagerFilters'
|
||||
import { getPagerTableColumns } from './components/PagerTableColumns'
|
||||
import { usePagerFiltersFields } from './components/PagerFiltersFields'
|
||||
import type { RowDataType } from '@/components/types/TableTypes'
|
||||
import { useGetPagers } from './hooks/usePagerData'
|
||||
import PagerDetailModal from './components/PagerDetailModal'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const PagersList: FC = () => {
|
||||
const token = `Bearer ${getToken()}`
|
||||
const { connect, isConnected, socket } = useSocket(import.meta.env.VITE_SOCKET_URL, token)
|
||||
const { t } = useTranslation('global')
|
||||
const [selectedPager, setSelectedPager] = useState<Pager | null>(null)
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
|
||||
const [pagers, setPagers] = useState<PagersType>()
|
||||
const { data: pagersData, isLoading } = useGetPagers()
|
||||
|
||||
const {
|
||||
filters,
|
||||
@@ -25,42 +27,9 @@ const PagersList: FC = () => {
|
||||
totalPages,
|
||||
} = usePagerFilters()
|
||||
|
||||
useEffect(() => {
|
||||
connect()
|
||||
}, [connect])
|
||||
|
||||
const handleJoinRestaurant = () => {
|
||||
socket?.emit('get:pagers')
|
||||
}
|
||||
|
||||
const handlePagersList = (data: PagersType) => {
|
||||
setPagers(data)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isConnected && socket) {
|
||||
socket.emit('join:restaurant')
|
||||
}
|
||||
}, [isConnected, socket])
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
socket.on('joined', handleJoinRestaurant)
|
||||
socket.on('pagers:list', handlePagersList)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (socket) {
|
||||
socket.off('joined', handleJoinRestaurant)
|
||||
socket.off('pagers:list', handlePagersList)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [socket])
|
||||
|
||||
const pagersList = useMemo(() => {
|
||||
return pagers?.pagers || []
|
||||
}, [pagers])
|
||||
return pagersData?.data || []
|
||||
}, [pagersData])
|
||||
|
||||
const filteredPagers = useMemo(() => {
|
||||
return filteredData(pagersList) as Pager[]
|
||||
@@ -74,7 +43,17 @@ const PagersList: FC = () => {
|
||||
return totalPages(filteredPagers.length)
|
||||
}, [filteredPagers.length, totalPages])
|
||||
|
||||
const columns = getPagerTableColumns()
|
||||
const handleViewDetails = (pager: Pager) => {
|
||||
setSelectedPager(pager)
|
||||
setIsModalOpen(true)
|
||||
}
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsModalOpen(false)
|
||||
setSelectedPager(null)
|
||||
}
|
||||
|
||||
const columns = getPagerTableColumns({ onViewDetails: handleViewDetails, t })
|
||||
const filterFields = usePagerFiltersFields()
|
||||
|
||||
return (
|
||||
@@ -95,13 +74,19 @@ const PagersList: FC = () => {
|
||||
<Table<Pager & RowDataType>
|
||||
columns={columns}
|
||||
data={paginatedPagers as (Pager & RowDataType)[]}
|
||||
isloading={false}
|
||||
isloading={isLoading}
|
||||
pagination={{
|
||||
currentPage,
|
||||
totalPages: totalPagesCount,
|
||||
onPageChange: handlePageChange,
|
||||
}}
|
||||
/>
|
||||
|
||||
<PagerDetailModal
|
||||
open={isModalOpen}
|
||||
close={handleCloseModal}
|
||||
pager={selectedPager}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { type FC, useState, useEffect } from 'react'
|
||||
import DefaulModal from '@/components/DefaulModal'
|
||||
import type { Pager } from '../types/Types'
|
||||
import { formatOptionalDate } from '@/helpers/func'
|
||||
import Status from '@/components/Status'
|
||||
import Select from '@/components/Select'
|
||||
import Button from '@/components/Button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useUpdatePagerStatus } from '../hooks/usePagerData'
|
||||
import { PagerStatus } from '../enum/Enum'
|
||||
import type { ItemsSelectType } from '@/components/Select'
|
||||
|
||||
interface Props {
|
||||
open: boolean
|
||||
close: () => void
|
||||
pager: Pager | null
|
||||
}
|
||||
|
||||
const PagerDetailModal: FC<Props> = ({ open, close, pager }) => {
|
||||
const { t } = useTranslation('global')
|
||||
const [selectedStatus, setSelectedStatus] = useState<string>('')
|
||||
const { mutate: updateStatus, isPending } = useUpdatePagerStatus()
|
||||
|
||||
useEffect(() => {
|
||||
if (pager) {
|
||||
setSelectedStatus(pager.status)
|
||||
}
|
||||
}, [pager])
|
||||
|
||||
if (!pager) return null
|
||||
|
||||
const statusOptions: ItemsSelectType[] = [
|
||||
{ value: PagerStatus.Pending, label: t('pager.status.pending') },
|
||||
{ value: PagerStatus.Acknowledged, label: t('pager.status.acknowledged') },
|
||||
{ value: PagerStatus.Rejected, label: t('pager.status.rejected') },
|
||||
{ value: PagerStatus.Completed, label: t('pager.status.completed') },
|
||||
]
|
||||
|
||||
const getStatusVariant = (status: string) => {
|
||||
switch (status) {
|
||||
case PagerStatus.Pending:
|
||||
return 'warning'
|
||||
case PagerStatus.Acknowledged:
|
||||
return 'info'
|
||||
case PagerStatus.Rejected:
|
||||
return 'error'
|
||||
case PagerStatus.Completed:
|
||||
return 'success'
|
||||
default:
|
||||
return 'pending'
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusLabel = (status: string) => {
|
||||
const option = statusOptions.find(opt => opt.value === status)
|
||||
return option?.label || status
|
||||
}
|
||||
|
||||
const handleSaveStatus = () => {
|
||||
if (selectedStatus && selectedStatus !== pager.status) {
|
||||
updateStatus(
|
||||
{ id: pager.id, status: selectedStatus as PagerStatus },
|
||||
{
|
||||
onSuccess: () => {
|
||||
close()
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const statusVariant = getStatusVariant(pager.status)
|
||||
const statusLabel = getStatusLabel(pager.status)
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={open}
|
||||
close={close}
|
||||
isHeader
|
||||
title_header="جزئیات پیجر"
|
||||
width={600}
|
||||
>
|
||||
<div className="mt-6 space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm text-gray-500 mb-1 block">
|
||||
شماره میز
|
||||
</label>
|
||||
<p className="text-sm font-medium">
|
||||
{pager.tableNumber || '-'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm text-gray-500 mb-1 block">
|
||||
وضعیت
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<Status variant={statusVariant} label={statusLabel} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div>
|
||||
<label className="text-sm text-gray-500 mb-1 block">
|
||||
کاربر
|
||||
</label>
|
||||
<p className="text-sm font-medium">
|
||||
{pager.user || '-'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{pager.deletedAt && (
|
||||
<div>
|
||||
<label className="text-sm text-gray-500 mb-1 block">
|
||||
تاریخ حذف
|
||||
</label>
|
||||
<p className="text-sm font-medium">
|
||||
{formatOptionalDate(pager.deletedAt, {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm text-gray-500 mb-1 block">
|
||||
پیام
|
||||
</label>
|
||||
<p className="text-sm font-medium bg-gray-50 p-3 rounded-lg whitespace-pre-wrap break-words">
|
||||
{pager.message || '-'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='border-t border-gray-200 pt-4'>
|
||||
<label className="text-sm text-gray-500 mb-1 block">
|
||||
{t('pager.change_status')}
|
||||
</label>
|
||||
<Select
|
||||
key={pager.id}
|
||||
items={statusOptions}
|
||||
defaultValue={selectedStatus}
|
||||
onChange={(e) => setSelectedStatus(e.target.value)}
|
||||
className="mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end mt-6">
|
||||
<Button
|
||||
label={t('pager.save')}
|
||||
onClick={handleSaveStatus}
|
||||
isloading={isPending}
|
||||
disabled={selectedStatus === pager.status || !selectedStatus}
|
||||
className="w-fit px-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default PagerDetailModal
|
||||
@@ -8,15 +8,5 @@ export const usePagerFiltersFields = (): FieldType[] => {
|
||||
name: 'search',
|
||||
placeholder: 'جستجو',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'status',
|
||||
placeholder: 'وضعیت',
|
||||
options: [
|
||||
{ label: 'همه', value: '' },
|
||||
{ label: 'فعال', value: 'active' },
|
||||
{ label: 'غیرفعال', value: 'inactive' },
|
||||
],
|
||||
},
|
||||
], [])
|
||||
}
|
||||
|
||||
@@ -2,8 +2,47 @@ import type { ColumnType } from '@/components/types/TableTypes'
|
||||
import type { Pager } from '../types/Types'
|
||||
import { formatOptionalDate } from '@/helpers/func'
|
||||
import Status from '@/components/Status'
|
||||
import { Eye } from 'iconsax-react'
|
||||
import { PagerStatus } from '../enum/Enum'
|
||||
import type { TFunction } from 'i18next'
|
||||
|
||||
interface GetPagerTableColumnsProps {
|
||||
onViewDetails: (pager: Pager) => void
|
||||
t: TFunction
|
||||
}
|
||||
|
||||
const getStatusVariant = (status: string) => {
|
||||
switch (status) {
|
||||
case PagerStatus.Pending:
|
||||
return 'warning'
|
||||
case PagerStatus.Acknowledged:
|
||||
return 'info'
|
||||
case PagerStatus.Rejected:
|
||||
return 'error'
|
||||
case PagerStatus.Completed:
|
||||
return 'success'
|
||||
default:
|
||||
return 'pending'
|
||||
}
|
||||
}
|
||||
|
||||
export const getPagerTableColumns = (props?: GetPagerTableColumnsProps): ColumnType<Pager>[] => {
|
||||
const getStatusLabel = (status: string) => {
|
||||
if (!props?.t) return status
|
||||
switch (status) {
|
||||
case PagerStatus.Pending:
|
||||
return props.t('pager.status.pending')
|
||||
case PagerStatus.Acknowledged:
|
||||
return props.t('pager.status.acknowledged')
|
||||
case PagerStatus.Rejected:
|
||||
return props.t('pager.status.rejected')
|
||||
case PagerStatus.Completed:
|
||||
return props.t('pager.status.completed')
|
||||
default:
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
export const getPagerTableColumns = (): ColumnType<Pager>[] => {
|
||||
return [
|
||||
{
|
||||
key: 'tableNumber',
|
||||
@@ -16,15 +55,21 @@ export const getPagerTableColumns = (): ColumnType<Pager>[] => {
|
||||
key: 'message',
|
||||
title: 'پیام',
|
||||
render: (item: Pager) => {
|
||||
return item.message || '-'
|
||||
const message = item.message || '-'
|
||||
const truncatedMessage = message.length > 50 ? `${message.substring(0, 50)}...` : message
|
||||
return (
|
||||
<div className="max-w-xs truncate" title={message}>
|
||||
{truncatedMessage}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
title: 'وضعیت',
|
||||
render: (item: Pager) => {
|
||||
const statusLabel = item.status === 'active' ? 'فعال' : 'غیرفعال'
|
||||
const statusVariant = item.status === 'active' ? 'success' : 'error'
|
||||
const statusLabel = getStatusLabel(item.status)
|
||||
const statusVariant = getStatusVariant(item.status)
|
||||
return (
|
||||
<Status variant={statusVariant} label={statusLabel} />
|
||||
)
|
||||
@@ -56,5 +101,19 @@ export const getPagerTableColumns = (): ColumnType<Pager>[] => {
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'details',
|
||||
title: '',
|
||||
render: (item: Pager) => {
|
||||
return (
|
||||
<button
|
||||
onClick={() => props?.onViewDetails(item)}
|
||||
className="cursor-pointer hover:opacity-70 transition-opacity"
|
||||
>
|
||||
<Eye size={20} color="#8C90A3" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export const enum PagerStatus {
|
||||
Pending = "pending",
|
||||
Acknowledged = "acknowledged",
|
||||
Rejected = "rejected",
|
||||
Completed = "completed",
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
# 🚀 useSocket Hook - راهنمای استفاده
|
||||
|
||||
یک Hook قدرتمند و ساده برای مدیریت Socket.IO در React
|
||||
|
||||
## ✨ ویژگیها
|
||||
|
||||
- ✅ دریافت URL و Token مستقیم از کاربر
|
||||
- ✅ اتصال خودکار
|
||||
- ✅ Reconnection هوشمند
|
||||
- ✅ مدیریت Event Listeners
|
||||
- ✅ TypeScript Support
|
||||
- ✅ مدیریت خطاها
|
||||
- ✅ Clean up خودکار
|
||||
|
||||
## 📦 نصب
|
||||
|
||||
```bash
|
||||
npm install socket.io-client
|
||||
```
|
||||
|
||||
## 📖 استفاده
|
||||
|
||||
### سادهترین روش (بدون token)
|
||||
|
||||
```tsx
|
||||
import { useSocket } from '@/pages/pager/hooks/useSocket';
|
||||
|
||||
function MyComponent() {
|
||||
const { isConnected, emit, on } = useSocket('http://localhost:3000');
|
||||
|
||||
useEffect(() => {
|
||||
on('message', (data) => {
|
||||
console.log('پیام:', data);
|
||||
});
|
||||
}, [on]);
|
||||
|
||||
return <div>{isConnected ? 'متصل' : 'قطع'}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### با Token
|
||||
|
||||
```tsx
|
||||
function MyComponent() {
|
||||
const token = localStorage.getItem('token') || '';
|
||||
const { isConnected, emit, on } = useSocket('http://localhost:3000', token);
|
||||
|
||||
return <div>{isConnected ? 'متصل' : 'قطع'}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### با تنظیمات کامل
|
||||
|
||||
```tsx
|
||||
function MyComponent() {
|
||||
const url = 'http://localhost:3000';
|
||||
const token = 'your-jwt-token';
|
||||
|
||||
const { isConnected, emit, on, error } = useSocket(url, token, {
|
||||
reconnectionAttempts: 10,
|
||||
reconnectionDelay: 2000,
|
||||
query: {
|
||||
userId: '123',
|
||||
room: 'general',
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{error && <p>خطا: {error.message}</p>}
|
||||
<p>{isConnected ? 'متصل' : 'قطع'}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 API
|
||||
|
||||
### پارامترها
|
||||
|
||||
```tsx
|
||||
useSocket(url: string, token?: string, options?: UseSocketOptions)
|
||||
```
|
||||
|
||||
| نام | نوع | الزامی | توضیح |
|
||||
|-----|-----|--------|-------|
|
||||
| `url` | string | ✅ | آدرس سرور سوکت |
|
||||
| `token` | string | ❌ | توکن احراز هویت |
|
||||
| `options` | object | ❌ | تنظیمات اضافی |
|
||||
|
||||
### Options
|
||||
|
||||
| نام | نوع | پیشفرض | توضیح |
|
||||
|-----|-----|---------|-------|
|
||||
| `reconnection` | boolean | true | تلاش مجدد برای اتصال |
|
||||
| `reconnectionAttempts` | number | 5 | تعداد تلاش مجدد |
|
||||
| `reconnectionDelay` | number | 1000 | تاخیر بین تلاشها (ms) |
|
||||
| `query` | object | - | پارامترهای query string |
|
||||
|
||||
### Return Values
|
||||
|
||||
| نام | نوع | توضیح |
|
||||
|-----|-----|-------|
|
||||
| `socket` | Socket \| null | instance سوکت |
|
||||
| `isConnected` | boolean | وضعیت اتصال |
|
||||
| `error` | Error \| null | خطای اتصال |
|
||||
| `emit` | function | ارسال event |
|
||||
| `on` | function | گوش دادن به event |
|
||||
| `off` | function | حذف listener |
|
||||
| `connect` | function | اتصال مجدد |
|
||||
| `disconnect` | function | قطع اتصال |
|
||||
|
||||
## 💡 مثالهای کاربردی
|
||||
|
||||
### 1️⃣ صفحه چت
|
||||
|
||||
```tsx
|
||||
function ChatPage() {
|
||||
const [messages, setMessages] = useState([]);
|
||||
const token = localStorage.getItem('token') || '';
|
||||
|
||||
const { isConnected, emit, on } = useSocket(
|
||||
'http://localhost:3000',
|
||||
token
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
on('chat:message', (message) => {
|
||||
setMessages(prev => [...prev, message]);
|
||||
});
|
||||
}, [on]);
|
||||
|
||||
const sendMessage = (text: string) => {
|
||||
emit('chat:send', { text, timestamp: Date.now() });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{messages.map((msg, i) => (
|
||||
<div key={i}>{msg.text}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 2️⃣ دریافت اعلانها
|
||||
|
||||
```tsx
|
||||
function Notifications() {
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const token = localStorage.getItem('authToken') || '';
|
||||
|
||||
const { on } = useSocket('http://localhost:4000', token);
|
||||
|
||||
useEffect(() => {
|
||||
on('notification:new', (notif) => {
|
||||
setNotifications(prev => [notif, ...prev]);
|
||||
});
|
||||
}, [on]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{notifications.map((n, i) => (
|
||||
<div key={i}>{n.message}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 3️⃣ صفحه Pager
|
||||
|
||||
```tsx
|
||||
function PagerList() {
|
||||
const socketUrl = import.meta.env.VITE_SOCKET_URL;
|
||||
const token = localStorage.getItem('token') || '';
|
||||
|
||||
const { isConnected, emit, on } = useSocket(socketUrl, token);
|
||||
|
||||
useEffect(() => {
|
||||
on('page:new', (page) => {
|
||||
console.log('پیج جدید:', page);
|
||||
});
|
||||
|
||||
on('page:status', (status) => {
|
||||
console.log('وضعیت:', status);
|
||||
});
|
||||
}, [on]);
|
||||
|
||||
const callPage = (number: string) => {
|
||||
emit('page:call', { number });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={isConnected ? 'text-green-500' : 'text-red-500'}>
|
||||
{isConnected ? 'آنلاین' : 'آفلاین'}
|
||||
</div>
|
||||
<button onClick={() => callPage('101')}>
|
||||
فراخوانی
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 4️⃣ استفاده با useAuth
|
||||
|
||||
```tsx
|
||||
function MyComponent() {
|
||||
const { token } = useAuth(); // hook شما برای احراز هویت
|
||||
const { isConnected, emit } = useSocket('http://localhost:3000', token);
|
||||
|
||||
return <div>{isConnected ? 'متصل' : 'قطع'}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### 5️⃣ Custom Hook برای استفاده راحتتر
|
||||
|
||||
```tsx
|
||||
// hooks/useAppSocket.ts
|
||||
export const useAppSocket = () => {
|
||||
const token = localStorage.getItem('token') || '';
|
||||
const url = import.meta.env.VITE_SOCKET_URL || 'http://localhost:3000';
|
||||
|
||||
return useSocket(url, token, {
|
||||
reconnectionAttempts: 3,
|
||||
reconnectionDelay: 1500,
|
||||
});
|
||||
};
|
||||
|
||||
// استفاده در کامپوننت
|
||||
function MyComponent() {
|
||||
const { isConnected, emit, on } = useAppSocket();
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 نمایش وضعیت اتصال
|
||||
|
||||
```tsx
|
||||
function ConnectionStatus() {
|
||||
const { isConnected, error } = useSocket('http://localhost:3000');
|
||||
|
||||
return (
|
||||
<div className={`
|
||||
px-3 py-1 rounded-full text-sm font-medium
|
||||
${isConnected
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-red-100 text-red-800'}
|
||||
`}>
|
||||
{isConnected ? '🟢 متصل' : '🔴 قطع'}
|
||||
{error && ` - خطا: ${error.message}`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 🔥 نکات مهم
|
||||
|
||||
1. **Token Security**: توکن رو توی localStorage نگه دار
|
||||
2. **URL از .env**: آدرس سوکت رو توی `.env` بذار
|
||||
3. **Clean Up خودکار**: نگران پاک کردن listeners نباش
|
||||
4. **یک instance در هر کامپوننت**: هر کامپوننت instance جدید میسازه
|
||||
|
||||
## 🐛 رفع مشکلات
|
||||
|
||||
### خطای اتصال
|
||||
|
||||
```tsx
|
||||
const { error, isConnected } = useSocket(url, token);
|
||||
|
||||
if (error) {
|
||||
console.error('خطا:', error.message);
|
||||
// نمایش پیام به کاربر
|
||||
}
|
||||
```
|
||||
|
||||
### قطع و وصل دستی
|
||||
|
||||
```tsx
|
||||
const { connect, disconnect, isConnected } = useSocket(url, token);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isConnected ? (
|
||||
<button onClick={disconnect}>قطع</button>
|
||||
) : (
|
||||
<button onClick={connect}>اتصال</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
### چند event یکجا
|
||||
|
||||
```tsx
|
||||
useEffect(() => {
|
||||
on('event1', handler1);
|
||||
on('event2', handler2);
|
||||
on('event3', handler3);
|
||||
}, [on]);
|
||||
```
|
||||
|
||||
## ⚙️ تنظیمات محیط (.env)
|
||||
|
||||
```env
|
||||
VITE_SOCKET_URL=http://localhost:3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**ساخته شده با ❤️ برای Danak**
|
||||
|
||||
موفق باشی! 🚀
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as api from "../service/PagerService";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { PagerStatus } from "../enum/Enum";
|
||||
|
||||
export const useGetPagers = () => {
|
||||
return useQuery({
|
||||
queryKey: ["pagers"],
|
||||
queryFn: api.getPagers,
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdatePagerStatus = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, status }: { id: string; status: PagerStatus }) =>
|
||||
api.updatePagerStatus(id, status),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["pagers"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import axios from "@/config/axios";
|
||||
import type { GetPagersResponse } from "../types/Types";
|
||||
import type { PagerStatus } from "../enum/Enum";
|
||||
|
||||
export const getPagers = async (): Promise<GetPagersResponse> => {
|
||||
const { data } = await axios.get<GetPagersResponse>("/admin/pager");
|
||||
return data;
|
||||
};
|
||||
|
||||
export const updatePagerStatus = async (id: string, status: PagerStatus) => {
|
||||
const { data } = await axios.patch(`/admin/pager/${id}/status`, { status });
|
||||
return data;
|
||||
};
|
||||
@@ -1,15 +1,17 @@
|
||||
import type { IResponse } from "@/types/response.types";
|
||||
|
||||
export type Pager = {
|
||||
id: string;
|
||||
tableNumber: string;
|
||||
message: string;
|
||||
status: string;
|
||||
cookieId: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
cookieId: string;
|
||||
restaurant: string;
|
||||
tableNumber: string;
|
||||
user: string | null;
|
||||
staff: string | null;
|
||||
message: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type Pagers = {
|
||||
pagers: Pager[];
|
||||
};
|
||||
|
||||
export type PagersType = Pagers;
|
||||
export type GetPagersResponse = IResponse<Pager[]>;
|
||||
|
||||
Reference in New Issue
Block a user