| 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"}`, |
| }; |
| } |
| }, |
| }); |
|
|