Midday / apps /api /src /ai /tools /get-tracker-projects.ts
Jules
Final deployment with all fixes and verified content
c09f67c
import type { AppContext } from "@api/ai/agents/config/shared";
import { db } from "@midday/db/client";
import { getTrackerProjects } from "@midday/db/queries";
import { getAppUrl } from "@midday/utils/envs";
import { formatAmount, formatDate } from "@midday/utils/format";
import { tool } from "ai";
import { formatDistance } from "date-fns";
import { z } from "zod";
const getTrackerProjectsSchema = z.object({
cursor: z.string().nullable().optional().describe("Pagination cursor"),
sort: z
.array(z.string())
.length(2)
.nullable()
.optional()
.describe("Sort order"),
pageSize: z.number().min(1).max(100).default(10).describe("Page size"),
q: z.string().nullable().optional().describe("Search query"),
start: z.string().nullable().optional().describe("Start date (ISO 8601)"),
end: z.string().nullable().optional().describe("End date (ISO 8601)"),
status: z
.enum(["in_progress", "completed"])
.nullable()
.optional()
.describe("Project status"),
customers: z.array(z.string()).nullable().optional().describe("Customer IDs"),
tags: z.array(z.string()).nullable().optional().describe("Tag IDs"),
});
export const getTrackerProjectsTool = tool({
description:
"Retrieve and filter tracker projects with pagination, sorting, and search.",
inputSchema: getTrackerProjectsSchema,
execute: async function* (
{ cursor, sort, pageSize = 10, q, start, end, status, customers, tags },
executionOptions,
) {
const appContext = executionOptions.experimental_context as AppContext;
const teamId = appContext.teamId as string;
if (!teamId) {
yield {
text: "Unable to retrieve tracker projects: Team ID not found in context.",
};
return;
}
try {
const params = {
teamId,
cursor: cursor ?? null,
sort: sort ?? null,
pageSize,
q: q ?? null,
start: start ?? null,
end: end ?? null,
status: status ?? null,
customers: customers ?? null,
tags: tags ?? null,
};
const result = await getTrackerProjects(db, params);
if (result.data.length === 0) {
yield { text: "No tracker projects found matching your criteria." };
return;
}
const locale = appContext.locale ?? "en-US";
const baseCurrency = appContext.baseCurrency ?? "USD";
const formattedProjects = result.data.map((project) => {
const formattedRate = project.rate
? formatAmount({
amount: project.rate,
currency: project.currency || baseCurrency,
locale,
})
: "N/A";
const formattedTotalAmount = project.totalAmount
? formatAmount({
amount: project.totalAmount,
currency: project.currency || baseCurrency,
locale,
})
: "N/A";
const durationStart = new Date(0);
const durationEnd = new Date((project.totalDuration ?? 0) * 1000);
const formattedDuration = formatDistance(durationStart, durationEnd, {
includeSeconds: false,
});
return {
id: project.id,
name: project.name,
status: project.status,
customerName: project.customer?.name || "No customer",
rate: formattedRate,
totalDuration: formattedDuration,
totalAmount: formattedTotalAmount,
createdAt: formatDate(project.createdAt),
};
});
const totalDuration = result.data.reduce(
(sum, p) => sum + (p.totalDuration ?? 0),
0,
);
const totalAmount = result.data.reduce(
(sum, p) => sum + (p.totalAmount ?? 0),
0,
);
const formattedTotalAmount = formatAmount({
amount: totalAmount,
currency: baseCurrency,
locale,
});
const inProgressCount = result.data.filter(
(p) => p.status === "in_progress",
).length;
const completedCount = result.data.filter(
(p) => p.status === "completed",
).length;
const durationStart = new Date(0);
const durationEnd = new Date(totalDuration * 1000);
const formattedTotalDuration = formatDistance(
durationStart,
durationEnd,
{ includeSeconds: false },
);
const response = `| Name | Status | Customer | Rate | Duration | Total Amount | Created |\n|------|--------|----------|------|----------|--------------|----------|\n${formattedProjects.map((p) => `| ${p.name} | ${p.status} | ${p.customerName} | ${p.rate} | ${p.totalDuration} | ${p.totalAmount} | ${p.createdAt} |`).join("\n")}\n\n**${result.data.length} projects** | Total Duration: ${formattedTotalDuration} | Total Amount: ${formattedTotalAmount} | In Progress: ${inProgressCount} | Completed: ${completedCount}`;
yield {
text: response,
link: {
text: "View all projects",
url: `${getAppUrl()}/tracker`,
},
};
} catch (error) {
yield {
text: `Failed to retrieve tracker projects: ${error instanceof Error ? error.message : "Unknown error"}`,
};
}
},
});