.
This commit is contained in:
parent
033d2363df
commit
1fd2d79bbe
@ -30,9 +30,12 @@ import {
|
|||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@/ui";
|
} from "@/ui";
|
||||||
|
import { useToast } from "@/ui/use-toast";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
|
import { useCallback } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useQuotes } from "../hooks";
|
import { useQuotes } from "../hooks";
|
||||||
|
import { DownloadQuoteDialog } from "./DownloadQuoteDialog";
|
||||||
import { QuoteStatusEditor } from "./editors";
|
import { QuoteStatusEditor } from "./editors";
|
||||||
|
|
||||||
type QuoteResumeProps = {
|
type QuoteResumeProps = {
|
||||||
@ -41,9 +44,37 @@ type QuoteResumeProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => {
|
export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => {
|
||||||
const { useOne, useUpdate } = useQuotes();
|
|
||||||
const { data, status, error: queryError } = useOne(quoteId);
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const { useOne, useSetStatus, useDownloader, getQuotePDFFilename } = useQuotes();
|
||||||
|
const { data, status, error: queryError } = useOne(quoteId);
|
||||||
|
const { mutate: setStatusMutation } = useSetStatus(quoteId);
|
||||||
|
const { download, ...downloadProps } = useDownloader();
|
||||||
|
|
||||||
|
const handleOnChangeStatus = (quoteId: string, newStatus: string) => {
|
||||||
|
setStatusMutation(
|
||||||
|
{ newStatus },
|
||||||
|
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
description: t("quotes.quote_status_editor.toast_status_changed"),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFinishDownload = useCallback(() => {
|
||||||
|
toast({
|
||||||
|
description: t("quotes.downloading_dialog.toast_success"),
|
||||||
|
});
|
||||||
|
}, [toast]);
|
||||||
|
|
||||||
|
const handleDownload = useCallback(() => {
|
||||||
|
if (data) download(data.id, getQuotePDFFilename(data));
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
if (status === "error") {
|
if (status === "error") {
|
||||||
return null;
|
return null;
|
||||||
@ -64,173 +95,197 @@ export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue='resume'>
|
<>
|
||||||
<Card className='w-full overflow-hidden'>
|
<DownloadQuoteDialog {...downloadProps} onFinishDownload={handleFinishDownload} />
|
||||||
<CardHeader className='flex flex-row items-start border-b'>
|
<Tabs defaultValue='resume'>
|
||||||
<div className='grid gap-0.5'>
|
<Card className='w-full overflow-hidden'>
|
||||||
<CardTitle className='flex items-center gap-2 text-lg group'>
|
<CardHeader className='gap-3 border-b'>
|
||||||
|
<CardTitle className='text-lg'>
|
||||||
{`${t("quotes.list.preview.quote")} #${data.reference}`}
|
{`${t("quotes.list.preview.quote")} #${data.reference}`}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>assaasassa</CardDescription>
|
<CardDescription className='flex items-center gap-1 mr-auto text-foreground'>
|
||||||
</div>
|
<Button
|
||||||
<div className='flex items-center gap-1 ml-auto'>
|
size='sm'
|
||||||
<Button
|
variant='outline'
|
||||||
size='sm'
|
className='h-8 gap-1'
|
||||||
variant='outline'
|
onClick={(e) => {
|
||||||
className='h-8 gap-1'
|
e.preventDefault();
|
||||||
onClick={(e) => {
|
navigate(`/quotes/edit/${data.id}`, { relative: "path" });
|
||||||
e.preventDefault();
|
}}
|
||||||
navigate(`/quotes/edit/${data.id}`, { relative: "path" });
|
>
|
||||||
}}
|
<FilePenLineIcon className='h-3.5 w-3.5' />
|
||||||
>
|
<span className='sr-only md:not-sr-only md:whitespace-nowrap'>
|
||||||
<FilePenLineIcon className='h-3.5 w-3.5' />
|
{t("quotes.list.columns.actions.edit")}
|
||||||
<span className='lg:sr-only xl:not-sr-only xl:whitespace-nowrap'>
|
</span>
|
||||||
{t("quotes.list.columns.actions.edit")}
|
</Button>
|
||||||
</span>
|
<QuoteStatusEditor quote={data} onChangeStatus={handleOnChangeStatus} />
|
||||||
</Button>
|
|
||||||
<Button size='sm' variant='outline' className='h-8 gap-1' onClick={() => null}>
|
<Button size='sm' variant='outline' className='h-8 gap-1' onClick={handleDownload}>
|
||||||
<DownloadIcon className='h-3.5 w-3.5' />
|
<DownloadIcon className='h-3.5 w-3.5' />
|
||||||
<span className='xl:sr-only 2xl:not-sr-only 2xl:whitespace-nowrap'>
|
<span className='sr-only lg:not-sr-only lg:whitespace-nowrap'>
|
||||||
{t("quotes.list.preview.download_quote")}
|
{t("quotes.list.preview.download_quote")}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button size='icon' variant='outline' className='w-8 h-8'>
|
<Button size='icon' variant='outline' className='w-8 h-8'>
|
||||||
<MoreVertical className='h-3.5 w-3.5' />
|
<MoreVertical className='h-3.5 w-3.5' />
|
||||||
<span className='sr-only'>More</span>
|
<span className='sr-only'>More</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align='end'>
|
<DropdownMenuContent align='end'>
|
||||||
<DropdownMenuItem>Edit</DropdownMenuItem>
|
<DropdownMenuItem>Edit</DropdownMenuItem>
|
||||||
<DropdownMenuItem>Export</DropdownMenuItem>
|
<DropdownMenuItem>Export</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem>Trash</DropdownMenuItem>
|
<DropdownMenuItem>Trash</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardHeader className='flex flex-row items-start border-b'>
|
<CardContent className='p-6 text-sm'>
|
||||||
<QuoteStatusEditor quote={data} onChangeStatus={() => null} />
|
<TabsList className='grid w-full grid-cols-2'>
|
||||||
</CardHeader>
|
<TabsTrigger value='resume'>Resume</TabsTrigger>
|
||||||
<CardContent className='p-6 text-sm'>
|
<TabsTrigger value='preview'>Preview</TabsTrigger>
|
||||||
<TabsList className='grid w-full grid-cols-2'>
|
</TabsList>
|
||||||
<TabsTrigger value='resume'>Resume</TabsTrigger>
|
<TabsContent value='resume' className='pt-4'>
|
||||||
<TabsTrigger value='preview'>Preview</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
<TabsContent value='resume' className='pt-4'>
|
|
||||||
<div className='grid gap-3'>
|
|
||||||
<div className='grid gap-3'>
|
<div className='grid gap-3'>
|
||||||
<div className='font-semibold'>Customer Information</div>
|
<div className='grid gap-3'>
|
||||||
Date: {new Date(data.date).toLocaleDateString()}
|
<div className='font-semibold'>Quote information</div>
|
||||||
<div>{data.customer_information}</div>
|
|
||||||
|
<dl className='grid gap-3'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<dt className='text-muted-foreground'>
|
||||||
|
{t("quotes.form_fields.date.label")}
|
||||||
|
</dt>
|
||||||
|
<dd>{new Date(data.date).toLocaleDateString()}</dd>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<dt className='text-muted-foreground'>Email</dt>
|
||||||
|
<dd>
|
||||||
|
<a href='mailto:'>liam@acme.com</a>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<dt className='text-muted-foreground'>Phone</dt>
|
||||||
|
<dd>
|
||||||
|
<a href='tel:'>+1 234 567 890</a>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<Separator className='my-4' />
|
||||||
|
<div className='grid gap-3'>
|
||||||
|
<div className='font-semibold'>Customer Information</div>
|
||||||
|
Date: {new Date(data.date).toLocaleDateString()}
|
||||||
|
<div>{data.customer_information}</div>
|
||||||
|
<dl className='grid gap-3'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<dt className='text-muted-foreground'>Customer</dt>
|
||||||
|
<dd>Liam Johnson</dd>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<dt className='text-muted-foreground'>Email</dt>
|
||||||
|
<dd>
|
||||||
|
<a href='mailto:'>liam@acme.com</a>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<dt className='text-muted-foreground'>Phone</dt>
|
||||||
|
<dd>
|
||||||
|
<a href='tel:'>+1 234 567 890</a>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<Separator className='my-4' />
|
||||||
|
<div className='font-semibold'>Order Details</div>
|
||||||
|
<ul className='grid gap-3'>
|
||||||
|
<li className='flex items-center justify-between'>
|
||||||
|
<span className='text-muted-foreground'>
|
||||||
|
Glimmer Lamps x <span>2</span>
|
||||||
|
</span>
|
||||||
|
<span>$250.00</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center justify-between'>
|
||||||
|
<span className='text-muted-foreground'>
|
||||||
|
Aqua Filters x <span>1</span>
|
||||||
|
</span>
|
||||||
|
<span>$49.00</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<Separator className='my-2' />
|
||||||
|
<ul className='grid gap-3'>
|
||||||
|
<li className='flex items-center justify-between'>
|
||||||
|
<span className='text-muted-foreground'>Subtotal</span>
|
||||||
|
<span>$299.00</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center justify-between'>
|
||||||
|
<span className='text-muted-foreground'>Shipping</span>
|
||||||
|
<span>$5.00</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center justify-between'>
|
||||||
|
<span className='text-muted-foreground'>Tax</span>
|
||||||
|
<span>$25.00</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center justify-between font-semibold'>
|
||||||
|
<span className='text-muted-foreground'>Total</span>
|
||||||
|
<span>$329.00</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Separator className='my-4' />
|
||||||
|
<div className='grid grid-cols-2 gap-4'>
|
||||||
|
<div className='grid gap-3'>
|
||||||
|
<div className='font-semibold'>Shipping Information</div>
|
||||||
|
<address className='grid gap-0.5 not-italic text-muted-foreground'>
|
||||||
|
<span>Liam Johnson</span>
|
||||||
|
<span>1234 Main St.</span>
|
||||||
|
<span>Anytown, CA 12345</span>
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
<div className='grid gap-3 auto-rows-max'>
|
||||||
|
<div className='font-semibold'>Billing Information</div>
|
||||||
|
<div className='text-muted-foreground'>Same as shipping address</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Separator className='my-4' />
|
||||||
|
|
||||||
|
<div className='grid gap-3'>
|
||||||
|
<div className='font-semibold'>Payment Information</div>
|
||||||
<dl className='grid gap-3'>
|
<dl className='grid gap-3'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<dt className='text-muted-foreground'>Customer</dt>
|
<dt className='flex items-center gap-1 text-muted-foreground'>
|
||||||
<dd>Liam Johnson</dd>
|
<CreditCard className='w-4 h-4' />
|
||||||
</div>
|
Visa
|
||||||
<div className='flex items-center justify-between'>
|
</dt>
|
||||||
<dt className='text-muted-foreground'>Email</dt>
|
<dd>**** **** **** 4532</dd>
|
||||||
<dd>
|
|
||||||
<a href='mailto:'>liam@acme.com</a>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<dt className='text-muted-foreground'>Phone</dt>
|
|
||||||
<dd>
|
|
||||||
<a href='tel:'>+1 234 567 890</a>
|
|
||||||
</dd>
|
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<Separator className='my-4' />
|
</TabsContent>
|
||||||
<div className='font-semibold'>Order Details</div>
|
<TabsContent value='preview'></TabsContent>
|
||||||
<ul className='grid gap-3'>
|
</CardContent>
|
||||||
<li className='flex items-center justify-between'>
|
<CardFooter className='flex flex-row items-center px-6 py-3 border-t'>
|
||||||
<span className='text-muted-foreground'>
|
<div className='text-xs text-muted-foreground'>Updated...</div>
|
||||||
Glimmer Lamps x <span>2</span>
|
<Pagination className='w-auto ml-auto mr-0'>
|
||||||
</span>
|
<PaginationContent>
|
||||||
<span>$250.00</span>
|
<PaginationItem>
|
||||||
</li>
|
<Button size='icon' variant='outline' className='w-6 h-6'>
|
||||||
<li className='flex items-center justify-between'>
|
<ChevronLeft className='h-3.5 w-3.5' />
|
||||||
<span className='text-muted-foreground'>
|
<span className='sr-only'>Previous Order</span>
|
||||||
Aqua Filters x <span>1</span>
|
</Button>
|
||||||
</span>
|
</PaginationItem>
|
||||||
<span>$49.00</span>
|
<PaginationItem>
|
||||||
</li>
|
<Button size='icon' variant='outline' className='w-6 h-6'>
|
||||||
</ul>
|
<ChevronRight className='h-3.5 w-3.5' />
|
||||||
<Separator className='my-2' />
|
<span className='sr-only'>Next Order</span>
|
||||||
<ul className='grid gap-3'>
|
</Button>
|
||||||
<li className='flex items-center justify-between'>
|
</PaginationItem>
|
||||||
<span className='text-muted-foreground'>Subtotal</span>
|
</PaginationContent>
|
||||||
<span>$299.00</span>
|
</Pagination>
|
||||||
</li>
|
</CardFooter>
|
||||||
<li className='flex items-center justify-between'>
|
</Card>
|
||||||
<span className='text-muted-foreground'>Shipping</span>
|
</Tabs>
|
||||||
<span>$5.00</span>
|
</>
|
||||||
</li>
|
|
||||||
<li className='flex items-center justify-between'>
|
|
||||||
<span className='text-muted-foreground'>Tax</span>
|
|
||||||
<span>$25.00</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center justify-between font-semibold'>
|
|
||||||
<span className='text-muted-foreground'>Total</span>
|
|
||||||
<span>$329.00</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<Separator className='my-4' />
|
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
|
||||||
<div className='grid gap-3'>
|
|
||||||
<div className='font-semibold'>Shipping Information</div>
|
|
||||||
<address className='grid gap-0.5 not-italic text-muted-foreground'>
|
|
||||||
<span>Liam Johnson</span>
|
|
||||||
<span>1234 Main St.</span>
|
|
||||||
<span>Anytown, CA 12345</span>
|
|
||||||
</address>
|
|
||||||
</div>
|
|
||||||
<div className='grid gap-3 auto-rows-max'>
|
|
||||||
<div className='font-semibold'>Billing Information</div>
|
|
||||||
<div className='text-muted-foreground'>Same as shipping address</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Separator className='my-4' />
|
|
||||||
|
|
||||||
<div className='grid gap-3'>
|
|
||||||
<div className='font-semibold'>Payment Information</div>
|
|
||||||
<dl className='grid gap-3'>
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<dt className='flex items-center gap-1 text-muted-foreground'>
|
|
||||||
<CreditCard className='w-4 h-4' />
|
|
||||||
Visa
|
|
||||||
</dt>
|
|
||||||
<dd>**** **** **** 4532</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent value='preview'></TabsContent>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className='flex flex-row items-center px-6 py-3 border-t'>
|
|
||||||
<div className='text-xs text-muted-foreground'>Updated...</div>
|
|
||||||
<Pagination className='w-auto ml-auto mr-0'>
|
|
||||||
<PaginationContent>
|
|
||||||
<PaginationItem>
|
|
||||||
<Button size='icon' variant='outline' className='w-6 h-6'>
|
|
||||||
<ChevronLeft className='h-3.5 w-3.5' />
|
|
||||||
<span className='sr-only'>Previous Order</span>
|
|
||||||
</Button>
|
|
||||||
</PaginationItem>
|
|
||||||
<PaginationItem>
|
|
||||||
<Button size='icon' variant='outline' className='w-6 h-6'>
|
|
||||||
<ChevronRight className='h-3.5 w-3.5' />
|
|
||||||
<span className='sr-only'>Next Order</span>
|
|
||||||
</Button>
|
|
||||||
</PaginationItem>
|
|
||||||
</PaginationContent>
|
|
||||||
</Pagination>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</Tabs>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,50 +1,113 @@
|
|||||||
import { Button } from "@/ui/button";
|
import {
|
||||||
import { Select, SelectContent, SelectTrigger, SelectValue } from "@/ui/select";
|
Button,
|
||||||
import { SelectItem } from "@radix-ui/react-select";
|
Dialog,
|
||||||
import { useState } from "react";
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
Label,
|
||||||
|
Switch,
|
||||||
|
} from "@/ui";
|
||||||
|
import { DialogDescription } from "@radix-ui/react-dialog";
|
||||||
|
import { IGetQuote_Response_DTO } from "@shared/contexts";
|
||||||
|
import { t } from "i18next";
|
||||||
|
import { RefreshCwIcon } from "lucide-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
const QUOTE_STATUS = ["draft", "ready", "delivered", "accepted", "rejected", "archived"];
|
const QuoteStatus = ["draft", "ready", "delivered", "accepted", "rejected", "archived"];
|
||||||
|
|
||||||
const QUOTE_NEXT_STATUS = {
|
type TQuoteStatus = "draft" | "ready" | "delivered" | "accepted" | "rejected" | "archived";
|
||||||
draft: ["ready", "archived"],
|
|
||||||
ready: ["delivered", "archived"],
|
const quoteStatusTransitions: Record<TQuoteStatus, TQuoteStatus[]> = {
|
||||||
delivered: ["accepted", "rejected", "archived"],
|
draft: ["draft", "ready", "archived"],
|
||||||
accepted: ["archived"],
|
ready: ["ready", "delivered", "archived"],
|
||||||
rejected: ["archived"],
|
delivered: ["delivered", "accepted", "rejected", "archived"],
|
||||||
archived: [],
|
accepted: ["accepted", "archived"],
|
||||||
|
rejected: ["rejected", "archived"],
|
||||||
|
archived: ["archived", "draft", "ready", "delivered", "accepted", "rejected"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QuoteStatusEditor = ({ quote, onChangeStatus }) => {
|
export const QuoteStatusEditor = ({
|
||||||
const [status, changeStatus] = useState(quote.status);
|
quote,
|
||||||
|
onChangeStatus,
|
||||||
|
}: {
|
||||||
|
quote: IGetQuote_Response_DTO;
|
||||||
|
onChangeStatus: (quoteId: string, newStatus: string) => void;
|
||||||
|
}) => {
|
||||||
|
const [newStatus, setNewStatus] = useState<string>("");
|
||||||
|
|
||||||
|
const changeStatus = (newStatus: string) => setNewStatus(newStatus);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (quote) {
|
||||||
|
setNewStatus(quote.status);
|
||||||
|
}
|
||||||
|
}, [quote]);
|
||||||
|
|
||||||
const handleChangeStatus = () => {
|
const handleChangeStatus = () => {
|
||||||
if (status !== quote.status) {
|
if (newStatus !== quote.status) {
|
||||||
onChangeStatus(quote.id, status);
|
onChangeStatus(quote.id, newStatus);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(newStatus);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center space-x-2 '>
|
<Dialog>
|
||||||
<Select value={status} onValueChange={changeStatus}>
|
<DialogTrigger asChild>
|
||||||
<SelectTrigger className='w-[180px]'>
|
<Button size='sm' variant='outline' className='h-8 gap-1'>
|
||||||
<SelectValue placeholder='Seleccionar estado' />
|
<RefreshCwIcon className='h-3.5 w-3.5' />
|
||||||
</SelectTrigger>
|
<span className='sr-only md:not-sr-only md:whitespace-nowrap'>Cambiar estado</span>
|
||||||
<SelectContent>
|
</Button>
|
||||||
{QUOTE_NEXT_STATUS[quote.status].map((_status) => (
|
</DialogTrigger>
|
||||||
<SelectItem key={_status} value={_status}>
|
<DialogContent>
|
||||||
{_status}
|
<DialogHeader>
|
||||||
</SelectItem>
|
<DialogTitle>{t("quotes.quote_status_editor.title")}</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogDescription></DialogDescription>
|
||||||
|
|
||||||
|
<div className='grid gap-4 py-4'>
|
||||||
|
{QuoteStatus.map((_status) => (
|
||||||
|
<div key={_status} className='flex items-start space-x-4'>
|
||||||
|
<Switch
|
||||||
|
id={_status}
|
||||||
|
checked={newStatus === _status}
|
||||||
|
onCheckedChange={() => changeStatus(_status)}
|
||||||
|
disabled={
|
||||||
|
!quoteStatusTransitions[quote.status as TQuoteStatus].includes(
|
||||||
|
_status as TQuoteStatus
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='grid gap-1.5 leading-none'>
|
||||||
|
<Label htmlFor={_status} className='font-medium'>
|
||||||
|
{t(`quotes.quote_status_editor.status.${_status}.title`)}
|
||||||
|
</Label>
|
||||||
|
<p className='text-sm text-muted-foreground'>
|
||||||
|
{t(`quotes.quote_status_editor.status.${_status}.description`)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</div>
|
||||||
</Select>
|
|
||||||
<Button
|
<DialogFooter className='pt-6 border-t'>
|
||||||
variant='secondary'
|
<DialogClose asChild>
|
||||||
size='sm'
|
<Button type='button' variant='secondary'>
|
||||||
onClick={handleChangeStatus}
|
{t("common.cancel")}
|
||||||
disabled={status === quote.status}
|
</Button>
|
||||||
>
|
</DialogClose>
|
||||||
Cambiar estado
|
|
||||||
</Button>
|
<DialogClose asChild>
|
||||||
</div>
|
<Button onClick={handleChangeStatus} disabled={newStatus === quote.status}>
|
||||||
|
{t("quotes.quote_status_editor.submit_button")}
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
IGetQuote_Response_DTO,
|
IGetQuote_Response_DTO,
|
||||||
IListQuotes_Response_DTO,
|
IListQuotes_Response_DTO,
|
||||||
IListResponse_DTO,
|
IListResponse_DTO,
|
||||||
|
ISetStatusQuote_Request_DTO,
|
||||||
IUpdateQuote_Request_DTO,
|
IUpdateQuote_Request_DTO,
|
||||||
IUpdateQuote_Response_DTO,
|
IUpdateQuote_Response_DTO,
|
||||||
UniqueID,
|
UniqueID,
|
||||||
@ -150,6 +151,50 @@ export const useQuotes = () => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
useSetStatus: (id: string) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<void, TDataSourceError, ISetStatusQuote_Request_DTO>({
|
||||||
|
mutationKey: keys().data().resource("quotes").action("one").id(id).params().get(),
|
||||||
|
mutationFn: (data) => {
|
||||||
|
const { newStatus } = data;
|
||||||
|
|
||||||
|
return dataSource.custom({
|
||||||
|
url: `${dataSource.getApiUrl()}/quotes/${id}/setStatus`,
|
||||||
|
method: "put",
|
||||||
|
data: {
|
||||||
|
newStatus,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["data", "default", "quotes"],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/*return useMutation<void, TDataSourceError, ISetStatusQuote_Request_DTO>({
|
||||||
|
mutationKey: keys().data().resource("quotes").action("one").id(id).params().get(),
|
||||||
|
mutationFn: (data) => {
|
||||||
|
const { newStatus } = data;
|
||||||
|
|
||||||
|
return dataSource.updateOne({
|
||||||
|
resource: "quotes",
|
||||||
|
id,
|
||||||
|
data: {
|
||||||
|
newStatus,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["data", "default", "quotes"],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});*/
|
||||||
|
},
|
||||||
|
|
||||||
useOne: (id?: string, params?: UseQuotesGetParamsType) =>
|
useOne: (id?: string, params?: UseQuotesGetParamsType) =>
|
||||||
useOne<IGetQuote_Response_DTO>({
|
useOne<IGetQuote_Response_DTO>({
|
||||||
queryKey: keys().data().resource("quotes").action("one").id(id).params().get(),
|
queryKey: keys().data().resource("quotes").action("one").id(id).params().get(),
|
||||||
|
|||||||
@ -50,51 +50,60 @@
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/* https://ui.jln.dev/ unicscode89-stripe */
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 210 40% 96.08%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 334 55% 1%;
|
--foreground: 222.2 84% 4.9%;
|
||||||
--muted: 214.29 31.82% 91.37%;
|
--card: 0 0% 100%;
|
||||||
--muted-foreground: 334 9% 37%;
|
--card-foreground: 222.2 84% 4.9%;
|
||||||
--popover: 334 62% 100%;
|
--popover: 0 0% 100%;
|
||||||
--popover-foreground: 334 55% 1%;
|
--popover-foreground: 222.2 84% 4.9%;
|
||||||
--card: 334 62% 100%;
|
--primary: 221.2 83.2% 53.3%;
|
||||||
--card-foreground: 334 55% 1%;
|
--primary-foreground: 210 40% 98%;
|
||||||
--border: 334 5% 95%;
|
--secondary: 210 40% 96.1%;
|
||||||
--input: 214.29 31.82% 91.37%;
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
--primary: 242.93 100% 67.84%;
|
--muted: 210 40% 96.1%;
|
||||||
--primary-foreground: 0 0% 100%;
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
--secondary: 213.75 20.25% 69.02%;
|
--accent: 210 40% 96.1%;
|
||||||
--secondary-foreground: 334 0% 100%;
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
--accent: 214.29 31.82% 91.37%;
|
--destructive: 0 84.2% 60.2%;
|
||||||
--accent-foreground: 334 20% 22%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
--destructive: 348.37 78.4% 49.02%;
|
--border: 214.3 31.8% 91.4%;
|
||||||
--destructive-foreground: 18 0% 100%;
|
--input: 214.3 31.8% 91.4%;
|
||||||
--ring: 228.33 94.74% 62.75%;
|
--ring: 221.2 83.2% 53.3%;
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
|
--chart-1: 12 76% 61%;
|
||||||
|
--chart-2: 173 58% 39%;
|
||||||
|
--chart-3: 197 37% 24%;
|
||||||
|
--chart-4: 43 74% 66%;
|
||||||
|
--chart-5: 27 87% 67%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 222.22 47.37% 11.18%;
|
--background: 222.2 84% 4.9%;
|
||||||
--foreground: 334 34% 98%;
|
--foreground: 210 40% 98%;
|
||||||
--muted: 215.38 16.32% 46.86%;
|
--card: 222.2 84% 4.9%;
|
||||||
--muted-foreground: 334 0% 87.69%;
|
--card-foreground: 210 40% 98%;
|
||||||
--popover: 217.24 32.58% 17.45%;
|
--popover: 222.2 84% 4.9%;
|
||||||
--popover-foreground: 334 34% 98%;
|
--popover-foreground: 210 40% 98%;
|
||||||
--card: 217.24 32.58% 17.45%;
|
--primary: 217.2 91.2% 59.8%;
|
||||||
--card-foreground: 334 34% 98%;
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
--border: 334 0% 32.31%;
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
--input: 215.29 25% 26.67%;
|
--secondary-foreground: 210 40% 98%;
|
||||||
--primary: 227.56 53.78% 49.22%;
|
--muted: 217.2 32.6% 17.5%;
|
||||||
--primary-foreground: 0 0% 100%;
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
--secondary: 214.29 5.04% 27.25%;
|
--accent: 217.2 32.6% 17.5%;
|
||||||
--secondary-foreground: 334 0% 100%;
|
--accent-foreground: 210 40% 98%;
|
||||||
--accent: 222.22 47.37% 11.18%;
|
--destructive: 0 62.8% 30.6%;
|
||||||
--accent-foreground: 226.73 0% 100%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
--destructive: 358.82 84.44% 64.71%;
|
--border: 217.2 32.6% 17.5%;
|
||||||
--destructive-foreground: 0 0% 100%;
|
--input: 217.2 32.6% 17.5%;
|
||||||
--ring: 227.56 53.78% 49.22%;
|
--ring: 224.3 76.3% 48%;
|
||||||
|
--chart-1: 220 70% 50%;
|
||||||
|
--chart-2: 160 60% 45%;
|
||||||
|
--chart-3: 30 80% 55%;
|
||||||
|
--chart-4: 280 65% 60%;
|
||||||
|
--chart-5: 340 75% 55%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -150,7 +150,7 @@ export const createAxiosDataProvider = (
|
|||||||
},
|
},
|
||||||
|
|
||||||
custom: async <R>(params: ICustomDataProviderParam): Promise<R> => {
|
custom: async <R>(params: ICustomDataProviderParam): Promise<R> => {
|
||||||
const { url, method, responseType, headers, signal, ...payload } = params;
|
const { url, method, responseType, headers, signal, data, ...payload } = params;
|
||||||
const requestUrl = `${url}?`;
|
const requestUrl = `${url}?`;
|
||||||
|
|
||||||
/*if (sort) {
|
/*if (sort) {
|
||||||
@ -186,9 +186,12 @@ export const createAxiosDataProvider = (
|
|||||||
case "put":
|
case "put":
|
||||||
case "post":
|
case "post":
|
||||||
case "patch":
|
case "patch":
|
||||||
customResponse = await httpClient[method]<R>(url, {
|
customResponse = await httpClient.request<R>({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
responseType,
|
responseType,
|
||||||
headers,
|
headers,
|
||||||
|
data,
|
||||||
...payload,
|
...payload,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -197,8 +197,44 @@
|
|||||||
"description": "To complete your quote, you can add items from the catalog.",
|
"description": "To complete your quote, you can add items from the catalog.",
|
||||||
"toast_article_added": "Catalog item added:"
|
"toast_article_added": "Catalog item added:"
|
||||||
},
|
},
|
||||||
|
"quote_status_editor": {
|
||||||
|
"title": "Change quote status",
|
||||||
|
"status": {
|
||||||
|
"draft": {
|
||||||
|
"title": "Draft",
|
||||||
|
"description": "The quote is in the initial stages of creation."
|
||||||
|
},
|
||||||
|
"ready": {
|
||||||
|
"title": "Ready",
|
||||||
|
"description": "The quote is completed and ready to be delivered to the customer."
|
||||||
|
},
|
||||||
|
"delivered": {
|
||||||
|
"title": "Delivered",
|
||||||
|
"description": "The quote has been sent to the client and a response is awaited."
|
||||||
|
},
|
||||||
|
"accepted": {
|
||||||
|
"title": "Accepted",
|
||||||
|
"description": "The customer has approved the quote."
|
||||||
|
},
|
||||||
|
"rejected": {
|
||||||
|
"title": "Rejected",
|
||||||
|
"description": "The customer has not accepted the quotation."
|
||||||
|
},
|
||||||
|
"archived": {
|
||||||
|
"title": "Archived",
|
||||||
|
"description": "The quote is archived."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit_button": "Change status",
|
||||||
|
"toast_status_changed": "Quote status changed to:"
|
||||||
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"draft": "Draft"
|
"draft": "Draft",
|
||||||
|
"ready": "Ready",
|
||||||
|
"delivered": "Delivered",
|
||||||
|
"accepted": "Accepted",
|
||||||
|
"rejected": "Rejected",
|
||||||
|
"archived": "Archived"
|
||||||
},
|
},
|
||||||
"form_fields": {
|
"form_fields": {
|
||||||
"date": {
|
"date": {
|
||||||
|
|||||||
@ -122,9 +122,9 @@
|
|||||||
"draft": "Borradores",
|
"draft": "Borradores",
|
||||||
"ready": "Preparados",
|
"ready": "Preparados",
|
||||||
"delivered": "Entregado",
|
"delivered": "Entregado",
|
||||||
"accepted": "Accepted",
|
"accepted": "Aceptados",
|
||||||
"rejected": "Rejected",
|
"rejected": "Rechazados",
|
||||||
"archived": "Archivadas"
|
"archived": "Archivados"
|
||||||
},
|
},
|
||||||
"columns": {
|
"columns": {
|
||||||
"date": "Fecha",
|
"date": "Fecha",
|
||||||
@ -193,8 +193,43 @@
|
|||||||
"description": "Para rellenar su cotización, puede añadir artículos del catálogo.",
|
"description": "Para rellenar su cotización, puede añadir artículos del catálogo.",
|
||||||
"toast_article_added": "Artículo del catálogo añadido:"
|
"toast_article_added": "Artículo del catálogo añadido:"
|
||||||
},
|
},
|
||||||
|
"quote_status_editor": {
|
||||||
|
"title": "Cambiar el estado de la cotización",
|
||||||
|
"status": {
|
||||||
|
"draft": {
|
||||||
|
"title": "Borrador",
|
||||||
|
"description": "La cotización está en fase inicial de creación."
|
||||||
|
},
|
||||||
|
"ready": {
|
||||||
|
"title": "Preparado",
|
||||||
|
"description": "La cotización está completo y listo para ser entregado al cliente."
|
||||||
|
},
|
||||||
|
"delivered": {
|
||||||
|
"title": "Entregado",
|
||||||
|
"description": "La cotización ha sido enviado al cliente y se espera su respuesta."
|
||||||
|
},
|
||||||
|
"accepted": {
|
||||||
|
"title": "Aceptado",
|
||||||
|
"description": "El cliente ha aprobado la cotización."
|
||||||
|
},
|
||||||
|
"rejected": {
|
||||||
|
"title": "Rechazado",
|
||||||
|
"description": "El cliente no ha aceptado la cotización."
|
||||||
|
},
|
||||||
|
"archived": {
|
||||||
|
"title": "Archivado",
|
||||||
|
"description": "La cotización se ha guardado para referencia futura."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit_button": "Change status"
|
||||||
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"draft": "Borrador"
|
"draft": "Borrador",
|
||||||
|
"ready": "Preparado",
|
||||||
|
"delivered": "Entregado",
|
||||||
|
"accepted": "Aceptado",
|
||||||
|
"rejected": "Rechazado",
|
||||||
|
"archived": "Archivado"
|
||||||
},
|
},
|
||||||
"form_fields": {
|
"form_fields": {
|
||||||
"date": {
|
"date": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user