Skip to content
Open
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
25 changes: 25 additions & 0 deletions client/src/components/tools/CreateTodoToolUI.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { makeAssistantToolUI } from "@assistant-ui/react";
import { useEffect, useRef } from "react";
import { queryClient } from "@/lib/queryClient";
import { FileText } from "lucide-react";

type CreateTodoArgs = {
item: string;
files?: string[];
};

type CreateTodoResult = {
id: number;
item: string;
files?: string[];
createdAt: string | Date;
};

Expand All @@ -19,6 +22,12 @@ export const CreateTodoToolUI = makeAssistantToolUI<CreateTodoArgs, CreateTodoRe
return (
<div className="rounded border p-3 text-sm">
Creating todo: <span className="font-medium">{args.item}</span>
{args.files && args.files.length > 0 && (
<div className="mt-2 text-xs text-muted-foreground">
<FileText className="inline h-3 w-3 mr-1" />
{args.files.length} file{args.files.length > 1 ? 's' : ''} attached
</div>
)}
</div>
);
}
Expand Down Expand Up @@ -61,6 +70,22 @@ export const CreateTodoToolUI = makeAssistantToolUI<CreateTodoArgs, CreateTodoRe
<InvalidateOnSuccess enabled={true} />
<div className="font-medium">Todo created</div>
<div className="mt-1">{result.item}</div>
{result.files && result.files.length > 0 && (
<div className="mt-2 space-y-1">
<div className="text-xs font-medium text-muted-foreground flex items-center">
<FileText className="h-3 w-3 mr-1" />
Attached files:
</div>
<div className="space-y-1">
{result.files.map((file, index) => (
<div key={index} className="text-xs bg-muted/50 px-2 py-1 rounded flex items-center">
<FileText className="h-3 w-3 mr-1 text-muted-foreground" />
<span className="truncate">{file.split('/').pop() || file}</span>
</div>
))}
</div>
</div>
)}
</div>
);
},
Expand Down
17 changes: 16 additions & 1 deletion client/src/pages/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/useToast";
import { SearchBar } from "@/components/SearchBar";
import { Plus } from "lucide-react";
import { Plus, FileText } from "lucide-react";
import { apiRequest } from "@/lib/queryClient";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
Expand Down Expand Up @@ -184,13 +184,28 @@
<TableHeader>
<TableRow>
<TableHead>Item</TableHead>
<TableHead>Files</TableHead>
<TableHead className="w-[100px]">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredItems.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.item}</TableCell>
<TableCell>
{item.files && item.files.length > 0 ? (
<div className="flex flex-wrap gap-1">
{item.files.map((file, index) => (

Check failure on line 198 in client/src/pages/dashboard.tsx

View workflow job for this annotation

GitHub Actions / test (22.x)

Parameter 'index' implicitly has an 'any' type.

Check failure on line 198 in client/src/pages/dashboard.tsx

View workflow job for this annotation

GitHub Actions / test (22.x)

Parameter 'file' implicitly has an 'any' type.
<div key={index} className="flex items-center gap-1 text-xs bg-muted/50 px-2 py-1 rounded">
<FileText className="h-3 w-3 text-muted-foreground" />
<span className="truncate max-w-[100px]">{file.split('/').pop() || file}</span>
</div>
))}
</div>
) : (
<span className="text-muted-foreground text-sm">No files</span>
)}
</TableCell>
<TableCell>
<Button
variant="destructive"
Expand Down
2 changes: 2 additions & 0 deletions migrations/0002_add_files_to_items.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Add files column to items table to store associated file paths
ALTER TABLE "items" ADD COLUMN "files" text[];
12 changes: 8 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 1 addition & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,14 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",

"cors": "^2.8.5",
"date-fns": "^3.6.0",
"dotenv": "^16.5.0",
"dotenv": "^16.6.1",
"drizzle-orm": "^0.39.1",
"drizzle-zod": "^0.7.0",
"embla-carousel-react": "^8.3.0",
"express": "^4.21.2",
"express-rate-limit": "^8.0.1",

