Skip to content

Commit cec8c11

Browse files
authored
Merge pull request #16 from Brandon-Shen/ui-update
Enhance error handling and UI improvements across templates
2 parents 557c273 + 210a331 commit cec8c11

File tree

5 files changed

+579
-95
lines changed

5 files changed

+579
-95
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,4 @@ dataset/train/images
164164
dataset/train/labels
165165
app_logs.txt
166166
.aider*
167+
*.tex

src/search_images.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import requests
2+
import json
23

34

45
def search_images(query, api_key, search_engine_id, num_results=10):
@@ -16,8 +17,8 @@ def search_images(query, api_key, search_engine_id, num_results=10):
1617

1718
response = requests.get(search_url)
1819
if response.status_code != 200:
19-
raise Exception(
20-
f"Failed to fetch images: Status code {response.status_code}, Response: {response.text}")
20+
error_message = _parse_api_error(response)
21+
raise Exception(error_message)
2122

2223
data = response.json()
2324
if 'items' not in data:
@@ -33,3 +34,36 @@ def search_images(query, api_key, search_engine_id, num_results=10):
3334
break # No more results available
3435

3536
return images
37+
38+
39+
def _parse_api_error(response):
40+
"""Parse Google API error response and return a user-friendly message"""
41+
try:
42+
data = response.json()
43+
if 'error' in data:
44+
error_obj = data['error']
45+
46+
# Handle different error formats
47+
if isinstance(error_obj, dict):
48+
message = error_obj.get('message', 'Unknown error')
49+
code = error_obj.get('code', response.status_code)
50+
status = error_obj.get('status', 'UNKNOWN')
51+
52+
# Map common errors to user-friendly messages
53+
if status == 'PERMISSION_DENIED' or code == 403:
54+
return f"Access Denied: {message} - Please check your Google API credentials and ensure the Custom Search JSON API is enabled in your Google Cloud project."
55+
elif status == 'INVALID_ARGUMENT' or code == 400:
56+
return f"Invalid Request: {message} - Please verify your search query and API configuration."
57+
elif status == 'UNAUTHENTICATED' or code == 401:
58+
return f"Authentication Failed: {message} - Your API key may be invalid or expired."
59+
elif status == 'RESOURCE_EXHAUSTED' or code == 429:
60+
return f"Rate Limited: {message} - You've exceeded your daily search quota. Please try again later."
61+
else:
62+
return f"API Error ({code}): {message}"
63+
else:
64+
return f"API Error: {str(error_obj)}"
65+
except:
66+
pass
67+
68+
# Fallback error message
69+
return f"Failed to fetch images: Status code {response.status_code}. The image search service returned an error. Please verify your API keys and search query."

src/templates/base.html

Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>{% block title %}SearchVision{% endblock %}</title>
7+
<link rel="preconnect" href="https://fonts.googleapis.com">
8+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
710
<style>
811
* {
912
margin: 0;
@@ -12,93 +15,134 @@
1215
}
1316

1417
body {
15-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
16-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
19+
background: #ffffff;
1720
min-height: 100vh;
18-
padding: 20px;
21+
padding: 0;
22+
overflow-x: hidden;
23+
color: #020817;
1924
}
2025

2126
.container {
22-
max-width: 1200px;
23-
margin: 0 auto;
24-
background: white;
25-
border-radius: 16px;
26-
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
27+
max-width: 100%;
28+
margin: 0;
29+
background: transparent;
30+
border-radius: 0;
31+
box-shadow: none;
2732
overflow: hidden;
33+
display: flex;
34+
flex-direction: column;
35+
min-height: 100vh;
2836
}
2937

3038
header {
31-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
32-
color: white;
33-
padding: 30px;
39+
background: transparent;
40+
color: #020817;
41+
padding: 40px 20px;
3442
text-align: center;
43+
position: relative;
44+
z-index: 1;
3545
}
3646

3747
header h1 {
3848
font-size: 2.5rem;
39-
margin-bottom: 10px;
49+
margin-bottom: 8px;
50+
font-weight: 600;
51+
letter-spacing: -0.5px;
52+
color: #020817;
4053
}
4154

4255
header p {
43-
opacity: 0.9;
44-
font-size: 1.1rem;
56+
opacity: 0.6;
57+
font-size: 1rem;
58+
color: #64748b;
59+
font-weight: 400;
4560
}
4661

4762
main {
48-
padding: 40px;
63+
padding: 0;
64+
flex: 1;
65+
display: flex;
66+
align-items: center;
67+
justify-content: center;
4968
}
5069

5170
{% block styles %}{% endblock %}
5271

72+
5373
.btn {
5474
display: inline-block;
55-
padding: 14px 32px;
56-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
57-
color: white;
58-
border: none;
59-
border-radius: 8px;
60-
font-size: 1rem;
61-
font-weight: 600;
75+
padding: 10px 24px;
76+
background: #020817;
77+
color: #ffffff;
78+
border: 1px solid #020817;
79+
border-radius: 6px;
80+
font-size: 0.95rem;
81+
font-weight: 500;
6282
cursor: pointer;
63-
transition: transform 0.2s, box-shadow 0.2s;
83+
transition: all 0.2s ease;
6484
text-decoration: none;
6585
margin-top: 20px;
86+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
6687
}
6788

6889
.btn:hover {
69-
transform: translateY(-2px);
70-
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
90+
background: #1e293b;
91+
border-color: #1e293b;
92+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
93+
}
94+
95+
.btn:active {
96+
transform: scale(0.98);
7197
}
7298

7399
.btn-secondary {
74-
background: #6c757d;
100+
background: #e2e8f0;
101+
color: #020817;
102+
border-color: #e2e8f0;
103+
}
104+
105+
.btn-secondary:hover {
106+
background: #cbd5e1;
107+
border-color: #cbd5e1;
75108
}
76109

77110
.btn-success {
78-
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
111+
background: #16a34a;
112+
border-color: #16a34a;
113+
color: #ffffff;
114+
}
115+
116+
.btn-success:hover {
117+
background: #15803d;
118+
border-color: #15803d;
79119
}
80120

81121
.error-message {
82-
background: #fee;
83-
border: 1px solid #fcc;
84-
color: #c33;
122+
background: #fee2e2;
123+
border: 1px solid #fecaca;
124+
color: #991b1b;
85125
padding: 16px;
86-
border-radius: 8px;
126+
border-radius: 6px;
87127
margin: 20px 0;
128+
font-size: 0.95rem;
88129
}
89130

131+
90132
footer {
91-
background: #f8f9fa;
92-
padding: 20px;
133+
background: #f8fafc;
134+
padding: 24px;
93135
text-align: center;
94-
color: #666;
136+
color: #64748b;
137+
border-top: 1px solid #e2e8f0;
138+
font-size: 0.9rem;
95139
}
96140
</style>
97141
</head>
98142
<body>
99143
<div class="container">
100144
<header>
101-
<h1>🔍 SearchVision</h1>
145+
<h1>SearchVision</h1>
102146
<p>Train your own object detection model in minutes</p>
103147
</header>
104148
<main>

0 commit comments

Comments
 (0)