notification perfer
This commit is contained in:
@@ -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 (
|
||||||
<Status
|
<div className="flex flex-wrap gap-2">
|
||||||
variant={getNotificationTypeVariant(item.notificationType)}
|
{item.channels.map((channel, index) => (
|
||||||
label={typeLabels[item.notificationType] || item.notificationType}
|
<Status
|
||||||
/>
|
key={index}
|
||||||
|
variant={getChannelVariant(channel)}
|
||||||
|
label={getChannelLabel(channel)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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[]>;
|
||||||
|
|||||||
Reference in New Issue
Block a user