"firebase": "^11.3.1",
"firebase-admin": "^13.4.0",
"framer-motion": "^11.13.1",
Expand All @@ -88,8 +86,6 @@
"input-otp": "^1.2.4",
"jest-environment-jsdom": "^29.7.0",
"lucide-react": "^0.453.0",


"multer": "^2.0.1",
"passport": "^0.7.0",
"passport-local": "^1.0.0",
Expand Down Expand Up @@ -122,9 +118,7 @@
"@tailwindcss/typography": "^0.5.15",
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",

"@types/express": "4.17.21",

"@types/jest": "^30.0.0",
"@types/node": "^20.16.11",
"@types/passport": "^1.0.16",
Expand Down
9 changes: 5 additions & 4 deletions server/routes/aiRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,10 @@ export async function registerAIRoutes(app: Express) {
description: "Create a new todo item for the current user",
parameters: z.object({
item: z.string().min(1).max(1000).describe("The todo item text"),
files: z.array(z.string()).optional().describe("Optional array of file paths to associate with this todo"),
}),
execute: async ({ item }) => {
console.log('[createTodo] start', { item, userId });
execute: async ({ item, files }) => {
console.log('[createTodo] start', { item, files, userId });
const currentUserId = userId;
// Enforce simple free-tier limit (mirror itemRoutes)
const user = await storage.getUserByFirebaseId(currentUserId);
Expand All @@ -147,9 +148,9 @@ export async function registerAIRoutes(app: Express) {
return { ok: false, error: 'Free plan item limit reached. Upgrade to Pro to add more todos.' };
}
try {
const created = await storage.createItem({ userId: currentUserId, item });
const created = await storage.createItem({ userId: currentUserId, item, files });
console.log('[createTodo] success', { id: created.id });
return { ok: true, id: created.id, item: created.item, createdAt: new Date().toISOString() };
return { ok: true, id: created.id, item: created.item, files: created.files, createdAt: new Date().toISOString() };
} catch (err) {
console.error('[createTodo] error', err);
return { ok: false, error: 'Failed to create todo. Please try again.' };
Expand Down
31 changes: 31 additions & 0 deletions server/storage/ItemStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,35 @@ export class ItemStorage {
async deleteItem(id: number): Promise<void> {
await db.delete(items).where(eq(items.id, id));
}

async updateItemFiles(id: number, filePaths: string[]): Promise<Item | null> {
const [updatedItem] = await db
.update(items)
.set({ files: filePaths })
.where(eq(items.id, id))
.returning();
return updatedItem || null;
}

async addFileToItem(id: number, filePath: string): Promise<Item | null> {
// First get the current item to append to existing files
const [currentItem] = await db.select().from(items).where(eq(items.id, id));
if (!currentItem) return null;

const currentFiles = currentItem.files || [];
const updatedFiles = [...currentFiles, filePath];

return this.updateItemFiles(id, updatedFiles);
}

async removeFileFromItem(id: number, filePath: string): Promise<Item | null> {
// First get the current item to remove from existing files
const [currentItem] = await db.select().from(items).where(eq(items.id, id));
if (!currentItem) return null;

const currentFiles = currentItem.files || [];
const updatedFiles = currentFiles.filter(file => file !== filePath);

return this.updateItemFiles(id, updatedFiles);
}
}
2 changes: 2 additions & 0 deletions shared/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const items = pgTable("items", {
id: serial("id").primaryKey(),
item: text("item").notNull(),
userId: text("user_id").notNull().references(() => users.firebaseId),
files: text("files").array(), // Array of file paths or identifiers
});

export const files = pgTable("files", {
Expand Down Expand Up @@ -114,6 +115,7 @@ export const insertUserSchema = createInsertSchema(users, {
export const insertItemSchema = createInsertSchema(items, {
userId: z.string(),
item: z.string(),
files: z.array(z.string()).optional(), // Optional array of file paths
});

export const insertFileSchema = createInsertSchema(files, {
Expand Down
Loading