+
-
+
{
/>
-
+
-
-
-
-
-
- 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,
+ });
+ }}
+ />
+
+
+
+
+
+
+
+
+ Supplier *
+
+ }
+ />
+ s.value === String(item.supplier)
+ )}
+ onChange={(opt: any) => {
+ if (opt) {
+ arrayHelpers.replace(index, {
+ ...item,
+ supplier: Number(opt.value),
+ });
+ } else {
+ arrayHelpers.replace(index, {
+ ...item,
+ supplier: 0,
+ });
+ }
+ }}
+ onCreateOption={async (inputValue: string) => {
+ try {
+ const response = await createSupplier({
+ campaign: campaignId,
+ body: { name: inputValue },
+ });
+ if ("data" in response) {
+ await refetchSuppliers();
+ const newSupplierId =
+ (suppliers?.length || 0) + 1;
+ arrayHelpers.replace(index, {
+ ...item,
+ supplier: newSupplierId,
+ });
+ }
+ } catch (e) {
+ console.error("Failed to create supplier:", e);
+ }
+ }}
+ placeholder="Start typing to select or add"
+ />
+
+
+
+ Cost (€) *
+
+ }
+ />
+ {
+ 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 {}