Skip to content

Calendar Icon #636

Closed
Closed

Description

@ngoiyaeric

Calendar Icon:

This is a calendar centric notepad for users to add time sensitive context regarding their geospatial workflows whilst collaborating with other users. Users may move dates from a top scroll bar into future or past dates retrieving notes or adding reminders for a future event.
Users may tag locations or tag their collaborators.

Information is indexed asynchronously to the language model into the context.
It should be a minimal design that retains information and enhances knowledge discovery.

Example:
User is indexing a plot for a commercial property with collaborators in Norway.
The collaborators are working asynchronously in the map render and prompting the system for discovery. The users have ragged additional documents. The users realize they need to revisit the site in a week when the local Municipal has performed their land divisions. They input this into the notepad with their survey requirements, tagging each other and the plot as well as this event’s future date.

The system understands and retains context and seeks additional knowledge asynchronously.

I need to clarify what you're asking about. Based on the codebase context I have access to, you appear to be describing a feature request or design specification for a calendar-based notepad component that doesn't currently exist in the QCX codebase.

Current State of Calendar Icon

The calendar icon (CalendarDays) currently exists in the UI but has no functionality implemented:

  • In the desktop header at components/header.tsx:38-40, there's a CalendarDays button with no click handler 1
  • In the mobile icons bar at components/mobile-icons-bar.tsx:43-45, the same icon appears without any associated functionality 2

What Doesn't Exist Yet

The features you're describing are not implemented in the current codebase:

  1. Calendar-centric notepad interface - No component exists for this
  2. Date scrolling/navigation - Not present
  3. Note creation with location/collaborator tagging - Not implemented
  4. Asynchronous indexing to language model - No such system exists
  5. Context retention for geospatial workflows - Not built

What Would Need to Be Built

To implement your specification, you would need to create:

  1. A new calendar component that renders when the CalendarDays icon is clicked
  2. A data model for storing notes with timestamps, location tags, and user tags
  3. Integration with the existing MapDataContext to link notes to map features 3
  4. A persistence layer (likely extending the existing Supabase schema)
  5. An indexing mechanism to feed note content into the AI context

Notes

This appears to be a feature specification rather than a question about existing code. The QCX application currently focuses on AI-powered geospatial chat with map integration, but does not have any calendar or time-based note-taking functionality. If you're looking to implement this feature, I'd be happy to help design the architecture or discuss how it could integrate with the existing map and AI systems.

Wiki pages you might want to explore:

Citations

File: components/header.tsx (L38-40)

        <Button variant="ghost" size="icon">
          <CalendarDays className="h-[1.2rem] w-[1.2rem]" />
        </Button>

File: components/mobile-icons-bar.tsx (L43-45)

      <Button variant="ghost" size="icon">
        <CalendarDays className="h-[1.2rem] w-[1.2rem] transition-all rotate-0 scale-100" />
      </Button>

File: components/map/map-query-handler.tsx (L5-5)

import { useMapData } from './map-data-context'; 

Implementation Overview: Calendar Notepad System

Based on your specification, you're asking how to implement a calendar-centric notepad feature that integrates with QCX's existing geospatial chat and map system.

System Architecture

The calendar notepad would connect to three core systems in QCX:

  1. Map System - For location tagging and spatial context
  2. Chat/Message System - For persistence and AI context
  3. AI Agent System - For asynchronous knowledge indexing 1 2

Implementation Components

1. Calendar UI Component

Create a new component components/calendar-notepad.tsx that renders when the CalendarDays icon is clicked. 3

Key features:

  • Date navigation bar (horizontal scroll)
  • Note creation/editing interface
  • Tag input for locations and collaborators
  • Integration with existing UI patterns
interface CalendarNote {
  id: string;
  userId: string;
  date: Date;
  content: string;
  locationTags: [number, number][]; // [lng, lat] coordinates
  userTags: string[]; // User IDs
  mapFeatureId?: string; // Link to drawn features
  createdAt: Date;
  updatedAt: Date;
}

