[WIP] Download contract

This commit is contained in:
Julien Aldon
2026-02-16 17:49:15 +01:00
parent 5354a74cac
commit ab98ba81c8
10 changed files with 98 additions and 15 deletions

View File

@@ -72,6 +72,7 @@
"shipment products is necessary only for occasional products (if all products are recurrent leave empty)": "shipment products configuration is only necessary for occasional products (leave empty if all products are recurrent).",
"recurrent product is for all shipments, occasional product is for a specific shipment (see shipment form)": "recurrent products are for all shipments, occasional products are for a specific shipment (see shipment form).",
"some contracts require a minimum value per shipment, ignore this field if it's not the case": "some contracts require a minimum value per shipment. Ignore this field if it does not apply to your contract.",
"contracts": "contracts",
"minimum price for this shipment should be at least": "minimum price for this shipment should be at least",
"there is": "there is",
"for this contract": "for this contract.",
@@ -166,6 +167,7 @@
"of the productor": "of the producer",
"of the shipment": "of the shipment",
"of the contract": "of the contract",
"login with keycloak": "login with keycloak",
"there is no contract for now": "There is no contract at the moment.",
"for transfer method contact your referer or productor": "for bank transfer, contact your referent or producer.",
"cheque quantity": "number of cheques",

View File

@@ -72,6 +72,7 @@
"shipment products is necessary only for occasional products (if all products are recurrent leave empty)": "il est nécessaire de configurer les produits pour la livraison uniquement si il y a des produits occasionnels (laisser vide si tous les produits sont récurents).",
"recurrent product is for all shipments, occasional product is for a specific shipment (see shipment form)": "les produits récurrents sont pour toutes les livraisons, les produits occasionnels sont pour une livraison particulière (voir formulaire de création de livraison).",
"some contracts require a minimum value per shipment, ignore this field if it's not the case": "certains contrats nécessitent une valeur minimum par livraison. Ce champ peut être ignoré sil ne sapplique pas à votre contrat.",
"contracts": "contrats",
"minimum price for this shipment should be at least": "le prix minimum d'une livraison doit être au moins de",
"there is": "il y a",
"for this contract": "pour ce contrat.",
@@ -166,6 +167,7 @@
"of the productor": "du producteur·trice",
"of the shipment": "de la livraison",
"of the contract": "du contrat",
"login with keycloak": "se connecter avec keycloak",
"there is no contract for now": "Il n'y a pas de contrats pour le moment.",
"for transfer method contact your referer or productor": "pour mettre en place le virement automatique, contactez votre référent ou le producteur.",
"cheque quantity": "quantité de chèques (pour le paiement en plusieurs fois)",

View File

@@ -1,8 +1,9 @@
import { ActionIcon, Table, Tooltip } from "@mantine/core";
import { type Contract } from "@/services/resources/contracts";
import { IconX } from "@tabler/icons-react";
import { IconDownload, IconX } from "@tabler/icons-react";
import { t } from "@/config/i18n";
import { useDeleteContract } from "@/services/api";
import { useDeleteContract, useGetContractFile } from "@/services/api";
import { useCallback } from "react";
export type ContractRowProps = {
contract: Contract;
@@ -12,7 +13,19 @@ export default function ContractRow({ contract }: ContractRowProps) {
// const [searchParams] = useSearchParams();
const deleteMutation = useDeleteContract();
// const navigate = useNavigate();
const {refetch, isFetching} = useGetContractFile(contract.id)
const handleDownload = useCallback(async () => {
const { data } = await refetch();
if (!data)
return;
const url = URL.createObjectURL(data.blob);
const link = document.createElement("a");
link.href = url;
link.download = data.filename;
link.click();
URL.revokeObjectURL(url);
}, [useGetContractFile])
return (
<Table.Tr key={contract.id}>
<Table.Td>
@@ -37,6 +50,20 @@ export default function ContractRow({ contract }: ContractRowProps) {
<IconEdit />
</ActionIcon>
</Tooltip> */}
<Tooltip
label={t("download contract")}
>
<ActionIcon
size="sm"
mr="5"
onClick={(e) => {
e.stopPropagation();
handleDownload()
}}
>
<IconDownload/>
</ActionIcon>
</Tooltip>
<Tooltip label={t("remove contract", { capfirst: true })}>
<ActionIcon
color="red"

View File

@@ -21,7 +21,7 @@ export function Navbar() {
</Group>
<NavLink
className={"navLink"}
aria-label={t("login with keycloak")}
aria-label={t("login with keycloak", { capfirst: true })}
to={`${Config.backend_uri}/auth/login`}
>
{t("login with keycloak", { capfirst: true })}

View File

@@ -14,7 +14,7 @@ export default function Dashboard() {
defaultValue={"help"}
onChange={(value) => navigate(`/dashboard/${value}`)}
>
<Tabs.List>
<Tabs.List mb="md">
<Tabs.Tab value="help">{t("help", { capfirst: true })}</Tabs.Tab>
<Tabs.Tab value="productors">{t("productors", { capfirst: true })}</Tabs.Tab>
<Tabs.Tab value="products">{t("products", { capfirst: true })}</Tabs.Tab>

View File

@@ -586,6 +586,25 @@ export function useGetContract(
});
}
export function useGetContractFile(
id?: number,
) {
return useQuery({
queryKey: ["contract"],
queryFn: () => fetch(`${Config.backend_uri}/contracts/${id}/file`)
.then(async (res) => {
const blob = await res.blob();
const disposition = res.headers.get("Content-Disposition");
const filename = disposition && disposition?.includes("filename=") ?
disposition.split("filname=")[1].replace(/"/g, "") :
`contract_${id}.pdf`
return {blob, filename};
}),
enabled: !!id,
});
}
export function useCreateContract() {
const queryClient = useQueryClient();