mahwizzzz commited on
Commit
b968b2c
·
verified ·
1 Parent(s): 872265f

Create ui_components.py

Browse files
Files changed (1) hide show
  1. ui_components.py +263 -0
ui_components.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import pandas as pd
3
+ from datetime import datetime
4
+
5
+ AQI_BANDS = [
6
+ (0, 50, "Good", "#10b981", "#ecfdf5"),
7
+ (51, 100, "Moderate", "#f59e0b", "#fffbeb"),
8
+ (101, 150, "Unhealthy for Sensitive Groups", "#f97316", "#fff7ed"),
9
+ (151, 200, "Unhealthy", "#ef4444", "#fef2f2"),
10
+ (201, 300, "Very Unhealthy", "#8b5cf6", "#f5f3ff"),
11
+ (301, 999, "Hazardous", "#7f1d1d", "#fef2f2"),
12
+ ]
13
+
14
+ AQI_TIPS = {
15
+ "Good": "Great day to be outdoors!",
16
+ "Moderate": "Sensitive groups should reduce prolonged outdoor exertion.",
17
+ "Unhealthy for Sensitive Groups": "Limit outdoor activity if you have asthma or heart conditions.",
18
+ "Unhealthy": "Wear a mask outdoors and keep windows closed.",
19
+ "Very Unhealthy": "Avoid going outside; use an air purifier indoors.",
20
+ "Hazardous": "Stay indoors — this is a health emergency level.",
21
+ }
22
+
23
+ CSS = """
24
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
25
+
26
+ .gradio-container {
27
+ background: #f0fdfa !important;
28
+ background-image: linear-gradient(180deg, #99f6e4 0%, #f0fdfa 40%, #ffffff 100%) !important;
29
+ font-family: 'Inter', sans-serif !important;
30
+ max-width: 1200px !important;
31
+ }
32
+
33
+ /* Header Styles */
34
+ .app-header {
35
+ display: flex;
36
+ justify-content: space-between;
37
+ align-items: center;
38
+ padding: 20px 0;
39
+ }
40
+ .location-info h2 {
41
+ margin: 0;
42
+ font-size: 1.2rem;
43
+ font-weight: 700;
44
+ color: #0f172a;
45
+ }
46
+ .location-info p {
47
+ margin: 0;
48
+ font-size: 0.9rem;
49
+ color: #64748b;
50
+ }
51
+
52
+ /* Card Styles */
53
+ .mobile-card {
54
+ background: white !important;
55
+ border-radius: 24px !important;
56
+ border: none !important;
57
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.05) !important;
58
+ padding: 20px !important;
59
+ overflow: hidden;
60
+ }
61
+
62
+ /* Status Card */
63
+ .status-hero {
64
+ position: relative;
65
+ border-radius: 24px;
66
+ padding: 24px;
67
+ color: white;
68
+ text-align: left;
69
+ overflow: hidden;
70
+ margin-bottom: 20px;
71
+ }
72
+ .status-hero h1 {
73
+ font-size: 2.5rem;
74
+ font-weight: 800;
75
+ margin: 0;
76
+ }
77
+ .status-hero p {
78
+ font-size: 1.1rem;
79
+ opacity: 0.9;
80
+ margin: 4px 0 0 0;
81
+ }
82
+
83
+ /* Metric Grid */
84
+ .metric-grid {
85
+ display: grid;
86
+ grid-template-columns: repeat(2, 1fr);
87
+ gap: 16px;
88
+ }
89
+ .metric-item {
90
+ background: #f8fafc;
91
+ border-radius: 20px;
92
+ padding: 16px;
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: 8px;
96
+ }
97
+ .metric-top {
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 10px;
101
+ }
102
+ .metric-icon-bg {
103
+ width: 40px;
104
+ height: 40px;
105
+ border-radius: 12px;
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ font-size: 1.2rem;
110
+ }
111
+ .metric-label {
112
+ font-size: 0.85rem;
113
+ font-weight: 500;
114
+ color: #64748b;
115
+ }
116
+ .metric-value {
117
+ font-size: 1.1rem;
118
+ font-weight: 700;
119
+ color: #0f172a;
120
+ }
121
+
122
+ /* Forecast Grid */
123
+ .forecast-scroll {
124
+ display: flex;
125
+ gap: 12px;
126
+ overflow-x: auto;
127
+ padding-bottom: 8px;
128
+ scrollbar-width: none;
129
+ }
130
+ .forecast-scroll::-webkit-scrollbar { display: none; }
131
+ .forecast-item {
132
+ min-width: 80px;
133
+ text-align: center;
134
+ padding: 12px;
135
+ border-radius: 16px;
136
+ background: #f8fafc;
137
+ }
138
+ .forecast-day {
139
+ font-size: 0.8rem;
140
+ font-weight: 600;
141
+ color: #64748b;
142
+ margin-bottom: 8px;
143
+ }
144
+ .forecast-icon {
145
+ font-size: 1.5rem;
146
+ margin-bottom: 8px;
147
+ }
148
+ .forecast-temp {
149
+ font-size: 1rem;
150
+ font-weight: 700;
151
+ color: #0f172a;
152
+ }
153
+
154
+ /* Buttons */
155
+ #predict-btn {
156
+ background: #0f172a !important;
157
+ color: white !important;
158
+ border-radius: 16px !important;
159
+ font-weight: 600 !important;
160
+ padding: 12px !important;
161
+ }
162
+
163
+ /* Tabs */
164
+ .tabs {
165
+ border: none !important;
166
+ }
167
+ .tab-nav {
168
+ border-bottom: none !important;
169
+ justify-content: center !important;
170
+ gap: 20px !important;
171
+ }
172
+ """
173
+
174
+ def get_header_html():
175
+ now = datetime.now()
176
+ date_str = now.strftime("%d %B %Y")
177
+ return f"""
178
+ <div class="app-header">
179
+ <div class="location-info">
180
+ <h2>Karachi, Pakistan ⌵</h2>
181
+ <p>{date_str}</p>
182
+ </div>
183
+ <div class="user-avatar">
184
+ <img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Skyloom" style="width: 40px; height: 40px; border-radius: 50%; background: #99f6e4;">
185
+ </div>
186
+ </div>
187
+ """
188
+
189
+ def make_metric_cards(pm25, fc):
190
+ n = len(fc)
191
+ idx_24 = min(23, n - 1)
192
+ idx_48 = min(47, n - 1)
193
+
194
+ metrics = [
195
+ ("💨", "PM2.5", f"{pm25:.1f} µg/m³", "#ecfdf5", "#10b981"),
196
+ ("🌡️", "Temp", "28°C", "#fffbeb", "#f59e0b"), # Static for demo or fetch
197
+ ("💧", "Humidity", "65%", "#eff6ff", "#3b82f6"),
198
+ ("🌬️", "Wind", "4.2 km/h", "#f5f3ff", "#8b5cf6")
199
+ ]
200
+
201
+ html = '<div class="metric-grid">'
202
+ for icon, label, value, bg, color in metrics:
203
+ html += f"""
204
+ <div class="metric-item">
205
+ <div class="metric-top">
206
+ <div class="metric-icon-bg" style="background: {bg}; color: {color};">{icon}</div>
207
+ <div class="metric-label">{label}</div>
208
+ </div>
209
+ <div class="metric-value">{value}</div>
210
+ </div>
211
+ """
212
+ html += '</div>'
213
+ return html
214
+
215
+ def make_day_cards(fc):
216
+ n_days = max(1, len(fc) // 24)
217
+ day_names = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
218
+ now = datetime.now()
219
+ start_day = now.weekday()
220
+
221
+ html = '<div class="forecast-scroll">'
222
+ for d in range(min(n_days, 7)):
223
+ start = d * 24
224
+ end = min(start + 24, len(fc))
225
+ chunk = fc[start:end]
226
+ if len(chunk) == 0: break
227
+ avg = sum(chunk) / len(chunk)
228
+
229
+ # Determine icon based on AQI
230
+ if avg < 50: icon = "☀️"
231
+ elif avg < 100: icon = "🌤️"
232
+ elif avg < 150: icon = "🌥️"
233
+ else: icon = "🌫️"
234
+
235
+ day_name = day_names[(start_day + d) % 7]
236
+ if d == 0: day_name = "Today"
237
+
238
+ html += f"""
239
+ <div class="forecast-item">
240
+ <div class="forecast-day">{day_name}</div>
241
+ <div class="forecast-icon">{icon}</div>
242
+ <div class="forecast-temp">{avg:.0f}</div>
243
+ </div>
244
+ """
245
+ html += '</div>'
246
+ return html
247
+
248
+ def get_status_hero(aqi):
249
+ cat = "Good"
250
+ color = "#10b981"
251
+ bg = "#10b981" # Solid for now, or gradient
252
+ for lo, hi, c, col, b in AQI_BANDS:
253
+ if aqi <= hi:
254
+ cat, color, bg = c, col, col
255
+ break
256
+
257
+ return f"""
258
+ <div class="status-hero" style="background: {bg};">
259
+ <p>Current Observation</p>
260
+ <h1>{aqi:.0f}</h1>
261
+ <p>Air Quality Index: <b>{cat}</b></p>
262
+ </div>
263
+ """