2. Database Schema Extension

Extend the existing Supabase schema to store calendar notes. The current system uses Drizzle ORM with chats and messages tables. 4

Add a new table:

CREATE TABLE calendar_notes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES auth.users(id),
  chat_id UUID REFERENCES chats(id), -- Link to chat context
  date TIMESTAMP NOT NULL,
  content TEXT NOT NULL,
  location_tags JSONB, -- Array of coordinates
  user_tags TEXT[], -- Array of user IDs
  map_feature_id TEXT, -- Reference to drawn features
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

3. Context Integration with MapDataContext

The calendar notes need to interact with the map system through MapDataContext. 1

Integration points:

  • Location tagging: When a user tags a location, capture mapData.targetPosition or mapData.drawnFeatures
  • Feature linking: Store references to drawn polygons/lines from the map
  • Bidirectional updates: Clicking a calendar note should update setMapData() to navigate to tagged locations 5

4. AI Context Indexing

The system already has a pattern for feeding context to AI agents through the chat system. 6

Implementation approach:

  1. Store notes as special message types: Similar to how updateDrawingContext stores drawn features as messages with role: 'data' 7

  2. Create a calendar context message type:

const calendarContextMessage: Omit<DbNewMessage, 'chatId'> = {
  userId: userId,
  role: 'data',
  content: JSON.stringify({
    type: 'calendar_note',
    note: calendarNote,
    date: note.date,
    locationTags: note.locationTags,
    userTags: note.userTags
  }),
  createdAt: new Date()
};
  1. Asynchronous indexing: When notes are created/updated, append them to the chat's message history so the AI agents can access them in future queries 8

5. Geospatial Tool Integration

The existing geospatialQueryTool can be extended to understand calendar context. 9

When the AI processes a query, it would:

  1. Check for relevant calendar notes based on date/location
  2. Include note content in the context window
  3. Use MapQueryHandler to navigate to tagged locations 10

6. Collaboration Features

For user tagging and collaboration:

  1. User mentions: Parse @username syntax in note content
  2. Notifications: Create a notification system (new table) when users are tagged
  3. Shared context: Notes are associated with chatId, so collaborators in the same chat see the same notes
  4. Real-time updates: Use Supabase real-time subscriptions to sync notes across users

Implementation Flow

graph TB
    User["User clicks CalendarDays icon"]
    CalendarUI["Calendar Notepad Component"]
    CreateNote["Create/Edit Note"]
    TagLocation["Tag Location from Map"]
    TagUsers["Tag Collaborators"]
    SaveNote["Save to Database"]
    CreateMessage["Create Calendar Context Message"]
    AIIndex["AI Agent Indexes Content"]
    MapUpdate["Update Map View"]
    
    User --> CalendarUI
    CalendarUI --> CreateNote
    CreateNote --> TagLocation
    CreateNote --> TagUsers
    TagLocation --> MapUpdate
    CreateNote --> SaveNote
    SaveNote --> CreateMessage
    CreateMessage --> AIIndex
    
    MapUpdate -.->|"setMapData()"| MapDataContext["MapDataContext"]
    TagLocation -.->|"reads"| MapDataContext
Loading

Key Integration Points

  1. Map Context: Use useMapData() hook to read/write map state 11

  2. Chat Persistence: Use saveChat() and createMessage() functions 12

  3. AI State: Notes become part of AIState messages array, accessible to all agents 13

  4. Map Features: Link to drawnFeatures from MapDataContext 5

Notes

The implementation leverages existing patterns in QCX:

  • The MapDataContext already handles bidirectional map-chat communication
  • The message system with role: 'data' provides a precedent for storing structured context
  • The MapQueryHandler pattern shows how to trigger map updates from AI responses

The calendar notepad would be a new top-level feature but integrates naturally with the existing architecture. The main new components are the UI layer and database schema; the integration points already exist in the codebase.

Wiki pages you might want to explore:

Citations

File: lib/actions/chat-db.ts (L74-148)

