From 372e44bed9b993055e119d4312cb38baf970cb9a Mon Sep 17 00:00:00 2001 From: Davide Bizzi Date: Mon, 8 Sep 2025 17:25:38 +0200 Subject: [PATCH 1/3] feat(CampaignForm): add AutoApply field to campaign form and update related interfaces --- .../components/campaignForm/FormProvider.tsx | 3 ++ .../campaignForm/fields/AutoApply.tsx | 34 +++++++++++++++++++ .../components/campaignForm/index.tsx | 2 ++ src/services/tryberApi/index.ts | 4 +++ 4 files changed, 43 insertions(+) create mode 100644 src/pages/campaigns/components/campaignForm/fields/AutoApply.tsx diff --git a/src/pages/campaigns/components/campaignForm/FormProvider.tsx b/src/pages/campaigns/components/campaignForm/FormProvider.tsx index 590ddf6c..7e000d8d 100644 --- a/src/pages/campaigns/components/campaignForm/FormProvider.tsx +++ b/src/pages/campaigns/components/campaignForm/FormProvider.tsx @@ -65,6 +65,7 @@ export interface NewCampaignValues { notes?: string; cuf?: { id: string; value: string[] }[]; provinces?: string[]; + autoApply?: boolean; } const useGetInitialCufCriteria = ({ @@ -187,6 +188,7 @@ const FormProvider = ({ })) || [], cuf: initialCufCriteria, provinces: dossier?.visibilityCriteria?.province || [], + autoApply: dossier?.autoApply === 1, }; const validationSchema = yup.object({ @@ -354,6 +356,7 @@ const FormProvider = ({ ? parseInt(values.productType, 10) : undefined, notes: values.notes, + autoApply: values.autoApply, visibilityCriteria: { gender: values.genderRequirements?.options || [], cuf: values.cuf diff --git a/src/pages/campaigns/components/campaignForm/fields/AutoApply.tsx b/src/pages/campaigns/components/campaignForm/fields/AutoApply.tsx new file mode 100644 index 00000000..71a0154e --- /dev/null +++ b/src/pages/campaigns/components/campaignForm/fields/AutoApply.tsx @@ -0,0 +1,34 @@ +import { + Checkbox, + FieldProps, + FormikField, + FormLabel, +} from "@appquality/appquality-design-system"; +import { useFormikContext } from "formik"; +import { NewCampaignValues } from "../FormProvider"; + +const AutoApply = () => { + const { setFieldValue } = useFormikContext(); + + return ( +
+ + + + {({ field }: FieldProps) => ( + { + setFieldValue("autoApply", e.target.checked); + }} + /> + )} + +
+ ); +}; + +export default AutoApply; diff --git a/src/pages/campaigns/components/campaignForm/index.tsx b/src/pages/campaigns/components/campaignForm/index.tsx index e63f6906..cd91339e 100644 --- a/src/pages/campaigns/components/campaignForm/index.tsx +++ b/src/pages/campaigns/components/campaignForm/index.tsx @@ -21,6 +21,7 @@ import { PhaseSelector } from "../PhaseSelector"; import { CampaignFormContext } from "./campaignFormContext"; import FormOverlay from "./feedbackMessages/FormOverlay"; import AgeRequirements from "./fields/AgeRequirements"; +import AutoApply from "./fields/AutoApply"; import BrowsersMultiselect from "./fields/BrowsersMultiselect"; import CountrySelect from "./fields/CountrySelect"; import CufCriteria from "./fields/CufCriteria"; @@ -129,6 +130,7 @@ const CampaignFormContent = ({ dossier, isEdit, duplicate }: FormProps) => { required /> + Give some context to your co-workers diff --git a/src/services/tryberApi/index.ts b/src/services/tryberApi/index.ts index 3334d5ac..c387c260 100644 --- a/src/services/tryberApi/index.ts +++ b/src/services/tryberApi/index.ts @@ -1367,6 +1367,8 @@ export type GetCampaignsByCampaignBugsAndBugIdApiResponse = id: number; media: { id: number; + type: string; + url: string; }[]; note: string; reason: string; @@ -1877,6 +1879,7 @@ export type PostDossiersApiArg = { }; }; export type GetDossiersByCampaignApiResponse = /** status 200 OK */ { + autoApply: number; browsers?: { id: number; name: string; @@ -3122,6 +3125,7 @@ export type DossierCreationData = { additionals?: ({ showInStats?: boolean; } & CampaignAdditionalField)[]; + autoApply?: boolean; browsers?: number[]; bugTypes?: number[]; closeDate?: string; From 7e42ff77d37457eb3046d64583eab70a0266cc35 Mon Sep 17 00:00:00 2001 From: Davide Bizzi <davide.bizzi@unguess.io> Date: Tue, 9 Sep 2025 10:26:26 +0200 Subject: [PATCH 2/3] feat(CampaignForm): conditionally render AutoApply field in edit mode --- src/pages/campaigns/components/campaignForm/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/campaigns/components/campaignForm/index.tsx b/src/pages/campaigns/components/campaignForm/index.tsx index cd91339e..3e266457 100644 --- a/src/pages/campaigns/components/campaignForm/index.tsx +++ b/src/pages/campaigns/components/campaignForm/index.tsx @@ -130,7 +130,7 @@ const CampaignFormContent = ({ dossier, isEdit, duplicate }: FormProps) => { required /> <TestTypeSelect /> - <AutoApply /> + {isEdit && <AutoApply />} </FieldWrapper> <Title size="s" className="aq-mb-2 aq-pt-4"> Give some context to your co-workers From f7f2d13f09307e2069285271274029aca93d6445 Mon Sep 17 00:00:00 2001 From: Davide Bizzi <davide.bizzi@unguess.io> Date: Tue, 16 Sep 2025 15:11:41 +0200 Subject: [PATCH 3/3] fix(FormProvider): update autoApply handling and refactor dossier creation data structure --- .../components/campaignForm/FormProvider.tsx | 7 +- src/services/tryberApi/index.ts | 183 +++++++++++++++++- 2 files changed, 183 insertions(+), 7 deletions(-) diff --git a/src/pages/campaigns/components/campaignForm/FormProvider.tsx b/src/pages/campaigns/components/campaignForm/FormProvider.tsx index 7e000d8d..0e2df8cb 100644 --- a/src/pages/campaigns/components/campaignForm/FormProvider.tsx +++ b/src/pages/campaigns/components/campaignForm/FormProvider.tsx @@ -3,7 +3,6 @@ import { useMemo } from "react"; import { useHistory } from "react-router-dom"; import { addMessage } from "src/redux/siteWideMessages/actionCreators"; import { - DossierCreationData, GetDossiersByCampaignApiResponse, PostDossiersApiArg, useGetDevicesByDeviceTypeOperatingSystemsQuery, @@ -322,7 +321,7 @@ const FormProvider = ({ }); } try { - const body: DossierCreationData = { + const body = { project: parseInt(values.projectId), testType: parseInt(values.testType), title: { @@ -356,7 +355,7 @@ const FormProvider = ({ ? parseInt(values.productType, 10) : undefined, notes: values.notes, - autoApply: values.autoApply, + autoApply: values.autoApply ? 1 : 0, visibilityCriteria: { gender: values.genderRequirements?.options || [], cuf: values.cuf @@ -397,7 +396,7 @@ const FormProvider = ({ if (isEdit) { await putDossiers({ campaign: dossier?.id.toString() || "", - dossierCreationData: body, + body, }).unwrap(); } else { const resp = await postDossiers({ diff --git a/src/services/tryberApi/index.ts b/src/services/tryberApi/index.ts index c387c260..554e5555 100644 --- a/src/services/tryberApi/index.ts +++ b/src/services/tryberApi/index.ts @@ -259,6 +259,16 @@ const injectedRtkApi = api.injectEndpoints({ >({ query: (queryArg) => ({ url: `/campaigns/${queryArg.campaign}/payouts` }), }), + putCampaignsByCampaignPayouts: build.mutation< + PutCampaignsByCampaignPayoutsApiResponse, + PutCampaignsByCampaignPayoutsApiArg + >({ + query: (queryArg) => ({ + url: `/campaigns/${queryArg.campaign}/payouts`, + method: "PUT", + body: queryArg.body, + }), + }), getCampaignsByCampaignProspect: build.query< GetCampaignsByCampaignProspectApiResponse, GetCampaignsByCampaignProspectApiArg @@ -459,7 +469,7 @@ const injectedRtkApi = api.injectEndpoints({ query: (queryArg) => ({ url: `/dossiers/${queryArg.campaign}`, method: "PUT", - body: queryArg.dossierCreationData, + body: queryArg.body, }), }), getDossiersByCampaignAvailableTesters: build.query< @@ -801,6 +811,40 @@ const injectedRtkApi = api.injectEndpoints({ body: queryArg.body, }), }), + getUsersMeCampaignsByCampaignIdPayoutData: build.query< + GetUsersMeCampaignsByCampaignIdPayoutDataApiResponse, + GetUsersMeCampaignsByCampaignIdPayoutDataApiArg + >({ + query: (queryArg) => ({ + url: `/users/me/campaigns/${queryArg.campaignId}/payout_data`, + }), + }), + getUsersMeCampaignsByCampaignIdPreview: build.query< + GetUsersMeCampaignsByCampaignIdPreviewApiResponse, + GetUsersMeCampaignsByCampaignIdPreviewApiArg + >({ + query: (queryArg) => ({ + url: `/users/me/campaigns/${queryArg.campaignId}/preview`, + }), + }), + getUsersMeCampaignsByCampaignIdTasks: build.query< + GetUsersMeCampaignsByCampaignIdTasksApiResponse, + GetUsersMeCampaignsByCampaignIdTasksApiArg + >({ + query: (queryArg) => ({ + url: `/users/me/campaigns/${queryArg.campaignId}/tasks`, + }), + }), + postUsersMeCampaignsByCampaignIdTasksAndTaskId: build.mutation< + PostUsersMeCampaignsByCampaignIdTasksAndTaskIdApiResponse, + PostUsersMeCampaignsByCampaignIdTasksAndTaskIdApiArg + >({ + query: (queryArg) => ({ + url: `/users/me/campaigns/${queryArg.campaignId}/tasks/${queryArg.taskId}`, + method: "POST", + body: queryArg.body, + }), + }), getUsersMeCampaignsByCampaignCompatibleDevices: build.query< GetUsersMeCampaignsByCampaignCompatibleDevicesApiResponse, GetUsersMeCampaignsByCampaignCompatibleDevicesApiArg @@ -1562,6 +1606,46 @@ export type GetCampaignsByCampaignPayoutsApiArg = { /** A campaign id */ campaign: string; }; +export type PutCampaignsByCampaignPayoutsApiResponse = /** status 200 OK */ { + campaign_complete_bonus_eur?: number; + campaign_pts?: number; + critical_bug_payout?: number; + high_bug_payout?: number; + low_bug_payout?: number; + medium_bug_payout?: number; + minimum_bugs?: number; + payout_limit?: number; + percent_usecases?: number; + point_multiplier_critical?: number; + point_multiplier_high?: number; + point_multiplier_low?: number; + point_multiplier_medium?: number; + point_multiplier_perfect?: number; + point_multiplier_refused?: number; + top_tester_bonus?: number; +}; +export type PutCampaignsByCampaignPayoutsApiArg = { + /** A campaign id */ + campaign: string; + body: { + campaign_complete_bonus_eur?: number; + campaign_pts?: number; + critical_bug_payout?: number; + high_bug_payout?: number; + low_bug_payout?: number; + medium_bug_payout?: number; + minimum_bugs?: number; + payout_limit?: number; + percent_usecases?: number; + point_multiplier_critical?: number; + point_multiplier_high?: number; + point_multiplier_low?: number; + point_multiplier_medium?: number; + point_multiplier_perfect?: number; + point_multiplier_refused?: number; + top_tester_bonus?: number; + }; +}; export type GetCampaignsByCampaignProspectApiResponse = /** status 200 OK */ { items: { bugs: { @@ -1875,6 +1959,8 @@ export type PostDossiersApiArg = { useCases?: number; }; } & { + autoApply?: number; + pageVersion?: "v1" | "v2"; skipPagesAndTasks?: number; }; }; @@ -1968,7 +2054,9 @@ export type PutDossiersByCampaignApiResponse = /** status 200 OK */ {}; export type PutDossiersByCampaignApiArg = { /** A campaign id */ campaign: string; - dossierCreationData: DossierCreationData; + body: DossierCreationData & { + autoApply?: number; + }; }; export type GetDossiersByCampaignAvailableTestersApiResponse = /** status 200 OK */ { @@ -2435,9 +2523,16 @@ export type GetUsersMeCampaignsByCampaignIdApiResponse = /** status 200 OK */ { invalid: string[]; valid: string[]; }; + campaign_type: { + icon: string; + id: number; + name: string; + }; devices?: ({ id: number; } & UserDevice)[]; + end_date: string; + goal: string; hasBugForm: boolean; id: number; language?: { @@ -2576,6 +2671,81 @@ export type PostUsersMeCampaignsByCampaignIdMediaApiArg = { media?: {} | string[]; }; }; +export type GetUsersMeCampaignsByCampaignIdPayoutDataApiResponse = + /** status 200 OK */ { + campaign_complete_bonus_eur: number; + campaign_pts: number; + critical_bug_payout: number; + high_bug_payout: number; + low_bug_payout: number; + medium_bug_payout: number; + minimum_bugs: number; + payout_limit: number; + percent_usecases: number; + point_multiplier_critical: number; + point_multiplier_high: number; + point_multiplier_low: number; + point_multiplier_medium: number; + point_multiplier_perfect: number; + point_multiplier_refused: number; + top_tester_bonus: number; + }; +export type GetUsersMeCampaignsByCampaignIdPayoutDataApiArg = { + campaignId: string; +}; +export type GetUsersMeCampaignsByCampaignIdPreviewApiResponse = + /** status 200 OK */ { + acceptedDevices: { + console?: AvailableDevice[] | "all"; + pc?: AvailableDevice[] | "all"; + smartTv?: AvailableDevice[] | "all"; + smartphone?: AvailableDevice[] | "all"; + smartwatch?: AvailableDevice[] | "all"; + tablet?: AvailableDevice[] | "all"; + }; + cap?: { + free: number; + value: number; + }; + content: string; + endDate: string; + selectionStatus?: "starting" | "excluded" | "ready" | "complete"; + startDate: string; + status: "available" | "applied" | "excluded" | "selected"; + title: string; + tl?: { + email: string; + name: string; + }; + type: { + icon: string; + name: string; + }; + }; +export type GetUsersMeCampaignsByCampaignIdPreviewApiArg = { + campaignId: string; +}; +export type GetUsersMeCampaignsByCampaignIdTasksApiResponse = + /** status 200 OK */ { + content: string; + id: number; + is_required: number; + name: string; + status: "completed" | "pending"; + }[]; +export type GetUsersMeCampaignsByCampaignIdTasksApiArg = { + campaignId: string; +}; +export type PostUsersMeCampaignsByCampaignIdTasksAndTaskIdApiResponse = + /** status 200 OK */ string; +export type PostUsersMeCampaignsByCampaignIdTasksAndTaskIdApiArg = { + /** the campaign id */ + campaignId: string; + taskId: string; + body: { + status: "completed"; + }; +}; export type GetUsersMeCampaignsByCampaignCompatibleDevicesApiResponse = /** status 200 OK */ UserDevice[]; export type GetUsersMeCampaignsByCampaignCompatibleDevicesApiArg = { @@ -3125,7 +3295,6 @@ export type DossierCreationData = { additionals?: ({ showInStats?: boolean; } & CampaignAdditionalField)[]; - autoApply?: boolean; browsers?: number[]; bugTypes?: number[]; closeDate?: string; @@ -3235,6 +3404,9 @@ export type UserDevice = { }; type: string; }; +export type AvailableDevice = { + name: string; +}; export type FiscalType = | "withholding" | "witholding-extra" @@ -3291,6 +3463,7 @@ export const { useGetCampaignsByCampaignGroupsQuery, useGetCampaignsByCampaignObservationsQuery, useGetCampaignsByCampaignPayoutsQuery, + usePutCampaignsByCampaignPayoutsMutation, useGetCampaignsByCampaignProspectQuery, usePatchCampaignsByCampaignProspectMutation, usePutCampaignsByCampaignProspectMutation, @@ -3357,6 +3530,10 @@ export const { useGetUsersMeCampaignsByCampaignIdFormsQuery, usePostUsersMeCampaignsByCampaignIdFormsMutation, usePostUsersMeCampaignsByCampaignIdMediaMutation, + useGetUsersMeCampaignsByCampaignIdPayoutDataQuery, + useGetUsersMeCampaignsByCampaignIdPreviewQuery, + useGetUsersMeCampaignsByCampaignIdTasksQuery, + usePostUsersMeCampaignsByCampaignIdTasksAndTaskIdMutation, useGetUsersMeCampaignsByCampaignCompatibleDevicesQuery, usePostUsersMeCertificationsMutation, useDeleteUsersMeCertificationsByCertificationIdMutation,