arif670 commited on
Commit
a12fc7d
ยท
verified ยท
1 Parent(s): 45daa94

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +342 -255
app.py CHANGED
@@ -1,9 +1,11 @@
1
  import streamlit as st
2
- import firebase_admin
3
- from firebase_admin import credentials, firestore, auth
4
- import pandas as pd
5
- import plotly.express as px
6
- from datetime import datetime, timedelta
 
 
7
 
8
  # Custom CSS for enhanced UI
9
  st.markdown("""
@@ -14,9 +16,9 @@ st.markdown("""
14
  max-width: 1200px;
15
  }
16
 
17
- /* Sidebar animations and styling */
18
  [data-testid="stSidebar"] {
19
- background: linear-gradient(145deg, #2c3e50, #3498db) !important;
20
  box-shadow: 5px 0 15px rgba(0,0,0,0.2);
21
  }
22
 
@@ -25,11 +27,13 @@ st.markdown("""
25
  border-radius: 8px;
26
  padding: 10px 15px;
27
  margin: 5px 0;
 
28
  }
29
 
30
  .st-emotion-cache-1dj0hjr:hover {
31
- transform: translateX(10px);
32
  background: rgba(255,255,255,0.1);
 
33
  }
34
 
35
  /* Professional table styling */
@@ -56,9 +60,27 @@ st.markdown("""
56
  .pending { background: #fff3cd; color: #856404; }
57
  .in-progress { background: #cce5ff; color: #004085; }
58
  .completed { background: #d4edda; color: #155724; }
 
 
 
 
 
 
 
 
 
 
 
59
  </style>
60
  """, unsafe_allow_html=True)
61
 
 
 
 
 
 
 
 
62
  # Initialize Firebase
63
  if not firebase_admin._apps:
64
  cred = credentials.Certificate("firebase_credentials.json")
@@ -66,9 +88,6 @@ if not firebase_admin._apps:
66
  db = firestore.client()
67
 
68
  # Authentication Functions
69
- def hash_password(password):
70
- return hashlib.sha256(password.encode()).hexdigest()
71
-
72
  def register_user(email, password):
73
  try:
74
  user = auth.create_user(email=email, password=password)
@@ -81,269 +100,337 @@ def authenticate_user(email, password):
81
  try:
82
  user = auth.get_user_by_email(email)
83
  return user is not None
84
- except Exception:
 
85
  return False
86
 
87
- # UI Layout
88
- st.set_page_config(page_title="Construction To-Do List", layout="wide")
89
-
90
- if "authenticated" not in st.session_state:
91
- st.session_state["authenticated"] = False
92
- st.session_state["email"] = ""
93
- st.session_state["first_login"] = True
 
94
 
95
- if not st.session_state["authenticated"]:
96
- st.title("๐Ÿ” User Authentication")
97
- choice = st.radio("Select Option", ["Sign In", "Sign Up"], horizontal=True)
 
98
 
99
- if choice == "Sign Up":
100
- with st.form("signup_form"):
101
- new_email = st.text_input("Enter your email")
102
- new_password = st.text_input("Enter a password", type="password")
103
- if st.form_submit_button("Register") and new_email and new_password:
104
- register_user(new_email, new_password)
 
 
 
 
105
 
106
- elif choice == "Sign In":
107
- with st.form("signin_form"):
108
- email = st.text_input("Enter your email")
109
- password = st.text_input("Enter your password", type="password")
110
- if st.form_submit_button("Login"):
111
- if authenticate_user(email, password):
112
- st.session_state["authenticated"] = True
113
- st.session_state["email"] = email
114
- st.rerun()
115
- else:
116
- st.error("Invalid email or password")
117
- st.stop()
 
 
 
 
 
 
118
 
119
- # Main Application
120
- def get_tasks():
121
- tasks_ref = db.collection("tasks").where("user", "==", st.session_state["email"])
122
- return [{"id": task.id, **task.to_dict()} for task in tasks_ref.stream()]
123
-
124
- def calculate_deadline_status(task_date):
125
- today = datetime.today().date()
126
- task_date = datetime.strptime(task_date, "%Y-%m-%d").date()
127
- delta = (task_date - today).days
128
-
129
- if delta < 0:
130
- return "Overdue", abs(delta)
131
- elif delta == 0:
132
- return "Due Today", 0
133
- elif delta <= 3:
134
- return f"Due in {delta} days", delta
135
- else:
136
- return "On Track", delta
137
-
138
- # Show reminders after login
139
- if st.session_state.get("first_login"):
140
- tasks = get_tasks()
141
- overdue = []
142
- due_soon = []
143
-
144
- for task in tasks:
145
- if task['status'] in ["Pending", "In Progress"]:
146
- status, _ = calculate_deadline_status(task['date'])
147
- if "Overdue" in status or "Due" in status:
148
- overdue.append(task) if "Overdue" in status else due_soon.append(task)
149
-
150
- if overdue or due_soon:
151
- with st.container():
152
- st.markdown("---")
153
- cols = st.columns([1, 4, 1])
154
- with cols[1]:
155
- st.subheader("๐Ÿ”” Task Reminders")
156
- if overdue:
157
- st.error("##### Overdue Tasks")
158
- for task in overdue:
159
- st.markdown(f"โš ๏ธ **{task['task']}** ({task['project']}) - Due {task['date']}")
160
- if due_soon:
161
- st.warning("##### Upcoming Deadlines")
162
- for task in due_soon:
163
- st.markdown(f"โณ **{task['task']}** ({task['project']}) - Due {task['date']}")
164
- st.markdown("---")
165
- st.session_state.first_login = False
166
 
167
- # Sidebar Navigation
168
- st.sidebar.title(f"Welcome, {st.session_state['email']}")
169
- menu = st.sidebar.radio("Navigation", [
170
- "๐Ÿ  Dashboard",
171
- "๐Ÿ“ Task Entry",
172
- "๐Ÿ‘€ View Tasks",
173
- "๐Ÿ—๏ธ Projects",
174
- "โš™๏ธ Settings"
175
- ], label_visibility="collapsed")
 
 
176
 
177
- # Dashboard View
178
- if menu == "๐Ÿ  Dashboard":
179
- st.title("๐Ÿ“Š Project Dashboard")
180
- tasks = get_tasks()
181
-
182
- if tasks:
183
- df = pd.DataFrame(tasks)
184
 
185
- # Metrics Row
186
- col1, col2, col3, col4 = st.columns(4)
187
- col1.metric("Total Tasks", len(df))
188
- col2.metric("Completed", len(df[df['status'] == "Completed"]), "tasks")
189
- col3.metric("In Progress", len(df[df['status'] == "In Progress"]), "tasks")
190
- col4.metric("Pending", len(df[df['status'] == "Pending"]), "tasks")
191
 
192
- # Visualizations
193
- col1, col2 = st.columns(2)
194
- with col1:
195
- st.subheader("Status Distribution")
196
- fig = px.pie(df, names='status', hole=0.3,
197
- color_discrete_sequence=px.colors.qualitative.Pastel)
198
- st.plotly_chart(fig, use_container_width=True)
199
-
200
- with col2:
201
- st.subheader("Project Timeline")
202
- timeline_df = df.copy()
203
- timeline_df['date'] = pd.to_datetime(timeline_df['date'])
204
- fig = px.timeline(timeline_df, x_start="date", x_end="date", y="project",
205
- color="status", title="Project Timeline",
206
- color_discrete_map={
207
- "Pending": "#FFD700",
208
- "In Progress": "#87CEEB",
209
- "Completed": "#90EE90"
210
- })
211
- st.plotly_chart(fig, use_container_width=True)
212
- else:
213
- st.info("No tasks found. Start by adding new tasks!")
214
 
215
- # Task Entry
216
- elif menu == "๐Ÿ“ Task Entry":
217
- st.title("๐Ÿ“ Add New Task")
218
- projects_ref = db.collection("projects")
219
- projects = [proj.id for proj in projects_ref.stream()]
220
- projects.append("Add New Project")
221
-
222
- with st.form("task_form", clear_on_submit=True):
223
- task = st.text_input("Task Description", placeholder="Enter task details...")
224
- task_type = st.selectbox("Task Type", ["Design", "Procurement", "Construction", "Testing", "Other"])
225
- selected_project = st.selectbox("Project", projects)
226
-
227
- if selected_project == "Add New Project":
228
- new_project = st.text_input("New Project Name")
229
- if new_project:
230
- db.collection("projects").document(new_project).set({"created_by": st.session_state["email"]})
231
- selected_project = new_project
232
-
233
- col1, col2 = st.columns(2)
234
- with col1:
235
- status = st.selectbox("Status", ["Pending", "In Progress", "Completed"])
236
- with col2:
237
- date = st.date_input("Target Date", min_value=datetime.today())
238
-
239
- if st.form_submit_button("Add Task"):
240
- db.collection("tasks").add({
241
- "user": st.session_state["email"],
242
- "task": task,
243
- "type": task_type,
244
- "project": selected_project,
245
- "status": status,
246
- "date": str(date)
247
- })
248
- st.success("Task added successfully!")
249
- st.rerun()
250
 
251
- # View Tasks
252
- elif menu == "๐Ÿ‘€ View Tasks":
253
- st.title("๐Ÿ” Task Explorer")
254
- tasks = get_tasks()
255
-
256
- if tasks:
257
- df = pd.DataFrame(tasks)
258
-
259
- # Filters
260
- col1, col2, col3 = st.columns(3)
261
- with col1:
262
- status_filter = st.multiselect("Filter by Status",
263
- options=df['status'].unique(),
264
- default=df['status'].unique())
265
- with col2:
266
- project_filter = st.multiselect("Filter by Project",
267
- options=df['project'].unique())
268
- with col3:
269
- date_filter = st.date_input("Filter by Date")
270
 
271
- # Apply filters
272
- filtered_df = df.copy()
273
- if status_filter:
274
- filtered_df = filtered_df[filtered_df['status'].isin(status_filter)]
275
- if project_filter:
276
- filtered_df = filtered_df[filtered_df['project'].isin(project_filter)]
277
- if date_filter:
278
- filtered_df = filtered_df[filtered_df['date'] == str(date_filter)]
279
-
280
- # Enhanced Table Display
281
- if not filtered_df.empty:
282
- styled_df = filtered_df[['task', 'project', 'status', 'date']].copy()
283
- styled_df['status'] = styled_df['status'].apply(
284
- lambda x: f'<span class="status-badge {x.lower().replace(" ", "-")}">{x}</span>'
285
- )
286
 
287
- st.markdown(styled_df.style.hide(axis="index")
288
- .set_table_styles([
289
- {'selector': 'th', 'props': 'background-color: #2c3e50; color: white;'},
290
- {'selector': 'td', 'props': 'padding: 12px; border-bottom: 1px solid #ddd;'}
291
- ]).to_html(), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  else:
293
- st.info("No tasks match the selected filters")
294
- else:
295
- st.info("No tasks found. Start by adding new tasks!")
296
 
297
- # Project Management
298
- elif menu == "๐Ÿ—๏ธ Projects":
299
- st.title("๐Ÿ—๏ธ Project Management")
300
-
301
- # Existing Projects
302
- projects_ref = db.collection("projects")
303
- projects = [proj.id for proj in projects_ref.stream()]
304
-
305
- if projects:
306
- st.subheader("Existing Projects")
307
- for project in projects:
308
- col1, col2 = st.columns([3, 1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  with col1:
310
- st.markdown(f"**{project}**")
311
  with col2:
312
- if st.button(f"Delete {project}", key=f"del_{project}"):
313
- db.collection("projects").document(project).delete()
 
 
 
 
 
 
 
 
 
 
 
314
  st.rerun()
315
-
316
- # Add New Project
317
- st.subheader("Add New Project")
318
- with st.form("project_form"):
319
- new_project = st.text_input("Project Name")
320
- if st.form_submit_button("Create Project"):
321
- if new_project:
322
- db.collection("projects").document(new_project).set({
323
- "created_by": st.session_state["email"],
324
- "created_at": datetime.now().strftime("%Y-%m-%d")
325
- })
326
- st.success("Project created successfully!")
327
- st.rerun()
328
 
329
- # Settings
330
- elif menu == "โš™๏ธ Settings":
331
- st.title("โš™๏ธ Account Settings")
332
-
333
- with st.form("password_change"):
334
- st.subheader("Change Password")
335
- old_password = st.text_input("Current Password", type="password")
336
- new_password = st.text_input("New Password", type="password")
337
- confirm_password = st.text_input("Confirm New Password", type="password")
338
 
339
- if st.form_submit_button("Update Password"):
340
- if new_password == confirm_password:
341
- # Implement password change logic
342
- st.success("Password updated successfully!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  else:
344
- st.error("New passwords do not match")
345
-
346
- st.markdown("---")
347
- if st.button("Logout", type="primary"):
348
- st.session_state.authenticated = False
349
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+
3
+ # Must be the FIRST Streamlit command
4
+ st.set_page_config(
5
+ page_title="Construction To-Do List",
6
+ layout="wide",
7
+ page_icon="๐Ÿ—๏ธ"
8
+ )
9
 
10
  # Custom CSS for enhanced UI
11
  st.markdown("""
 
16
  max-width: 1200px;
17
  }
18
 
19
+ /* 3D Sidebar effects */
20
  [data-testid="stSidebar"] {
21
+ background: linear-gradient(145deg, #2c3e50, #3498db);
22
  box-shadow: 5px 0 15px rgba(0,0,0,0.2);
23
  }
24
 
 
27
  border-radius: 8px;
28
  padding: 10px 15px;
29
  margin: 5px 0;
30
+ transform-style: preserve-3d;
31
  }
32
 
33
  .st-emotion-cache-1dj0hjr:hover {
34
+ transform: translateX(10px) rotateY(15deg);
35
  background: rgba(255,255,255,0.1);
36
+ box-shadow: 5px 5px 15px rgba(0,0,0,0.3);
37
  }
38
 
39
  /* Professional table styling */
 
60
  .pending { background: #fff3cd; color: #856404; }
61
  .in-progress { background: #cce5ff; color: #004085; }
62
  .completed { background: #d4edda; color: #155724; }
63
+
64
+ /* 3D Button effects */
65
+ .stButton>button {
66
+ transition: all 0.3s ease;
67
+ transform-style: preserve-3d;
68
+ }
69
+
70
+ .stButton>button:hover {
71
+ transform: translateY(-2px) rotateX(15deg);
72
+ box-shadow: 0 5px 15px rgba(0,0,0,0.3);
73
+ }
74
  </style>
75
  """, unsafe_allow_html=True)
76
 
77
+ import firebase_admin
78
+ from firebase_admin import credentials, firestore, auth
79
+ import pandas as pd
80
+ import plotly.express as px
81
+ from datetime import datetime, timedelta
82
+ import hashlib
83
+
84
  # Initialize Firebase
85
  if not firebase_admin._apps:
86
  cred = credentials.Certificate("firebase_credentials.json")
 
88
  db = firestore.client()
89
 
90
  # Authentication Functions
 
 
 
91
  def register_user(email, password):
92
  try:
93
  user = auth.create_user(email=email, password=password)
 
100
  try:
101
  user = auth.get_user_by_email(email)
102
  return user is not None
103
+ except Exception as e:
104
+ st.error(f"Authentication error: {str(e)}")
105
  return False
106
 
107
+ # Main Application Logic
108
+ def main():
109
+ # Session state initialization
110
+ if "authenticated" not in st.session_state:
111
+ st.session_state.authenticated = False
112
+ st.session_state.email = ""
113
+ st.session_state.first_login = True
114
+ st.session_state.user_uid = ""
115
 
116
+ # Authentication flow
117
+ if not st.session_state.authenticated:
118
+ st.title("๐Ÿ” User Authentication")
119
+ choice = st.radio("Select Option", ["Sign In", "Sign Up"], horizontal=True)
120
 
121
+ if choice == "Sign Up":
122
+ with st.form("signup_form"):
123
+ new_email = st.text_input("Email")
124
+ new_password = st.text_input("Password", type="password")
125
+ if st.form_submit_button("Register"):
126
+ if new_email and new_password:
127
+ try:
128
+ register_user(new_email, new_password)
129
+ except Exception as e:
130
+ st.error(f"Registration failed: {str(e)}")
131
 
132
+ elif choice == "Sign In":
133
+ with st.form("signin_form"):
134
+ email = st.text_input("Email")
135
+ password = st.text_input("Password", type="password")
136
+ if st.form_submit_button("Login"):
137
+ try:
138
+ if authenticate_user(email, password):
139
+ st.session_state.authenticated = True
140
+ st.session_state.email = email
141
+ # Get user UID for password updates
142
+ user = auth.get_user_by_email(email)
143
+ st.session_state.user_uid = user.uid
144
+ st.rerun()
145
+ else:
146
+ st.error("Invalid credentials")
147
+ except Exception as e:
148
+ st.error(f"Login failed: {str(e)}")
149
+ return
150
 
151
+ # Main App Functionality
152
+ def get_tasks():
153
+ try:
154
+ tasks_ref = db.collection("tasks").where("user", "==", st.session_state.email)
155
+ return [{"id": task.id, **task.to_dict()} for task in tasks_ref.stream()]
156
+ except Exception as e:
157
+ st.error(f"Error loading tasks: {str(e)}")
158
+ return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
+ def calculate_deadline_status(task_date):
161
+ try:
162
+ today = datetime.today().date()
163
+ task_date = datetime.strptime(task_date, "%Y-%m-%d").date()
164
+ delta = (task_date - today).days
165
+ if delta < 0: return "Overdue", abs(delta)
166
+ if delta == 0: return "Due Today", 0
167
+ if delta <= 3: return f"Due in {delta} days", delta
168
+ return "On Track", delta
169
+ except:
170
+ return "Invalid Date", 0
171
 
172
+ # Show reminders after login
173
+ if st.session_state.first_login:
174
+ tasks = get_tasks()
175
+ overdue = []
176
+ due_soon = []
 
 
177
 
178
+ for task in tasks:
179
+ if task['status'] in ["Pending", "In Progress"]:
180
+ status, _ = calculate_deadline_status(task['date'])
181
+ if "Overdue" in status or "Due" in status:
182
+ overdue.append(task) if "Overdue" in status else due_soon.append(task)
 
183
 
184
+ if overdue or due_soon:
185
+ with st.container():
186
+ st.markdown("---")
187
+ cols = st.columns([1, 4, 1])
188
+ with cols[1]:
189
+ st.subheader("๐Ÿ”” Task Reminders")
190
+ if overdue:
191
+ st.error("##### Overdue Tasks")
192
+ for task in overdue:
193
+ st.markdown(f"โš ๏ธ **{task['task']}** ({task['project']}) - Due {task['date']}")
194
+ if due_soon:
195
+ st.warning("##### Upcoming Deadlines")
196
+ for task in due_soon:
197
+ st.markdown(f"โณ **{task['task']}** ({task['project']}) - Due {task['date']}")
198
+ st.markdown("---")
199
+ st.session_state.first_login = False
 
 
 
 
 
 
200
 
201
+ # Sidebar Navigation
202
+ st.sidebar.title(f"Welcome, {st.session_state.email}")
203
+ menu = st.sidebar.radio("Navigation", [
204
+ "๐Ÿ  Dashboard",
205
+ "๐Ÿ“ Task Entry",
206
+ "๐Ÿ‘€ View Tasks",
207
+ "โœ๏ธ Edit Tasks",
208
+ "๐Ÿ—๏ธ Projects",
209
+ "โš™๏ธ Settings"
210
+ ], label_visibility="collapsed")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
+ # Dashboard View
213
+ if menu == "๐Ÿ  Dashboard":
214
+ st.title("๐Ÿ“Š Project Dashboard")
215
+ tasks = get_tasks()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ if tasks:
218
+ df = pd.DataFrame(tasks)
219
+ col1, col2, col3, col4 = st.columns(4)
220
+ col1.metric("Total Tasks", len(df))
221
+ col2.metric("Completed", len(df[df['status'] == "Completed"]))
222
+ col3.metric("In Progress", len(df[df['status'] == "In Progress"]))
223
+ col4.metric("Pending", len(df[df['status'] == "Pending"]))
 
 
 
 
 
 
 
 
224
 
225
+ col1, col2 = st.columns(2)
226
+ with col1:
227
+ st.subheader("Status Distribution")
228
+ fig = px.pie(df, names='status', hole=0.3,
229
+ color_discrete_sequence=px.colors.qualitative.Pastel)
230
+ st.plotly_chart(fig, use_container_width=True)
231
+
232
+ with col2:
233
+ st.subheader("Project Timeline")
234
+ timeline_df = df.copy()
235
+ timeline_df['date'] = pd.to_datetime(timeline_df['date'])
236
+ fig = px.timeline(timeline_df, x_start="date", x_end="date", y="project",
237
+ color="status", color_discrete_map={
238
+ "Pending": "#FFD700",
239
+ "In Progress": "#87CEEB",
240
+ "Completed": "#90EE90"
241
+ })
242
+ st.plotly_chart(fig, use_container_width=True)
243
  else:
244
+ st.info("No tasks found. Start by adding new tasks!")
 
 
245
 
246
+ # Task Entry
247
+ elif menu == "๐Ÿ“ Task Entry":
248
+ st.title("๐Ÿ“ Add New Task")
249
+ try:
250
+ projects_ref = db.collection("projects")
251
+ projects = [proj.id for proj in projects_ref.stream()]
252
+ projects.append("Add New Project")
253
+ except Exception as e:
254
+ st.error(f"Error loading projects: {str(e)}")
255
+ projects = ["Add New Project"]
256
+
257
+ with st.form("task_form", clear_on_submit=True):
258
+ task = st.text_input("Task Description", help="Enter detailed task description")
259
+ task_type = st.selectbox("Task Type", ["Design", "Procurement", "Construction", "Testing", "Other"])
260
+ selected_project = st.selectbox("Project", projects)
261
+
262
+ if selected_project == "Add New Project":
263
+ new_project = st.text_input("New Project Name")
264
+ if new_project:
265
+ try:
266
+ db.collection("projects").document(new_project).set({"created_by": st.session_state.email})
267
+ selected_project = new_project
268
+ except Exception as e:
269
+ st.error(f"Error creating project: {str(e)}")
270
+
271
+ col1, col2 = st.columns(2)
272
  with col1:
273
+ status = st.selectbox("Status", ["Pending", "In Progress", "Completed"])
274
  with col2:
275
+ date = st.date_input("Target Date", min_value=datetime.today())
276
+
277
+ if st.form_submit_button("Add Task"):
278
+ try:
279
+ db.collection("tasks").add({
280
+ "user": st.session_state.email,
281
+ "task": task,
282
+ "type": task_type,
283
+ "project": selected_project,
284
+ "status": status,
285
+ "date": str(date)
286
+ })
287
+ st.success("Task added successfully!")
288
  st.rerun()
289
+ except Exception as e:
290
+ st.error(f"Error saving task: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
291
 
292
+ # View Tasks with Filtering
293
+ elif menu == "๐Ÿ‘€ View Tasks":
294
+ st.title("๐Ÿ” Task Explorer")
295
+ tasks = get_tasks()
 
 
 
 
 
296
 
297
+ if tasks:
298
+ df = pd.DataFrame(tasks)
299
+ col1, col2, col3 = st.columns(3)
300
+ with col1:
301
+ status_filter = st.multiselect("Status", df['status'].unique(), df['status'].unique())
302
+ with col2:
303
+ project_filter = st.multiselect("Project", df['project'].unique())
304
+ with col3:
305
+ date_filter = st.date_input("Filter by Date")
306
+
307
+ filtered_df = df.copy()
308
+ if status_filter:
309
+ filtered_df = filtered_df[filtered_df['status'].isin(status_filter)]
310
+ if project_filter:
311
+ filtered_df = filtered_df[filtered_df['project'].isin(project_filter)]
312
+ if date_filter:
313
+ filtered_df = filtered_df[filtered_df['date'] == str(date_filter)]
314
+
315
+ if not filtered_df.empty:
316
+ styled_df = filtered_df[['task', 'project', 'status', 'date']].copy()
317
+ styled_df['status'] = styled_df['status'].apply(
318
+ lambda x: f'<span class="status-badge {x.lower().replace(" ", "-")}">{x}</span>'
319
+ )
320
+ st.markdown(styled_df.style.hide(axis="index")
321
+ .set_table_styles([
322
+ {'selector': 'th', 'props': 'background-color: #2c3e50; color: white;'},
323
+ {'selector': 'td', 'props': 'padding: 12px; border-bottom: 1px solid #ddd;'}
324
+ ]).to_html(), unsafe_allow_html=True)
325
  else:
326
+ st.info("No tasks match the filters")
327
+ else:
328
+ st.info("No tasks found. Start by adding new tasks!")
329
+
330
+ # Edit Tasks
331
+ elif menu == "โœ๏ธ Edit Tasks":
332
+ st.title("โœ๏ธ Edit Task")
333
+ tasks = get_tasks()
334
+
335
+ if tasks:
336
+ task_options = [f"{task['task']} ({task['project']} - {task['date']})" for task in tasks]
337
+ selected_task = st.selectbox("Select Task to Edit", task_options)
338
+ task_id = next(task['id'] for task in tasks if f"{task['task']} ({task['project']} - {task['date']})" == selected_task)
339
+ task_to_edit = next(task for task in tasks if task['id'] == task_id)
340
+
341
+ with st.form("edit_form"):
342
+ new_task = st.text_input("Task Description", value=task_to_edit['task'])
343
+ new_type = st.selectbox("Task Type",
344
+ ["Design", "Procurement", "Construction", "Testing", "Other"],
345
+ index=["Design", "Procurement", "Construction", "Testing", "Other"].index(task_to_edit['type']))
346
+ new_status = st.selectbox("Status",
347
+ ["Pending", "In Progress", "Completed"],
348
+ index=["Pending", "In Progress", "Completed"].index(task_to_edit['status']))
349
+ new_project = st.text_input("Project", value=task_to_edit['project'])
350
+ new_date = st.date_input("Target Date",
351
+ value=datetime.strptime(task_to_edit['date'], "%Y-%m-%d"))
352
+
353
+ if st.form_submit_button("Update Task"):
354
+ try:
355
+ db.collection("tasks").document(task_id).update({
356
+ "task": new_task,
357
+ "type": new_type,
358
+ "status": new_status,
359
+ "project": new_project,
360
+ "date": str(new_date)
361
+ })
362
+ st.success("Task updated successfully!")
363
+ st.rerun()
364
+ except Exception as e:
365
+ st.error(f"Error updating task: {str(e)}")
366
+ else:
367
+ st.info("No tasks available for editing")
368
+
369
+ # Project Management
370
+ elif menu == "๐Ÿ—๏ธ Projects":
371
+ st.title("๐Ÿ—๏ธ Project Management")
372
+ try:
373
+ projects_ref = db.collection("projects")
374
+ projects = [proj.id for proj in projects_ref.stream()]
375
+ except Exception as e:
376
+ st.error(f"Error loading projects: {str(e)}")
377
+ projects = []
378
+
379
+ if projects:
380
+ st.subheader("Existing Projects")
381
+ for project in projects:
382
+ col1, col2 = st.columns([3, 1])
383
+ with col1:
384
+ st.markdown(f"**{project}**")
385
+ with col2:
386
+ if st.button(f"Delete {project}", key=f"del_{project}"):
387
+ try:
388
+ db.collection("projects").document(project).delete()
389
+ st.rerun()
390
+ except Exception as e:
391
+ st.error(f"Error deleting project: {str(e)}")
392
+
393
+ st.subheader("Add New Project")
394
+ with st.form("project_form"):
395
+ new_project = st.text_input("Project Name")
396
+ if st.form_submit_button("Create Project"):
397
+ if new_project:
398
+ try:
399
+ db.collection("projects").document(new_project).set({
400
+ "created_by": st.session_state.email,
401
+ "created_at": datetime.now().strftime("%Y-%m-%d")
402
+ })
403
+ st.success("Project created successfully!")
404
+ st.rerun()
405
+ except Exception as e:
406
+ st.error(f"Error creating project: {str(e)}")
407
+
408
+ # Settings
409
+ elif menu == "โš™๏ธ Settings":
410
+ st.title("โš™๏ธ Account Settings")
411
+ with st.form("password_change"):
412
+ st.subheader("Change Password")
413
+ old_password = st.text_input("Current Password", type="password")
414
+ new_password = st.text_input("New Password", type="password")
415
+ confirm_password = st.text_input("Confirm New Password", type="password")
416
+
417
+ if st.form_submit_button("Update Password"):
418
+ if new_password == confirm_password:
419
+ try:
420
+ auth.update_user(
421
+ st.session_state.user_uid,
422
+ password=new_password
423
+ )
424
+ st.success("Password updated successfully!")
425
+ except Exception as e:
426
+ st.error(f"Password update failed: {str(e)}")
427
+ else:
428
+ st.error("New passwords do not match")
429
+
430
+ st.markdown("---")
431
+ if st.button("Logout", type="primary"):
432
+ st.session_state.authenticated = False
433
+ st.rerun()
434
+
435
+ if __name__ == "__main__":
436
+ main()