Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const schema = z.object({
programId: z.string(),
groupId: z.string(),
partnerIds: z.array(z.string()).min(1),
userId: z.string(),
userId: z.string().nullish(),
isGroupDeleted: z.boolean().optional(),
});

Expand Down Expand Up @@ -158,7 +158,7 @@ export async function POST(req: Request) {
tenantId: programEnrollment?.tenantId ?? undefined,
partnerGroupDefaultLinkId: link.partnerGroupDefaultLinkId,
},
userId,
userId: userId ?? undefined,
});
}),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ export async function checkoutSessionCompleted(
trigger: "partnerMetricsUpdated",
reason: "sale",
identity: {
workspaceId: workspace.id,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down Expand Up @@ -664,6 +665,7 @@ async function attributeViaPromoCode({
trigger: "partnerMetricsUpdated",
reason: "lead",
identity: {
workspaceId: workspace.id,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ export async function invoicePaid(event: Stripe.Event, mode: StripeMode) {
trigger: "partnerMetricsUpdated",
reason: "sale",
identity: {
workspaceId: workspace.id,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export async function createNewCustomer(event: Stripe.Event) {
trigger: "partnerMetricsUpdated",
reason: "lead",
identity: {
workspaceId: workspace.id,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down
1 change: 1 addition & 0 deletions apps/web/app/(ee)/api/workflows/partner-approved/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ export const { POST } = serve<Payload>(
await executeWorkflows({
trigger: "partnerEnrolled",
identity: {
workspaceId: program.workspaceId,
programId,
partnerId,
},
Expand Down
10 changes: 7 additions & 3 deletions apps/web/app/api/activity-logs/route.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw";
import { withWorkspace } from "@/lib/auth";
import {
activityLogSchema,
Expand All @@ -7,16 +8,19 @@ import { prisma } from "@dub/prisma";
import { NextResponse } from "next/server";
import * as z from "zod/v4";

// GET /api/activity-logs - get activity logs for a resource
// GET /api/activity-logs – get activity logs for a resource
export const GET = withWorkspace(async ({ workspace, searchParams }) => {
const { resourceType, resourceId } =
const { resourceType, resourceId, action } =
getActivityLogsQuerySchema.parse(searchParams);

const programId = getDefaultProgramIdOrThrow(workspace);

const activityLogs = await prisma.activityLog.findMany({
where: {
workspaceId: workspace.id,
programId,
resourceType,
resourceId,
action,
},
orderBy: {
createdAt: "desc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ export function useCommissionFilters() {
})
: undefined,
};
}),
},
),
},
],
[commissionsCount, partners, customers, groups],
Expand Down
1 change: 1 addition & 0 deletions apps/web/lib/actions/partners/create-manual-commission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ export const createManualCommissionAction = authActionClient
trigger: "partnerMetricsUpdated",
reason: "commission",
identity: {
workspaceId: workspace.id,
programId,
partnerId,
},
Expand Down
17 changes: 5 additions & 12 deletions apps/web/lib/actions/referrals/update-referral-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { getReferralOrThrow } from "@/lib/api/referrals/get-referral-or-throw";
import { markReferralClosedWon } from "@/lib/api/referrals/mark-referral-closed-won";
import { markReferralQualified } from "@/lib/api/referrals/mark-referral-qualified";
import { notifyReferralStatusUpdate } from "@/lib/api/referrals/notify-referral-status-update";
import { REFERRAL_STATUS_TRANSITIONS } from "@/lib/referrals/constants";
import {
REFERRAL_STATUS_TO_ACTIVITY_ACTION,
REFERRAL_STATUS_TRANSITIONS,
} from "@/lib/referrals/constants";
import { ReferralWithCustomer } from "@/lib/types";
import { updateReferralStatusSchema } from "@/lib/zod/schemas/referrals";
import { prisma } from "@dub/prisma";
Expand All @@ -16,16 +19,6 @@ import { waitUntil } from "@vercel/functions";
import { authActionClient } from "../safe-action";
import { throwIfNoPermission } from "../throw-if-no-permission";

const REFERRAL_EVENT_TYPES = {
[ReferralStatus.pending]: "referral.created",
[ReferralStatus.qualified]: "referral.qualified",
[ReferralStatus.meeting]: "referral.meeting",
[ReferralStatus.negotiation]: "referral.negotiation",
[ReferralStatus.unqualified]: "referral.unqualified",
[ReferralStatus.closedWon]: "referral.closedWon",
[ReferralStatus.closedLost]: "referral.closedLost",
} as const;

export const updateReferralStatusAction = authActionClient
.inputSchema(updateReferralStatusSchema)
.action(async ({ parsedInput, ctx }) => {
Expand Down Expand Up @@ -92,7 +85,7 @@ export const updateReferralStatusAction = authActionClient
resourceType: "referral",
resourceId: referral.id,
userId: user.id,
action: REFERRAL_EVENT_TYPES[status],
action: REFERRAL_STATUS_TO_ACTIVITY_ACTION[status],
description: notes,
changeSet: {
status: {
Expand Down
8 changes: 1 addition & 7 deletions apps/web/lib/api/activity-log/build-change-set.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import type { ChangeSet } from "@/lib/types";
import { PartnerGroup } from "@dub/prisma/client";

export type FieldDiff<T = unknown> = {
old: T | null;
new: T | null;
};

export type ChangeSet = Record<string, FieldDiff>;

interface BuildProgramEnrollmentChangeSetInput {
oldEnrollment:
| { partnerGroup: Pick<PartnerGroup, "id" | "name"> | null | undefined }
Expand Down
8 changes: 5 additions & 3 deletions apps/web/lib/api/activity-log/track-activity-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { logger } from "@/lib/axiom/server";
import {
ActivityLogAction,
ActivityLogResourceType,
} from "@/lib/zod/schemas/activity-log";
ChangeSet,
} from "@/lib/types";
import { prisma } from "@dub/prisma";
import { Prisma } from "@dub/prisma/client";
import { prettyPrint } from "@dub/utils";
import { ChangeSet } from "./build-change-set";

export interface TrackActivityLogInput
extends Pick<
Expand All @@ -24,7 +24,9 @@ export const trackActivityLog = async (
let inputs = Array.isArray(input) ? input : [input];

inputs = inputs.filter(
(i) => i.changeSet && Object.keys(i.changeSet).length > 0,
(i) =>
i.action === "referral.created" ||
(i.changeSet && Object.keys(i.changeSet).length > 0),
);

if (inputs.length === 0) {
Expand Down
1 change: 1 addition & 0 deletions apps/web/lib/api/conversions/track-lead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ export const trackLead = async ({
trigger: "partnerMetricsUpdated",
reason: "lead",
identity: {
workspaceId: workspace.id,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down
2 changes: 2 additions & 0 deletions apps/web/lib/api/conversions/track-sale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ const _trackLead = async ({
trigger: "partnerMetricsUpdated",
reason: "lead",
identity: {
workspaceId: workspace.id,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down Expand Up @@ -567,6 +568,7 @@ const _trackSale = async ({
trigger: "partnerMetricsUpdated",
reason: "sale",
identity: {
workspaceId: workspace.id,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down
22 changes: 19 additions & 3 deletions apps/web/lib/api/groups/move-partners-to-group.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { qstash } from "@/lib/cron";
import { recordLink } from "@/lib/tinybird";
import { prisma } from "@dub/prisma";
import { PartnerGroup } from "@dub/prisma/client";
import { PartnerGroup, WorkspaceRole } from "@dub/prisma/client";
import { APP_DOMAIN_WITH_NGROK } from "@dub/utils";
import { waitUntil } from "@vercel/functions";
import { buildProgramEnrollmentChangeSet } from "../activity-log/build-change-set";
Expand All @@ -10,6 +10,7 @@ import {
TrackActivityLogInput,
} from "../activity-log/track-activity-log";
import { triggerDraftBountySubmissionCreation } from "../bounties/trigger-draft-bounty-submissions";
import { getWorkspaceUsers } from "../get-workspace-users";
import { includeProgramEnrollment } from "../links/include-program-enrollment";
import { includeTags } from "../links/include-tags";
import { notifyPartnerGroupChange } from "../partners/notify-partner-group-change";
Expand All @@ -18,7 +19,7 @@ interface MovePartnersToGroupParams {
workspaceId: string;
programId: string;
partnerIds: string[];
userId: string;
userId: string | null;
group: Pick<
PartnerGroup,
| "id"
Expand Down Expand Up @@ -143,14 +144,29 @@ export async function movePartnersToGroup({
};
});

// If the userId is not provided, get the workspace user id from the workspace users
// userId will be null for workflow-initiated actions
let workspaceUserId = userId;

if (!workspaceUserId) {
const { users } = await getWorkspaceUsers({
programId,
role: WorkspaceRole.owner,
});

if (users.length > 0) {
workspaceUserId = users[0].id;
}
}

await Promise.allSettled([
qstash.publishJSON({
url: `${APP_DOMAIN_WITH_NGROK}/api/cron/groups/remap-default-links`,
body: {
programId,
groupId: group.id,
partnerIds,
userId,
userId: workspaceUserId,
},
}),

Expand Down
2 changes: 1 addition & 1 deletion apps/web/lib/api/partners/generate-partner-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface GeneratePartnerLinkInput {
key?: string;
partnerGroupDefaultLinkId?: string | null;
};
userId: string;
userId?: string;
}

// Generates and processes a partner link without creating it
Expand Down
17 changes: 3 additions & 14 deletions apps/web/lib/api/workflows/execute-move-group-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { WorkflowConditionAttribute, WorkflowContext } from "@/lib/types";
import { redis } from "@/lib/upstash/redis";
import { WORKFLOW_ACTION_TYPES } from "@/lib/zod/schemas/workflows";
import { prisma } from "@dub/prisma";
import { Workflow, WorkspaceRole } from "@dub/prisma/client";
import { getWorkspaceUsers } from "../get-workspace-users";
import { Workflow } from "@dub/prisma/client";
import { movePartnersToGroup } from "../groups/move-partners-to-group";
import { evaluateWorkflowConditions } from "./evaluate-workflow-conditions";
import { parseWorkflowConfig } from "./parse-workflow-config";
Expand All @@ -25,7 +24,7 @@ export const executeMoveGroupWorkflow = async ({
}

const { identity, metrics } = context;
const { programId, partnerId, groupId } = identity;
const { workspaceId, programId, partnerId, groupId } = identity;

if (!groupId) {
console.error("Partner groupId not set in the context. Skipping..");
Expand Down Expand Up @@ -97,16 +96,6 @@ export const executeMoveGroupWorkflow = async ({
return;
}

const { id: workspaceId, users } = await getWorkspaceUsers({
programId,
role: WorkspaceRole.owner,
});

if (users.length === 0) {
console.log("No owners found for the program. Skipping..");
return;
}

// Prevents duplicate moves when a workflow with matching conditions
// are triggered by the same partnerMetricsUpdated event.
const lockKey = `workflow:moveGroup:${programId}:${newGroupId}:${partnerId}`;
Expand All @@ -122,7 +111,7 @@ export const executeMoveGroupWorkflow = async ({
workspaceId,
programId,
partnerIds: [partnerId],
userId: users[0].id,
userId: null,
group: newGroup,
});
} finally {
Expand Down
1 change: 1 addition & 0 deletions apps/web/lib/integrations/shopify/create-sale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export async function createShopifySale({
trigger: "partnerMetricsUpdated",
reason: "sale",
identity: {
workspaceId: workspaceId,
programId: link.programId,
partnerId: link.partnerId,
},
Expand Down
1 change: 1 addition & 0 deletions apps/web/lib/partners/create-partner-commission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ export const createPartnerCommission = async ({
trigger: "partnerMetricsUpdated",
reason: "commission",
identity: {
workspaceId: workspace.id,
programId,
partnerId,
},
Expand Down
14 changes: 14 additions & 0 deletions apps/web/lib/referrals/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ActivityLogAction } from "@/lib/types";
import { textFieldSchema } from "@/lib/zod/schemas/referral-form";
import { ReferralStatus } from "@dub/prisma/client";
import { ACME_PROGRAM_ID } from "@dub/utils";
Expand Down Expand Up @@ -53,6 +54,19 @@ export const REFERRAL_ENABLED_PROGRAM_IDS = [
"prog_1KFZQJZRDRV62C037FQZSY0Y8", // FFG
];

export const REFERRAL_STATUS_TO_ACTIVITY_ACTION: Record<
ReferralStatus,
ActivityLogAction
> = {
[ReferralStatus.pending]: "referral.created",
[ReferralStatus.qualified]: "referral.qualified",
[ReferralStatus.meeting]: "referral.meeting",
[ReferralStatus.negotiation]: "referral.negotiation",
[ReferralStatus.unqualified]: "referral.unqualified",
[ReferralStatus.closedWon]: "referral.closedWon",
[ReferralStatus.closedLost]: "referral.closedLost",
};

export const REFERRAL_STATUS_TRANSITIONS: Record<
ReferralStatus,
readonly ReferralStatus[]
Expand Down
42 changes: 42 additions & 0 deletions apps/web/lib/swr/use-activity-logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import useWorkspace from "@/lib/swr/use-workspace";
import { ActivityLog, GetActivityLogsQuery } from "@/lib/types";
import { fetcher } from "@dub/utils";
import useSWR from "swr";

export function useActivityLogs({
query,
enabled = true,
}: {
query?: GetActivityLogsQuery;
enabled?: boolean;
} = {}) {
const { id: workspaceId } = useWorkspace();

const searchParams = query
? new URLSearchParams({
workspaceId: workspaceId!,
resourceType: query.resourceType,
resourceId: query.resourceId,
...(query.action && { action: query.action }),
}).toString()
: "";

const { data, error, isLoading, mutate } = useSWR<ActivityLog[]>(
enabled &&
workspaceId &&
query?.resourceType &&
query?.resourceId &&
`/api/activity-logs?${searchParams}`,
fetcher,
{
keepPreviousData: true,
},
);

return {
activityLogs: data,
error,
loading: isLoading,
mutate,
};
}
Loading