notification perfer

This commit is contained in:
hamid zarghami
2025-12-22 09:10:09 +03:30
parent bc91153231
commit 4a6fb229d8
7 changed files with 95 additions and 43 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
VITE_TOKEN_NAME = 'dmnu_a_t' VITE_TOKEN_NAME = 'dmnu_a_t'
VITE_REFRESH_TOKEN_NAME = 'dmnu-a-rt' VITE_REFRESH_TOKEN_NAME = 'dmnu-a-rt'
VITE_BASE_URL = 'https://dmenuplus-api.dev.danakcorp.com' # VITE_BASE_URL = 'https://dmenuplus-api.dev.danakcorp.com'
# VITE_BASE_URL = 'http://192.168.99.242:4000' VITE_BASE_URL = 'http://192.168.99.242:4000'
VITE_SOCKET_URL = 'https://dmenuplus-api.dev.danakcorp.com' VITE_SOCKET_URL = 'https://dmenuplus-api.dev.danakcorp.com'
@@ -1,9 +1,8 @@
import { type FC, useState, useEffect } from 'react' import { type FC, useState, useEffect } from 'react'
import DefaulModal from '@/components/DefaulModal' import DefaulModal from '@/components/DefaulModal'
import Select from '@/components/Select'
import Button from '@/components/Button' import Button from '@/components/Button'
import { Checkbox } from '@/components/ui/checkbox'
import { useUpdateNotification } from '../hooks/useNotificationData' import { useUpdateNotification } from '../hooks/useNotificationData'
import { NotificationType } from '../enum/Enum'
import type { Notification } from '../types/Types' import type { Notification } from '../types/Types'
interface Props { interface Props {
@@ -12,29 +11,50 @@ interface Props {
notification: Notification | null notification: Notification | null
} }
type ChannelType = "sms" | "push" | "in-app"
const channelOptions: { value: ChannelType; label: string }[] = [
{ value: "sms", label: 'پیامک' },
{ value: "push", label: 'پوش' },
{ value: "in-app", label: 'درون برنامه' },
]
const getTitleLabel = (title: string): string => {
const titleMap: Record<string, string> = {
'order.created': 'سفارش ایجاد شد',
'order.status.changed': 'تغییر وضعیت سفارش',
'pager.created': 'پیجر ایجاد شد',
'payment.success': 'پرداخت موفق',
'review.created': 'نظر ایجاد شد',
}
return titleMap[title] || title
}
const EditNotificationTypeModal: FC<Props> = ({ open, close, notification }) => { const EditNotificationTypeModal: FC<Props> = ({ open, close, notification }) => {
const [selectedType, setSelectedType] = useState<NotificationType>(NotificationType.NONE) const [selectedChannels, setSelectedChannels] = useState<ChannelType[]>([])
const { mutate: updateNotification, isPending } = useUpdateNotification() const { mutate: updateNotification, isPending } = useUpdateNotification()
useEffect(() => { useEffect(() => {
if (notification) { if (notification) {
setSelectedType(notification.notificationType as NotificationType) setSelectedChannels([...notification.channels])
} }
}, [notification]) }, [notification])
if (!notification) return null if (!notification) return null
const notificationTypeOptions = [ const handleToggleChannel = (channel: ChannelType, checked: boolean) => {
{ value: NotificationType.SMS, label: 'پیامک' }, if (checked) {
{ value: NotificationType.PUSH, label: 'پوش' }, setSelectedChannels((prev) => [...prev, channel])
{ value: NotificationType.Both, label: 'هر دو' }, } else {
] setSelectedChannels((prev) => prev.filter((c) => c !== channel))
}
}
const handleSubmit = () => { const handleSubmit = () => {
updateNotification( updateNotification(
{ {
id: notification.id, id: notification.id,
notificationType: selectedType, channels: selectedChannels,
}, },
{ {
onSuccess: () => { onSuccess: () => {
@@ -58,17 +78,29 @@ const EditNotificationTypeModal: FC<Props> = ({ open, close, notification }) =>
عنوان عنوان
</label> </label>
<p className="text-sm font-medium"> <p className="text-sm font-medium">
{notification.title || '-'} {notification.title ? getTitleLabel(notification.title) : '-'}
</p> </p>
</div> </div>
<div> <div>
<Select <label className="text-sm mb-3 block">
label="نوع اعلان" کانالهای اعلان
items={notificationTypeOptions} </label>
value={selectedType} <div className="space-y-3">
onChange={(e) => setSelectedType(e.target.value as NotificationType)} {channelOptions.map((option) => (
<div key={option.value} className="flex items-center gap-2">
<Checkbox
checked={selectedChannels.includes(option.value)}
onCheckedChange={(checked) =>
handleToggleChannel(option.value, checked as boolean)
}
/> />
<label className="text-sm cursor-pointer">
{option.label}
</label>
</div>
))}
</div>
</div> </div>
<div className="flex justify-end gap-3 mt-6 border-t border-gray-200 pt-4"> <div className="flex justify-end gap-3 mt-6 border-t border-gray-200 pt-4">
@@ -4,13 +4,33 @@ import type { Notification } from '../types/Types'
import { formatOptionalDate } from '@/helpers/func' import { formatOptionalDate } from '@/helpers/func'
import Status from '@/components/Status' import Status from '@/components/Status'
const getNotificationTypeVariant = (type: string): 'success' | 'error' | 'warning' | 'info' | 'pending' => { const getChannelVariant = (channel: string): 'success' | 'error' | 'warning' | 'info' | 'pending' => {
const variantMap: Record<string, 'success' | 'error' | 'warning' | 'info' | 'pending'> = { const variantMap: Record<string, 'success' | 'error' | 'warning' | 'info' | 'pending'> = {
'sms': 'info', 'sms': 'info',
'push': 'warning', 'push': 'warning',
'both': 'success', 'in-app': 'success',
} }
return variantMap[type] || 'info' return variantMap[channel] || 'info'
}
const getChannelLabel = (channel: string): string => {
const labels: Record<string, string> = {
'sms': 'پیامک',
'push': 'پوش',
'in-app': 'درون برنامه',
}
return labels[channel] || channel
}
const getTitleLabel = (title: string): string => {
const titleMap: Record<string, string> = {
'order.created': 'سفارش ایجاد شد',
'order.status.changed': 'تغییر وضعیت سفارش',
'pager.created': 'پیجر ایجاد شد',
'payment.success': 'پرداخت موفق',
'review.created': 'نظر ایجاد شد',
}
return titleMap[title] || title
} }
type Props = { type Props = {
@@ -25,25 +45,28 @@ export const getNotificationTableColumns = ({ onEdit }: Props): ColumnType<Notif
render: (item: Notification) => { render: (item: Notification) => {
return ( return (
<div className="max-w-xs truncate" title={item.title}> <div className="max-w-xs truncate" title={item.title}>
{item.title || '-'} {item.title ? getTitleLabel(item.title) : '-'}
</div> </div>
) )
} }
}, },
{ {
key: 'notificationType', key: 'channels',
title: 'نوع اعلان', title: 'کانال‌های اعلان',
render: (item: Notification) => { render: (item: Notification) => {
const typeLabels: Record<string, string> = { if (!item.channels || item.channels.length === 0) {
'sms': 'پیامک', return <span className="text-gray-400">-</span>
'push': 'پوش',
'both': 'هر دو',
} }
return ( return (
<div className="flex flex-wrap gap-2">
{item.channels.map((channel, index) => (
<Status <Status
variant={getNotificationTypeVariant(item.notificationType)} key={index}
label={typeLabels[item.notificationType] || item.notificationType} variant={getChannelVariant(channel)}
label={getChannelLabel(channel)}
/> />
))}
</div>
) )
} }
}, },
+1 -2
View File
@@ -1,6 +1,5 @@
export const enum NotificationType { export const enum NotificationType {
NONE = "none", IN_APP = "in-app",
SMS = "sms", SMS = "sms",
PUSH = "push", PUSH = "push",
Both = "both",
} }
@@ -1,4 +1,3 @@
import type { NotificationType } from "../enum/Enum";
import * as api from "../service/NotificationService"; import * as api from "../service/NotificationService";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -16,11 +15,11 @@ export const useUpdateNotification = () => {
return useMutation({ return useMutation({
mutationFn: ({ mutationFn: ({
id, id,
notificationType, channels,
}: { }: {
id: string; id: string;
notificationType: NotificationType; channels: ("sms" | "push" | "in-app")[];
}) => api.updateNotification(id, notificationType), }) => api.updateNotification(id, channels),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["notifications"] }); queryClient.invalidateQueries({ queryKey: ["notifications"] });
}, },
@@ -1,6 +1,5 @@
import axios from "@/config/axios"; import axios from "@/config/axios";
import type { GetNotificationsResponse } from "../types/Types"; import type { GetNotificationsResponse } from "../types/Types";
import type { NotificationType } from "../enum/Enum";
export const getNotifications = async ( export const getNotifications = async (
params?: Record<string, string | number> params?: Record<string, string | number>
@@ -14,10 +13,10 @@ export const getNotifications = async (
export const updateNotification = async ( export const updateNotification = async (
id: string, id: string,
notificationType: NotificationType channels: ("sms" | "push" | "in-app")[]
) => { ) => {
const { data } = await axios.patch(`/admin/notification-preferences/${id}`, { const { data } = await axios.patch(`/admin/notification-preferences/${id}`, {
notificationType, channels,
}); });
return data; return data;
}; };
+1 -1
View File
@@ -8,7 +8,7 @@ export interface Notification extends RowDataType {
deletedAt: string | null; deletedAt: string | null;
restaurant: string; restaurant: string;
title: string; title: string;
notificationType: "sms" | "push" | "both"; channels: ("sms" | "push" | "in-app")[];
} }
export type GetNotificationsResponse = IResponse<Notification[]>; export type GetNotificationsResponse = IResponse<Notification[]>;