/**
 * Saves a chat and its messages. If the chat exists, it updates it.
 * This function should handle both creating new chats and appending messages.
 * The PR implies complex logic for saving, including message IDs.
 * This is a simplified version; PR #533 might have more granular message saving.
 * @param chatData - The chat data to save.
 * @param messagesData - An array of messages to save with the chat.
 * @returns The saved chat ID.
 */
export async function saveChat(chatData: NewChat, messagesData: Omit<NewMessage, 'chatId'>[]): Promise<string | null> {
  if (!chatData.userId) {
    console.error('Cannot save chat without a userId');
    return null;
  }

  // Transaction to ensure atomicity
  return db.transaction(async (tx) => {
    let chatId = chatData.id;

    if (chatId) { // If chat ID is provided, assume update or append messages
      const existingChat = await tx.select({ id: chats.id }).from(chats).where(eq(chats.id, chatId)).limit(1);
      if (!existingChat.length) {
        // Chat doesn't exist, so create it
        const newChatResult = await tx.insert(chats).values(chatData).returning({ id: chats.id });
        chatId = newChatResult[0].id;
      } else {
        // Optionally update chat metadata here if needed, e.g., title
        if (chatData.title) {
          await tx.update(chats).set({ title: chatData.title }).where(eq(chats.id, chatId));
        }
      }
    } else { // No chat ID, create new chat
      const newChatResult = await tx.insert(chats).values(chatData).returning({ id: chats.id });
      chatId = newChatResult[0].id;
    }

    if (!chatId) {
      // console.error('Failed to establish chatId within transaction.'); // Optional: for server logs
      throw new Error('Failed to establish chatId for chat operation.');
    }

    // Save messages
    if (messagesData && messagesData.length > 0) {
      const messagesToInsert = messagesData.map(msg => ({
        ...msg,
        chatId: chatId!, // Ensure chatId is set for all messages
        userId: msg.userId || chatData.userId!, // Ensure userId is set
      }));
      await tx.insert(messages).values(messagesToInsert);
    }
    return chatId;
  });
}


/**
 * Creates a single message within a chat.
 * PR #533 has commits like "feat: Add message update and trailing deletion logic",
 * suggesting more granular message operations. This is a basic create.
 * @param messageData - The message data to save.
 * @returns The created message object or null if error.
 */
export async function createMessage(messageData: NewMessage): Promise<Message | null> {
  if (!messageData.chatId || !messageData.userId || !messageData.role || !messageData.content) {
    console.error('Missing required fields for creating a message.');
    return null;
  }
  try {
    const result = await db.insert(messages).values(messageData).returning();
    return result[0] || null;
  } catch (error) {
    console.error('Error creating message:', error);
    return null;
  }
}

File: components/header.tsx (L38-40)

        <Button variant="ghost" size="icon">
          <CalendarDays className="h-[1.2rem] w-[1.2rem]" />
        </Button>

File: components/chat.tsx (L74-83)

  // Get mapData to access drawnFeatures
  const { mapData } = useMapData();

  // useEffect to call the server action when drawnFeatures changes
  useEffect(() => {
    if (id && mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
      console.log('Chat.tsx: drawnFeatures changed, calling updateDrawingContext', mapData.drawnFeatures);
      updateDrawingContext(id, mapData.drawnFeatures);
    }
  }, [id, mapData.drawnFeatures]);

File: app/actions.tsx (L1-29)

