This commit is contained in:
@@ -69,7 +69,7 @@ const LoginStep1: FC = () => {
|
||||
label={t('auth.mobile_or_email')}
|
||||
placeholder={t('auth.enter_mobile_or_email')}
|
||||
type='text'
|
||||
className='text-right'
|
||||
dir='ltr'
|
||||
name='phone_email'
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.phone_email}
|
||||
|
||||
@@ -63,6 +63,7 @@ const LoginStep3: FC = () => {
|
||||
type='password'
|
||||
className='text-right'
|
||||
name='password'
|
||||
dir='ltr'
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.password}
|
||||
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Input from '../../components/Input'
|
||||
import Textarea from '../../components/Textarea'
|
||||
import UploadBox from '../../components/UploadBox'
|
||||
import Button from '../../components/Button'
|
||||
import { CloseCircle, InfoCircle, Paperclip2 } from 'iconsax-react'
|
||||
import { ArrowRight, CloseCircle, InfoCircle, Paperclip2 } from 'iconsax-react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useAddMessageTicket, useCloseTicket, useGetMessages } from './hooks/useTicketData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
@@ -89,9 +89,19 @@ const TicketDetail: FC = () => {
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<div className='flex gap-1'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>{getMessages.data?.data?.ticket?.numericId}</div>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div className='flex gap-1'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>{getMessages.data?.data?.ticket?.numericId}</div>
|
||||
</div>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => navigate(-1)}
|
||||
className='flex gap-1 items-center px-3 rounded-lg bg-white h-8 border border-border cursor-pointer'
|
||||
>
|
||||
<ArrowRight size={18} color='black' />
|
||||
<span className='text-xs'>{t('back')}</span>
|
||||
</button>
|
||||
</div>
|
||||
{
|
||||
getMessages.isPending ?
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { FC, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Eye, MessageRemove, MessageTick, MessageTime } from 'iconsax-react'
|
||||
import Tabs from '../../components/Tabs'
|
||||
@@ -11,23 +11,63 @@ import { TicketItemType } from './types/TicketTypes'
|
||||
import OpenTicket from './components/OpenTicket'
|
||||
import Pagination from '../../components/Pagination'
|
||||
|
||||
const VALID_STATUSES = ['ANSWERED', 'PENDING', 'CLOSED'] as const
|
||||
type TicketStatus = (typeof VALID_STATUSES)[number] | ''
|
||||
|
||||
const TicketList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [page, setPage] = useState<number>(1)
|
||||
const [searchParams] = useSearchParams()
|
||||
const [customerId, setCustomerId] = useState<string>('')
|
||||
const [activeTab, setActiveTab] = useState<'ANSWERED' | 'PENDING' | 'CLOSED' | ''>('PENDING')
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
|
||||
const customerId = searchParams.get('user') ?? ''
|
||||
const statusParam = searchParams.get('status')
|
||||
|
||||
const activeTab: TicketStatus = (() => {
|
||||
if (statusParam && (VALID_STATUSES as readonly string[]).includes(statusParam)) {
|
||||
return statusParam as (typeof VALID_STATUSES)[number]
|
||||
}
|
||||
// Customer deep-link without status → show all statuses for that user
|
||||
if (customerId && !statusParam) return ''
|
||||
return 'PENDING'
|
||||
})()
|
||||
|
||||
const page = Math.max(1, parseInt(searchParams.get('page') || '1', 10) || 1)
|
||||
const getTicket = useGetTickets(activeTab, customerId, page)
|
||||
|
||||
// Keep status & page in the URL (replace so back button is not polluted)
|
||||
useEffect(() => {
|
||||
const urlCustomerId = searchParams.get('user')
|
||||
if (urlCustomerId) {
|
||||
setCustomerId(urlCustomerId)
|
||||
setActiveTab('')
|
||||
}
|
||||
}, [searchParams])
|
||||
const next = new URLSearchParams(searchParams)
|
||||
let changed = false
|
||||
|
||||
if (!statusParam && !customerId) {
|
||||
next.set('status', 'PENDING')
|
||||
changed = true
|
||||
}
|
||||
|
||||
if (!searchParams.get('page')) {
|
||||
next.set('page', String(page))
|
||||
changed = true
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
setSearchParams(next, { replace: true })
|
||||
}
|
||||
}, [customerId, page, searchParams, setSearchParams, statusParam])
|
||||
|
||||
const updateParams = (updates: { status?: string; page?: number }) => {
|
||||
const next = new URLSearchParams(searchParams)
|
||||
|
||||
if (updates.status !== undefined) {
|
||||
if (updates.status === '') next.delete('status')
|
||||
else next.set('status', updates.status)
|
||||
}
|
||||
|
||||
if (updates.page !== undefined) {
|
||||
next.set('page', String(Math.max(1, updates.page)))
|
||||
}
|
||||
|
||||
setSearchParams(next)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
@@ -75,7 +115,7 @@ const TicketList: FC = () => {
|
||||
value: 'CLOSED'
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setActiveTab(value as 'ANSWERED' | 'PENDING' | 'CLOSED')}
|
||||
onChange={(value) => updateParams({ status: value, page: 1 })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -135,7 +175,7 @@ const TicketList: FC = () => {
|
||||
|
||||
<Pagination
|
||||
currentPage={page}
|
||||
onPageChange={setPage}
|
||||
onPageChange={(newPage) => updateParams({ page: newPage })}
|
||||
totalPages={getTicket.data?.data?.pager?.totalPages}
|
||||
/>
|
||||
</div>
|
||||
@@ -143,4 +183,4 @@ const TicketList: FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export default TicketList
|
||||
export default TicketList
|
||||
|
||||
+16
-30
@@ -1,18 +1,4 @@
|
||||
import {
|
||||
Box,
|
||||
Category,
|
||||
DocumentLike,
|
||||
Headphone,
|
||||
Home,
|
||||
Logout,
|
||||
Money3,
|
||||
Profile,
|
||||
Profile2User,
|
||||
Setting2,
|
||||
TickSquare,
|
||||
UserOctagon,
|
||||
UserTick,
|
||||
} from "iconsax-react";
|
||||
import { Box, Category, DocumentLike, Headphone, Home, Logout, Money3, Profile, Profile2User, Setting2, TickSquare, UserOctagon, UserTick } from "iconsax-react";
|
||||
import { FC, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocation } from "react-router-dom";
|
||||
@@ -133,6 +119,21 @@ const SideBar: FC = () => {
|
||||
activeName=""
|
||||
/>
|
||||
|
||||
<SideBarItem
|
||||
icon={
|
||||
<Headphone
|
||||
variant={isGroupActive("support") ? "Bold" : "Outline"}
|
||||
color={isGroupActive("support") ? "black" : "#8C90A3"}
|
||||
size={iconSizeSideBar}
|
||||
/>
|
||||
}
|
||||
title={t("sidebar.support_section")}
|
||||
isActive={isGroupActive("support")}
|
||||
link={Pages.ticket.list}
|
||||
name="support"
|
||||
activeNames={SUPPORT_PERMISSION_RESOURCES}
|
||||
/>
|
||||
|
||||
<SideBarItem
|
||||
icon={
|
||||
<Category
|
||||
@@ -256,21 +257,6 @@ const SideBar: FC = () => {
|
||||
activeNames={FINANCIAL_PERMISSION_RESOURCES}
|
||||
/>
|
||||
|
||||
<SideBarItem
|
||||
icon={
|
||||
<Headphone
|
||||
variant={isGroupActive("support") ? "Bold" : "Outline"}
|
||||
color={isGroupActive("support") ? "black" : "#8C90A3"}
|
||||
size={iconSizeSideBar}
|
||||
/>
|
||||
}
|
||||
title={t("sidebar.support_section")}
|
||||
isActive={isGroupActive("support")}
|
||||
link={Pages.support.list}
|
||||
name="support"
|
||||
activeNames={SUPPORT_PERMISSION_RESOURCES}
|
||||
/>
|
||||
|
||||
{profile?.data?.user?.roles?.some((role: { name: string }) => role.name === "super_admin") && (
|
||||
<SideBarItem
|
||||
icon={
|
||||
|
||||
@@ -23,18 +23,18 @@ const SupportSubMenu: FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col mt-10 gap-4">
|
||||
<SubMenuItem
|
||||
title={t("sidebar.support")}
|
||||
isActive={isActive("/support")}
|
||||
link={Pages.support.list}
|
||||
activeName="support_plan"
|
||||
/>
|
||||
<SubMenuItem
|
||||
title={t("submenu.ticket_list")}
|
||||
isActive={isActive("/tickets/list") || isActive("/tickets/messages") || isActive("/tickets/create")}
|
||||
link={Pages.ticket.list}
|
||||
activeName="tickets"
|
||||
/>
|
||||
<SubMenuItem
|
||||
title={t("sidebar.support")}
|
||||
isActive={isActive("/support")}
|
||||
link={Pages.support.list}
|
||||
activeName="support_plan"
|
||||
/>
|
||||
<SubMenuItem
|
||||
title={t("submenu.ticket_category")}
|
||||
isActive={isActive("/tickets/category")}
|
||||
|
||||
Reference in New Issue
Block a user