From a5fb11e982d2e23559683c351b3814d30d6857d0 Mon Sep 17 00:00:00 2001 From: jinyang628 Date: Sat, 7 Jun 2025 21:19:46 +0800 Subject: [PATCH 1/4] Add sonner and tanstack dependencies --- frontend/package-lock.json | 61 ++++++++++++++++++++- frontend/package.json | 6 +- frontend/src/app/layout.tsx | 30 ++++++---- frontend/src/components/providers/query.tsx | 11 ++++ frontend/src/components/ui/sonner.tsx | 26 +++++++++ 5 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 frontend/src/components/providers/query.tsx create mode 100644 frontend/src/components/ui/sonner.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 14dbc2d..d55c9f7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,13 +8,17 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "@tanstack/react-query": "^5.80.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.513.0", "next": "15.3.3", + "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", - "tailwind-merge": "^3.3.0" + "sonner": "^2.0.5", + "tailwind-merge": "^3.3.0", + "zod": "^3.25.56" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -1415,6 +1419,32 @@ "tailwindcss": "4.1.8" } }, + "node_modules/@tanstack/query-core": { + "version": "5.80.6", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.80.6.tgz", + "integrity": "sha512-nl7YxT/TAU+VTf+e2zTkObGTyY8YZBMnbgeA1ee66lIVqzKlYursAII6z5t0e6rXgwUMJSV4dshBTNacNpZHbQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.80.6", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.80.6.tgz", + "integrity": "sha512-izX+5CnkpON3NQGcEm3/d7LfFQNo9ZpFtX2QsINgCYK9LT2VCIdi8D3bMaMSNhrAJCznRoAkFic76uvLroALBw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.80.6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@trivago/prettier-plugin-sort-imports": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz", @@ -5080,6 +5110,16 @@ } } }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -6004,6 +6044,16 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sonner": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.5.tgz", + "integrity": "sha512-YwbHQO6cSso3HBXlbCkgrgzDNIhws14r4MO87Ofy+cV2X7ES4pOoAK3+veSmVTvqNx1BWUxlhPmZzP00Crk2aQ==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6730,6 +6780,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.56", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.56.tgz", + "integrity": "sha512-rd6eEF3BTNvQnR2e2wwolfTmUTnp70aUTqr0oaGbHifzC3BKJsoV+Gat8vxUMR1hwOKBs6El+qWehrHbCpW6SQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/frontend/package.json b/frontend/package.json index 693997f..9ff7ff3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,13 +9,17 @@ "lint": "next lint" }, "dependencies": { + "@tanstack/react-query": "^5.80.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.513.0", "next": "15.3.3", + "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", - "tailwind-merge": "^3.3.0" + "sonner": "^2.0.5", + "tailwind-merge": "^3.3.0", + "zod": "^3.25.56" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 76fe855..3bcfe64 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,17 +1,13 @@ import type { Metadata } from 'next'; -import { Geist, Geist_Mono } from 'next/font/google'; +import { ThemeProvider } from 'next-themes'; +import { Inter } from 'next/font/google'; -import './globals.css'; +import { QueryProvider } from '@/components/providers/query'; +import { Toaster } from '@/components/ui/sonner'; -const geistSans = Geist({ - variable: '--font-geist-sans', - subsets: ['latin'], -}); +import './globals.css'; -const geistMono = Geist_Mono({ - variable: '--font-geist-mono', - subsets: ['latin'], -}); +const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { title: 'Create Next App', @@ -24,8 +20,18 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - {children} + + + + {children} + + + ); } diff --git a/frontend/src/components/providers/query.tsx b/frontend/src/components/providers/query.tsx new file mode 100644 index 0000000..504874e --- /dev/null +++ b/frontend/src/components/providers/query.tsx @@ -0,0 +1,11 @@ +'use client'; + +import { ReactNode } from 'react'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +export const queryClient = new QueryClient(); + +export const QueryProvider = ({ children }: { children: ReactNode }) => { + return {children}; +}; diff --git a/frontend/src/components/ui/sonner.tsx b/frontend/src/components/ui/sonner.tsx new file mode 100644 index 0000000..660ebee --- /dev/null +++ b/frontend/src/components/ui/sonner.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { useTheme } from 'next-themes'; + +import { Toaster as Sonner, ToasterProps } from 'sonner'; + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = 'system' } = useTheme(); + + return ( + + ); +}; + +export { Toaster }; From 3e3b8d2778ad293cf2efc965119f1a0d4a3c5a29 Mon Sep 17 00:00:00 2001 From: jinyang628 Date: Sat, 7 Jun 2025 21:31:13 +0800 Subject: [PATCH 2/4] Implement messages action --- frontend/src/actions/messages.ts | 0 frontend/src/types/messages.ts | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 frontend/src/actions/messages.ts create mode 100644 frontend/src/types/messages.ts diff --git a/frontend/src/actions/messages.ts b/frontend/src/actions/messages.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/types/messages.ts b/frontend/src/types/messages.ts new file mode 100644 index 0000000..26d29ff --- /dev/null +++ b/frontend/src/types/messages.ts @@ -0,0 +1,15 @@ +import { z } from 'zod'; + +export const messageRequestSchema = z.object({ + id: z.string(), + content: z.string(), +}); + +export type MessageRequest = z.infer; + +export const messageResponseSchema = z.object({ + id: z.string(), + content: z.string(), +}); + +export type MessageResponse = z.infer; From 18e6569002f603bf2edbbae095becebed54e66d0 Mon Sep 17 00:00:00 2001 From: jinyang628 Date: Sat, 7 Jun 2025 21:36:31 +0800 Subject: [PATCH 3/4] Implement chat action --- frontend/package-lock.json | 116 +++++++++++++++++++++++++++---- frontend/package.json | 1 + frontend/src/actions/messages.ts | 19 +++++ 3 files changed, 122 insertions(+), 14 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d55c9f7..98376bb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@tanstack/react-query": "^5.80.6", + "axios": "^1.9.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.513.0", @@ -2320,6 +2321,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -2384,6 +2391,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2492,7 +2510,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2648,6 +2665,18 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2799,6 +2828,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -2826,7 +2864,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2938,7 +2975,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2948,7 +2984,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2986,7 +3021,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2999,7 +3033,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3670,6 +3703,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3686,6 +3739,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -3704,7 +3773,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3745,7 +3813,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3770,7 +3837,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3858,7 +3924,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3937,7 +4002,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3950,7 +4014,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -3966,7 +4029,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4916,7 +4978,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4946,6 +5007,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5591,6 +5673,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9ff7ff3..7bacd1b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@tanstack/react-query": "^5.80.6", + "axios": "^1.9.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.513.0", diff --git a/frontend/src/actions/messages.ts b/frontend/src/actions/messages.ts index e69de29..9f48071 100644 --- a/frontend/src/actions/messages.ts +++ b/frontend/src/actions/messages.ts @@ -0,0 +1,19 @@ +'use server'; + +import axios from 'axios'; + +import { MessageRequest, MessageResponse, messageResponseSchema } from '@/types/messages'; + +export async function chat(request: MessageRequest): Promise { + try { + console.log('Sending message:', request); + const response = await axios.post( + `${process.env.EXPO_PUBLIC_API_BASE_URL}/api/messages`, + request, + ); + return messageResponseSchema.parse(response.data); + } catch (error) { + console.error('Error sending message:', error); + throw error; + } +} From 4efe5b8a6750aebffddf55092c48e51783eec076 Mon Sep 17 00:00:00 2001 From: jinyang628 Date: Sat, 7 Jun 2025 21:43:42 +0800 Subject: [PATCH 4/4] Set up basic API call to backend --- backend/app/services/messages.py | 2 +- frontend/.env.example | 1 + frontend/package-lock.json | 38 ++++++++- frontend/package.json | 1 + frontend/src/app/page.tsx | 107 ++++++++------------------ frontend/src/components/ui/button.tsx | 57 ++++++++++++++ frontend/src/components/ui/input.tsx | 21 +++++ frontend/src/types/messages.ts | 2 +- 8 files changed, 151 insertions(+), 78 deletions(-) create mode 100644 frontend/src/components/ui/button.tsx create mode 100644 frontend/src/components/ui/input.tsx diff --git a/backend/app/services/messages.py b/backend/app/services/messages.py index 14ce39b..400ac48 100644 --- a/backend/app/services/messages.py +++ b/backend/app/services/messages.py @@ -7,4 +7,4 @@ class MessagesService: async def chat(self, input: MessageRequest) -> MessageResponse: - return MessageResponse(id="123", content="Hello, world!") + return MessageResponse(id="123", content=f"Why did you say {input.content}?") diff --git a/frontend/.env.example b/frontend/.env.example index e69de29..677c54c 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -0,0 +1 @@ +EXPO_PUBLIC_API_BASE_URL=http://0.0.0.0:8080 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 98376bb..2fe0264 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "@radix-ui/react-slot": "^1.2.3", "@tanstack/react-query": "^5.80.6", "axios": "^1.9.0", "class-variance-authority": "^0.7.1", @@ -1115,6 +1116,39 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1527,7 +1561,7 @@ "version": "19.1.6", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.6.tgz", "integrity": "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -2703,7 +2737,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { diff --git a/frontend/package.json b/frontend/package.json index 7bacd1b..a279878 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "@radix-ui/react-slot": "^1.2.3", "@tanstack/react-query": "^5.80.6", "axios": "^1.9.0", "class-variance-authority": "^0.7.1", diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index c18a5e0..c538f32 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,83 +1,42 @@ -import Image from 'next/image'; +'use client'; + +import { useState } from 'react'; + +import { chat } from '@/actions/messages'; + +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; + +import { messageRequestSchema } from '@/types/messages'; export default function Home() { + const [message, setMessage] = useState(''); + const [response, setResponse] = useState(''); return (
-
- Next.js logo +

Chat with your data

+ setMessage(e.target.value)} + placeholder="Enter a message" + className="w-full" + autoFocus /> -
    -
  1. - Get started by editing{' '} - - src/app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
- - -
- + Send + +

{response}

+
); } diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx new file mode 100644 index 0000000..2bd6c29 --- /dev/null +++ b/frontend/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; + +import { Slot } from '@radix-ui/react-slot'; +import { type VariantProps, cva } from 'class-variance-authority'; + +import { cn } from '@/lib/utils'; + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', + destructive: + 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<'button'> & + VariantProps & { + asChild?: boolean; + }) { + const Comp = asChild ? Slot : 'button'; + + return ( + + ); +} + +export { Button, buttonVariants }; diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx new file mode 100644 index 0000000..3c1cfca --- /dev/null +++ b/frontend/src/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +function Input({ className, type, ...props }: React.ComponentProps<'input'>) { + return ( + + ); +} + +export { Input }; diff --git a/frontend/src/types/messages.ts b/frontend/src/types/messages.ts index 26d29ff..6685d11 100644 --- a/frontend/src/types/messages.ts +++ b/frontend/src/types/messages.ts @@ -1,7 +1,7 @@ import { z } from 'zod'; export const messageRequestSchema = z.object({ - id: z.string(), + id: z.string().nullable(), content: z.string(), });