Skip to content

Commit

Permalink
b-2716-add-billings-profile-column-and-popover (#669)
Browse files Browse the repository at this point in the history
  • Loading branch information
haydencleary authored Nov 20, 2024
1 parent 5f3e673 commit d228c89
Show file tree
Hide file tree
Showing 17 changed files with 299 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ColumnDef, createColumnHelper } from "@tanstack/react-table";

import { MeReactQueryAdapter } from "@/core/application/react-query-adapter/me";
import { bootstrap } from "@/core/bootstrap";
import { MeContributorProjectsInterface } from "@/core/domain/me/models/me-contributor-projects-model";

Expand All @@ -11,6 +12,7 @@ import { MARKETPLACE_ROUTER } from "@/shared/constants/router";
import { TABLE_CELL_SIZE } from "@/shared/constants/table";
import { ContributionsPopover } from "@/shared/features/contributions/contributions-popover/contributions-popover";
import { ReposPopover } from "@/shared/features/repos/repos-popover/repos-popover";
import { CellBillingProfile } from "@/shared/features/table/cell/cell-billing-profile/cell-billing-profile";
import { CellEmpty } from "@/shared/features/table/cell/cell-empty/cell-empty";
import { CellLanguages } from "@/shared/features/table/cell/cell-languages/cell-languages";
import { CellLeads } from "@/shared/features/table/cell/cell-leads/cell-leads";
Expand All @@ -23,6 +25,8 @@ export function useFilterColumns() {
const moneyKernelPort = bootstrap.getMoneyKernelPort();
const columnHelper = createColumnHelper<MeContributorProjectsInterface>();

const { data: myPayoutPreferences } = MeReactQueryAdapter.client.useGetMyPayoutPreferences({});

const columnMap: Partial<Record<TableColumns, object>> = {
name: columnHelper.accessor("name", {
enableSorting: false,
Expand Down Expand Up @@ -146,7 +150,27 @@ export function useFilterColumns() {
return <ReposPopover repos={repos} />;
},
}),
billingProfile: columnHelper.accessor("billingProfile", {
enableSorting: false,
size: TABLE_CELL_SIZE.LG,
minSize: TABLE_CELL_SIZE.LG,
header: () => <Translate token={"myDashboard:detail.projectsTable.columns.billingProfile"} />,
cell: info => {
const billingProfile = info.getValue();
const projectId = info.row.original.id;

const projectExistsInPayoutPreferences = Boolean(
myPayoutPreferences?.find(item => item.project.id === projectId)
);

// To select a billing profile, a billing profile must already be set or the project must exist in the payout preferences
if (!billingProfile && !projectExistsInPayoutPreferences) {
return <CellEmpty />;
}

return <CellBillingProfile projectId={projectId} billingProfile={billingProfile} />;
},
}),
actions: columnHelper.display({
id: "actions",
enableResizing: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export type TableColumns =
| "languages"
| "repos"
| "issues"
| "billingProfile"
| "actions";
1 change: 1 addition & 0 deletions app/my-dashboard/_translations/my-dashboard.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"rewardedUsdAmount": "Rewarded",
"languages": "Languages",
"repositories": "Repositories",
"billingProfile": "Billing profile",
"actions": "Actions",
"issues": "Issues availables"
},
Expand Down
2 changes: 2 additions & 0 deletions core/application/react-query-adapter/me/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export * from "./use-set-my-profile";
export * from "./use-replace-my-profile";
export * from "./use-get-my-projects-as-maintainer";
export * from "./use-get-my-projects-as-contributor";
export * from "./use-get-my-payout-preferences";
export * from "./use-set-my-preference-for-project";
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useQuery } from "@tanstack/react-query";

import {
UseQueryFacadeParams,
useQueryAdapter,
} from "@/core/application/react-query-adapter/helpers/use-query-adapter";
import { bootstrap } from "@/core/bootstrap";
import { MeFacadePort } from "@/core/domain/me/inputs/me-facade-port";
import { GetMyPayoutPreferencesModel } from "@/core/domain/me/me-contract.types";

export function useGetMyPayoutPreferences({
options,
}: UseQueryFacadeParams<MeFacadePort["getMyPayoutPreferences"], GetMyPayoutPreferencesModel>) {
const meStoragePort = bootstrap.getMeStoragePortForClient();

return useQuery(
useQueryAdapter({
...meStoragePort.getMyPayoutPreferences({}),
options,
})
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";

import {
UseMutationFacadeParams,
useMutationAdapter,
} from "@/core/application/react-query-adapter/helpers/use-mutation-adapter";
import { bootstrap } from "@/core/bootstrap";
import { MeFacadePort } from "@/core/domain/me/inputs/me-facade-port";
import { SetMyPayoutPreferenceForProjectBody } from "@/core/domain/me/me-contract.types";

export function useSetMyPreferenceForProject({
options,
}: UseMutationFacadeParams<
MeFacadePort["setMyPayoutPreferenceForProject"],
undefined,
never,
SetMyPayoutPreferenceForProjectBody
> = {}) {
const meStoragePort = bootstrap.getMeStoragePortForClient();
const rewardStoragePort = bootstrap.getRewardStoragePortForClient();
const billingProfileStoragePort = bootstrap.getBillingProfileStoragePortForClient();

const queryClient = useQueryClient();

return useMutation(
useMutationAdapter({
...meStoragePort.setMyPayoutPreferenceForProject({}),
options: {
...options,
onSuccess: async (data, variables, context) => {
await queryClient.invalidateQueries({
queryKey: meStoragePort.getMe({}).tag,
exact: false,
});

await queryClient.invalidateQueries({
queryKey: meStoragePort.getMyProjectsAsContributor({}).tag,
exact: false,
});

await queryClient.invalidateQueries({
queryKey: meStoragePort.getMyPayoutPreferences({}).tag,
exact: false,
});

await queryClient.invalidateQueries({
queryKey: rewardStoragePort.getRewards({}).tag,
exact: false,
});

await queryClient.invalidateQueries({
queryKey: billingProfileStoragePort.getMyBillingProfiles({}).tag,
exact: false,
});

options?.onSuccess?.(data, variables, context);
},
},
})
);
}
8 changes: 8 additions & 0 deletions core/domain/me/inputs/me-facade-port.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
GetMeResponsePortParams,
GetMeResponsePortResponse,
GetMyPayoutPreferencesPortParams,
GetMyPayoutPreferencesPortResponse,
GetMyProfilePortParams,
GetMyProfilePortResponse,
GetMyProjectsAsContributorPortParams,
Expand All @@ -13,6 +15,8 @@ import {
ReplaceMyProfilePortResponse,
SetMePortParams,
SetMePortResponse,
SetMyPayoutPreferenceForProjectPortParams,
SetMyPayoutPreferenceForProjectPortResponse,
SetMyProfilePortParams,
SetMyProfilePortResponse,
} from "@/core/domain/me/me-contract.types";
Expand All @@ -26,4 +30,8 @@ export interface MeFacadePort {
replaceMyProfile(params: ReplaceMyProfilePortParams): ReplaceMyProfilePortResponse;
getMyProjectsAsMaintainer(p: GetMyProjectsAsMaintainerPortParams): GetMyProjectsAsMaintainerPortResponse;
getMyProjectsAsContributor(p: GetMyProjectsAsContributorPortParams): GetMyProjectsAsContributorPortResponse;
getMyPayoutPreferences(p: GetMyPayoutPreferencesPortParams): GetMyPayoutPreferencesPortResponse;
setMyPayoutPreferenceForProject(
p: SetMyPayoutPreferenceForProjectPortParams
): SetMyPayoutPreferenceForProjectPortResponse;
}
26 changes: 26 additions & 0 deletions core/domain/me/me-contract.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { BillingProfileShortInterface } from "@/core/domain/billing-profile/models/billing-profile-short-model";
import { MeContributorProjectsInterface } from "@/core/domain/me/models/me-contributor-projects-model";
import { MeMaintainerProjectsInterface } from "@/core/domain/me/models/me-maintainer-projects-model";
import { MeInterface } from "@/core/domain/me/models/me-model";
import { MeProfileInterface } from "@/core/domain/me/models/me-profile-model";
import { ProjectShortInterface } from "@/core/domain/project/models/project-short-model";
import { components, operations } from "@/core/infrastructure/marketplace-api-client-adapter/__generated/api";
import {
HttpClientParameters,
Expand Down Expand Up @@ -87,3 +89,27 @@ export type GetMyProjectsAsContributorPortParams = HttpClientParameters<{
}>;

export type GetMyProjectsAsContributorPortResponse = HttpStorageResponse<GetMyProjectsAsContributorModel>;

/* ------------------------------ Get My Payout Preferences ------------------------------ */

export type GetMyPayoutPreferencesResponse = components["schemas"]["PayoutPreferencesItemResponse"][];

export type GetMyPayoutPreferencesModel = {
project: ProjectShortInterface;
billingProfile?: BillingProfileShortInterface;
}[];

export type GetMyPayoutPreferencesPortParams = HttpClientParameters<object>;

export type GetMyPayoutPreferencesPortResponse = HttpStorageResponse<GetMyPayoutPreferencesModel>;

/* ------------------------------ Set My Payout Preference For Project ------------------------------ */

export type SetMyPayoutPreferenceForProjectBody = components["schemas"]["PayoutPreferenceRequest"];

export type SetMyPayoutPreferenceForProjectPortParams = HttpClientParameters<object>;

export type SetMyPayoutPreferenceForProjectPortResponse = HttpStorageResponse<
never,
SetMyPayoutPreferenceForProjectBody
>;
8 changes: 8 additions & 0 deletions core/domain/me/outputs/me-storage-port.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
GetMeResponsePortParams,
GetMeResponsePortResponse,
GetMyPayoutPreferencesPortParams,
GetMyPayoutPreferencesPortResponse,
GetMyProfilePortParams,
GetMyProfilePortResponse,
GetMyProjectsAsContributorPortParams,
Expand All @@ -13,6 +15,8 @@ import {
ReplaceMyProfilePortResponse,
SetMePortParams,
SetMePortResponse,
SetMyPayoutPreferenceForProjectPortParams,
SetMyPayoutPreferenceForProjectPortResponse,
SetMyProfilePortParams,
SetMyProfilePortResponse,
} from "@/core/domain/me/me-contract.types";
Expand All @@ -27,4 +31,8 @@ export interface MeStoragePort {
replaceMyProfile(params: ReplaceMyProfilePortParams): ReplaceMyProfilePortResponse;
getMyProjectsAsMaintainer(p: GetMyProjectsAsMaintainerPortParams): GetMyProjectsAsMaintainerPortResponse;
getMyProjectsAsContributor(p: GetMyProjectsAsContributorPortParams): GetMyProjectsAsContributorPortResponse;
getMyPayoutPreferences(p: GetMyPayoutPreferencesPortParams): GetMyPayoutPreferencesPortResponse;
setMyPayoutPreferenceForProject(
p: SetMyPayoutPreferenceForProjectPortParams
): SetMyPayoutPreferenceForProjectPortResponse;
}
19 changes: 19 additions & 0 deletions core/domain/project/models/project-short-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { components } from "@/core/infrastructure/marketplace-api-client-adapter/__generated/api";

export type ProjectShortResponse = components["schemas"]["ProjectShortResponse"];

export interface ProjectShortInterface extends ProjectShortResponse {}

export class ProjectShort implements ProjectShortInterface {
shortDescription!: ProjectShortResponse["shortDescription"];
id!: ProjectShortResponse["id"];
languages!: ProjectShortResponse["languages"];
logoUrl!: ProjectShortResponse["logoUrl"];
name!: ProjectShortResponse["name"];
slug!: ProjectShortResponse["slug"];
visibility!: ProjectShortResponse["visibility"];

constructor(props: ProjectShortResponse) {
Object.assign(this, props);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { bootstrap } from "@/core/bootstrap";
import { BillingProfileShort } from "@/core/domain/billing-profile/models/billing-profile-short-model";
import {
GetMeResponse,
GetMyPayoutPreferencesResponse,
GetMyProfileResponse,
GetMyProjectsAsContributorResponse,
GetMyProjectsAsMaintainerResponse,
LogoutMeResponse,
ReplaceMyProfileBody,
SetMeBody,
SetMyPayoutPreferenceForProjectBody,
SetMyProfileBody,
} from "@/core/domain/me/me-contract.types";
import { MeContributorProjects } from "@/core/domain/me/models/me-contributor-projects-model";
import { MeMaintainerProjects } from "@/core/domain/me/models/me-maintainer-projects-model";
import { Me } from "@/core/domain/me/models/me-model";
import { MeProfile } from "@/core/domain/me/models/me-profile-model";
import { MeStoragePort } from "@/core/domain/me/outputs/me-storage-port";
import { ProjectShort } from "@/core/domain/project/models/project-short-model";
import { HttpClient } from "@/core/infrastructure/marketplace-api-client-adapter/http/http-client/http-client";
import { AnyType, FirstParameter } from "@/core/kernel/types";

Expand All @@ -30,6 +34,8 @@ export class MeClientAdapter implements MeStoragePort {
getMeProjects: "me/projects",
getMyProjectsAsMaintainer: "me/as-maintainer/projects",
getMyProjectsAsContributor: "me/as-contributor/projects",
getMyPayoutPreferences: "me/payout-preferences",
setMyPayoutPreferenceForProject: "me/payout-preferences",
} as const;

logoutMe = () => {
Expand Down Expand Up @@ -207,4 +213,46 @@ export class MeClientAdapter implements MeStoragePort {
tag,
};
};

getMyPayoutPreferences = () => {
const path = this.routes["getMyPayoutPreferences"];
const method = "GET";
const tag = HttpClient.buildTag({ path });
const request = async () => {
const data = await this.client.request<GetMyPayoutPreferencesResponse>({
path,
method,
tag,
});

return data.map(item => ({
project: new ProjectShort(item.project),
billingProfile: item.billingProfile ? new BillingProfileShort(item.billingProfile) : undefined,
}));
};

return {
request,
tag,
};
};

setMyPayoutPreferenceForProject = () => {
const path = this.routes["setMyPayoutPreferenceForProject"];
const method = "PUT";
const tag = HttpClient.buildTag({ path });

const request = async (body: SetMyPayoutPreferenceForProjectBody) =>
this.client.request<never>({
path,
method,
tag,
body: JSON.stringify(body),
});

return {
request,
tag,
};
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ export class MeClientAdapterMock implements MeStoragePort {
getMyProjectsAsMaintainer = mockHttpStorageResponse<MeStoragePort["getMyProjectsAsMaintainer"]>;

getMyProjectsAsContributor = mockHttpStorageResponse<MeStoragePort["getMyProjectsAsContributor"]>;

getMyPayoutPreferences = mockHttpStorageResponse<MeStoragePort["getMyPayoutPreferences"]>;

setMyPayoutPreferenceForProject = mockHttpStorageResponse<MeStoragePort["setMyPayoutPreferenceForProject"]>;
}
3 changes: 3 additions & 0 deletions shared/features/table/cell/_translations/cell.en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"cellLeadsAvatars": {
"projectLead": "Project lead"
},
"cellBillingProfile": {
"pendingBillingProfile": "Pending billing profile"
}
}
Loading

0 comments on commit d228c89

Please sign in to comment.