From 0ad05a1c112da8aab12973ed9089512de19dd902 Mon Sep 17 00:00:00 2001
From: chloe
Date: Tue, 17 Mar 2026 19:53:56 +0000
Subject: [PATCH] UI update
---
.../app/assets/css/app-theme.css | 109 +++++++++++++++---
Seasoned.Frontend/app/assets/css/login.css | 77 +++++++++++--
Seasoned.Frontend/app/pages/chat.vue | 41 ++++---
Seasoned.Frontend/app/pages/index.vue | 8 +-
Seasoned.Frontend/app/pages/login.vue | 99 +++++++++++++---
5 files changed, 274 insertions(+), 60 deletions(-)
diff --git a/Seasoned.Frontend/app/assets/css/app-theme.css b/Seasoned.Frontend/app/assets/css/app-theme.css
index 6133f00..dd7100e 100644
--- a/Seasoned.Frontend/app/assets/css/app-theme.css
+++ b/Seasoned.Frontend/app/assets/css/app-theme.css
@@ -94,7 +94,7 @@ html, body {
justify-content: center !important;
}
-.drop-zone, .chat-container {
+.drop-zone {
width: 100%;
background-color: rgba(62, 42, 20, 0.03) !important;
border: 2px dashed #8c857b;
@@ -133,7 +133,7 @@ html, body {
text-transform: none !important;
}
-.column-button {
+.column-btn {
font-family: 'Libre Baskerville', serif !important;
font-size: 1.1rem !important;
background-color: #556b2f !important;
@@ -142,9 +142,8 @@ html, body {
text-transform: none !important;
}
-.column-button:hover {
+.column-btn:hover {
background-color: #2e1e0a !important;
-
text-shadow: none !important;
border-radius: 4px;
}
@@ -170,12 +169,6 @@ html, body {
opacity: 1;
}
-.chat-placeholder {
- font-style: italic;
- color: #8c7e6a;
- text-align: center;
-}
-
.menu-text {
font-family: 'Libre Baskerville', serif !important;
color: #2e1e0a !important;
@@ -234,13 +227,13 @@ html, body {
.chat-container {
width: 100%;
- background-color: rgba(244, 237, 225, 0.4) !important;
- border: 1px solid #dccca7;
+ background-color: rgba(62, 42, 20, 0.03) !important;
+ border: 2px solid #8c857b;
border-radius: 12px;
padding: 20px;
+ transition: all 0.3s ease;
display: flex;
flex-direction: column;
- min-height: 500px;
}
.chat-display {
@@ -251,18 +244,98 @@ html, body {
display: flex;
flex-direction: column;
gap: 12px;
+ scroll-behavior: smooth;
+ -webkit-overflow-scrolling: touch;
+}
+
+.chat-display::-webkit-scrollbar {
+ width: 6px;
+}
+
+.chat-display::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.chat-display::-webkit-scrollbar-thumb {
+ background: #dccca7;
+ border-radius: 10px;
+}
+
+.chat-display::-webkit-scrollbar-thumb:hover {
+ background: #8c7e6a;
}
.chat-input .v-field__input {
+ font-family: 'Libre Baskerville', serif !important;
+ font-size: 1.1rem;
min-height: 56px !important;
padding-top: 15px !important;
- font-family: 'Inter', sans-serif;
- font-size: 1.1rem;
+ color: #2c2925 !important;
+
}
-.chat-input.v-field--focused {
- background-color: #ffffff !important;
- transition: background-color 0.3s ease;
+.chat-container:focus-within {
+ background-color: rgba(85, 107, 47, 0.05) !important;
+ border-color: #556b2f;
+}
+
+.chat-placeholder {
+ font-family: 'Libre Baskerville', serif !important;
+ font-size: 1.1rem;
+ color: #8c7e6a;
+ text-align: center;
+ padding: 20px;
+}
+
+.send-btn {
+ color: #2e1e0a !important;
+ transition: all 0.3s ease !important;
+ border-radius: 6px !important;
+ height: 36px !important;
+ width: 36px !important;
+ min-width: 36px !important;
+ padding: 0 !important;
+}
+
+.send-btn:hover {
+ background-color: #556b2f !important;
+ color: #f4e4bc !important;
+ border-radius: 6px !important;
+ transform: scale(1.05);
+}
+
+.thinking-bubble {
+ display: flex;
+ align-items: center;
+ padding: 10px 16px !important;
+ width: fit-content;
+ background-color: #ffffff; /* Same as assistant bubble */
+ border: 1px solid #dccca7;
+ border-radius: 15px 15px 15px 2px;
+}
+
+.typing {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+
+.typing .dot {
+ width: 6px;
+ height: 6px;
+ background-color: #556b2f;
+ border-radius: 50%;
+ opacity: 0.4;
+ animation: typing-bounce 1.4s infinite ease-in-out both;
+}
+
+.typing .dot:nth-child(1) { animation-delay: 0s; }
+.typing .dot:nth-child(2) { animation-delay: 0.2s; }
+.typing .dot:nth-child(3) { animation-delay: 0.4s; }
+
+@keyframes typing-bounce {
+ 0%, 80%, 100% { transform: scale(0); }
+ 40% { transform: scale(1); opacity: 1; }
}
.message {
diff --git a/Seasoned.Frontend/app/assets/css/login.css b/Seasoned.Frontend/app/assets/css/login.css
index c6e43f8..10902a8 100644
--- a/Seasoned.Frontend/app/assets/css/login.css
+++ b/Seasoned.Frontend/app/assets/css/login.css
@@ -4,17 +4,33 @@
}
.auth-title {
- font-family: 'Libre Baskerville', serif;
- font-weight: 700;
- font-size: 2.2rem;
- color: #2e1e0a;
- margin-bottom: 8px;
+ font-family: 'Libre Baskerville', serif;
+ font-weight: 700;
+ font-size: 2.2rem;
+ margin-top: -5px !important; /* Pulls the text up toward the pot */
+ margin-bottom: 8px;
+ line-height: 1.0 !important;
background: linear-gradient(to bottom, #8c4a32 20%, #4a2a14 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
- line-height: 0.9 !important;
- letter-spacing: -1px;
filter: drop-shadow(1px 1px 0px rgba(255,255,255,0.1));
+ line-height: 1.1 !important;
+ letter-spacing: -0.5px;
+}
+
+.auth-btn {
+ font-family: 'Libre Baskerville', serif !important;
+ font-size: 1.1rem !important;
+ background-color: #556b2f !important;
+ color: #f4e4bc !important;
+ transition: all 0.3s ease !important;
+ text-transform: none !important;
+}
+
+.auth-btn:hover {
+ background-color: #2e1e0a !important;
+ text-shadow: none !important;
+ border-radius: 4px;
}
.auth-input .v-field__prepend-inner {
@@ -23,13 +39,13 @@
}
.auth-input .v-icon {
- color: #f8f1e0 !important;
+ color: #2e1e0a !important;
opacity: 0.6;
}
.auth-toggle-btn {
- font-family: 'Inter', sans-serif !important;
- font-size: 0.85rem !important;
+ font-family: 'Libre Baskerville', serif;
+ font-size: 1.0rem !important;
color: #6d5e4a !important;
text-decoration: underline;
cursor: pointer;
@@ -43,7 +59,48 @@
.auth-switch-enter-active, .auth-switch-leave-active {
transition: all 0.3s ease;
}
+
.auth-switch-enter-from, .auth-switch-leave-to {
opacity: 0;
transform: translateY(10px);
+}
+
+.auth-input :deep(.v-field) {
+ --v-theme-on-surface: 46, 30, 10 !important;
+ --v-field-label-color: 46, 30, 10 !important;
+ color: #2e1e0a !important;
+}
+
+.auth-input :deep(input),
+.auth-input :deep(.v-field__input) {
+ font-family: 'Libre Baskerville', serif !important;
+ color: #2e1e0a !important;
+ -webkit-text-fill-color: #2e1e0a !important;
+ caret-color: #2e1e0a !important;
+ opacity: 1 !important;
+}
+
+/* Force the font on the floating label */
+.auth-input :deep(.v-label) {
+ font-family: 'Libre Baskerville', serif !important;
+ color: #2e1e0a !important;
+ opacity: 1 !important;
+}
+
+/* Fix for the white Success Message in your screenshot */
+.auth-success {
+ background-color: rgba(85, 107, 47, 0.1) !important;
+ border-color: #556b2f !important;
+ color: #556b2f !important; /* Forces the green text */
+}
+
+.auth-success :deep(.v-icon) {
+ color: #556b2f !important;
+}
+
+/* Fix for the Error Message */
+.auth-error {
+ background-color: rgba(140, 74, 50, 0.1) !important;
+ border-color: #8c4a32 !important;
+ color: #8c4a32 !important;
}
\ No newline at end of file
diff --git a/Seasoned.Frontend/app/pages/chat.vue b/Seasoned.Frontend/app/pages/chat.vue
index f07e281..5720984 100644
--- a/Seasoned.Frontend/app/pages/chat.vue
+++ b/Seasoned.Frontend/app/pages/chat.vue
@@ -1,7 +1,7 @@
-
-
-
+
-
+
@@ -88,8 +93,7 @@ const askChef = async () => {
userQuery.value = ''
chatLoading.value = true
- await nextTick()
- scrollToBottom()
+ await scrollToBottom()
try {
const data = await $fetch(`${config.public.apiBase}api/recipe/consult`, {
@@ -105,9 +109,6 @@ const askChef = async () => {
localStorage.removeItem('pending_recipe')
}
- await nextTick()
- scrollToBottom()
-
} catch (err) {
chatMessages.value.push({
role: 'assistant',
@@ -115,12 +116,22 @@ const askChef = async () => {
})
} finally {
chatLoading.value = false
+ await scrollToBottom()
}
}
-const scrollToBottom = () => {
+const scrollToBottom = async () => {
+ await nextTick()
if (chatDisplay.value) {
- chatDisplay.value.scrollTop = chatDisplay.value.scrollHeight
+ const { scrollTop, scrollHeight, clientHeight } = chatDisplay.value
+ const isAtBottom = scrollHeight - scrollTop <= clientHeight + 100
+
+ if (isAtBottom) {
+ chatDisplay.value.scrollTo({
+ top: scrollHeight,
+ behavior: 'smooth'
+ })
+ }
}
}
\ No newline at end of file
diff --git a/Seasoned.Frontend/app/pages/index.vue b/Seasoned.Frontend/app/pages/index.vue
index 14d87e7..ba0e0f8 100644
--- a/Seasoned.Frontend/app/pages/index.vue
+++ b/Seasoned.Frontend/app/pages/index.vue
@@ -28,7 +28,7 @@
Turn handwritten cards into searchable digital text instantly.
-
+
Got to Uploader
@@ -39,7 +39,7 @@
Chat with an AI chef to scale ingredients, find substitutes, or get inspiration.
-
+
Talk to Chef
@@ -50,13 +50,13 @@
Build a private collection that keeps your family traditions alive and organized.
-
+
View Collection
-
+
Get Started
diff --git a/Seasoned.Frontend/app/pages/login.vue b/Seasoned.Frontend/app/pages/login.vue
index cea66d3..1b3c39e 100644
--- a/Seasoned.Frontend/app/pages/login.vue
+++ b/Seasoned.Frontend/app/pages/login.vue
@@ -1,43 +1,92 @@
-
+
-
+
+
+
+
{{ isLogin ? 'Sign In' : 'Join Us' }}
+
+
+
+ {{ errorMessage }}
+
+
+
+
+
+
+
import { ref } from 'vue'
import '@/assets/css/login.css'
+import '@/assets/css/app-theme.css'
const isLogin = ref(true)
const email = ref('')
const password = ref('')
+const confirmPassword = ref('')
+const errorMessage = ref('')
const authLoading = ref(false)
const config = useRuntimeConfig()
+const toggleMode = () => {
+ isLogin.value = !isLogin.value
+ errorMessage.value = ''
+ confirmPassword.value = ''
+}
+
const handleAuth = async () => {
+ errorMessage.value = ''
+ if (!isLogin.value && password.value !== confirmPassword.value) {
+ errorMessage.value = "Passwords do not match."
+ return
+ }
+
authLoading.value = true
const endpoint = isLogin.value ? 'api/auth/login' : 'api/auth/register'
@@ -98,9 +162,18 @@ const handleAuth = async () => {
} else {
isLogin.value = true
authLoading.value = false
+ errorMessage.value = "Account created! Please sign in."
}
} catch (err) {
authLoading.value = false
+ if (err.status === 401) {
+ errorMessage.value = "Invalid email or password. Please try again."
+ } else if (err.status === 404) {
+ errorMessage.value = "Account not found. Would you like to register?"
+ } else {
+ errorMessage.value = "Something went wrong. Please check your connection."
+ }
+
console.error('Auth error:', err)
}
}