Skip to content

Commit

Permalink
Merge pull request #217 from midday-ai/feature/filters
Browse files Browse the repository at this point in the history
Feature/filters
  • Loading branch information
pontusab authored Aug 9, 2024
2 parents 2322e80 + c424431 commit 04cbfe0
Show file tree
Hide file tree
Showing 52 changed files with 1,113 additions and 1,001 deletions.
8 changes: 4 additions & 4 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@ai-sdk/openai": "^0.0.40",
"@ai-sdk/openai": "^0.0.43",
"@date-fns/utc": "^1.2.0",
"@hookform/resolvers": "^3.9.0",
"@midday-ai/engine": "^0.1.0-alpha.22",
Expand All @@ -29,7 +29,7 @@
"@novu/headless": "^0.24.2",
"@sentry/nextjs": "^8",
"@supabase/sentry-js-integration": "^0.2.0",
"@tanstack/react-table": "^8.19.3",
"@tanstack/react-table": "^8.20.1",
"@team-plain/typescript-sdk": "4.7.0",
"@todesktop/client-active-win": "^0.15.0",
"@todesktop/client-core": "^1.8.0",
Expand All @@ -38,7 +38,7 @@
"@uidotdev/usehooks": "^2.4.1",
"@upstash/ratelimit": "^2.0.1",
"@zip.js/zip.js": "2.7.48",
"ai": "^3.3.0",
"ai": "^3.3.3",
"change-case": "^5.4.4",
"dub": "^0.35.0",
"framer-motion": "^11.3.21",
Expand All @@ -51,7 +51,7 @@
"next-international": "1.2.4",
"next-safe-action": "^7.4.3",
"next-themes": "^0.3.0",
"nuqs": "^1.17.6",
"nuqs": "^1.17.8",
"react": "18.3.1",
"react-colorful": "^5.6.1",
"react-dom": "18.3.1",
Expand Down
6 changes: 2 additions & 4 deletions apps/dashboard/src/actions/ai/chat/tools/get-transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,8 @@ export function getTransactionsTool({ aiState }: Args) {
const searchQuery = name || amount;

const filter = {
date: {
from: fromDate,
to: toDate,
},
start: fromDate,
end: toDate,
categories,
attachments,
};
Expand Down
19 changes: 16 additions & 3 deletions apps/dashboard/src/actions/ai/chat/tools/ui/show-all-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,29 @@ import { isDesktopApp } from "@todesktop/client-core/platform/todesktop";
import { useRouter } from "next/navigation";

type Props = {
path: string;
filter: Record<string, string>;
q: string;
};

export function ShowAllButton({ path }: Props) {
export function ShowAllButton({ filter, q }: Props) {
const { setOpen } = useAssistantStore();
const router = useRouter();

const params = new URLSearchParams();

if (q) {
params.append("q", q);
}

if (Object.keys(filter).length > 0) {
for (const [key, value] of Object.entries(filter)) {
params.append(key, value);
}
}

const handleOnClick = () => {
setOpen();
router.push(path);
router.push(`/transactions?${params.toString()}`);
};

if (isDesktopApp()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Props = {
meta: any;
data: any;
q: string;
filter: string[];
filter: Record<string, string>;
};

export function TransactionsUI({ meta, data, q, filter }: Props) {
Expand Down Expand Up @@ -95,11 +95,7 @@ export function TransactionsUI({ meta, data, q, filter }: Props) {
</Table>
)}

{meta.count > 5 && (
<ShowAllButton
path={`/transactions?q=${q}&filter=${JSON.stringify(filter)}`}
/>
)}
{meta.count > 5 && <ShowAllButton filter={filter} q={q} />}
</BotCard>
);
}
41 changes: 41 additions & 0 deletions apps/dashboard/src/actions/ai/filters/generate-filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use server";

import { filterQuerySchema } from "@/actions/schema";
import { openai } from "@ai-sdk/openai";
import { streamObject } from "ai";
import { createStreamableValue } from "ai/rsc";

export async function generateFilters(
prompt: string,
validFilters: string[],
context: string,
) {
const stream = createStreamableValue();

(async () => {
const { partialObjectStream } = await streamObject({
model: openai("gpt-4o-mini"),
system: `You are a helpful assistant that generates filters for a given prompt. \n
Current date is: ${new Date().toISOString().split("T")[0]} \n
Only use categories if it's specificed in the prompt. \n
${context}
`,
schema: filterQuerySchema.pick({
...(validFilters.reduce((acc, filter) => {
acc[filter] = true;
return acc;
}, {}) as any),
}),
prompt,
temperature: 0.7,
});

for await (const partialObject of partialObjectStream) {
stream.update(partialObject);
}

stream.done();
})();

return { object: stream.value };
}
28 changes: 28 additions & 0 deletions apps/dashboard/src/actions/schema.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isValid } from "date-fns";
import { z } from "zod";

export const updateUserSchema = z.object({
Expand Down Expand Up @@ -426,3 +427,30 @@ export const assistantSettingsSchema = z.object({
});

export const requestAccessSchema = z.void();

export const parseDateSchema = z
.string()
.transform((v) => isValid(v))
.refine((v) => !!v, { message: "Invalid date" });

export const filterQuerySchema = z.object({
name: z.string().optional().describe("The name to search for"),
start: parseDateSchema
.optional()
.describe("The start date when to retrieve from. Return ISO-8601 format."),
end: parseDateSchema
.optional()
.describe(
"The end date when to retrieve data from. If not provided, defaults to the current date. Return ISO-8601 format.",
),
attachments: z
.enum(["exclude", "include"])
.optional()
.describe(
"Whether to include or exclude results with attachments or receipts.",
),
categories: z
.array(z.string())
.optional()
.describe("The categories to filter by"),
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TeamsTable } from "@/components/tables/teams";
import { TeamsSkeleton } from "@/components/tables/teams/table";
import { Metadata } from "next";
import type { Metadata } from "next";
import { Suspense } from "react";

export const metadata: Metadata = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const VALID_FILTERS = [
"name",
"attachments",
"categories",
"start",
"end",
"accounts",
"assignees",
];
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { ErrorFallback } from "@/components/error-fallback";
import { TransactionsModal } from "@/components/modals/transactions-modal";
import { SearchField } from "@/components/search-field";
import { Table } from "@/components/tables/transactions";
import { Loading } from "@/components/tables/transactions/loading";
import { TransactionsActions } from "@/components/transactions-actions";
import { TransactionsSearchFilter } from "@/components/transactions-search-filter";
import {
getCategories,
getTeamBankAccounts,
getTeamMembers,
} from "@midday/supabase/cached-queries";
import { cn } from "@midday/ui/cn";
import type { Metadata } from "next";
import { ErrorBoundary } from "next/dist/client/components/error-boundary";
import { Suspense } from "react";
import { VALID_FILTERS } from "./filters";
import { searchParamsCache } from "./search-params";

export const metadata: Metadata = {
title: "Transactions | Midday",
Expand All @@ -20,35 +23,67 @@ export const metadata: Metadata = {
export default async function Transactions({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined };
searchParams: Record<string, string | string[] | undefined>;
}) {
const [accounts, categories] = await Promise.all([
const {
q: query,
page,
attachments,
start,
end,
categories,
assignees,
statuses,
} = searchParamsCache.parse(searchParams);

// Move this in a suspense
const [accountsData, categoriesData, teamMembersData] = await Promise.all([
getTeamBankAccounts(),
getCategories(),
getTeamMembers(),
]);

const page = typeof searchParams.page === "string" ? +searchParams.page : 0;
const filter =
(searchParams?.filter && JSON.parse(searchParams.filter)) ?? {};
const filter = {
attachments,
start,
end,
categories,
assignees,
statuses,
};

const sort = searchParams?.sort?.split(":");

const isOpen = Boolean(searchParams.step);
const isEmpty = !accounts?.data?.length && !isOpen;
const isEmpty = !accountsData?.data?.length && !isOpen;
const loadingKey = JSON.stringify({
page,
filter,
sort,
query: searchParams?.q,
query,
});

return (
<>
<div className="flex justify-between py-6">
<SearchField placeholder="Search transactions" />
<TransactionsActions
categories={categories?.data}
accounts={accounts?.data}
<TransactionsSearchFilter
placeholder="Search or type filter"
validFilters={VALID_FILTERS}
categories={categoriesData?.data?.map((category) => ({
slug: category.slug,
name: category.name,
}))}
accounts={accountsData?.data?.map((account) => ({
id: account.id,
name: account.name,
currency: account.currency,
}))}
members={teamMembersData?.data?.map((member) => ({
id: member?.user.id,
name: member.user?.full_name,
}))}
/>
<TransactionsActions />
</div>

<div className={cn(isEmpty && "opacity-20 pointer-events-none")}>
Expand All @@ -59,7 +94,7 @@ export default async function Transactions({
page={page}
sort={sort}
noAccounts={isEmpty}
query={searchParams?.q}
query={query}
/>
</Suspense>
</ErrorBoundary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
createSearchParamsCache,
parseAsArrayOf,
parseAsInteger,
parseAsString,
parseAsStringLiteral,
} from "nuqs/server";

export const searchParamsCache = createSearchParamsCache({
q: parseAsString,
page: parseAsInteger.withDefault(0),
attachments: parseAsStringLiteral(["exclude", "include"] as const),
start: parseAsString,
end: parseAsString,
categories: parseAsArrayOf(parseAsString),
accounts: parseAsArrayOf(parseAsString),
assignees: parseAsArrayOf(parseAsString),
statuses: parseAsStringLiteral([
"fullfilled",
"unfulfilled",
"excluded",
] as const),
});
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/assistant/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function Toolbar({ onNewChat }: Props) {
return (
<button onClick={onNewChat} type="button">
<div className="left-4 right-4 absolute z-20 bottom-4 flex items-center justify-center">
<div className="dark:bg-[#1A1A1A]/95 bg-[#F6F6F3]/95 h-8 w-full justify-between items-center flex px-2 rounded-lg space-x-4 text-[#878787]">
<div className="dark:bg-[#1A1A1A]/95 bg-[#F6F6F3]/95 h-8 w-full justify-between items-center flex px-2 space-x-4 text-[#878787]">
<div className="flex items-center space-x-3">
<kbd className="pointer-events-none h-5 select-none items-center gap-1.5 rounded border bg-accent px-1.5 font-mono text-[11px] font-medium flex bg-[#2C2C2C]">
<span className="text-[16px]"></span>J
Expand Down
Loading

0 comments on commit 04cbfe0

Please sign in to comment.