import {
  StreamableValue,
  createAI,
  createStreamableUI,
  createStreamableValue,
  getAIState,
  getMutableAIState
} from 'ai/rsc'
import { CoreMessage, ToolResultPart } from 'ai'
import { nanoid } from 'nanoid'
import type { FeatureCollection } from 'geojson'
import { Spinner } from '@/components/ui/spinner'
import { Section } from '@/components/section'
import { FollowupPanel } from '@/components/followup-panel'
import { inquire, researcher, taskManager, querySuggestor, resolutionSearch } from '@/lib/agents'
// Removed import of useGeospatialToolMcp as it no longer exists and was incorrectly used here.
// The geospatialTool (if used by agents like researcher) now manages its own MCP client.
import { writer } from '@/lib/agents/writer'
import { saveChat, getSystemPrompt } from '@/lib/actions/chat' // Added getSystemPrompt
import { Chat, AIMessage } from '@/lib/types'
import { UserMessage } from '@/components/user-message'
import { BotMessage } from '@/components/message'
import { SearchSection } from '@/components/search-section'
import SearchRelated from '@/components/search-related'
import { GeoJsonLayer } from '@/components/map/geojson-layer'
import { CopilotDisplay } from '@/components/copilot-display'
import RetrieveSection from '@/components/retrieve-section'
import { VideoSearchSection } from '@/components/video-search-section'
import { MapQueryHandler } from '@/components/map/map-query-handler' // Add this import

