diff --git a/src/pages/campaigns/quote/sections/CostAndResourceDetailsSection.tsx b/src/pages/campaigns/quote/sections/CostAndResourceDetailsSection.tsx index 3f6d7251..21fb2d39 100644 --- a/src/pages/campaigns/quote/sections/CostAndResourceDetailsSection.tsx +++ b/src/pages/campaigns/quote/sections/CostAndResourceDetailsSection.tsx @@ -9,6 +9,7 @@ import { useGetDossiersByCampaignCostsQuery } from "src/services/tryberApi"; import { HorizontalDivider } from "../components/Dividers"; import HumanResources from "./HumanResources"; import { Section } from "./Section"; +import OtherCosts from "./OtherCosts"; type CostAndResourceDetailsSectionProps = { campaignId?: string; @@ -93,6 +94,9 @@ export const CostAndResourceDetailsSection = ({ + + + ); }; diff --git a/src/pages/campaigns/quote/sections/HumanResources/index.tsx b/src/pages/campaigns/quote/sections/HumanResources/index.tsx index 3ce96fd6..f78c4cc7 100644 --- a/src/pages/campaigns/quote/sections/HumanResources/index.tsx +++ b/src/pages/campaigns/quote/sections/HumanResources/index.tsx @@ -199,7 +199,7 @@ const FormContent = ({ campaignId }: { campaignId: string }) => { return (
-
+
{ noOptionsMessage={() => "No options"} />
- -
- -
- - Subtotal:{" "} - {subtotal}€ - +
+ + Subtotal:{" "} + + {subtotal}€ + + +
+
); diff --git a/src/pages/campaigns/quote/sections/OtherCosts/AttachmentsDropzone.tsx b/src/pages/campaigns/quote/sections/OtherCosts/AttachmentsDropzone.tsx new file mode 100644 index 00000000..e149a3f6 --- /dev/null +++ b/src/pages/campaigns/quote/sections/OtherCosts/AttachmentsDropzone.tsx @@ -0,0 +1,160 @@ +import { Dropzone, Spinner } from "@appquality/appquality-design-system"; +import { useFormikContext, getIn } from "formik"; +import { useState } from "react"; +import { usePostCampaignsByCampaignFinanceAttachmentsMutation } from "src/services/tryberApi"; +import { normalizeFileName } from "./utils"; +import { FormProps } from "./CostsFormProvider"; + +interface Props { + campaignId: string; + name: string; +} + +export const AttachmentsDropzone = ({ campaignId, name }: Props) => { + const [createAttachment] = + usePostCampaignsByCampaignFinanceAttachmentsMutation(); + const { values, setFieldValue, errors, touched } = + useFormikContext(); + const [isUploading, setIsUploading] = useState(false); + const currentFiles = getIn(values, name) || []; + const error = getIn(errors, name); + const isTouched = getIn(touched, name); + + const uploadMedia = async (files: File[]) => { + setIsUploading(true); + const updatedList = [...currentFiles]; + + for (const f of files) { + const formData = new FormData(); + formData.append("media", f, normalizeFileName(f.name)); + + try { + const res = await createAttachment({ + campaign: campaignId, + // @ts-ignore + body: formData, + }).unwrap(); + + if (res.attachments && res.attachments.length > 0) { + const newFile = res.attachments[0]; + updatedList.push({ + url: newFile.url, + mimeType: newFile.mime_type, + }); + } + } catch (e) { + console.error(e); + } + } + + setFieldValue(name, updatedList); + setIsUploading(false); + }; + + const handleDelete = (index: number) => { + const updatedList = currentFiles.filter((_: any, i: number) => i !== index); + setFieldValue(name, updatedList); + }; + + const downloadFile = (file: any) => { + const fileName = file.url.split("/").pop() || "attachment"; + const link = document.createElement("a"); + link.href = file.presignedUrl; + link.download = fileName; + link.target = "_blank"; + link.rel = "noopener noreferrer"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + + return ( +
+ {}} + disabled={isUploading} + danger={!!error && isTouched} + /> + + {isUploading && ( +
+ +
+ )} + +
+ {currentFiles.map((file: any, idx: number) => ( +
+ {file.presignedUrl ? ( + downloadFile(file)} + style={{ + cursor: "pointer", + color: "#0066cc", + display: "flex", + alignItems: "center", + gap: "4px", + flex: 1, + }} + title="Click to download" + > + 📎 {file.url.split("/").pop()} ⬇ + + ) : ( + 📎 {file.url.split("/").pop()} + )} + +
+ ))} +
+ + {error && isTouched && ( +
+ {error} +
+ )} +
+ ); +}; diff --git a/src/pages/campaigns/quote/sections/OtherCosts/CostsFormProvider.tsx b/src/pages/campaigns/quote/sections/OtherCosts/CostsFormProvider.tsx new file mode 100644 index 00000000..485aa621 --- /dev/null +++ b/src/pages/campaigns/quote/sections/OtherCosts/CostsFormProvider.tsx @@ -0,0 +1,191 @@ +import { Formik } from "@appquality/appquality-design-system"; +import { useRef, useEffect } from "react"; +import siteWideMessageStore from "src/redux/siteWideMessages"; +import { + useGetCampaignsByCampaignFinanceOtherCostsQuery, + usePostCampaignsByCampaignFinanceOtherCostsMutation, + usePatchCampaignsByCampaignFinanceOtherCostsMutation, +} from "src/services/tryberApi"; +import * as yup from "yup"; + +export type FormProps = { + items: Array<{ + cost_id?: number; + notSaved?: boolean; + description: string; + type: number; + supplier: number; + cost: number; + files: { url: string; mimeType: string; presignedUrl?: string }[]; + }>; +}; + +const CostsFormProvider = ({ + children, + campaignId, +}: { + children: React.ReactNode; + campaignId: string; +}) => { + const { data, isLoading } = useGetCampaignsByCampaignFinanceOtherCostsQuery({ + campaign: campaignId, + }); + const [createOtherCosts] = + usePostCampaignsByCampaignFinanceOtherCostsMutation(); + const [updateOtherCosts] = + usePatchCampaignsByCampaignFinanceOtherCostsMutation(); + const { add } = siteWideMessageStore(); + + const initialValuesRef = useRef(null); + + useEffect(() => { + if (data) { + initialValuesRef.current = { + items: (data?.items || []).map((item) => ({ + cost_id: item.cost_id, + description: item.description || "", + type: item.type?.id || 0, + supplier: item.supplier?.id || 0, + cost: item.cost || 0, + files: (item.attachments || []).map((attachment) => ({ + url: attachment.url || "", + mimeType: attachment.mimetype || "", + presignedUrl: attachment.presigned_url || "", + })), + })), + }; + } + }, [data]); + + const handleSubmit = async (values: FormProps) => { + try { + const newItems = values.items.filter((item) => item.notSaved); + + const modifiedItems = values.items.filter((item) => { + if (item.notSaved || !item.cost_id) return false; + + const initialItem = initialValuesRef.current?.items.find( + (i) => i.cost_id === item.cost_id + ); + if (!initialItem) return false; + + // Compare files without presignedUrl + const currentFilesForComparison = item.files.map((f) => ({ + url: f.url, + mimeType: f.mimeType, + })); + const initialFilesForComparison = initialItem.files.map((f) => ({ + url: f.url, + mimeType: f.mimeType, + })); + + return ( + item.description !== initialItem.description || + item.type !== initialItem.type || + item.supplier !== initialItem.supplier || + item.cost !== initialItem.cost || + JSON.stringify(currentFilesForComparison) !== + JSON.stringify(initialFilesForComparison) + ); + }); + + if (newItems.length > 0) { + await createOtherCosts({ + campaign: campaignId, + body: newItems.map((item) => ({ + description: item.description, + type_id: item.type, + supplier_id: item.supplier, + cost: item.cost, + attachments: item.files.map((file) => ({ + url: file.url, + mime_type: file.mimeType, + })), + })), + }).unwrap(); + } + + if (modifiedItems.length > 0) { + await updateOtherCosts({ + campaign: campaignId, + body: modifiedItems.map((item) => ({ + cost_id: item.cost_id!, + description: item.description, + type_id: item.type, + supplier_id: item.supplier, + cost: item.cost, + attachments: item.files.map((file) => ({ + url: file.url, + mime_type: file.mimeType, + })), + })), + }).unwrap(); + } + + add({ + message: "Other costs saved successfully", + type: "success", + }); + } catch (error) { + console.error("Failed to save other costs:", error); + add({ + message: "Failed to save other costs", + type: "danger", + }); + } + }; + const validationSchema = yup.object({ + items: yup.array().of( + yup.object({ + description: yup.string().required("Required"), + type: yup.number().required("Required").min(0), + supplier: yup.number().required("Required").min(1), + cost: yup.number().required("Required").min(0), + files: yup + .array() + .of( + yup.object({ + url: yup.string().required(), + mimeType: yup.string().required(), + }) + ) + .required() + .min(1, "At least one attachment is required"), + }) + ), + }); + + if (!data || isLoading) { + return null; + } + + const initialValues: FormProps = { + items: (data?.items || []).map((item) => ({ + cost_id: item.cost_id, + description: item.description || "", + type: item.type?.id || 0, + supplier: item.supplier?.id || 0, + cost: item.cost || 0, + files: (item.attachments || []).map((attachment) => ({ + url: attachment.url || "", + mimeType: attachment.mimetype || "", + presignedUrl: attachment.presigned_url || "", + })), + })), + }; + + return ( + + enableReinitialize + validateOnMount + validateOnChange + initialValues={initialValues} + validationSchema={validationSchema} + onSubmit={handleSubmit} + > +
{children}
+ + ); +}; + +export default CostsFormProvider; diff --git a/src/pages/campaigns/quote/sections/OtherCosts/index.tsx b/src/pages/campaigns/quote/sections/OtherCosts/index.tsx new file mode 100644 index 00000000..02ffc9b4 --- /dev/null +++ b/src/pages/campaigns/quote/sections/OtherCosts/index.tsx @@ -0,0 +1,440 @@ +import { + aqBootstrapTheme, + Button, + Dropdown, + FormLabel, + Input, + Modal, + ModalBody, + Select, + Text, +} from "@appquality/appquality-design-system"; +import { FieldArray, useFormikContext } from "formik"; +import { useState, useMemo } from "react"; +import { ReactComponent as DeleteIcon } from "src/assets/trash.svg"; +import { styled } from "styled-components"; +import siteWideMessageStore from "src/redux/siteWideMessages"; +import CostsFormProvider, { FormProps } from "./CostsFormProvider"; +import { AttachmentsDropzone } from "./AttachmentsDropzone"; +import { + useGetCampaignsByCampaignFinanceSupplierQuery, + useGetCampaignsByCampaignFinanceTypeQuery, + usePostCampaignsByCampaignFinanceSupplierMutation, + useDeleteCampaignsByCampaignFinanceOtherCostsMutation, +} from "src/services/tryberApi"; + +const StyledRow = styled.div` + margin-top: ${({ theme }) => theme.grid.spacing.default}; + display: flex; + gap: ${({ theme }) => theme.grid.sizes[4]}; + align-items: flex-end; + flex-direction: row; + margin-bottom: ${({ theme }) => theme.grid.sizes[3]}; + + > div:not(:last-child) { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + min-width: 0; + } + + > div:last-child { + flex: 0; + } +`; + +const useCostTypes = ({ campaignId }: { campaignId: string }) => { + const { data, isLoading } = useGetCampaignsByCampaignFinanceTypeQuery({ + campaign: campaignId, + }); + + const options = useMemo(() => { + if (!data?.items) return []; + return data.items.map((item, index) => ({ + value: String(index + 1), + label: item.name || `Type ${index + 1}`, + })); + }, [data]); + + return { data: options, isLoading }; +}; + +const useSuppliers = ({ campaignId }: { campaignId: string }) => { + const { data, isLoading, refetch } = + useGetCampaignsByCampaignFinanceSupplierQuery({ + campaign: campaignId, + }); + + const options = useMemo(() => { + if (!data?.items) return []; + return data.items.map((item, index) => ({ + value: String(index + 1), + label: item.name, + })); + }, [data]); + + return { data: options, isLoading, refetch }; +}; + +const OtherCosts = ({ campaignId }: { campaignId: string }) => { + return ( + + + + ); +}; + +const FormContent = ({ campaignId }: { campaignId: string }) => { + const { values, isValid, submitForm, dirty, isSubmitting } = + useFormikContext(); + const [rowPendingRemoval, setRowPendingRemoval] = useState( + null + ); + + const { data: costTypes, isLoading: costTypesLoading } = useCostTypes({ + campaignId, + }); + const { data: suppliers, refetch: refetchSuppliers } = useSuppliers({ + campaignId, + }); + const [createSupplier] = usePostCampaignsByCampaignFinanceSupplierMutation(); + const [deleteOtherCost] = + useDeleteCampaignsByCampaignFinanceOtherCostsMutation(); + const { add } = siteWideMessageStore(); + + const handleDelete = async (index: number, arrayHelpers: any) => { + const item = values.items[index]; + + if (item.notSaved) { + arrayHelpers.remove(index); + setRowPendingRemoval(null); + } else if (item.cost_id) { + try { + await deleteOtherCost({ + campaign: campaignId, + body: { cost_id: item.cost_id }, + }).unwrap(); + + arrayHelpers.remove(index); + setRowPendingRemoval(null); + + add({ + message: "Cost deleted successfully", + type: "success", + }); + } catch (error) { + console.error("Failed to delete cost:", error); + add({ + message: "Failed to delete cost", + type: "danger", + }); + setRowPendingRemoval(null); + } + } + }; + + const totalOtherCosts = values.items + ? values.items + .reduce((sum, item) => sum + (Number(item.cost) || 0), 0) + .toFixed(2) + : "0.00"; + + return ( + <> + + 💡 + Add Other Costs and{" "} + fill all required fields (*) + + + ( + <> + {values.items && + values.items.map((item, index) => { + const selectedType = costTypes.find( + (t) => t.value === String(item.type) + ); + + const isLastItem = index === values.items.length - 1; + + return ( +
+ +
+ + Description{" "} + * + + } + /> + { + arrayHelpers.replace(index, { + ...item, + description: value, + }); + }} + /> +
+
+ + +
+ { + const numValue = + value === "" ? 0 : parseFloat(value); + arrayHelpers.replace(index, { + ...item, + cost: numValue, + }); + }} + /> +
+
+ +
+ + Attachments * + + } + /> + +
+ +
+
+ + Subtotal:{" "} + + {(Number(item.cost) || 0).toFixed(2)}€ + + +
+ +
+
+ ); + })} + + setRowPendingRemoval(null)} + footer={ +
+ + +
+ } + > + + + This will permanently remove this cost item. + + +
+ +
+ +
+ TOTAL OTHER COSTS: + + {totalOtherCosts}€ + +
+
+ + )} + /> + +
+ +
+ + ); +}; + +export default OtherCosts; diff --git a/src/pages/campaigns/quote/sections/OtherCosts/utils.ts b/src/pages/campaigns/quote/sections/OtherCosts/utils.ts new file mode 100644 index 00000000..6a3e2d4f --- /dev/null +++ b/src/pages/campaigns/quote/sections/OtherCosts/utils.ts @@ -0,0 +1,3 @@ +export const normalizeFileName = (fileName: string) => { + return fileName.normalize("NFD").replace(/\p{Diacritic}/gu, ""); +}; diff --git a/src/pages/campaigns/quote/sections/SummaryFinanceCard.tsx b/src/pages/campaigns/quote/sections/SummaryFinanceCard.tsx index 88609636..bc6c0af9 100644 --- a/src/pages/campaigns/quote/sections/SummaryFinanceCard.tsx +++ b/src/pages/campaigns/quote/sections/SummaryFinanceCard.tsx @@ -6,6 +6,7 @@ import { Title, } from "@appquality/appquality-design-system"; import { + useGetCampaignsByCampaignFinanceOtherCostsQuery, useGetDossiersByCampaignAgreementsQuery, useGetDossiersByCampaignCostsQuery, useGetDossiersByCampaignHumanResourcesQuery, @@ -29,6 +30,11 @@ export const SummaryFinanceCard = ({ campaignId }: { campaignId: string }) => { campaign: campaignId, }); + const { data: otherCostsData, isLoading: isOtherCostsDataLoading } = + useGetCampaignsByCampaignFinanceOtherCostsQuery({ + campaign: campaignId, + }); + const hrCostsTotal = hrCostsData?.items && hrCostsData?.items.length > 0 ? hrCostsData.items.reduce( @@ -37,10 +43,16 @@ export const SummaryFinanceCard = ({ campaignId }: { campaignId: string }) => { ) : 0; + const otherCostsTotal = + otherCostsData?.items && otherCostsData?.items.length > 0 + ? otherCostsData.items.reduce((acc, cost) => acc + (cost?.cost ?? 0), 0) + : 0; + if ( isAgreementDataLoading || isCommunityCostsDataLoading || - isHrCostsDataLoading + isHrCostsDataLoading || + isOtherCostsDataLoading ) return ; @@ -114,6 +126,24 @@ export const SummaryFinanceCard = ({ campaignId }: { campaignId: string }) => {
+
+ Other costs: + + {otherCostsTotal.toFixed(2)}€{" "} + +
+
{ color: aqBootstrapTheme.palette.primary, }} > - {((communityCostsData?.totalCost || 0) + hrCostsTotal).toFixed(2)}€ + {( + (communityCostsData?.totalCost || 0) + + hrCostsTotal + + otherCostsTotal + ).toFixed(2)} + €
@@ -168,7 +203,9 @@ export const SummaryFinanceCard = ({ campaignId }: { campaignId: string }) => { {agreementData?.tokens && agreementData?.agreement?.value ? `${( ((agreementData.tokens * agreementData.agreement.value - - ((communityCostsData?.totalCost || 0) + hrCostsTotal)) / + ((communityCostsData?.totalCost || 0) + + hrCostsTotal + + otherCostsTotal)) / (agreementData.tokens * agreementData.agreement.value)) * 100 diff --git a/src/services/tryberApi/api.ts b/src/services/tryberApi/api.ts index e92edd23..17e92ed9 100644 --- a/src/services/tryberApi/api.ts +++ b/src/services/tryberApi/api.ts @@ -39,6 +39,7 @@ export const api = createApi({ "HumanResources", "Agreements", "Quote", + "OtherCosts", ], endpoints: () => ({}), // auto generated npm run generate-api }); diff --git a/src/services/tryberApi/apiTags.ts b/src/services/tryberApi/apiTags.ts index 671b5dc5..e66b1d6b 100644 --- a/src/services/tryberApi/apiTags.ts +++ b/src/services/tryberApi/apiTags.ts @@ -260,6 +260,18 @@ tryberApi.enhanceEndpoints({ putDossiersByCampaignAgreements: { invalidatesTags: ["Agreements"], }, + getCampaignsByCampaignFinanceOtherCosts: { + providesTags: ["OtherCosts"], + }, + postCampaignsByCampaignFinanceOtherCosts: { + invalidatesTags: ["OtherCosts"], + }, + deleteCampaignsByCampaignFinanceOtherCosts: { + invalidatesTags: ["OtherCosts"], + }, + patchCampaignsByCampaignFinanceOtherCosts: { + invalidatesTags: ["OtherCosts"], + }, }, }); diff --git a/src/services/tryberApi/index.ts b/src/services/tryberApi/index.ts index 9a3d77d3..8c2091d3 100644 --- a/src/services/tryberApi/index.ts +++ b/src/services/tryberApi/index.ts @@ -242,6 +242,16 @@ const injectedRtkApi = api.injectEndpoints({ url: `/campaigns/${queryArg.campaign}/clusters`, }), }), + postCampaignsByCampaignFinanceAttachments: build.mutation< + PostCampaignsByCampaignFinanceAttachmentsApiResponse, + PostCampaignsByCampaignFinanceAttachmentsApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/attachments`, + method: "POST", + body: queryArg.body, + }), + }), getCampaignsByCampaignForms: build.query< GetCampaignsByCampaignFormsApiResponse, GetCampaignsByCampaignFormsApiArg @@ -1188,6 +1198,70 @@ const injectedRtkApi = api.injectEndpoints({ body: queryArg.body, }), }), + getCampaignsByCampaignFinanceSupplier: build.query< + GetCampaignsByCampaignFinanceSupplierApiResponse, + GetCampaignsByCampaignFinanceSupplierApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/supplier`, + }), + }), + postCampaignsByCampaignFinanceSupplier: build.mutation< + PostCampaignsByCampaignFinanceSupplierApiResponse, + PostCampaignsByCampaignFinanceSupplierApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/supplier`, + method: "POST", + body: queryArg.body, + }), + }), + getCampaignsByCampaignFinanceType: build.query< + GetCampaignsByCampaignFinanceTypeApiResponse, + GetCampaignsByCampaignFinanceTypeApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/type`, + }), + }), + getCampaignsByCampaignFinanceOtherCosts: build.query< + GetCampaignsByCampaignFinanceOtherCostsApiResponse, + GetCampaignsByCampaignFinanceOtherCostsApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/otherCosts`, + }), + }), + postCampaignsByCampaignFinanceOtherCosts: build.mutation< + PostCampaignsByCampaignFinanceOtherCostsApiResponse, + PostCampaignsByCampaignFinanceOtherCostsApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/otherCosts`, + method: "POST", + body: queryArg.body, + }), + }), + deleteCampaignsByCampaignFinanceOtherCosts: build.mutation< + DeleteCampaignsByCampaignFinanceOtherCostsApiResponse, + DeleteCampaignsByCampaignFinanceOtherCostsApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/otherCosts`, + method: "DELETE", + body: queryArg.body, + }), + }), + patchCampaignsByCampaignFinanceOtherCosts: build.mutation< + PatchCampaignsByCampaignFinanceOtherCostsApiResponse, + PatchCampaignsByCampaignFinanceOtherCostsApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/finance/otherCosts`, + method: "PATCH", + body: queryArg.body, + }), + }), }), overrideExisting: false, }); @@ -1670,6 +1744,24 @@ export type GetCampaignsByCampaignClustersApiArg = { /** A campaign id */ campaign: string; }; +export type PostCampaignsByCampaignFinanceAttachmentsApiResponse = + /** status 200 OK */ { + attachments?: { + url: string; + name: string; + mime_type: string; + }[]; + failed?: { + name: string; + path: string; + }[]; + }; +export type PostCampaignsByCampaignFinanceAttachmentsApiArg = { + campaign: string; + body: { + attachment?: Blob | Blob[]; + }; +}; export type GetCampaignsByCampaignFormsApiResponse = /** status 200 OK */ { id: number; question: string; @@ -3412,6 +3504,116 @@ export type PostCampaignsByCampaignTasksAndUsecaseSurveyJotformApiArg = { testerQuestionId: string; }; }; +export type GetCampaignsByCampaignFinanceSupplierApiResponse = + /** status 200 OK */ { + items: { + name: string; + created_at?: string; + created_by?: number; + id: number; + }[]; + }; +export type GetCampaignsByCampaignFinanceSupplierApiArg = { + campaign: string; +}; +export type PostCampaignsByCampaignFinanceSupplierApiResponse = + /** status 201 Created */ { + supplier_id: number; + }; +export type PostCampaignsByCampaignFinanceSupplierApiArg = { + campaign: string; + body: { + name: string; + }; +}; +export type GetCampaignsByCampaignFinanceTypeApiResponse = + /** status 200 OK */ { + items: { + name: string; + id: number; + }[]; + }; +export type GetCampaignsByCampaignFinanceTypeApiArg = { + campaign: string; +}; +export type GetCampaignsByCampaignFinanceOtherCostsApiResponse = + /** status 200 OK */ { + items: { + cost_id: number; + type: { + name: string; + id: number; + }; + supplier: { + name: string; + id: number; + }; + description: string; + attachments: { + id: number; + url: string; + mimetype: string; + presigned_url: string; + }[]; + cost: number; + }[]; + }; +export type GetCampaignsByCampaignFinanceOtherCostsApiArg = { + /** A campaign id */ + campaign: string; +}; +export type PostCampaignsByCampaignFinanceOtherCostsApiResponse = + /** status 201 Created */ undefined; +export type PostCampaignsByCampaignFinanceOtherCostsApiArg = { + /** A campaign id */ + campaign: string; + body: { + description: string; + type_id: number; + supplier_id: number; + cost: number; + attachments: { + url: string; + mime_type: string; + }[]; + }[]; +}; +export type DeleteCampaignsByCampaignFinanceOtherCostsApiResponse = + /** status 200 OK */ undefined; +export type DeleteCampaignsByCampaignFinanceOtherCostsApiArg = { + /** A campaign id */ + campaign: string; + body: { + cost_id: number; + }; +}; +export type PatchCampaignsByCampaignFinanceOtherCostsApiResponse = + /** status 200 OK */ { + description: string; + type: string; + cost_id: number; + supplier: string; + cost: number; + attachments: { + url: string; + mime_type: string; + }[]; + }[]; +export type PatchCampaignsByCampaignFinanceOtherCostsApiArg = { + /** A campaign id */ + campaign: string; + body: { + description: string; + type_id: number; + supplier_id: number; + cost: number; + attachments: { + url: string; + mime_type: string; + }[]; + cost_id: number; + }[]; +}; export type Agreement = { expirationDate: string; isTokenBased?: boolean; @@ -3761,6 +3963,7 @@ export const { useGetCampaignsByCampaignCandidatesQuery, usePostCampaignsByCampaignCandidatesMutation, useGetCampaignsByCampaignClustersQuery, + usePostCampaignsByCampaignFinanceAttachmentsMutation, useGetCampaignsByCampaignFormsQuery, useGetCampaignsByCampaignGroupsQuery, useGetCampaignsByCampaignObservationsQuery, @@ -3872,4 +4075,11 @@ export const { useGetUsersMeRankQuery, useGetUsersMeRankListQuery, usePostCampaignsByCampaignTasksAndUsecaseSurveyJotformMutation, + useGetCampaignsByCampaignFinanceSupplierQuery, + usePostCampaignsByCampaignFinanceSupplierMutation, + useGetCampaignsByCampaignFinanceTypeQuery, + useGetCampaignsByCampaignFinanceOtherCostsQuery, + usePostCampaignsByCampaignFinanceOtherCostsMutation, + useDeleteCampaignsByCampaignFinanceOtherCostsMutation, + usePatchCampaignsByCampaignFinanceOtherCostsMutation, } = injectedRtkApi; diff --git a/src/utils/schema.ts b/src/utils/schema.ts index b2b29ff9..6ab07ba0 100644 --- a/src/utils/schema.ts +++ b/src/utils/schema.ts @@ -146,6 +146,14 @@ export interface paths { }; }; }; + "/campaigns/{campaign}/finance/attachments": { + post: operations["post-campaigns-campaign-finance-attachments"]; + parameters: { + path: { + campaign: string; + }; + }; + }; "/campaigns/{campaign}/forms": { get: operations["get-campaigns-campaign-forms"]; parameters: { @@ -796,6 +804,37 @@ export interface paths { }; }; }; + "/campaigns/{campaign}/finance/supplier": { + /** Get all finance suppliers */ + get: operations["get-campaigns-campaign-finance-supplier"]; + post: operations["post-campaigns-campaign-finance-supplier"]; + parameters: { + path: { + campaign: string; + }; + }; + }; + "/campaigns/{campaign}/finance/type": { + get: operations["get-campaigns-campaign-finance-type"]; + parameters: { + path: { + campaign: string; + }; + }; + }; + "/campaigns/{campaign}/finance/otherCosts": { + get: operations["get-campaigns-campaign-finance-otherCosts"]; + /** Create a new campaign cost */ + post: operations["post-campaigns-campaign-finance-otherCosts"]; + delete: operations["delete-campaigns-campaign-finance-otherCosts"]; + patch: operations["patch-campaigns-campaign-finance-otherCosts"]; + parameters: { + path: { + /** A campaign id */ + campaign: string; + }; + }; + }; } export interface components { @@ -2119,6 +2158,41 @@ export interface operations { 404: components["responses"]["NotFound"]; }; }; + "post-campaigns-campaign-finance-attachments": { + parameters: { + path: { + campaign: string; + }; + }; + responses: { + /** OK */ + 200: { + content: { + "application/json": { + attachments?: { + url: string; + name: string; + mime_type: string; + }[]; + failed?: { + name: string; + path: string; + }[]; + }; + }; + }; + 403: components["responses"]["NotAuthorized"]; + /** Internal Server Error */ + 500: unknown; + }; + requestBody: { + content: { + "multipart/form-data": { + attachment?: string | string[]; + }; + }; + }; + }; "get-campaigns-campaign-forms": { parameters: { path: { @@ -5457,6 +5531,11 @@ export interface operations { "application/json": { [key: string]: unknown }; }; }; + "": { + content: { + "application/json": { [key: string]: unknown }; + }; + }; }; requestBody: { content: { @@ -5467,6 +5546,237 @@ export interface operations { }; }; }; + /** Get all finance suppliers */ + "get-campaigns-campaign-finance-supplier": { + parameters: { + path: { + campaign: string; + }; + }; + responses: { + /** OK */ + 200: { + content: { + "application/json": { + items: { + name: string; + created_at?: string; + created_by?: number; + id: number; + }[]; + }; + }; + }; + /** Bad Request */ + 400: unknown; + /** Forbidden */ + 403: unknown; + /** Internal Server Error */ + 500: unknown; + }; + }; + "post-campaigns-campaign-finance-supplier": { + parameters: { + path: { + campaign: string; + }; + }; + responses: { + /** Created */ + 201: { + content: { + "application/json": { + supplier_id: number; + }; + }; + }; + /** Bad Request */ + 400: unknown; + /** Forbidden */ + 403: unknown; + /** Internal Server Error */ + 500: unknown; + }; + requestBody: { + content: { + "application/json": { + name: string; + }; + }; + }; + }; + "get-campaigns-campaign-finance-type": { + parameters: { + path: { + campaign: string; + }; + }; + responses: { + /** OK */ + 200: { + content: { + "application/json": { + items: { + name: string; + id: number; + }[]; + }; + }; + }; + 403: components["responses"]["NotAuthorized"]; + 404: components["responses"]["NotFound"]; + /** Internal Server Error */ + 500: unknown; + }; + }; + "get-campaigns-campaign-finance-otherCosts": { + parameters: { + path: { + /** A campaign id */ + campaign: string; + }; + }; + responses: { + /** OK */ + 200: { + content: { + "application/json": { + items: { + cost_id: number; + type: { + name: string; + id: number; + }; + supplier: { + name: string; + id: number; + }; + description: string; + attachments: { + id: number; + url: string; + mimetype: string; + presigned_url: string; + }[]; + cost: number; + }[]; + }; + }; + }; + /** Forbidden */ + 403: unknown; + /** Not Found */ + 404: unknown; + /** Internal Server Error */ + 500: unknown; + }; + }; + /** Create a new campaign cost */ + "post-campaigns-campaign-finance-otherCosts": { + parameters: { + path: { + /** A campaign id */ + campaign: string; + }; + }; + responses: { + /** Created */ + 201: unknown; + /** Bad Request */ + 400: unknown; + 403: components["responses"]["NotAuthorized"]; + 404: components["responses"]["NotFound"]; + /** Internal Server Error */ + 500: unknown; + }; + requestBody: { + content: { + "application/json": { + description: string; + type_id: number; + supplier_id: number; + cost: number; + attachments: { + url: string; + mime_type: string; + }[]; + }[]; + }; + }; + }; + "delete-campaigns-campaign-finance-otherCosts": { + parameters: { + path: { + /** A campaign id */ + campaign: string; + }; + }; + responses: { + /** OK */ + 200: unknown; + /** Bad Request */ + 400: unknown; + 403: components["responses"]["NotAuthorized"]; + 404: components["responses"]["NotFound"]; + /** Internal Server Error */ + 500: unknown; + }; + requestBody: { + content: { + "application/json": { + cost_id: number; + }; + }; + }; + }; + "patch-campaigns-campaign-finance-otherCosts": { + parameters: { + path: { + /** A campaign id */ + campaign: string; + }; + }; + responses: { + /** OK */ + 200: { + content: { + "application/json": { + description: string; + type: string; + cost_id: number; + supplier: string; + cost: number; + attachments: { + url: string; + mime_type: string; + }[]; + }[]; + }; + }; + /** Bad Request */ + 400: unknown; + /** Forbidden */ + 403: unknown; + 404: components["responses"]["NotFound"]; + /** Internal Server Error */ + 500: unknown; + }; + requestBody: { + content: { + "application/json": { + description: string; + type_id: number; + supplier_id: number; + cost: number; + attachments: { + url: string; + mime_type: string; + }[]; + cost_id: number; + }[]; + }; + }; + }; } export interface external {}