Skip to content

Commit

Permalink
Merge pull request #11 from lordlinus/theme_cust
Browse files Browse the repository at this point in the history
Theme cust from Raf
  • Loading branch information
lordlinus authored Nov 7, 2024
2 parents 0dc93ea + 092b4c6 commit 9912c69
Show file tree
Hide file tree
Showing 14 changed files with 1,296 additions and 230 deletions.
15 changes: 15 additions & 0 deletions frontend/app/assets/bot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions frontend/app/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 32 additions & 53 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,50 @@
@tailwind components;
@tailwind utilities;

body {
font-family: Arial, Helvetica, sans-serif;
}
@import url("theme.css");
@import url("boxicons/css/boxicons.min.css");

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--background: hsl(0 0% 100%);
--foreground: hsl(0 0% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(0 0% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(0 0% 3.9%);
--primary: var(--theme-primary-color);
--primary-light: var(--theme-primary-light-color);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(0 0% 96.1%);
--secondary-foreground: hsl(0 0% 9%);
--muted: hsl(0 0% 96.1%);
--muted-foreground: hsl(0 0% 45.1%);
--accent: hsl(0 0% 96.1%);
--accent-foreground: hsl(0 0% 9%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(0 0% 89.8%);
--input: hsl(0 0% 89.8%);
--ring: var(--theme-ring-outline-color);
--chart-1: hsl(12 76% 61%);
--chart-2: hsl(173 58% 39%);
--chart-3: hsl(197 37% 24%);
--chart-4: hsl(43 74% 66%);
--chart-5: hsl(27 87% 67%);
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}

@layer base {
* {
@apply border-border;
}

body {
@apply bg-background text-foreground;
}
}

.logo {
height: 30px;
}
166 changes: 95 additions & 71 deletions frontend/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"use client";

import { useState, useEffect, useRef } from "react";
import { Send, MapPin, Loader2 } from "lucide-react";
import React, { useState, useEffect, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import Icon from "@/components/ui/icon";
import LogoImg from "@/app/assets/logo.svg";
import BotImg from "@/app/assets/bot.svg";

type Message = {
id: number;
Expand All @@ -30,7 +29,7 @@ export default function Component() {

useEffect(() => {
const WEBSOCKET_URL =
process.env.NEXT_PUBLIC_WEBSOCKET_URL || "wss://echo.websocket.org";
process.env.NEXT_PUBLIC_WEBSOCKET_URL || "wss://travel-chatbot.grayground-1ee6f428.southeastasia.azurecontainerapps.io/chat";
const socket = new WebSocket(WEBSOCKET_URL);
setWs(socket);

Expand All @@ -39,7 +38,7 @@ export default function Component() {
{
id: Date.now(),
content:
"Hello! I'm your travel assistant. How can I help you plan your trip to Singapore?",
"Hello! I'm your AI travel agent. How can I help you plan your trip to Singapore?",
sender: "bot",
},
]);
Expand Down Expand Up @@ -127,55 +126,71 @@ export default function Component() {
};

return (
<div className="flex items-center justify-center min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 p-4">
<Card className="w-full max-w-4xl h-[800px] bg-white rounded-xl shadow-lg overflow-hidden flex flex-col">
<div className="flex items-center justify-between px-6 py-4 border-b">
<h1 className="text-xl font-semibold">Travel Assistant</h1>
<Badge variant="secondary" className="bg-green-100 text-green-700">
Online
</Badge>
<div className="flex items-start justify-center min-h-screen">
<div className="overflow-hidden flex flex-col flex-1 h-screen">
<div className="flex items-center justify-between px-4 py-4">
<div className="flex items-center">
<img src={LogoImg} className="logo" alt="AI Travel Agent" />
<h1 className="text-l pl-4 font-semibold">AI Travel Agent</h1>
</div>
<Icon name="info-circle" size="20" className="text-primary" />
</div>

<ScrollArea className="flex-1 p-6" ref={scrollAreaRef}>
<div className="space-y-4">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${
message.sender === "user" ? "justify-end" : "justify-start"
}`}
>
<div
className={`max-w-[80%] rounded-lg px-4 py-2 ${
message.sender === "user"
? "bg-black text-white"
: "bg-gray-100 text-gray-900"
}`}
>
{message.sender === "bot" && message.content.startsWith("{")
? formatJsonResponse(message.content)
: message.content}
<div className="space-y-4 p-4 flex-1 overflow-y-auto h-full">
{messages.map((message) => (
<React.Fragment key={message.id}>
{message.sender === "user" ? (
<div className="flex gap-2 justify-end">
<div className="max-w-[80%]">
<div className="rounded-lg text-sm p-4 theme-message-bg-user">
{message.content}
</div>
<div className="text-xs text-gray-500 mt-1 text-right">
{new Date(message.id).toLocaleTimeString()}
</div>
</div>
<div className="theme-avatar-user relative inline-flex items-center justify-center w-8 h-8 overflow-hidden rounded-full">
<span className="font-medium">JL</span>
</div>
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-gray-100 text-gray-900 rounded-lg px-4 py-2 flex items-center space-x-2">
<Loader2 className="h-4 w-4 animate-spin" />
<span>Thinking...</span>
) : (
<div className="flex gap-2 justify-start">
<img src={BotImg} alt="Bot" className="w-8 h-8 rounded-full" />
<div className="max-w-[80%]">
<div className="rounded-lg text-sm p-4 theme-message-bg-bot">
{message.content.startsWith("{")
? formatJsonResponse(message.content)
: message.content}
</div>
<div className="text-xs text-gray-500 mt-1">
{new Date(message.id).toLocaleTimeString()}
</div>
</div>
</div>
)}
</React.Fragment>
))}
{isLoading && (
<div className="flex gap-2 justify-start">
<img src={BotImg} alt="Bot" className="w-8 h-8 rounded-full" />
<div className="max-w-[80%]">
<div className="flex items-center rounded-lg text-sm p-4 theme-message-bg-bot">
<Icon name="loader" className="bx-spin mr-2" />
Thinking...
</div>
</div>
)}
</div>
</ScrollArea>
</div>
)}
</div>

<div className="border-t bg-gray-50/50">
<div className="px-6 py-4">
<div className="mb-4">
<p className="text-sm font-medium text-gray-500 mb-2">
Sample Questions:
</p>
<div className="flex flex-wrap gap-2">
<div className="flex flex-row items-end gap-2 p-4">
<div className="flex-1">
<h2 className="flex items-center text-l font-semibold mb-4">
<Icon name="message-square-dots mr-2" />
Quick Questions
</h2>
<div className="flex flex-col gap-2">
{sampleQuestions.map((question, index) => (
<Button
key={index}
Expand All @@ -185,45 +200,54 @@ export default function Component() {
className="text-sm bg-white"
disabled={isLoading}
>
<MapPin className="w-4 h-4 mr-2" />
{question}
</Button>
))}
</div>
<form
onSubmit={(e) => {
e.preventDefault();
sendMessage(inputMessage);
}}
className="flex gap-2 mt-4"
>

<div className="relative w-full">
<Input
type="text"
placeholder="Ask me anything or select above..."
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
className="flex-grow bg-white"
disabled={isLoading}
/>
<Button
size="icon"
variant="ghost"
disabled={isLoading}
className="absolute inset-y-0 end-0 text-primary"
> <Icon name="microphone" size="18" /></Button>
</div>
</form>
</div>
<form
onSubmit={(e) => {
e.preventDefault();
sendMessage(inputMessage);
}}
className="flex gap-2"
>
<Input
type="text"
placeholder="Type your travel question or choose from the samples above..."
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
className="flex-grow bg-white"
disabled={isLoading}
/>
<div>
<Button
type="submit"
className="bg-black text-white hover:bg-gray-800 px-6"
size="icon"
disabled={isLoading}
>
{isLoading ? (
<Loader2 className="h-4 w-4 animate-spin" />
<Icon name="loader" className="bx-spin" />
) : (
<>
<Send className="h-4 w-4 mr-2" />
Send
<Icon name="send" size="18" />
</>
)}
</Button>
</form>
</div>
</div>
</div>
</Card>
</div>
</div>
);
}
27 changes: 27 additions & 0 deletions frontend/app/theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
:root {
--theme-font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Noto Sans, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
--theme-primary-color: hsl(217, 71%, 51%);
--theme-primary-light-color: hsla(217, 71%, 51%, 0.45);
--theme-ring-outline-color: var(--theme-primary-color);
--theme-message-bg-bot: hsla(305, 39%, 45%, 0.15);
--theme-message-bg-user: hsla(148, 92%, 36%, 0.15);
--theme-avatar-bg-user: hsl(148, 92%, 36%);
--theme-avatar-text-color-user: white;
}

body {
font-family: var(--theme-font-family);
}

.theme-message-bg-bot {
background-color: var(--theme-message-bg-bot);
}

.theme-message-bg-user {
background-color: var(--theme-message-bg-user);
}

.theme-avatar-user {
background-color: var(--theme-avatar-bg-user);
color: var(--theme-avatar-text-color-user);
}
3 changes: 3 additions & 0 deletions frontend/boxicons.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module 'boxicons' {
export type IconName = string;
}
Loading

0 comments on commit 9912c69

Please sign in to comment.