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