add back and refresh button
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-02 23:52:16 +03:30
parent 505ff2f7b5
commit 54e2cb6b28
15 changed files with 165 additions and 52 deletions
+20
View File
@@ -0,0 +1,20 @@
import { ArrowRight2 } from 'iconsax-react'
import { type FC } from 'react'
import { Link } from 'react-router-dom'
type Props = {
to: string
label?: string
}
const BackButton: FC<Props> = ({ to, label = 'بازگشت' }) => (
<Link
to={to}
className='flex items-center gap-1 text-sm text-[#8C90A3] hover:text-black shrink-0'
>
<ArrowRight2 size={18} color='currentColor' />
{label}
</Link>
)
export default BackButton
+31
View File
@@ -0,0 +1,31 @@
import { Refresh2 } from 'iconsax-react'
import { type FC } from 'react'
import MoonLoader from 'react-spinners/MoonLoader'
type Props = {
onClick: () => void
isLoading?: boolean
title?: string
}
const RefreshButton: FC<Props> = ({
onClick,
isLoading = false,
title = 'بروزرسانی',
}) => (
<button
type='button'
onClick={onClick}
disabled={isLoading}
title={title}
className='flex items-center justify-center size-10 rounded-xl border border-border bg-white hover:bg-[#F5F7FC] disabled:opacity-60 transition-colors'
>
{isLoading ? (
<MoonLoader size={18} color='#8C90A3' />
) : (
<Refresh2 size={20} color='#8C90A3' />
)}
</button>
)
export default RefreshButton
+6 -1
View File
@@ -4,6 +4,7 @@ import { Link } from 'react-router-dom'
import { useDeleteAnnoncement, useGetAnnoncements } from './hooks/useAnnoncementData' import { useDeleteAnnoncement, useGetAnnoncements } from './hooks/useAnnoncementData'
import type { AnnoncementItemType } from './types/AnnoncementTypes' import type { AnnoncementItemType } from './types/AnnoncementTypes'
import Button from '../../components/Button' import Button from '../../components/Button'
import RefreshButton from '@/components/RefreshButton'
import { Add, Edit } from 'iconsax-react' import { Add, Edit } from 'iconsax-react'
import Table from '../../components/Table' import Table from '../../components/Table'
import moment from 'moment-jalaali' import moment from 'moment-jalaali'
@@ -100,7 +101,11 @@ const AnnouncementList: FC = () => {
<div> <div>
{t('announcement.announcements')} {t('announcement.announcements')}
</div> </div>
<div> <div className='flex items-center gap-3'>
<RefreshButton
onClick={() => getAnnoncements.refetch()}
isLoading={getAnnoncements.isFetching}
/>
<Link to={Paths.announcement.create}> <Link to={Paths.announcement.create}>
<Button className='px-5'> <Button className='px-5'>
<div className='flex gap-2'> <div className='flex gap-2'>
+2 -3
View File
@@ -1,5 +1,6 @@
import { type FC, useEffect } from 'react' import { type FC, useEffect } from 'react'
import Button from '../../components/Button' import Button from '../../components/Button'
import BackButton from '@/components/BackButton'
import { InfoCircle, TickCircle } from 'iconsax-react' import { InfoCircle, TickCircle } from 'iconsax-react'
import Input from '../../components/Input' import Input from '../../components/Input'
import DatePickerComponent from '../../components/DatePicker' import DatePickerComponent from '../../components/DatePicker'
@@ -73,9 +74,7 @@ const Update: FC = () => {
return ( return (
<div className='w-full mt-4'> <div className='w-full mt-4'>
<div className='flex w-full justify-between items-center'> <div className='flex w-full justify-between items-center'>
<div> <BackButton to={Paths.announcement.list} />
ویرایش اطلاعیه
</div>
<div> <div>
<Button <Button
className='px-5' className='px-5'
+4 -1
View File
@@ -1,6 +1,8 @@
import { type FC, Fragment } from 'react' import { type FC, Fragment } from 'react'
import { useGetDetailCriticisms } from './hooks/useCriticismsData' import { useGetDetailCriticisms } from './hooks/useCriticismsData'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import BackButton from '@/components/BackButton'
import { Paths } from '@/config/Paths'
// import { useTranslation } from 'react-i18next' // import { useTranslation } from 'react-i18next'
// import PageLoading from '../../components/PageLoading' // import PageLoading from '../../components/PageLoading'
import { Paperclip2 } from 'iconsax-react' import { Paperclip2 } from 'iconsax-react'
@@ -21,6 +23,7 @@ const CriticismsDetail: FC = () => {
return ( return (
<div className='mt-4'> <div className='mt-4'>
<BackButton to={Paths.criticisms.list} />
{ {
getDetail.isPending ? getDetail.isPending ?
<div className="flex justify-center items-center py-8"> <div className="flex justify-center items-center py-8">
@@ -28,7 +31,7 @@ const CriticismsDetail: FC = () => {
</div> </div>
: :
<Fragment> <Fragment>
<div> <div className='mt-4'>
{t('criticisms.criticism')} {t('criticisms.criticism')}
{/* {' #' + getDetail.data?.data?.criticism?.numericId} */} {/* {' #' + getDetail.data?.data?.criticism?.numericId} */}
</div> </div>
+9 -2
View File
@@ -3,6 +3,7 @@ import { useDeleteCriticismsDetail, useGetCriticismsList } from './hooks/useCrit
// import { useTranslation } from 'react-i18next' // import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Eye } from 'iconsax-react' import { Eye } from 'iconsax-react'
import RefreshButton from '@/components/RefreshButton'
import { Paths } from '../../config/Paths' import { Paths } from '../../config/Paths'
import type { CriticismsItemTypes } from './types/CriticismsTypes' import type { CriticismsItemTypes } from './types/CriticismsTypes'
import moment from 'moment-jalaali' import moment from 'moment-jalaali'
@@ -81,8 +82,14 @@ const CriticismsList: FC = () => {
return ( return (
<div className='mt-4'> <div className='mt-4'>
<div> <div className='flex justify-between items-center'>
{t('criticisms.criticisms_list')} <div>
{t('criticisms.criticisms_list')}
</div>
<RefreshButton
onClick={() => getCriticismsList.refetch()}
isLoading={getCriticismsList.isFetching}
/>
</div> </div>
<Table <Table
+20 -13
View File
@@ -3,6 +3,8 @@ import { Link, useParams } from 'react-router-dom';
import moment from 'moment-jalaali'; import moment from 'moment-jalaali';
import { Edit } from 'iconsax-react'; import { Edit } from 'iconsax-react';
import { Paths } from '@/config/Paths'; import { Paths } from '@/config/Paths';
import BackButton from '@/components/BackButton';
import RefreshButton from '@/components/RefreshButton';
import { useGetInvoiceDetail } from './hooks/useInvoiceData'; import { useGetInvoiceDetail } from './hooks/useInvoiceData';
import InvoicePaymentsSection from './components/InvoicePaymentsSection'; import InvoicePaymentsSection from './components/InvoicePaymentsSection';
import { formatItemDiscountDisplay } from './utils/invoiceItem'; import { formatItemDiscountDisplay } from './utils/invoiceItem';
@@ -12,7 +14,7 @@ const formatAmount = (amount: number) =>
const DetailPerfomaInvoice: FC = () => { const DetailPerfomaInvoice: FC = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const { data: invoiceData, isLoading } = useGetInvoiceDetail(id); const { data: invoiceData, isLoading, refetch, isFetching } = useGetInvoiceDetail(id);
const invoice = invoiceData?.data; const invoice = invoiceData?.data;
if (isLoading || !invoice) { if (isLoading || !invoice) {
@@ -31,20 +33,25 @@ const DetailPerfomaInvoice: FC = () => {
return ( return (
<div className="mt-5"> <div className="mt-5">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h1 className="text-lg font-light"> <BackButton to={Paths.perfomaInvoice.list} />
پیش فاکتور #{invoice.invoiceNumber} <div className="flex items-center gap-3">
</h1> <RefreshButton onClick={() => refetch()} isLoading={isFetching} />
<Link to={Paths.perfomaInvoice.update + invoice.id}> <Link to={Paths.perfomaInvoice.update + invoice.id}>
<button <button
type="button" type="button"
className="flex items-center gap-2 rounded-xl bg-primary px-5 py-2.5 text-sm hover:opacity-90" className="flex items-center gap-2 rounded-xl bg-primary px-5 py-2.5 text-sm hover:opacity-90"
> >
<Edit size={18} color="black" /> <Edit size={18} color="black" />
ویرایش ویرایش
</button> </button>
</Link> </Link>
</div>
</div> </div>
<h1 className="text-lg font-light mt-4">
پیش فاکتور #{invoice.invoiceNumber}
</h1>
<div className="mt-8 rounded-3xl bg-white p-6"> <div className="mt-8 rounded-3xl bg-white p-6">
<div className="font-light">اطلاعات پیش فاکتور</div> <div className="font-light">اطلاعات پیش فاکتور</div>
+11 -7
View File
@@ -2,6 +2,7 @@ import Tabs from '@/components/Tabs'
import { useMemo, useState, type FC } from 'react' import { useMemo, useState, type FC } from 'react'
import { ProformaInvoiceStatusEnum } from './enum/InvoiceEnum' import { ProformaInvoiceStatusEnum } from './enum/InvoiceEnum'
import Filters from '@/components/Filters' import Filters from '@/components/Filters'
import RefreshButton from '@/components/RefreshButton'
import Table from '@/components/Table' import Table from '@/components/Table'
import { Eye, Add, Edit2 } from 'iconsax-react' import { Eye, Add, Edit2 } from 'iconsax-react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
@@ -16,7 +17,7 @@ const ProformaInvoice: FC = () => {
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const [filters, setFilters] = useState<FilterValues>({}) const [filters, setFilters] = useState<FilterValues>({})
const { data, isLoading } = useGetInvoice(page) const { data, isLoading, refetch, isFetching } = useGetInvoice(page)
const meta = data?.meta const meta = data?.meta
@@ -86,12 +87,15 @@ const ProformaInvoice: FC = () => {
<div className='mt-5'> <div className='mt-5'>
<div className='flex justify-between items-center'> <div className='flex justify-between items-center'>
<h1 className='text-lg font-light'>پیش فاکتورها</h1> <h1 className='text-lg font-light'>پیش فاکتورها</h1>
<Link to={Paths.perfomaInvoice.create}> <div className='flex items-center gap-3'>
<button className='flex items-center gap-2 px-4 py-2 bg-primary text-black text-sm font-medium rounded-xl hover:opacity-90 transition-opacity'> <RefreshButton onClick={() => refetch()} isLoading={isFetching} />
<Add size={18} /> <Link to={Paths.perfomaInvoice.create}>
پیش فاکتور جدید <button className='flex items-center gap-2 px-4 py-2 bg-primary text-black text-sm font-medium rounded-xl hover:opacity-90 transition-opacity'>
</button> <Add size={18} />
</Link> پیش فاکتور جدید
</button>
</Link>
</div>
</div> </div>
<div className='mt-8'> <div className='mt-8'>
+13 -9
View File
@@ -8,6 +8,7 @@ import { Link, useSearchParams } from 'react-router-dom'
import { Paths } from '@/config/Paths' import { Paths } from '@/config/Paths'
import type { OrderListItemType } from './types/Types' import type { OrderListItemType } from './types/Types'
import Button from '@/components/Button' import Button from '@/components/Button'
import RefreshButton from '@/components/RefreshButton'
import UserSearch from '../user/UserSearch' import UserSearch from '../user/UserSearch'
import DesignerSearch from '../designer/DesignerSearch' import DesignerSearch from '../designer/DesignerSearch'
import CategoriesSelect from '../product/components/CategoriesSelect' import CategoriesSelect from '../product/components/CategoriesSelect'
@@ -50,20 +51,23 @@ const OrdersList: FC = () => {
}, { replace: true }) }, { replace: true })
} }
const { data } = useGetOrders({ designId, categoryId, userId, ...filters }) const { data, refetch, isFetching } = useGetOrders({ designId, categoryId, userId, ...filters })
return ( return (
<div className='mt-5'> <div className='mt-5'>
<div className='flex justify-between items-center'> <div className='flex justify-between items-center'>
<h1 className='text-lg font-light'>سفارش ها</h1> <h1 className='text-lg font-light'>سفارش ها</h1>
<Link to={Paths.convertToOrder}> <div className='flex items-center gap-3'>
<Button className='w-fit px-6'> <RefreshButton onClick={() => refetch()} isLoading={isFetching} />
<div className='flex gap-2 items-center'> <Link to={Paths.convertToOrder}>
<AddSquare size={18} color='black' /> <Button className='w-fit px-6'>
سفارش جدید <div className='flex gap-2 items-center'>
</div> <AddSquare size={18} color='black' />
</Button> سفارش جدید
</Link> </div>
</Button>
</Link>
</div>
</div> </div>
<div className='mt-8 flex flex-wrap items-end gap-4'> <div className='mt-8 flex flex-wrap items-end gap-4'>
+10 -3
View File
@@ -2,6 +2,9 @@ import { type FC } from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import moment from 'moment-jalaali' import moment from 'moment-jalaali'
import { User } from 'iconsax-react' import { User } from 'iconsax-react'
import BackButton from '@/components/BackButton'
import RefreshButton from '@/components/RefreshButton'
import { Paths } from '@/config/Paths'
import { useGetOrderDetails } from './hooks/useOrderData' import { useGetOrderDetails } from './hooks/useOrderData'
import TicketSection from './components/TicketSection' import TicketSection from './components/TicketSection'
import OrderItem from './components/OrderItem' import OrderItem from './components/OrderItem'
@@ -20,7 +23,7 @@ const InfoRow: FC<{ label: string; value?: string | number | null }> = ({ label,
const OrderDetail: FC = () => { const OrderDetail: FC = () => {
const { id } = useParams() const { id } = useParams()
const { data } = useGetOrderDetails(id!) const { data, refetch, isFetching } = useGetOrderDetails(id!)
const order = data?.data as (OrderDetailDataType & { items?: OrderItemType[] }) | undefined const order = data?.data as (OrderDetailDataType & { items?: OrderItemType[] }) | undefined
const creator = order?.creator const creator = order?.creator
const creatorName = creator ? `${creator.firstName} ${creator.lastName}`.trim() : '' const creatorName = creator ? `${creator.firstName} ${creator.lastName}`.trim() : ''
@@ -28,8 +31,12 @@ const OrderDetail: FC = () => {
return ( return (
<div className="w-full min-h-screen bg-[#eceef6] p-6"> <div className="w-full min-h-screen bg-[#eceef6] p-6">
{/* Header */} {/* Header */}
<header className="mb-6"> <header className="flex items-center justify-between mb-6">
<span className="text-sm text-[#8C90A3]">سفارش #{order?.orderNumber}</span> <BackButton to={Paths.order.list} />
<div className="flex items-center gap-3">
<RefreshButton onClick={() => refetch()} isLoading={isFetching} />
<span className="text-sm text-[#8C90A3]">سفارش #{order?.orderNumber}</span>
</div>
</header> </header>
{/* اطلاعات سفارش */} {/* اطلاعات سفارش */}
+6 -1
View File
@@ -1,4 +1,5 @@
import Button from '@/components/Button' import Button from '@/components/Button'
import RefreshButton from '@/components/RefreshButton'
import UploadBox from '@/components/UploadBox' import UploadBox from '@/components/UploadBox'
import { Microphone, Paperclip2 } from 'iconsax-react' import { Microphone, Paperclip2 } from 'iconsax-react'
import { useState, type FC } from 'react' import { useState, type FC } from 'react'
@@ -20,7 +21,7 @@ const TicketSection: FC = () => {
const [message, setMessage] = useState('') const [message, setMessage] = useState('')
const [files, setFiles] = useState<File[]>([]) const [files, setFiles] = useState<File[]>([])
const addTicket = useAddTicket() const addTicket = useAddTicket()
const { data, refetch } = useGetTickets(id!) const { data, refetch, isFetching } = useGetTickets(id!)
const singleUpload = useSingleUpload() const singleUpload = useSingleUpload()
const multiUpload = useMultiUpload() const multiUpload = useMultiUpload()
@@ -88,6 +89,10 @@ const TicketSection: FC = () => {
return ( return (
<div className="bg-white rounded-2xl p-6"> <div className="bg-white rounded-2xl p-6">
<div className="flex items-center justify-between mb-2">
<h2 className="text-lg font-light">گفتگو</h2>
<RefreshButton onClick={() => refetch()} isLoading={isFetching} />
</div>
{ {
data?.data?.map((item) => { data?.data?.map((item) => {
if (item.user) if (item.user)
+14 -7
View File
@@ -3,29 +3,36 @@ import { useGetRequestDetail } from './hooks/useRequestData'
import { Link, useParams } from 'react-router-dom' import { Link, useParams } from 'react-router-dom'
import RequestItem from './components/RequestItem' import RequestItem from './components/RequestItem'
import { Paths } from '@/config/Paths' import { Paths } from '@/config/Paths'
import BackButton from '@/components/BackButton'
import Button from '@/components/Button' import Button from '@/components/Button'
import RefreshButton from '@/components/RefreshButton'
import { TickSquare } from 'iconsax-react' import { TickSquare } from 'iconsax-react'
import TicketSection from '../order/components/TicketSection' import TicketSection from '../order/components/TicketSection'
const RequestDetail: FC = () => { const RequestDetail: FC = () => {
const { id } = useParams() const { id } = useParams()
const { data } = useGetRequestDetail(id!) const { data, refetch, isFetching } = useGetRequestDetail(id!)
return ( return (
<div className="w-full min-h-screen bg-[#eceef6] p-6"> <div className="w-full min-h-screen bg-[#eceef6] p-6">
{/* Header Section */} {/* Header Section */}
<div className="flex items-start justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<div className="text-sm text-[#8C90A3]"> <BackButton to={Paths.requests.list} />
درخواست #{data?.data?.requestNumber} <div className="flex items-center gap-3">
</div> <RefreshButton onClick={() => refetch()} isLoading={isFetching} />
<Link to={Paths.perfomaInvoice.create + `?requestId=${id}`}> <Link to={Paths.perfomaInvoice.create + `?requestId=${id}`}>
<Button className="w-fit px-5"> <Button className="w-fit px-5">
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<TickSquare size={20} color="black" /> <TickSquare size={20} color="black" />
<div>ثبت پیش فاکتور</div> <div>ثبت پیش فاکتور</div>
</div> </div>
</Button> </Button>
</Link> </Link>
</div>
</div>
<div className="text-sm text-[#8C90A3] mb-6">
درخواست #{data?.data?.requestNumber}
</div> </div>
{/* Request Information Section */} {/* Request Information Section */}
+6 -2
View File
@@ -1,4 +1,5 @@
import Filters from '@/components/Filters' import Filters from '@/components/Filters'
import RefreshButton from '@/components/RefreshButton'
import Table from '@/components/Table' import Table from '@/components/Table'
import TrashWithConfrim from '@/components/TrashWithConfrim' import TrashWithConfrim from '@/components/TrashWithConfrim'
import { extractErrorMessage } from '@/config/func' import { extractErrorMessage } from '@/config/func'
@@ -12,7 +13,7 @@ import { toast } from 'react-toastify'
const RequestList: FC = () => { const RequestList: FC = () => {
const { data } = useGetRequests() const { data, refetch, isFetching } = useGetRequests()
const { mutate: deleteRequest, isPending: isDeleting } = useDeleteRequest() const { mutate: deleteRequest, isPending: isDeleting } = useDeleteRequest()
const handleDelete = (id: string) => { const handleDelete = (id: string) => {
@@ -28,7 +29,10 @@ const RequestList: FC = () => {
return ( return (
<div className='mt-5'> <div className='mt-5'>
<h1 className='text-lg font-light'>درخواست ها</h1> <div className='flex justify-between items-center'>
<h1 className='text-lg font-light'>درخواست ها</h1>
<RefreshButton onClick={() => refetch()} isLoading={isFetching} />
</div>
<div className='mt-8'> <div className='mt-8'>
<Filters <Filters
+8 -3
View File
@@ -1,8 +1,10 @@
import { type FC, useMemo, useState } from "react"; import { type FC, useMemo, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import BackButton from "@/components/BackButton";
import Input from "@/components/Input"; import Input from "@/components/Input";
import { clx } from "@/helpers/utils"; import { clx } from "@/helpers/utils";
import { t } from "@/locale"; import { t } from "@/locale";
import { Paths } from "@/config/Paths";
import { getTicketDisplayName } from "./types/TicketTypes"; import { getTicketDisplayName } from "./types/TicketTypes";
import type { TicketDetailType } from "./types/TicketTypes"; import type { TicketDetailType } from "./types/TicketTypes";
import { useCloseTicket, useCreateOrReplyTicket, useGetTicketById } from "./hooks/useTicketData"; import { useCloseTicket, useCreateOrReplyTicket, useGetTicketById } from "./hooks/useTicketData";
@@ -77,7 +79,8 @@ const TicketDetail: FC = () => {
if (getTicket.isPending) { if (getTicket.isPending) {
return ( return (
<div className="mt-4"> <div className="mt-4">
<div className="flex gap-1">{t("ticket.ticket_number")}</div> <BackButton to={Paths.tickets.list} />
<div className="flex gap-1 mt-4">{t("ticket.ticket_number")}</div>
<div className="mt-6">Loading...</div> <div className="mt-6">Loading...</div>
</div> </div>
); );
@@ -86,7 +89,8 @@ const TicketDetail: FC = () => {
if (!ticket) { if (!ticket) {
return ( return (
<div className="mt-4"> <div className="mt-4">
<div className="flex gap-1">{t("ticket.ticket_number")}</div> <BackButton to={Paths.tickets.list} />
<div className="flex gap-1 mt-4">{t("ticket.ticket_number")}</div>
<div className="mt-6 text-description">{t("ticket.info_ticket")}</div> <div className="mt-6 text-description">{t("ticket.info_ticket")}</div>
</div> </div>
); );
@@ -99,7 +103,8 @@ const TicketDetail: FC = () => {
return ( return (
<div className="mt-4"> <div className="mt-4">
<div className="flex items-center gap-2 flex-wrap"> <BackButton to={Paths.tickets.list} />
<div className="flex items-center gap-2 flex-wrap mt-4">
<span className="text-description">{t("ticket.ticket_number")}:</span> <span className="text-description">{t("ticket.ticket_number")}:</span>
<span className="font-medium">{extendedTicket.numericId ?? ticket.id}</span> <span className="font-medium">{extendedTicket.numericId ?? ticket.id}</span>
<span <span
+5
View File
@@ -1,5 +1,6 @@
import { type FC, useState } from 'react' import { type FC, useState } from 'react'
import { Eye } from 'iconsax-react' import { Eye } from 'iconsax-react'
import RefreshButton from '@/components/RefreshButton'
import Tabs from '../../components/Tabs' import Tabs from '../../components/Tabs'
import Table from '../../components/Table' import Table from '../../components/Table'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
@@ -74,6 +75,10 @@ const TicketList: FC = () => {
<div> <div>
تیکتهای کاربران تیکتهای کاربران
</div> </div>
<RefreshButton
onClick={() => getTickets.refetch()}
isLoading={getTickets.isFetching}
/>
</div> </div>
<div className='mt-14'> <div className='mt-14'>