├── .env.local.example ├── .eslintrc.json ├── .gitignore ├── README.md ├── next.config.mjs ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── next.svg ├── run-demo-cta.png ├── screenshot.png └── vercel.svg ├── renovate.json ├── src └── app │ ├── api │ ├── copilotkit │ │ ├── research.ts │ │ ├── route.ts │ │ └── tavily.ts │ ├── transcribe │ │ └── route.ts │ └── tts │ │ └── route.ts │ ├── components │ ├── PreviewSpreadsheetChanges.tsx │ ├── Sidebar.tsx │ ├── SingleSpreadsheet.tsx │ └── ThemeProvider.tsx │ ├── favicon.ico │ ├── globals.css │ ├── instructions.ts │ ├── layout.tsx │ ├── page.tsx │ ├── types.ts │ └── utils │ ├── canonicalSpreadsheetData.ts │ └── sampleData.ts ├── tailwind.config.ts └── tsconfig.json /.env.local.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=xxxxxxx 2 | OPENAI_MODEL=gpt-4-1106-preview 3 | TAVILY_API_KEY=xxxxxxx -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | .env 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a demo that showcases using CopilotKit to build an Excel like web app. 2 | 3 | ## Run the live demo 4 | 5 | Want to see CopilotKit in action? Click the button below to try the live demo. 6 | 7 | 8 | Spreadsheet Demo Screenshot 9 | 10 | 11 | 12 | Run the live demo 13 | 14 | 15 | ## Deploy with Vercel 16 | 17 | To deploy with Vercel, click the button below: 18 | 19 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FCopilotKit%2Fdemo-spreadsheet&env=NEXT_PUBLIC_COPILOT_CLOUD_API_KEY,TAVILY_API_KEY&envDescription=By%20setting%20the%20TAVILY_API_KEY%2C%20you%20control%20whether%20the%20web%20search%20capabilities%20are%20enabled.%20Set%20it%20to%20NONE%20to%20disable%20this%20feature.&project-name=copilotkit-demo-spreadsheet&repository-name=copilotkit-demo-spreadsheet) 20 | 21 | ## How to Build: A spreadsheet app with an AI-copilot 22 | 23 | Learn how to create a powerful spreadsheet app using CopilotKit. This tutorial will guide you through the process step-by-step. 24 | 25 | Tutorial: [How to Build: A spreadsheet app with an AI-copilot](https://dev.to/copilotkit/build-an-ai-powered-spreadsheet-app-nextjs-langchain-copilotkit-109d) 26 | 27 | ## Getting Started 28 | 29 | ### 1. install the needed package: 30 | 31 | ```bash 32 | npm i 33 | ``` 34 | 35 | ### 2. Set the required environment variables: 36 | 37 | copy `.env.local.example` to `.env.local` and populate the required environment variables. 38 | 39 | > ⚠️ **Important:** Not all users have access to the GPT-4 model yet. If you don't have access, you can use GPT-3 by setting `OPENAI_MODEL` to `gpt-3.5-turbo` in the `.env.local` file. 40 | 41 | ### 3. Run the app 42 | 43 | ```bash 44 | npm run dev 45 | ``` 46 | 47 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 48 | 49 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 50 | 51 | ### 4. Use the Copilot 52 | 53 | TODO add details what to do as a user 54 | 55 | ## Zoom in on the CopilotKit code 56 | 57 | 1. Look for `/api/copilotkit/route.ts` and `/api/copilotkit/tavily.ts` - for the research agent integrated into the spreadsheet 58 | 59 | 2. Look for `useCopilotReadable` to see where frontend application context is being made accessible to the Copilot engine 60 | 61 | 3. Search for `updateSpreadsheet`, `appendToSpreadsheet`, and `createSpreadsheet` to see application interaction hooks made available to agents. 62 | 63 | ## Learn More 64 | 65 | To learn more about CopilotKit, take a look at the following resources: 66 | 67 | - [CopilotKit Documentation](https://docs.copilotkit.ai/getting-started/quickstart-chatbot) - learn about CopilotKit features and API. 68 | - [GitHub](https://github.com/CopilotKit/CopilotKit) - Check out the CopilotKit GitHub repository. 69 | - [Discord](https://discord.gg/6dffbvGU3D) - Join the CopilotKit Discord community. 70 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | eslint : { 4 | ignoreDuringBuilds : true, 5 | } 6 | }; 7 | 8 | export default nextConfig; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spreadsheet-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@copilotkit/react-core": "^1.8.12-next.3", 13 | "@copilotkit/react-textarea": "^1.8.12-next.3", 14 | "@copilotkit/react-ui": "^1.8.12-next.3", 15 | "@copilotkit/runtime": "^1.8.12-next.3", 16 | "@copilotkit/shared": "^1.8.12-next.3", 17 | "@heroicons/react": "^2.2.0", 18 | "@langchain/community": "^0.3.17", 19 | "@langchain/core": "^0.3.20", 20 | "@langchain/langgraph": "^0.2.24", 21 | "@langchain/openai": "^0.3.14", 22 | "@syncfusion/ej2-react-spreadsheet": "^29.1.39", 23 | "langchain": "^0.3.6", 24 | "next": "15.0.3", 25 | "openai": "^4.96.2", 26 | "react": "^18", 27 | "react-dom": "^18", 28 | "react-spreadsheet": "^0.9.5", 29 | "scheduler": "^0.23.2" 30 | }, 31 | "devDependencies": { 32 | "@types/node": "^22", 33 | "@types/react": "^18", 34 | "@types/react-dom": "^18", 35 | "autoprefixer": "^10.4.20", 36 | "eslint": "^9", 37 | "eslint-config-next": "15.0.3", 38 | "postcss": "^8", 39 | "tailwindcss": "^3.4.16", 40 | "typescript": "^5" 41 | }, 42 | "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39" 43 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/run-demo-cta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CopilotKit/demo-spreadsheet/a048c90b01df2f6d696706df86939fa32995962d/public/run-demo-cta.png -------------------------------------------------------------------------------- /public/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CopilotKit/demo-spreadsheet/a048c90b01df2f6d696706df86939fa32995962d/public/screenshot.png -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "dependencyDashboard": true, 4 | "extends": [ 5 | "config:recommended" 6 | ], 7 | "poetry": { 8 | "enabled": true 9 | }, 10 | "pep621": { 11 | "enabled": false 12 | }, 13 | "npm": { 14 | "enabled": true 15 | }, 16 | "ignorePaths": [ 17 | "node_modules" 18 | ], 19 | "packageRules": [ 20 | { 21 | "enabled": false, 22 | "matchPackageNames": [ 23 | "*" 24 | ], 25 | "labels": [ 26 | "dependencies" 27 | ] 28 | }, 29 | { 30 | "enabled": true, 31 | "matchPackageNames": [ 32 | "/^@copilotkit/", 33 | "/^copilotkit/" 34 | ], 35 | "labels": [ 36 | "copilotkit" 37 | ], 38 | "groupName": "CopilotKit dependencies" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /src/app/api/copilotkit/research.ts: -------------------------------------------------------------------------------- 1 | import { TavilySearchAPIRetriever } from "@langchain/community/retrievers/tavily_search_api"; 2 | import { HumanMessage, SystemMessage } from "@langchain/core/messages"; 3 | import { RunnableLambda } from "@langchain/core/runnables"; 4 | import { Annotation, END, MemorySaver, StateGraph } from "@langchain/langgraph"; 5 | import { ChatOpenAI } from "@langchain/openai"; 6 | 7 | interface AgentState { 8 | topic: string; 9 | searchResults?: string; 10 | article?: string; 11 | critique?: string; 12 | } 13 | 14 | const StateAnnotation = Annotation.Root({ 15 | agentState: Annotation({ 16 | value: (x: AgentState, y: AgentState) => y, 17 | default: () => ({ 18 | topic: "", 19 | }), 20 | }), 21 | }); 22 | 23 | function model() { 24 | return new ChatOpenAI({ 25 | temperature: 0, 26 | modelName: "gpt-3.5-turbo-0125", 27 | }); 28 | } 29 | 30 | async function search(state: typeof StateAnnotation.State) { 31 | const retriever = new TavilySearchAPIRetriever({ k: 10 }); 32 | let topic = state.agentState.topic; 33 | if (topic.length < 5) topic = "topic: " + topic; 34 | const docs = await retriever.invoke(topic); 35 | return { 36 | agentState: { ...state.agentState, searchResults: JSON.stringify(docs) }, 37 | }; 38 | } 39 | 40 | async function curate(state: typeof StateAnnotation.State) { 41 | const response = await model().invoke( 42 | [ 43 | new SystemMessage( 44 | 'Return 5 most relevant article URLs as JSON: {urls: ["url1",...]}' 45 | ), 46 | new HumanMessage( 47 | `Topic: ${state.agentState.topic}\nArticles: ${state.agentState.searchResults}` 48 | ), 49 | ], 50 | { response_format: { type: "json_object" } } 51 | ); 52 | 53 | const urls = JSON.parse(response.content as string).urls; 54 | const searchResults = JSON.parse(state.agentState.searchResults!); 55 | const filtered = searchResults.filter((r: any) => 56 | urls.includes(r.metadata.source) 57 | ); 58 | return { 59 | agentState: { 60 | ...state.agentState, 61 | searchResults: JSON.stringify(filtered), 62 | }, 63 | }; 64 | } 65 | 66 | async function write(state: typeof StateAnnotation.State) { 67 | const response = await model().invoke([ 68 | new SystemMessage("Write a 5-paragraph article in markdown."), 69 | new HumanMessage( 70 | `Topic: ${state.agentState.topic}\nSources: ${state.agentState.searchResults}` 71 | ), 72 | ]); 73 | return { 74 | agentState: { ...state.agentState, article: response.content as string }, 75 | }; 76 | } 77 | 78 | async function critique(state: typeof StateAnnotation.State) { 79 | const feedback = state.agentState.critique 80 | ? `Previous critique: ${state.agentState.critique}` 81 | : ""; 82 | const response = await model().invoke([ 83 | new SystemMessage( 84 | "Review article. Return [DONE] if good, or provide brief feedback." 85 | ), 86 | new HumanMessage(`${feedback}\nArticle: ${state.agentState.article}`), 87 | ]); 88 | const content = response.content as string; 89 | return { 90 | agentState: { 91 | ...state.agentState, 92 | critique: content.includes("[DONE]") ? undefined : content, 93 | }, 94 | }; 95 | } 96 | 97 | async function revise(state: typeof StateAnnotation.State) { 98 | const response = await model().invoke([ 99 | new SystemMessage("Edit article based on critique."), 100 | new HumanMessage( 101 | `Article: ${state.agentState.article}\nCritique: ${state.agentState.critique}` 102 | ), 103 | ]); 104 | return { 105 | agentState: { ...state.agentState, article: response.content as string }, 106 | }; 107 | } 108 | 109 | function shouldContinue(state: typeof StateAnnotation.State) { 110 | return state.agentState.critique === undefined ? "end" : "continue"; 111 | } 112 | 113 | export async function createNewspaperWorkflow() { 114 | const workflow = new StateGraph(StateAnnotation) 115 | .addNode("search", new RunnableLambda({ func: search })) 116 | .addNode("curate", new RunnableLambda({ func: curate })) 117 | .addNode("write", new RunnableLambda({ func: write })) 118 | .addNode("critique", new RunnableLambda({ func: critique })) 119 | .addNode("revise", new RunnableLambda({ func: revise })) 120 | .addEdge("search", "curate") 121 | .addEdge("curate", "write") 122 | .addEdge("write", "critique") 123 | .addConditionalEdges("critique", shouldContinue, { 124 | continue: "revise", 125 | end: END, 126 | }) 127 | .addEdge("revise", "critique") 128 | .addEdge("__start__", "search"); 129 | 130 | const checkpointer = new MemorySaver(); 131 | return workflow.compile({ checkpointer }); 132 | } 133 | 134 | export async function researchWithLangGraph(topic: string) { 135 | const app = await createNewspaperWorkflow(); 136 | const result = await app.invoke( 137 | { agentState: { topic } }, 138 | { 139 | configurable: { thread_id: "research-" + Date.now(), checkpoint_id: "1" }, 140 | } 141 | ); 142 | return result?.agentState?.article?.replace( 143 | /[\s\S]*?<\/FEEDBACK>/g, 144 | "" 145 | ); 146 | } 147 | -------------------------------------------------------------------------------- /src/app/api/copilotkit/route.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "@copilotkit/shared"; 2 | import { researchWithLangGraph } from "./research"; 3 | 4 | import { NextRequest } from "next/server"; 5 | import { 6 | CopilotRuntime, 7 | LangChainAdapter, 8 | copilotRuntimeNextJSAppRouterEndpoint, 9 | } from "@copilotkit/runtime"; 10 | import { ChatOpenAI } from "@langchain/openai"; 11 | 12 | const researchAction: Action = { 13 | name: "research", 14 | description: 15 | "Call this function to conduct research on a certain topic. Respect other notes about when to call this function", 16 | parameters: [ 17 | { 18 | name: "topic", 19 | type: "string", 20 | description: "The topic to research. 5 characters or longer.", 21 | }, 22 | ], 23 | handler: async ({ topic }) => { 24 | console.log("Researching topic: ", topic); 25 | return await researchWithLangGraph(topic); 26 | }, 27 | }; 28 | 29 | const actions: Action[] = []; 30 | if (process.env["TAVILY_API_KEY"] && process.env["TAVILY_API_KEY"] !== "NONE") { 31 | actions.push(researchAction); 32 | } 33 | 34 | const model = new ChatOpenAI({ 35 | modelName: "gpt-4o-mini", 36 | temperature: 0, 37 | apiKey: process.env["OPENAI_API_KEY"], 38 | }); 39 | 40 | export const POST = async (req: NextRequest) => { 41 | const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ 42 | runtime: new CopilotRuntime({ 43 | actions: actions, 44 | }), 45 | serviceAdapter: new LangChainAdapter({ 46 | chainFn: async ({ messages, tools, threadId }) => { 47 | console.log("POST messages: ", messages) 48 | console.log("POST tools: ", Object.keys(tools)) 49 | console.log("POST tools: ", tools.map(tool => { 50 | return { 51 | name: tool.lc_kwargs.name, 52 | func: JSON.stringify(tool.lc_kwargs.func) 53 | } 54 | })) 55 | console.log("POST threadId: ", threadId) 56 | const modelWithTools = model.bindTools(tools, { strict: true }); 57 | return modelWithTools.stream(messages, { tools, metadata: { conversation_id: threadId } }); 58 | }, 59 | }), 60 | endpoint: req.nextUrl.pathname, 61 | }); 62 | return handleRequest(req); 63 | }; 64 | -------------------------------------------------------------------------------- /src/app/api/copilotkit/tavily.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from "openai"; 2 | 3 | export async function research(query: string) { 4 | const response = await fetch("https://api.tavily.com/search", { 5 | method: "POST", 6 | headers: { 7 | "Content-Type": "application/json", 8 | }, 9 | body: JSON.stringify({ 10 | api_key: process.env.TAVILY_API_KEY, 11 | query, 12 | search_depth: "basic", 13 | include_answer: true, 14 | include_images: false, 15 | include_raw_content: false, 16 | max_results: 20, 17 | }), 18 | }); 19 | 20 | const responseJson = await response.json(); 21 | const openai = new OpenAI(); 22 | 23 | const completion = await openai.chat.completions.create({ 24 | messages: [ 25 | { 26 | role: "system", 27 | content: `Summarize the following JSON to answer the research query \`"${query}"\`: ${JSON.stringify( 28 | responseJson 29 | )} in plain English.`, 30 | }, 31 | ], 32 | model: process.env.OPENAI_MODEL || "gpt-4", 33 | }); 34 | 35 | return completion.choices[0].message.content; 36 | } 37 | -------------------------------------------------------------------------------- /src/app/api/transcribe/route.ts: -------------------------------------------------------------------------------- 1 | import { OpenAI } from "openai"; 2 | 3 | // export const runtime = "edge"; 4 | 5 | const openai = new OpenAI(); 6 | 7 | export async function POST(req: Request): Promise { 8 | try { 9 | const formData = await req.formData(); 10 | const file = formData.get("file") as File; 11 | 12 | if (!file) { 13 | return new Response("File not provided", { status: 400 }); 14 | } 15 | 16 | const transcription = await openai.audio.transcriptions.create({ 17 | file, 18 | model: "whisper-1", 19 | }); 20 | 21 | return new Response(JSON.stringify(transcription), { 22 | status: 200, 23 | headers: { "Content-Type": "application/json" }, 24 | }); 25 | } catch (error: any) { 26 | return new Response(JSON.stringify({ error: error.message }), { 27 | status: 500, 28 | headers: { "Content-Type": "application/json" }, 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/api/tts/route.ts: -------------------------------------------------------------------------------- 1 | import { OpenAI } from "openai"; 2 | 3 | export const runtime = "edge"; 4 | 5 | export async function GET(req: Request): Promise { 6 | const openai = new OpenAI(); 7 | 8 | const url = new URL(req.url); 9 | const text = url.searchParams.get("text"); // 'text' is the query parameter name 10 | 11 | if (!text) { 12 | return new Response("Text parameter is missing", { status: 400 }); 13 | } 14 | 15 | const response = await openai.audio.speech.create({ 16 | voice: "alloy", 17 | input: text, 18 | model: "tts-1", 19 | }); 20 | 21 | return response; 22 | } 23 | -------------------------------------------------------------------------------- /src/app/components/PreviewSpreadsheetChanges.tsx: -------------------------------------------------------------------------------- 1 | import { CheckCircleIcon } from '@heroicons/react/20/solid' 2 | import { SpreadsheetRow } from '../types'; 3 | import { useState } from 'react'; 4 | import Spreadsheet from 'react-spreadsheet'; 5 | 6 | 7 | export interface PreviewSpreadsheetChanges { 8 | preCommitTitle: string; 9 | postCommitTitle: string; 10 | newRows: SpreadsheetRow[]; 11 | commit: (rows: SpreadsheetRow[]) => void; 12 | } 13 | 14 | export function PreviewSpreadsheetChanges(props: PreviewSpreadsheetChanges) { 15 | const [changesCommitted, setChangesCommitted] = useState(false); 16 | 17 | const commitChangesButton = () => { 18 | return ( 19 | 28 | ); 29 | } 30 | 31 | const changesCommittedButtonPlaceholder = () => { 32 | return ( 33 | 40 | ); 41 | } 42 | 43 | return ( 44 |
45 | 48 | 49 |
50 | {changesCommitted ? changesCommittedButtonPlaceholder() : commitChangesButton() } 51 |
52 | 53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/app/components/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { SpreadsheetData } from "../types"; 3 | import { ThemeContext } from "./ThemeProvider"; 4 | 5 | interface SidebarProps { 6 | spreadsheets: SpreadsheetData[]; 7 | selectedSpreadsheetIndex: number; 8 | setSelectedSpreadsheetIndex: (index: number) => void; 9 | } 10 | 11 | const Sidebar = ({ 12 | spreadsheets, 13 | selectedSpreadsheetIndex, 14 | setSelectedSpreadsheetIndex, 15 | }: SidebarProps) => { 16 | const { theme, toggleTheme } = useContext(ThemeContext); 17 | 18 | return ( 19 |
20 |
21 |

Spreadsheets

22 | 37 |
38 |
    39 | {spreadsheets.map((spreadsheet, index) => ( 40 |
  • setSelectedSpreadsheetIndex(index)} 47 | > 48 | {spreadsheet.title} 49 |
  • 50 | ))} 51 |
52 |
53 | ); 54 | }; 55 | 56 | export default Sidebar; 57 | -------------------------------------------------------------------------------- /src/app/components/SingleSpreadsheet.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | useCopilotAction, 3 | useCopilotReadable, 4 | } from "@copilotkit/react-core"; 5 | import React, { useContext, useState } from "react"; 6 | import Spreadsheet from "react-spreadsheet"; 7 | import { canonicalSpreadsheetData } from "../utils/canonicalSpreadsheetData"; 8 | import { SpreadsheetData, SpreadsheetRow } from "../types"; 9 | import { PreviewSpreadsheetChanges } from "./PreviewSpreadsheetChanges"; 10 | import { ThemeContext } from "./ThemeProvider"; 11 | 12 | interface MainAreaProps { 13 | spreadsheet: SpreadsheetData; 14 | setSpreadsheet: (spreadsheet: SpreadsheetData) => void; 15 | spreadSheets: SpreadsheetData[]; 16 | selectedSpreadsheetIndex: number; 17 | setSelectedSpreadsheetIndex: (index: number) => void; 18 | } 19 | 20 | const SingleSpreadsheet = ({ spreadsheet, setSpreadsheet, spreadSheets, selectedSpreadsheetIndex, setSelectedSpreadsheetIndex }: MainAreaProps) => { 21 | const { theme, toggleTheme } = useContext(ThemeContext); 22 | 23 | useCopilotReadable({ 24 | description: "The current spreadsheet", 25 | value: spreadsheet, 26 | }) 27 | 28 | useCopilotAction({ 29 | name: "suggestSpreadsheetOverride", 30 | description: "Suggest an override of the current spreadsheet", 31 | parameters: [ 32 | { 33 | name: "rows", 34 | type: "object[]", 35 | description: "The rows of the spreadsheet", 36 | attributes: [ 37 | { 38 | name: "cells", 39 | type: "object[]", 40 | description: "The cells of the row", 41 | attributes: [ 42 | { 43 | name: "value", 44 | type: "string", 45 | description: "The value of the cell", 46 | }, 47 | ], 48 | }, 49 | ], 50 | }, 51 | { 52 | name: "title", 53 | type: "string", 54 | description: "The title of the spreadsheet", 55 | required: false, 56 | nullable: true, 57 | }, 58 | ], 59 | render: (props) => { 60 | const { rows } = props.args 61 | const newRows = canonicalSpreadsheetData(rows); 62 | 63 | return ( 64 | { 69 | const updatedSpreadsheet: SpreadsheetData = { 70 | title: spreadsheet.title, 71 | rows: rows, 72 | }; 73 | setSpreadsheet(updatedSpreadsheet); 74 | }} 75 | /> 76 | ) 77 | }, 78 | handler: ({ rows, title }) => { 79 | // Do nothing. 80 | // The preview component will optionally handle committing the changes. 81 | }, 82 | }); 83 | 84 | useCopilotAction({ 85 | name: "appendToSpreadsheet", 86 | description: "Append rows to the current spreadsheet", 87 | parameters: [ 88 | { 89 | name: "rows", 90 | type: "object[]", 91 | description: "The new rows of the spreadsheet", 92 | attributes: [ 93 | { 94 | name: "cells", 95 | type: "object[]", 96 | description: "The cells of the row", 97 | attributes: [ 98 | { 99 | name: "value", 100 | type: "string", 101 | description: "The value of the cell", 102 | }, 103 | ], 104 | }, 105 | ], 106 | }, 107 | ], 108 | render: (props) => { 109 | const status = props.status; 110 | const { rows } = props.args 111 | const newRows = canonicalSpreadsheetData(rows); 112 | return ( 113 |
114 |

Status: {status}

115 | 118 |
119 | ) 120 | }, 121 | handler: ({ rows }) => { 122 | const canonicalRows = canonicalSpreadsheetData(rows); 123 | const updatedSpreadsheet: SpreadsheetData = { 124 | title: spreadsheet.title, 125 | rows: [...spreadsheet.rows, ...canonicalRows], 126 | }; 127 | setSpreadsheet(updatedSpreadsheet); 128 | }, 129 | }); 130 | 131 | return ( 132 | <> 133 |
134 | 139 | setSpreadsheet({ ...spreadsheet, title: e.target.value }) 140 | } 141 | /> 142 |
143 | { 146 | console.log("data", data); 147 | setSpreadsheet({ ...spreadsheet, rows: data as any }); 148 | }} 149 | /> 150 | 166 |
167 | 184 |
185 |
186 |
187 |
188 | {/* Replace with dynamic sheet buttons */} 189 | {spreadSheets.map((sheet, index) => ( 190 | 199 | ))} 200 | 215 |
216 | 217 |
218 |
219 | 220 | ); 221 | }; 222 | 223 | export default SingleSpreadsheet; 224 | 225 | 226 | -------------------------------------------------------------------------------- /src/app/components/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { createContext, useState, useEffect, ReactNode } from "react"; 3 | 4 | // Create a theme context 5 | export const ThemeContext = createContext({ 6 | theme: "light", 7 | toggleTheme: () => { }, 8 | }); 9 | 10 | interface ThemeProviderProps { 11 | children: ReactNode; 12 | } 13 | 14 | export const ThemeProvider = ({ children }: ThemeProviderProps) => { 15 | const [theme, setTheme] = useState("light"); 16 | 17 | const toggleTheme = () => { 18 | setTheme(prevTheme => (prevTheme === "light" ? "dark" : "light")); 19 | }; 20 | 21 | useEffect(() => { 22 | // Apply theme class to the document element 23 | document.documentElement.classList.remove("light", "dark"); 24 | document.documentElement.classList.add(theme); 25 | }, [theme]); 26 | 27 | return ( 28 | 29 | {children} 30 | 31 | ); 32 | }; -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CopilotKit/demo-spreadsheet/a048c90b01df2f6d696706df86939fa32995962d/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | html.dark { 12 | --foreground-rgb: 255, 255, 255; 13 | --background-start-rgb: 0, 0, 0; 14 | --background-end-rgb: 0, 0, 0; 15 | } 16 | 17 | body { 18 | color: rgb(var(--foreground-rgb)); 19 | background: linear-gradient(to bottom, 20 | transparent, 21 | rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); 22 | } 23 | 24 | @layer utilities { 25 | .text-balance { 26 | text-wrap: balance; 27 | } 28 | } 29 | 30 | /* Dark mode overrides */ 31 | html.dark .bg-gray-800 { 32 | background-color: #111827; 33 | /* Darker shade */ 34 | } 35 | 36 | html.dark .ring-blue-500 { 37 | --tw-ring-color: #3b82f6; 38 | } -------------------------------------------------------------------------------- /src/app/instructions.ts: -------------------------------------------------------------------------------- 1 | export const INSTRUCTIONS = ` 2 | You assist the user with a spreadsheet. Basic formulas are permitted, for example \`=SUM(B1:B9)\`. 3 | When setting numbers, don't use commas. For example, use 1000 instead of 1,000. 4 | Don't use words like 'million' etc, just the numbers. 5 | 6 | When the user prompts you to create a new spreadsheet, focus on providing interesting numerical data and 7 | insert formulas when appropriate. 8 | 9 | If you have values that are a range, create a column for min and one for max. 10 | 11 | Available functions in formulas: 12 | ABS, ACOS, ACOSH, ACOT, ACOTH, ADDRESS, AND, ARABIC, AREAS, ASC, ASIN, ASINH, ATAN, ATAN2, ATANH, AVEDEV, AVERAGE, AVERAGEA, AVERAGEIF, BAHTTEXT, BASE, BESSELI, BESSELJ, BESSELK, BESSELY, BETA.DIST, BETA.INV, BIN2DEC, BIN2HEX, BIN2OCT, BINOM.DIST, BINOM.DIST.RANGE, BINOM.INV, BITAND, BITLSHIFT, BITOR, 13 | BITRSHIFT, BITXOR, CEILING, CEILING.MATH, CEILING.PRECISE, CHAR, CHISQ.DIST, CHISQ.DIST.RT, CHISQ.INV, CHISQ.INV.RT, CHISQ.TEST, CLEAN, CODE, COLUMN, COLUMNS, COMBIN, COMBINA, COMPLEX, CONCAT, CONCATENATE, CONFIDENCE.NORM, CONFIDENCE.T, CORREL, COS, COSH, COT, COTH, COUNT, COUNTIF, COVARIANCE.P, 14 | COVARIANCE.S, CSC, CSCH, DATE, DATEDIF, DATEVALUE, DAY, DAYS, DAYS360, DBCS, DEC2BIN, DEC2HEX, DEC2OCT, DECIMAL, DEGREES, DELTA, DEVSQ, DOLLAR, EDATE, ENCODEURL, EOMONTH, ERF, ERFC, ERROR.TYPE, EVEN, EXACT, EXP, EXPON.DIST, F.DIST, F.DIST.RT, F.INV, F.INV.RT, F.TEST, FACT, FACTDOUBLE, FALSE, FIND, FINDB, 15 | FISHER, FISHERINV, FIXED, FLOOR, FLOOR.MATH, FLOOR.PRECISE, FORECAST, FORECAST.LINEAR, FREQUENCY, GAMMA, GAMMA.DIST, GAMMA.INV, GAMMALN, GAMMALN.PRECISE, GAUSS, GCD, GEOMEAN, GESTEP, GROWTH, HARMEAN, HEX2BIN, HEX2DEC, HEX2OCT, HLOOKUP, HOUR, HYPGEOM.DIST, IF, IFERROR, IFNA, IFS, IMABS, IMAGINARY, IMARGUMENT, 16 | IMCONJUGATE, IMCOS, IMCOSH, IMCOT, IMCSC, IMCSCH, IMDIV, IMEXP, IMLN, IMLOG10, IMLOG2, IMPOWER, IMPRODUCT, IMREAL, IMSEC, IMSECH, IMSIN, IMSINH, IMSQRT, IMSUB, IMSUM, IMTAN, INDEX, INT, INTERCEPT, ISBLANK, ISERR, ISERROR, ISEVEN, ISLOGICAL, ISNA, ISNONTEXT, ISNUMBER, ISO.CEILING, ISOWEEKNUM, ISREF, ISTEXT, 17 | KURT, LCM, LEFT, LEFTB, LN, LOG, LOG10, LOGNORM.DIST, LOGNORM.INV, LOWER, MDETERM, MID, MIDB, MINUTE, MMULT, MOD, MONTH, MROUND, MULTINOMIAL, MUNIT, N, NA, NEGBINOM.DIST, NETWORKDAYS, NETWORKDAYS.INTL, NORM.DIST, NORM.INV, NORM.S.DIST, NORM.S.INV, NOT, NOW, NUMBERVALUE, OCT2BIN, OCT2DEC, OCT2HEX, ODD, OR, 18 | PHI, PI, POISSON.DIST, POWER, PRODUCT, PROPER, QUOTIENT, RADIANS, RAND, RANDBETWEEN, REPLACE, REPLACEB, REPT, RIGHT, RIGHTB, ROMAN, ROUND, ROUNDDOWN, ROUNDUP, ROW, ROWS, SEARCH, SEARCHB, SEC, SECH, SECOND, SERIESSUM, SIGN, SIN, SINH, SQRT, SQRTPI, STANDARDIZE, SUM, SUMIF, SUMPRODUCT, SUMSQ, SUMX2MY2, 19 | SUMX2PY2, SUMXMY2, T, T.DIST, T.DIST.2T, T.DIST.RT, T.INV, T.INV.2T, TAN, TANH, TEXT, TIME, TIMEVALUE, TODAY, TRANSPOSE, TRIM, TRUE, TRUNC, TYPE, UNICHAR, UNICODE, UPPER, VLOOKUP, WEBSERVICE, WEEKDAY, WEEKNUM, WEIBULL.DIST, WORKDAY, WORKDAY.INTL, XOR, YEAR, YEARFRAC 20 | `; 21 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import { ThemeProvider } from "./components/ThemeProvider"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Create Next App", 10 | description: "Generated by create next app", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | 21 | {children} 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import "@copilotkit/react-ui/styles.css"; 3 | 4 | import React, { useState } from "react"; 5 | import SingleSpreadsheet from "./components/SingleSpreadsheet"; 6 | import { 7 | CopilotKit, 8 | useCopilotAction, 9 | useCopilotReadable, 10 | } from "@copilotkit/react-core"; 11 | import { CopilotSidebar, useCopilotChatSuggestions } from "@copilotkit/react-ui"; 12 | import { INSTRUCTIONS } from "./instructions"; 13 | import { canonicalSpreadsheetData } from "./utils/canonicalSpreadsheetData"; 14 | import { SpreadsheetData } from "./types"; 15 | import { PreviewSpreadsheetChanges } from "./components/PreviewSpreadsheetChanges"; 16 | import { sampleData, sampleData2 } from "./utils/sampleData"; 17 | // import { Bottombar } from "./components/Bottombar"; 18 | 19 | const HomePage = () => { 20 | return ( 21 | 26 | 34 |
35 | 36 | 37 | ); 38 | }; 39 | 40 | // function rowsGenerator(n : number) { 41 | // const rows = []; 42 | 43 | // for (let i = 0; i < 25; i++) { 44 | // const row = []; 45 | 46 | // for (let j = 0; j < 25; j++) { 47 | // if (i < n && j < n) { 48 | // row.push({ value: "Sample data" }); 49 | // } 50 | // else { 51 | // row.push({ value: "" }); 52 | // } 53 | 54 | // } 55 | // rows.push(row); 56 | 57 | // } 58 | // return rows; 59 | 60 | // } 61 | 62 | const Main = () => { 63 | const [spreadsheets, setSpreadsheets] = React.useState([ 64 | { 65 | title: "Revenue by department", 66 | rows: sampleData, 67 | }, 68 | { 69 | title: "Projects Tracker", 70 | rows: sampleData2, 71 | }, 72 | 73 | ]); 74 | 75 | const [selectedSpreadsheetIndex, setSelectedSpreadsheetIndex] = useState(0); 76 | 77 | useCopilotAction({ 78 | name: "createSpreadsheet", 79 | description: "Create a new spreadsheet", 80 | parameters: [ 81 | { 82 | name: "rows", 83 | type: "object[]", 84 | description: "The rows of the spreadsheet", 85 | attributes: [ 86 | { 87 | name: "cells", 88 | type: "object[]", 89 | description: "The cells of the row", 90 | attributes: [ 91 | { 92 | name: "value", 93 | type: "string", 94 | description: "The value of the cell", 95 | }, 96 | ], 97 | }, 98 | ], 99 | }, 100 | { 101 | name: "title", 102 | type: "string", 103 | description: "The title of the spreadsheet", 104 | }, 105 | ], 106 | render: (props) => { 107 | console.log("createSpreadsheet => props: => ", props) 108 | const { rows, title } = props.args; 109 | const newRows = canonicalSpreadsheetData(rows); 110 | 111 | return ( 112 | { 117 | const newSpreadsheet: SpreadsheetData = { 118 | title: title || "Untitled Spreadsheet", 119 | rows: rows, 120 | }; 121 | setSpreadsheets((prev) => [...prev, newSpreadsheet]); 122 | setSelectedSpreadsheetIndex(spreadsheets.length); 123 | }} 124 | /> 125 | ); 126 | }, 127 | handler: ({ rows, title }) => { 128 | // Do nothing. 129 | // The preview component will optionally handle committing the changes. 130 | }, 131 | }); 132 | 133 | useCopilotChatSuggestions({ 134 | instructions: "Provide suggestions for the user like creating a new sheet with sample data, appending rows, telling them about this view. Strictly show only these options at the start of the chat.", 135 | maxSuggestions: 3, 136 | minSuggestions: 1 137 | }) 138 | useCopilotReadable({ 139 | description: "Today's date", 140 | value: new Date().toLocaleDateString(), 141 | }); 142 | 143 | return ( 144 |
145 | { 151 | setSpreadsheets((prev) => { 152 | console.log("setSpreadsheet", spreadsheet); 153 | const newSpreadsheets = [...prev]; 154 | newSpreadsheets[selectedSpreadsheetIndex] = spreadsheet; 155 | return newSpreadsheets; 156 | }); 157 | }} 158 | /> 159 |
160 | ); 161 | }; 162 | 163 | export default HomePage; 164 | -------------------------------------------------------------------------------- /src/app/types.ts: -------------------------------------------------------------------------------- 1 | export interface Cell { 2 | value: string; 3 | } 4 | 5 | export type SpreadsheetRow = Cell[]; 6 | 7 | export interface SpreadsheetData { 8 | title: string; 9 | rows: SpreadsheetRow[]; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/utils/canonicalSpreadsheetData.ts: -------------------------------------------------------------------------------- 1 | import { SpreadsheetRow } from "../types"; 2 | 3 | export interface RowLike { 4 | cells: CellLike[] | undefined; 5 | } 6 | 7 | export interface CellLike { 8 | value: string; 9 | } 10 | 11 | export function canonicalSpreadsheetData( 12 | rows: RowLike[] | undefined 13 | ): SpreadsheetRow[] { 14 | const canonicalRows: SpreadsheetRow[] = []; 15 | 16 | for (const row of rows || []) { 17 | const canonicalRow: SpreadsheetRow = []; 18 | for (const cell of row.cells || []) { 19 | canonicalRow.push({value: cell.value}); 20 | } 21 | canonicalRows.push(canonicalRow); 22 | } 23 | 24 | return canonicalRows; 25 | } -------------------------------------------------------------------------------- /src/app/utils/sampleData.ts: -------------------------------------------------------------------------------- 1 | export const sampleData = [ 2 | [ 3 | {value: "Department"}, 4 | {value: "Region"}, 5 | {value: "Q1 Revenue"}, 6 | {value: "Q2 Revenue"}, 7 | {value: "Growth %"}, 8 | {value: "Team Size"}, 9 | {value: "Status"} 10 | ], 11 | [ 12 | {value: "Sales"}, 13 | {value: "North America"}, 14 | {value: "$1,250,000"}, 15 | {value: "$1,450,000"}, 16 | {value: "16%"}, 17 | {value: "12"}, 18 | {value: "Active"} 19 | ], 20 | [ 21 | {value: "Sales"}, 22 | {value: "Europe"}, 23 | {value: "$980,000"}, 24 | {value: "$1,150,000"}, 25 | {value: "17.3%"}, 26 | {value: "8"}, 27 | {value: "Active"} 28 | ], 29 | [ 30 | {value: "Sales"}, 31 | {value: "Asia Pacific"}, 32 | {value: "$850,000"}, 33 | {value: "$1,020,000"}, 34 | {value: "20%"}, 35 | {value: "10"}, 36 | {value: "Active"} 37 | ], 38 | [ 39 | {value: "Marketing"}, 40 | {value: "Global"}, 41 | {value: "$850,000"}, 42 | {value: "$920,000"}, 43 | {value: "8.2%"}, 44 | {value: "8"}, 45 | {value: "Active"} 46 | ], 47 | [ 48 | {value: "R&D"}, 49 | {value: "North America"}, 50 | {value: "$2,100,000"}, 51 | {value: "$2,300,000"}, 52 | {value: "9.5%"}, 53 | {value: "15"}, 54 | {value: "Active"} 55 | ], 56 | [ 57 | {value: "R&D"}, 58 | {value: "Europe"}, 59 | {value: "$1,800,000"}, 60 | {value: "$1,950,000"}, 61 | {value: "8.3%"}, 62 | {value: "12"}, 63 | {value: "Active"} 64 | ], 65 | [ 66 | {value: "Operations"}, 67 | {value: "North America"}, 68 | {value: "$750,000"}, 69 | {value: "$780,000"}, 70 | {value: "4%"}, 71 | {value: "10"}, 72 | {value: "Active"} 73 | ], 74 | [ 75 | {value: "Operations"}, 76 | {value: "Europe"}, 77 | {value: "$620,000"}, 78 | {value: "$650,000"}, 79 | {value: "4.8%"}, 80 | {value: "8"}, 81 | {value: "Active"} 82 | ], 83 | [ 84 | {value: "Finance"}, 85 | {value: "Global"}, 86 | {value: "$500,000"}, 87 | {value: "$520,000"}, 88 | {value: "4%"}, 89 | {value: "6"}, 90 | {value: "Active"} 91 | ], 92 | [ 93 | {value: "HR"}, 94 | {value: "Global"}, 95 | {value: "$300,000"}, 96 | {value: "$310,000"}, 97 | {value: "3.3%"}, 98 | {value: "5"}, 99 | {value: "Active"} 100 | ], 101 | [ 102 | {value: "IT"}, 103 | {value: "Global"}, 104 | {value: "$1,200,000"}, 105 | {value: "$1,300,000"}, 106 | {value: "8.3%"}, 107 | {value: "14"}, 108 | {value: "Active"} 109 | ], 110 | [ 111 | {value: "Customer Support"}, 112 | {value: "North America"}, 113 | {value: "$450,000"}, 114 | {value: "$480,000"}, 115 | {value: "6.7%"}, 116 | {value: "9"}, 117 | {value: "Active"} 118 | ], 119 | [ 120 | {value: "Customer Support"}, 121 | {value: "Europe"}, 122 | {value: "$380,000"}, 123 | {value: "$410,000"}, 124 | {value: "7.9%"}, 125 | {value: "7"}, 126 | {value: "Active"} 127 | ], 128 | [ 129 | {value: "Legal"}, 130 | {value: "Global"}, 131 | {value: "$400,000"}, 132 | {value: "$420,000"}, 133 | {value: "5%"}, 134 | {value: "4"}, 135 | {value: "Active"} 136 | ], 137 | [ 138 | {value: "Product Development"}, 139 | {value: "North America"}, 140 | {value: "$1,500,000"}, 141 | {value: "$1,650,000"}, 142 | {value: "10%"}, 143 | {value: "11"}, 144 | {value: "Active"} 145 | ], 146 | [ 147 | {value: "Product Development"}, 148 | {value: "Europe"}, 149 | {value: "$1,200,000"}, 150 | {value: "$1,320,000"}, 151 | {value: "10%"}, 152 | {value: "9"}, 153 | {value: "Active"} 154 | ], 155 | [ 156 | {value: "Quality Assurance"}, 157 | {value: "Global"}, 158 | {value: "$600,000"}, 159 | {value: "$630,000"}, 160 | {value: "5%"}, 161 | {value: "7"}, 162 | {value: "Active"} 163 | ], 164 | [ 165 | {value: "Business Development"}, 166 | {value: "North America"}, 167 | {value: "$800,000"}, 168 | {value: "$950,000"}, 169 | {value: "18.8%"}, 170 | {value: "6"}, 171 | {value: "Active"} 172 | ], 173 | [ 174 | {value: "Business Development"}, 175 | {value: "Europe"}, 176 | {value: "$700,000"}, 177 | {value: "$820,000"}, 178 | {value: "17.1%"}, 179 | {value: "5"}, 180 | {value: "Active"} 181 | ], 182 | [ 183 | {value: "Business Development"}, 184 | {value: "Asia Pacific"}, 185 | {value: "$600,000"}, 186 | {value: "$750,000"}, 187 | {value: "25%"}, 188 | {value: "4"}, 189 | {value: "Active"} 190 | ], 191 | [ 192 | {value: "Data Analytics"}, 193 | {value: "Global"}, 194 | {value: "$900,000"}, 195 | {value: "$1,000,000"}, 196 | {value: "11.1%"}, 197 | {value: "8"}, 198 | {value: "Active"} 199 | ], 200 | [ 201 | {value: "Security"}, 202 | {value: "Global"}, 203 | {value: "$550,000"}, 204 | {value: "$580,000"}, 205 | {value: "5.5%"}, 206 | {value: "6"}, 207 | {value: "Active"} 208 | ], 209 | [ 210 | {value: "Facilities"}, 211 | {value: "North America"}, 212 | {value: "$300,000"}, 213 | {value: "$310,000"}, 214 | {value: "3.3%"}, 215 | {value: "4"}, 216 | {value: "Active"} 217 | ], 218 | [ 219 | {value: "Facilities"}, 220 | {value: "Europe"}, 221 | {value: "$250,000"}, 222 | {value: "$260,000"}, 223 | {value: "4%"}, 224 | {value: "3"}, 225 | {value: "Active"} 226 | ], 227 | [ 228 | {value: "Communications"}, 229 | {value: "Global"}, 230 | {value: "$400,000"}, 231 | {value: "$430,000"}, 232 | {value: "7.5%"}, 233 | {value: "5"}, 234 | {value: "Active"} 235 | ], 236 | [ 237 | {value: "Compliance"}, 238 | {value: "Global"}, 239 | {value: "$350,000"}, 240 | {value: "$370,000"}, 241 | {value: "5.7%"}, 242 | {value: "4"}, 243 | {value: "Active"} 244 | ], 245 | [ 246 | {value: "Training"}, 247 | {value: "Global"}, 248 | {value: "$280,000"}, 249 | {value: "$300,000"}, 250 | {value: "7.1%"}, 251 | {value: "3"}, 252 | {value: "Active"} 253 | ], 254 | [ 255 | {value: "Procurement"}, 256 | {value: "Global"}, 257 | {value: "$450,000"}, 258 | {value: "$470,000"}, 259 | {value: "4.4%"}, 260 | {value: "5"}, 261 | {value: "Active"} 262 | ], 263 | [ 264 | {value: "Strategy"}, 265 | {value: "Global"}, 266 | {value: "$600,000"}, 267 | {value: "$650,000"}, 268 | {value: "8.3%"}, 269 | {value: "4"}, 270 | {value: "Active"} 271 | ] 272 | ]; 273 | 274 | export const sampleData2 = [ 275 | [ 276 | {value: "Project Name"}, 277 | {value: "Client"}, 278 | {value: "Start Date"}, 279 | {value: "End Date"}, 280 | {value: "Budget"}, 281 | {value: "Spent"}, 282 | {value: "Status"}, 283 | {value: "Team Size"} 284 | ], 285 | [ 286 | {value: "Cloud Migration"}, 287 | {value: "TechCorp Inc"}, 288 | {value: "2024-01-15"}, 289 | {value: "2024-06-30"}, 290 | {value: "$500,000"}, 291 | {value: "$150,000"}, 292 | {value: "In Progress"}, 293 | {value: "8"} 294 | ], 295 | [ 296 | {value: "Mobile App Development"}, 297 | {value: "StartUpX"}, 298 | {value: "2024-02-01"}, 299 | {value: "2024-08-31"}, 300 | {value: "$300,000"}, 301 | {value: "$75,000"}, 302 | {value: "In Progress"}, 303 | {value: "6"} 304 | ], 305 | [ 306 | {value: "Data Center Upgrade"}, 307 | {value: "GlobalBank"}, 308 | {value: "2023-11-01"}, 309 | {value: "2024-04-30"}, 310 | {value: "$1,200,000"}, 311 | {value: "$900,000"}, 312 | {value: "In Progress"}, 313 | {value: "12"} 314 | ], 315 | [ 316 | {value: "Security Audit"}, 317 | {value: "HealthCare Plus"}, 318 | {value: "2024-01-01"}, 319 | {value: "2024-03-31"}, 320 | {value: "$200,000"}, 321 | {value: "$180,000"}, 322 | {value: "Completed"}, 323 | {value: "4"} 324 | ], 325 | [ 326 | {value: "Website Redesign"}, 327 | {value: "RetailCo"}, 328 | {value: "2023-12-01"}, 329 | {value: "2024-02-28"}, 330 | {value: "$150,000"}, 331 | {value: "$150,000"}, 332 | {value: "Completed"}, 333 | {value: "5"} 334 | ], 335 | [ 336 | {value: "AI Integration"}, 337 | {value: "ManufacturingPro"}, 338 | {value: "2024-03-01"}, 339 | {value: "2024-09-30"}, 340 | {value: "$800,000"}, 341 | {value: "$100,000"}, 342 | {value: "In Progress"}, 343 | {value: "10"} 344 | ], 345 | [ 346 | {value: "ERP Implementation"}, 347 | {value: "LogisticsCo"}, 348 | {value: "2023-10-01"}, 349 | {value: "2024-12-31"}, 350 | {value: "$2,000,000"}, 351 | {value: "$800,000"}, 352 | {value: "In Progress"}, 353 | {value: "15"} 354 | ], 355 | [ 356 | {value: "Network Upgrade"}, 357 | {value: "EducationFirst"}, 358 | {value: "2024-02-15"}, 359 | {value: "2024-05-31"}, 360 | {value: "$400,000"}, 361 | {value: "$120,000"}, 362 | {value: "In Progress"}, 363 | {value: "7"} 364 | ], 365 | [ 366 | {value: "Digital Transformation"}, 367 | {value: "InsuranceCo"}, 368 | {value: "2023-09-01"}, 369 | {value: "2024-08-31"}, 370 | {value: "$1,500,000"}, 371 | {value: "$900,000"}, 372 | {value: "In Progress"}, 373 | {value: "14"} 374 | ], 375 | [ 376 | {value: "IoT Implementation"}, 377 | {value: "SmartHome Inc"}, 378 | {value: "2024-01-01"}, 379 | {value: "2024-07-31"}, 380 | {value: "$600,000"}, 381 | {value: "$200,000"}, 382 | {value: "In Progress"}, 383 | {value: "9"} 384 | ], 385 | [ 386 | {value: "Blockchain Integration"}, 387 | {value: "FinTech Solutions"}, 388 | {value: "2024-02-01"}, 389 | {value: "2024-10-31"}, 390 | {value: "$900,000"}, 391 | {value: "$150,000"}, 392 | {value: "In Progress"}, 393 | {value: "11"} 394 | ], 395 | [ 396 | {value: "CRM Upgrade"}, 397 | {value: "SalesForce Co"}, 398 | {value: "2023-11-15"}, 399 | {value: "2024-03-31"}, 400 | {value: "$250,000"}, 401 | {value: "$220,000"}, 402 | {value: "In Progress"}, 403 | {value: "6"} 404 | ], 405 | [ 406 | {value: "Mobile Payment System"}, 407 | {value: "PayTech"}, 408 | {value: "2024-01-01"}, 409 | {value: "2024-06-30"}, 410 | {value: "$700,000"}, 411 | {value: "$300,000"}, 412 | {value: "In Progress"}, 413 | {value: "10"} 414 | ], 415 | [ 416 | {value: "Cloud Security"}, 417 | {value: "SecureCloud"}, 418 | {value: "2023-12-01"}, 419 | {value: "2024-05-31"}, 420 | {value: "$450,000"}, 421 | {value: "$200,000"}, 422 | {value: "In Progress"}, 423 | {value: "8"} 424 | ], 425 | [ 426 | {value: "E-commerce Platform"}, 427 | {value: "ShopOnline"}, 428 | {value: "2024-02-01"}, 429 | {value: "2024-08-31"}, 430 | {value: "$550,000"}, 431 | {value: "$100,000"}, 432 | {value: "In Progress"}, 433 | {value: "9"} 434 | ], 435 | [ 436 | {value: "Data Analytics"}, 437 | {value: "MarketInsights"}, 438 | {value: "2023-10-01"}, 439 | {value: "2024-04-30"}, 440 | {value: "$350,000"}, 441 | {value: "$280,000"}, 442 | {value: "In Progress"}, 443 | {value: "7"} 444 | ], 445 | [ 446 | {value: "API Development"}, 447 | {value: "TechStartup"}, 448 | {value: "2024-01-15"}, 449 | {value: "2024-05-31"}, 450 | {value: "$200,000"}, 451 | {value: "$80,000"}, 452 | {value: "In Progress"}, 453 | {value: "5"} 454 | ], 455 | [ 456 | {value: "DevOps Implementation"}, 457 | {value: "SoftwareCo"}, 458 | {value: "2023-11-01"}, 459 | {value: "2024-03-31"}, 460 | {value: "$300,000"}, 461 | {value: "$250,000"}, 462 | {value: "In Progress"}, 463 | {value: "6"} 464 | ], 465 | [ 466 | {value: "Machine Learning Model"}, 467 | {value: "AI Solutions"}, 468 | {value: "2024-02-01"}, 469 | {value: "2024-09-30"}, 470 | {value: "$750,000"}, 471 | {value: "$150,000"}, 472 | {value: "In Progress"}, 473 | {value: "10"} 474 | ], 475 | [ 476 | {value: "Mobile Banking App"}, 477 | {value: "DigitalBank"}, 478 | {value: "2023-12-01"}, 479 | {value: "2024-07-31"}, 480 | {value: "$1,000,000"}, 481 | {value: "$400,000"}, 482 | {value: "In Progress"}, 483 | {value: "12"} 484 | ], 485 | [ 486 | {value: "Supply Chain System"}, 487 | {value: "LogisticsPro"}, 488 | {value: "2024-01-01"}, 489 | {value: "2024-12-31"}, 490 | {value: "$1,800,000"}, 491 | {value: "$500,000"}, 492 | {value: "In Progress"}, 493 | {value: "15"} 494 | ], 495 | [ 496 | {value: "Video Platform"}, 497 | {value: "StreamTech"}, 498 | {value: "2024-02-15"}, 499 | {value: "2024-08-31"}, 500 | {value: "$650,000"}, 501 | {value: "$100,000"}, 502 | {value: "In Progress"}, 503 | {value: "9"} 504 | ], 505 | [ 506 | {value: "Cybersecurity Audit"}, 507 | {value: "SecureNet"}, 508 | {value: "2023-11-01"}, 509 | {value: "2024-02-28"}, 510 | {value: "$280,000"}, 511 | {value: "$250,000"}, 512 | {value: "Completed"}, 513 | {value: "5"} 514 | ], 515 | [ 516 | {value: "Social Media Platform"}, 517 | {value: "ConnectSocial"}, 518 | {value: "2024-01-01"}, 519 | {value: "2024-10-31"}, 520 | {value: "$1,200,000"}, 521 | {value: "$300,000"}, 522 | {value: "In Progress"}, 523 | {value: "13"} 524 | ], 525 | [ 526 | {value: "IoT Security"}, 527 | {value: "SmartSecure"}, 528 | {value: "2024-02-01"}, 529 | {value: "2024-07-31"}, 530 | {value: "$400,000"}, 531 | {value: "$80,000"}, 532 | {value: "In Progress"}, 533 | {value: "7"} 534 | ], 535 | [ 536 | {value: "AR/VR Development"}, 537 | {value: "VirtualTech"}, 538 | {value: "2023-12-01"}, 539 | {value: "2024-09-30"}, 540 | {value: "$950,000"}, 541 | {value: "$350,000"}, 542 | {value: "In Progress"}, 543 | {value: "11"} 544 | ], 545 | [ 546 | {value: "Payment Gateway"}, 547 | {value: "PaySecure"}, 548 | {value: "2024-01-15"}, 549 | {value: "2024-06-30"}, 550 | {value: "$500,000"}, 551 | {value: "$150,000"}, 552 | {value: "In Progress"}, 553 | {value: "8"} 554 | ], 555 | [ 556 | {value: "Cloud Storage Solution"}, 557 | {value: "StorageTech"}, 558 | {value: "2023-10-01"}, 559 | {value: "2024-05-31"}, 560 | {value: "$800,000"}, 561 | {value: "$600,000"}, 562 | {value: "In Progress"}, 563 | {value: "10"} 564 | ], 565 | [ 566 | {value: "Mobile Game Development"}, 567 | {value: "GameStudio"}, 568 | {value: "2024-02-01"}, 569 | {value: "2024-11-30"}, 570 | {value: "$1,500,000"}, 571 | {value: "$200,000"}, 572 | {value: "In Progress"}, 573 | {value: "14"} 574 | ] 575 | ]; 576 | 577 | 578 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | darkMode: 'class', 10 | theme: { 11 | extend: { 12 | backgroundImage: { 13 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 14 | "gradient-conic": 15 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 16 | }, 17 | }, 18 | }, 19 | plugins: [], 20 | }; 21 | export default config; 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "dom.iterable", 6 | "esnext" 7 | ], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "module": "esnext", 14 | "moduleResolution": "bundler", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve", 18 | "incremental": true, 19 | "plugins": [ 20 | { 21 | "name": "next" 22 | } 23 | ], 24 | "paths": { 25 | "@/*": [ 26 | "./src/*" 27 | ], 28 | "@copilotkit/react-core": [ 29 | "../CopilotKit/CopilotKit/packages/react-core/dist" 30 | ], 31 | "@copilotkit/shared": [ 32 | "../CopilotKit/CopilotKit/packages/shared/dist" 33 | ], 34 | "@copilotkit/react-ui": [ 35 | "../CopilotKit/CopilotKit/packages/react-ui/dist" 36 | ], 37 | "@copilotkit/react-ui/*": [ 38 | "../CopilotKit/CopilotKit/packages/react-ui/dist/*" 39 | ], 40 | "@copilotkit/runtime": [ 41 | "../CopilotKit/CopilotKit/packages/runtime/dist" 42 | ], 43 | }, 44 | "target": "ES2017" 45 | }, 46 | "include": [ 47 | "next-env.d.ts", 48 | "**/*.ts", 49 | "**/*.tsx", 50 | ".next/types/**/*.ts", 51 | ], 52 | "exclude": [ 53 | "node_modules" 54 | ] 55 | } --------------------------------------------------------------------------------