diff --git a/package-lock.json b/package-lock.json index 4251d5d..c9d1de9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "glob": "^11.0.0", "gsap": "^3.12.5", "lottie-react": "^2.4.0", + "openai": "^4.78.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-locomotive-scroll": "^0.2.2", @@ -5215,6 +5216,30 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@types/node-forge": { "version": "1.3.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", @@ -5785,6 +5810,18 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "deprecated": "Use your platform's native atob() and btoa() methods instead" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -5883,6 +5920,18 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -9213,6 +9262,15 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -9811,6 +9869,25 @@ "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -10568,6 +10645,15 @@ "node": ">=10.17.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -13902,6 +13988,67 @@ "tslib": "^2.0.3" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -14185,6 +14332,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "4.78.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.78.0.tgz", + "integrity": "sha512-4rRsKkx++5m1zayxkryVH+K/z91cv1sRbaNJAhSQjZiSCQOR7eaM8KpfIssXrS9Hlpta7+VcuO/fi57pW8xGjA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.70", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz", + "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -19344,6 +19532,15 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/web-vitals": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", diff --git a/package.json b/package.json index 348cc1f..6ec8c5a 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "glob": "^11.0.0", "gsap": "^3.12.5", "lottie-react": "^2.4.0", + "openai": "^4.78.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-locomotive-scroll": "^0.2.2", diff --git a/src/Pages/ExpertBaristas.js b/src/Pages/ExpertBaristas.js index 556af4f..b804315 100644 --- a/src/Pages/ExpertBaristas.js +++ b/src/Pages/ExpertBaristas.js @@ -1,7 +1,9 @@ -import React from 'react'; +import React, { useState } from 'react'; import styled from 'styled-components'; import { motion } from 'framer-motion'; +const OPENAI_API_KEY = 'REACT_APP_OPENAI_API_KEY'; + const ExpertContainer = styled.div` padding: 6rem 2rem 4rem 2rem; max-width: 1200px; @@ -27,9 +29,9 @@ const FeatureTitle = styled(motion.h1)` font-weight: bold; @media (max-width: 768px) { - font-size: 2.5rem; /* Adjust font size for smaller screens */ - text-align: center; /* Center the title on smaller screens */ - margin-top: 0; /* Reset margin for better spacing on smaller screens */ + font-size: 2.5rem; + text-align: center; + margin-top: 0; } `; @@ -78,15 +80,78 @@ const FeatureDescription = styled(motion.p)` font-size: 1.2rem; margin-left: 1rem; margin-right: 1rem; - text-align: justify; /* Ensure text is justified on small screens */ + text-align: justify; } `; +const ChatContainer = styled.div` + position: fixed; + bottom: 1rem; + right: 1rem; + width: 400px; + height: 500px; + background: white; + border: 1px solid #ccc; + border-radius: 10px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + z-index: 1000; +`; + +const ChatHeader = styled.div` + background-color: #d2691e; + color: white; + padding: 0.5rem; + text-align: center; + border-radius: 10px 10px 0 0; + display: flex; + justify-content: space-between; + align-items: center; +`; + +const ChatMessages = styled.div` + flex: 1; + overflow-y: auto; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; +`; + +const Message = styled.div` + align-self: ${props => props.sender === 'user' ? 'flex-end' : 'flex-start'}; + background-color: ${props => props.sender === 'user' ? '#d2691e' : '#f4f4f4'}; + color: ${props => props.sender === 'user' ? 'white' : 'black'}; + padding: 0.5rem 1rem; + border-radius: 10px; + max-width: 80%; +`; + +const TypingIndicator = styled.div` + align-self: flex-start; + background-color: #f4f4f4; + color: black; + padding: 0.5rem 1rem; + border-radius: 10px; + font-style: italic; +`; + +const InputContainer = styled.div` + display: flex; + gap: 0.5rem; + padding: 1rem; +`; + +const TextInput = styled.input` + flex: 1; + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 5px; +`; + const Button = styled.button` - type: 'button'; - class: 'btn btn-warning mx-20'; - margin-top: 2rem; - padding: 0.8rem 1.5rem; + padding: ${props => props.small ? '0.5rem 1rem' : '0.8rem 1.5rem'}; background-color: #d2691e; color: white; border: none; @@ -98,13 +163,98 @@ const Button = styled.button` } @media (max-width: 768px) { - width: 100%; - padding: 1rem 2rem; - font-size: 1.2rem; + width: ${props => props.small ? 'auto' : '100%'}; + padding: ${props => props.small ? '0.5rem 1rem' : '1rem 2rem'}; + font-size: ${props => props.small ? '1rem' : '1.2rem'}; } `; function ExpertBaristas() { + const [isChatOpen, setIsChatOpen] = useState(false); + const [messages, setMessages] = useState([{ + message: "Hello! I'm your coffee expert. How can I help you today?", + sender: "ChatGPT" + }]); + const [isTyping, setIsTyping] = useState(false); + const [userInput, setUserInput] = useState(''); + + const toggleChat = () => setIsChatOpen(prev => !prev); + + const handleSendMessage = async () => { + if (userInput.trim() === '') return; + + const newMessage = { + message: userInput, + sender: "user" + }; + + const newMessages = [...messages, newMessage]; + setMessages(newMessages); + setUserInput(''); + setIsTyping(true); + + await processMessageToChatGPT(newMessages); + }; + + const handleKeyPress = (e) => { + if (e.key === 'Enter') { + handleSendMessage(); + } + }; + + async function processMessageToChatGPT(chatMessages) { + const systemMessage = { + role: "system", + content: "You are an expert barista with deep knowledge of coffee. Provide detailed, professional advice about coffee, brewing methods, and coffee culture." + }; + + const apiMessages = chatMessages.map((messageObject) => ({ + role: messageObject.sender === "ChatGPT" ? "assistant" : "user", + content: messageObject.message + })); + + const apiRequestBody = { + model: "gpt-4o", + messages: [systemMessage, ...apiMessages] + }; + + try { + const response = await fetch( { + method: "POST", + headers: { + "Authorization": "Bearer " + OPENAI_API_KEY, + "Content-Type": "application/json" + }, + body: JSON.stringify(apiRequestBody) + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.error?.message || 'An error occurred'); + } + + setMessages([ + ...chatMessages, + { + message: data.choices[0].message.content, + sender: "ChatGPT" + } + ]); + } catch (error) { + console.error('Error:', error); + setMessages([ + ...chatMessages, + { + message: "I apologize, but I'm having trouble connecting right now. Please try again later.", + sender: "ChatGPT" + } + ]); + } finally { + setIsTyping(false); + } + } + return ( <> @@ -130,11 +280,43 @@ function ExpertBaristas() { animate={{ opacity: 1, x: 0 }} transition={{ duration: 0.5, delay: 0.2 }} > - Expert baristas excel in making coffee due to their comprehensive knowledge of coffee, practical brewing skills, and passion for the craft. They ensure optimal flavor extraction by meticulously controlling variables like grind size, water temperature, and brewing time. Their expertise allows them to create a perfect cup of coffee tailored to individual preferences, showcasing their attention to detail and dedication. The skillset is honed through extensive training and practice. Expert baristas demonstrate exceptional precision in every step of the coffee-making process. A true expert barista has a passion for coffee that drives them to constantly improve and innovate. They experiment with new coffee blends, brewing methods, and flavor combinations to create unique and memorable coffee experiences. + Expert baristas excel in making coffee due to their comprehensive knowledge of coffee, practical brewing skills, and passion for the craft... + + + + + {isChatOpen && ( + + + Chat with Coffee Expert + + + + {messages.map((message, index) => ( + + {message.message} + + ))} + {isTyping && ( + Coffee Expert is typing... + )} + + + setUserInput(e.target.value)} + onKeyPress={handleKeyPress} + /> + + + + )} ); } -export default ExpertBaristas; +export default ExpertBaristas; \ No newline at end of file