File: app/actions.tsx (L640-655)

        case 'tool':
          try {
            const toolOutput = JSON.parse(content as string)
            const isCollapsed = createStreamableValue()
            isCollapsed.done(true)

            if (
              toolOutput.type === 'MAP_QUERY_TRIGGER' &&
              name === 'geospatialQueryTool'
            ) {
              return {
                id,
                component: <MapQueryHandler toolOutput={toolOutput} />,
                isCollapsed: false
              }
            }

File: lib/actions/chat.ts (L165-205)

export async function updateDrawingContext(chatId: string, drawnFeatures: any[]) {
  'use server';
  console.log('[Action] updateDrawingContext called for chatId:', chatId);

  const userId = await getCurrentUserIdOnServer(); // Essential for creating a user-associated message
  if (!userId) {
    console.error('updateDrawingContext: Could not get current user ID. User must be authenticated.');
    return { error: 'User not authenticated' };
  }

  // The old version fetched the whole chat. Now we just create a new message.
  // The AIMessage type might be from '@/lib/types' and need mapping to DbNewMessage
  const newDrawingMessage: Omit<DbNewMessage, 'chatId'> = {
    // id: `drawnData-${Date.now().toString()}`, // Let DB generate UUID
    userId: userId,
    role: 'data' as 'user' | 'assistant' | 'system' | 'tool' | 'data', // Cast 'data' if not in standard roles
    content: JSON.stringify(drawnFeatures), // Store features as stringified JSON
    // type: 'drawing_context', // This field is not in the Drizzle 'messages' schema.
    // If `type` is important, the schema needs to be updated or content needs to reflect it.
    // For now, we'll assume 'content' holds the necessary info and role='data' signifies it.
    createdAt: new Date(),
  };

  try {
    // We need to ensure the message is associated with the chat.
    // dbCreateMessage requires chatId.
    const messageToSave: DbNewMessage = {
      ...newDrawingMessage,
      chatId: chatId,
    };
    const savedMessage = await dbCreateMessage(messageToSave);
    if (!savedMessage) {
      throw new Error('Failed to save drawing context message.');
    }
    console.log('Drawing context message added to chat:', chatId, 'messageId:', savedMessage.id);
    return { success: true, messageId: savedMessage.id };
  } catch (error) {
    console.error('updateDrawingContext: Error saving drawing context message:', error);
    return { error: 'Failed to save drawing context message' };
  }
}

File: components/map/map-query-handler.tsx (L30-74)

export const MapQueryHandler: React.FC<MapQueryHandlerProps> = ({ toolOutput }) => {
  const { setMapData } = useMapData();

  useEffect(() => {
    if (toolOutput && toolOutput.mcp_response && toolOutput.mcp_response.location) {
      const { latitude, longitude, place_name } = toolOutput.mcp_response.location;

      if (typeof latitude === 'number' && typeof longitude === 'number') {
        console.log(`MapQueryHandler: Received data from geospatialTool. Place: ${place_name}, Lat: ${latitude}, Lng: ${longitude}`);
        setMapData(prevData => ({
          ...prevData,
          // Ensure coordinates are in [lng, lat] format for MapboxGL
          targetPosition: [longitude, latitude], 
          // Optionally store more info from mcp_response if needed by MapboxMap component later
          mapFeature: { 
            place_name, 
            // Potentially add mapUrl or other details from toolOutput.mcp_response
            mapUrl: toolOutput.mcp_response?.mapUrl 
          } 
        }));
      } else {
        console.warn("MapQueryHandler: Invalid latitude/longitude in toolOutput.mcp_response:", toolOutput.mcp_response.location);
        // Clear target position if data is invalid
        setMapData(prevData => ({
          ...prevData,
          targetPosition: null,
          mapFeature: null
        }));
      }
    } else {
      // This case handles when toolOutput or its critical parts are missing.
      // Depending on requirements, could fall back to originalUserInput and useMCPMapClient,
      // or simply log that no valid data was provided from the tool.
      // For this subtask, we primarily focus on using the new toolOutput.
      if (toolOutput) { // It exists, but data is not as expected
        console.warn("MapQueryHandler: toolOutput provided, but mcp_response or location data is missing.", toolOutput);
      }
      // If toolOutput is null/undefined, this component might not need to do anything,
      // or it's an indication that it shouldn't have been rendered/triggered.
      // For now, if no valid toolOutput, we clear map data or leave it as is.
      // setMapData(prevData => ({ ...prevData, targetPosition: null, mapFeature: null }));
    }
    // The dependencies for this useEffect should be based on the props that trigger its logic.
    // If originalUserInput and the old MCP client were still used as a fallback, they'd be dependencies.
  }, [toolOutput, setMapData]);

File: components/map/mapbox-map.tsx (L34-34)

  const { mapData, setMapData } = useMapData(); // Consume the new context, get setMapData

File: app/search/[id]/page.tsx (L44-60)

  // Fetch messages for the chat
  const dbMessages: DrizzleMessage[] = await getChatMessages(chat.id);

  // Transform DrizzleMessages to AIMessages
  const initialMessages: AIMessage[] = dbMessages.map((dbMsg): AIMessage => {
    return {
      id: dbMsg.id,
      role: dbMsg.role as AIMessage['role'], // Cast role, ensure AIMessage['role'] includes all dbMsg.role possibilities
      content: dbMsg.content,
      createdAt: dbMsg.createdAt ? new Date(dbMsg.createdAt) : undefined,
      // 'type' and 'name' are not in the basic Drizzle 'messages' schema.
      // These would be undefined unless specific logic is added to derive them.
      // For instance, if a message with role 'tool' should have a 'name',
      // or if some messages have a specific 'type' based on content or other flags.
      // This mapping assumes standard user/assistant messages primarily.
    };
  });

Activity

  1. ngoiyaeric commented on Jun 1, 2026

    @ngoiyaeric
    CollaboratorAuthor
  2. coderabbitai commented on Jun 1, 2026

    @coderabbitai
    Contributor

    Coding Plan

    Summary

    • Wire existing toggle infrastructure to enable calendar panel access from header and mobile navigation
    • Implement @mention autocomplete using the existing searchUsers action and integrate into the calendar notepad textarea
    • Follow the drawnFeatures pattern for AI context injection by fetching notes server-side and embedding them in agent system prompts
    • Minimize new abstractions by extending existing components and patterns rather than creating parallel systems
    Design Choices
    Design Choice 1: How should calendar notes be injected into AI context?

    Options Considered:

    1. Load notes via formData on submit and inject into system prompt (like drawnFeatures)
    2. Parse role: 'data' messages from AIState and extract calendar notes
    3. Fetch notes directly from database in the submit action based on chatId

    Chosen Option: 3

    Rationale: Fetch notes directly from database in submit action — this ensures freshness, avoids formData complexity, and the getNotes action already exists.

    💡 User Tips

    Regenerate the plan with different choices with @coderabbitai <feedback>.

    Implementation Steps

    Phase 1: Wire Calendar Toggle Buttons

    Connect the existing CalendarDays icon buttons in the header and mobile navigation to the CalendarToggleContext, enabling users to open/close the calendar notepad panel.

    Task 1: Update CalendarToggleContext API

    Enhance the toggle context to expose a closeCalendar method for consistency with other toggle contexts (UsageToggleContext pattern).

    • Modify components/calendar-toggle-context.tsx to add closeCalendar: () => void to the context type
    • Implement closeCalendar as () => setIsCalendarOpen(false) wrapped in startTransition
    • Export the new method alongside existing isCalendarOpen and toggleCalendar
    Task 2: Wire Header Calendar Button

    Connect the desktop header's CalendarDays button to the toggle context.

    • Update components/header.tsx to import useCalendarToggle hook
    • Add onClick={toggleCalendar} handler to the CalendarDays <Button> element
    • Ensure the button has appropriate accessibility attributes (title, aria-label)
    Task 3: Wire Mobile Icons Bar Calendar Button

    Connect the mobile navigation's CalendarDays button to the toggle context.

    • Update components/mobile-icons-bar.tsx to import useCalendarToggle hook
    • Add onClick={toggleCalendar} handler to the CalendarDays <Button> element
    • Consider adding logic to close other views (profile) when opening calendar, following the handleUsageToggle pattern
    🤖 Prompt for AI agents
    Wire the existing CalendarDays icon buttons in the header and mobile navigation
    to the `CalendarToggleContext`, enabling users to open/close the calendar
    notepad panel.
    
    **Task 1: Update `components/calendar-toggle-context.tsx`**
    - Add `closeCalendar: () => void` to the context type definition
    - Implement `closeCalendar` as `() => setIsCalendarOpen(false)` wrapped in
    `startTransition`
    - Export `closeCalendar` alongside the existing `isCalendarOpen` and
    `toggleCalendar`
    
    **Task 2: Update `components/header.tsx`**
    - Import the `useCalendarToggle` hook
    - Destructure `toggleCalendar` from the hook
    - Add `onClick={toggleCalendar}` to the CalendarDays `<Button>` element
    - Ensure the button has appropriate accessibility attributes (e.g., `title`,
    `aria-label`)
    
    **Task 3: Update `components/mobile-icons-bar.tsx`**
    - Import the `useCalendarToggle` hook
    - Destructure `toggleCalendar` from the hook
    - Add `onClick={toggleCalendar}` to the CalendarDays `<Button>` element
    - Follow the `handleUsageToggle` pattern to close other views (e.g., profile)
    when opening the calendar
    

    Phase 2: User Tagging UI

    Implement @mention autocomplete functionality allowing users to tag collaborators in calendar notes, populating the existing userTags field.

    Task 1: Create User Mention Autocomplete Component

    Build a reusable mention autocomplete dropdown that triggers on @ symbol.

    • Create components/user-mention-input.tsx as a controlled textarea wrapper
    • Implement @ character detection to trigger autocomplete mode
    • Use searchUsers action from lib/actions/users.ts for user lookup (debounced)
    • Display dropdown with matching user emails/names below the cursor position
    • Handle selection by inserting @user-email into textarea content and collecting user ID
    Task 2: Integrate Mention Input into Calendar Notepad

    Replace the plain textarea with the mention-enabled input component.

    • Update components/calendar-notepad.tsx to use the new mention input component
    • Add local state to track selected user tags (array of user IDs)
    • Pass userTags array to the saveNote call instead of hardcoded null
    • Display tagged users as visual pills/badges within or below the textarea
    Task 3: Render User Tags on Existing Notes

    Display tagged collaborators when viewing saved notes.

    • Update note display section in components/calendar-notepad.tsx to show userTags
    • Render user tags as compact badges or inline mentions
    • Optionally fetch user emails for display if only IDs are stored
    🤖 Prompt for AI agents
    Implement `@mention` autocomplete functionality for tagging collaborators in
    calendar notes, and wire the selected tags into the existing `userTags` field on
    save.
    
    **Task 1: Create `components/user-mention-input.tsx`**
    - Build a controlled textarea wrapper component
    - Detect the `@` character in the textarea input to trigger autocomplete mode
    - Call the `searchUsers` action from `lib/actions/users.ts` (debounced) to look
    up users
    - Render a dropdown below the cursor position showing matching user emails/names
    - On user selection, insert `@user-email` into the textarea content and collect
    the selected user's ID
    
    **Task 2: Update `components/calendar-notepad.tsx` — input integration**
    - Replace the plain textarea with the new `UserMentionInput` component
    - Add local state to track selected user tags as an array of user IDs
    - Pass the `userTags` array to the `saveNote` call (replacing the hardcoded
    `null`)
    - Display tagged users as visual pills/badges within or below the textarea
    
    **Task 3: Update `components/calendar-notepad.tsx` — note display**
    - In the note display/read section, render `userTags` values as compact badges
    or inline mentions
    - Optionally fetch and display user emails if only IDs are stored
    

    Phase 3: AI Context Integration

    Inject calendar notes into the AI agent's system prompt so the language model has access to time-sensitive context during queries.

    Task 1: Fetch Calendar Notes in Submit Action

    Load relevant calendar notes when processing a user query.

    • Modify app/actions.tsx submit action to import getNotes from lib/actions/calendar
    • After extracting chatId from AIState, call getNotes to fetch recent notes for the chat
    • Fetch notes within a reasonable time window (e.g., past 30 days and future 14 days) to capture relevant context
    Task 2: Inject Notes into Researcher System Prompt

    Pass calendar notes to the researcher agent for inclusion in the system prompt.

    • Update the researcher function signature in lib/agents/researcher.tsx to accept optional calendarNotes parameter
    • Extend getDefaultSystemPrompt to include a calendar notes section when notes are present
    • Format notes as structured text: date, content, tagged locations, tagged users
    • Pass notes from submit action through to researcher call
    Task 3: Handle Calendar Notes in Resolution Search Agent

    Ensure calendar context is available to the resolution search agent.

    • Update lib/agents/resolution-search.tsx to accept and format calendar notes in its system prompt
    • Include note content and associated location data for spatial context
    • Pass notes from submit action through to resolutionSearch call
    🤖 Prompt for AI agents
    Inject calendar notes into the AI agent system prompts so the language model has
    access to time-sensitive context during queries. Follow the `drawnFeatures`
    pattern for server-side injection.
    
    **Task 1: Update `app/actions.tsx`**
    - Import `getNotes` from `lib/actions/calendar`
    - After extracting `chatId` from AIState in the submit action, call `getNotes`
    to fetch recent notes for that chat
    - Fetch notes within a reasonable time window (e.g., past 30 days and future 14
    days)
    
    **Task 2: Update `lib/agents/researcher.tsx`**
    - Update the `researcher` function signature to accept an optional
    `calendarNotes` parameter
    - Extend `getDefaultSystemPrompt` to include a calendar notes section when notes
    are present
    - Format notes as structured text including: date, content, tagged locations,
    and tagged users
    - Pass the `calendarNotes` from the submit action through to the researcher call
    
    **Task 3: Update `lib/agents/resolution-search.tsx`**
    - Update the agent to accept and format calendar notes in its system prompt
    - Include note content and associated location data for spatial context
    - Pass `calendarNotes` from the submit action through to the resolutionSearch
    call
    
    Research

    The calendar notepad system is partially implemented. A CalendarNotepad component exists with week-based date navigation, note creation, and location tagging via MapPin. The CalendarToggleContext provides isCalendarOpen and toggleCalendar() state management. The database schema (calendarNotes table) and server actions (getNotes, saveNote) are complete, including AI context message creation for chat-linked notes. Key gaps: header/mobile toggle buttons are not wired, user tagging UI is missing (field is always null), and calendar notes are not injected into the AI system prompt for agent consumption.


    🚀 Next Steps

    🤖 All AI agent prompts combined
    Task: 1
    
    Wire the existing CalendarDays icon buttons in the header and mobile navigation
    to the `CalendarToggleContext`, enabling users to open/close the calendar
    notepad panel.
    
    **Task 1: Update `components/calendar-toggle-context.tsx`**
    - Add `closeCalendar: () => void` to the context type definition
    - Implement `closeCalendar` as `() => setIsCalendarOpen(false)` wrapped in
    `startTransition`
    - Export `closeCalendar` alongside the existing `isCalendarOpen` and
    `toggleCalendar`
    
    **Task 2: Update `components/header.tsx`**
    - Import the `useCalendarToggle` hook
    - Destructure `toggleCalendar` from the hook
    - Add `onClick={toggleCalendar}` to the CalendarDays `<Button>` element
    - Ensure the button has appropriate accessibility attributes (e.g., `title`,
    `aria-label`)
    
    **Task 3: Update `components/mobile-icons-bar.tsx`**
    - Import the `useCalendarToggle` hook
    - Destructure `toggleCalendar` from the hook
    - Add `onClick={toggleCalendar}` to the CalendarDays `<Button>` element
    - Follow the `handleUsageToggle` pattern to close other views (e.g., profile)
    when opening the calendar
    ===============================================================================
    
    Task: 2
    
    Implement `@mention` autocomplete functionality for tagging collaborators in
    calendar notes, and wire the selected tags into the existing `userTags` field on
    save.
    
    **Task 1: Create `components/user-mention-input.tsx`**
    - Build a controlled textarea wrapper component
    - Detect the `@` character in the textarea input to trigger autocomplete mode
    - Call the `searchUsers` action from `lib/actions/users.ts` (debounced) to look
    up users
    - Render a dropdown below the cursor position showing matching user emails/names
    - On user selection, insert `@user-email` into the textarea content and collect
    the selected user's ID
    
    **Task 2: Update `components/calendar-notepad.tsx` — input integration**
    - Replace the plain textarea with the new `UserMentionInput` component
    - Add local state to track selected user tags as an array of user IDs
    - Pass the `userTags` array to the `saveNote` call (replacing the hardcoded
    `null`)
    - Display tagged users as visual pills/badges within or below the textarea
    
    **Task 3: Update `components/calendar-notepad.tsx` — note display**
    - In the note display/read section, render `userTags` values as compact badges
    or inline mentions
    - Optionally fetch and display user emails if only IDs are stored
    ===============================================================================
    
    Task: 3
    
    Inject calendar notes into the AI agent system prompts so the language model has
    access to time-sensitive context during queries. Follow the `drawnFeatures`
    pattern for server-side injection.
    
    **Task 1: Update `app/actions.tsx`**
    - Import `getNotes` from `lib/actions/calendar`
    - After extracting `chatId` from AIState in the submit action, call `getNotes`
    to fetch recent notes for that chat
    - Fetch notes within a reasonable time window (e.g., past 30 days and future 14
    days)
    
    **Task 2: Update `lib/agents/researcher.tsx`**
    - Update the `researcher` function signature to accept an optional
    `calendarNotes` parameter
    - Extend `getDefaultSystemPrompt` to include a calendar notes section when notes
    are present
    - Format notes as structured text including: date, content, tagged locations,
    and tagged users
    - Pass the `calendarNotes` from the submit action through to the researcher call
    
    **Task 3: Update `lib/agents/resolution-search.tsx`**
    - Update the agent to accept and format calendar notes in its system prompt
    - Include note content and associated location data for spatial context
    - Pass `calendarNotes` from the submit action through to the resolutionSearch
    call
    
    💡 Iterate on the plan with: `@coderabbitai` <feedback>
    Example Feedback
    - `@coderabbitai` You can skip phase 3. Add a simple unit test case for phase 2.
    - `@coderabbitai` For design choice 1 go ahead with option 3 and replan.
    

    💬 Have feedback or questions? Drop into our discord!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions