diff --git a/.gitignore b/.gitignore index 5ac73e1..a930a3f 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,6 @@ cython_debug/ db/db.sqlite /HabitTracker.iml + +static/profile_images + diff --git a/ER.dia b/ER.dia index 2e41641..5d00e0b 100644 Binary files a/ER.dia and b/ER.dia differ diff --git a/ER.png b/ER.png index 77f8f50..49ebe6e 100644 Binary files a/ER.png and b/ER.png differ diff --git a/UML.dia b/UML.dia index fd38487..54da21e 100644 Binary files a/UML.dia and b/UML.dia differ diff --git a/UML.png b/UML.png index 1770aa7..2515778 100644 Binary files a/UML.png and b/UML.png differ diff --git a/app.py b/app.py index 3f68c80..4f7bc54 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,14 @@ import datetime import hashlib +import os +from PIL import Image from flask import Flask, render_template, redirect, url_for, request from flask_login import login_required, LoginManager, login_user, logout_user, current_user from models.Habit import Habit from models.HabitList import HabitList -from models.HabitTrackings import HabitTrackings +from models.HabitTracking import HabitTracking from models.User import User from utils import anonymous_required @@ -271,6 +273,7 @@ def profile(): "profile.html", name=current_user.name, email=current_user.email, + profile_image_url=current_user.profile_image, errors={} ) @@ -293,15 +296,15 @@ def profile_change(): if not oldPassword: errors['oldPassword'] = 'Du musst dein aktuelles Passwort angeben.' - else: - if hashlib.sha256(oldPassword.encode()).hexdigest() != current_user.password: - errors['oldPassword'] = 'Das Passwort ist falsch.' + elif hashlib.sha256(oldPassword.encode()).hexdigest() != current_user.password: + errors['oldPassword'] = 'Das Passwort ist falsch.' if errors: return render_template( "profile.html", name=current_user.name, email=current_user.email, + profile_image_url=current_user.profile_image, errors=errors ) @@ -317,6 +320,69 @@ def profile_change(): "profile.html", name=current_user.name, email=current_user.email, + profile_image_url=current_user.profile_image, + errors={} + ) + +UPLOAD_FOLDER = 'static/profile_images/' # Folder to store profile images +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER + +def save_profile_image(image_file): + print("we are saving...") + filename = image_file.filename + if '.' not in filename: + # Ensure the filename has an extension + raise ValueError("Invalid filename") + + # Check if the file extension is allowed + allowed_extensions = {'jpg', 'jpeg', 'png', 'gif'} + file_extension = filename.rsplit('.', 1)[1].lower() + if file_extension not in allowed_extensions: + raise ValueError("Invalid file extension") + + # Open the uploaded image + image = Image.open(image_file) + + # Determine the size of the square image + min_dimension = min(image.size) + square_size = (min_dimension, min_dimension) + + # Calculate the coordinates for cropping + left = (image.width - min_dimension) / 2 + top = (image.height - min_dimension) / 2 + right = (image.width + min_dimension) / 2 + bottom = (image.height + min_dimension) / 2 + + # Crop the image to a square and resize it + image = image.crop((left, top, right, bottom)) + image.thumbnail(square_size) + image = image.resize((128, 128)) + + # Save the processed image + image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) + image.save(image_path) + + return filename + +@app.route('/upload', methods=['POST']) +def upload_profile_image(): + if 'file' not in request.files: + return 'No file part' + + file = request.files['file'] + + filename = save_profile_image(file) + + # Update user + current_user.profile_image = url_for('static', filename=f'profile_images/{filename}') + current_user.update() + + # Back to profile + return render_template( + "profile.html", + name=current_user.name, + email=current_user.email, + profile_image_url=current_user.profile_image, errors={} ) @@ -336,7 +402,7 @@ def check_habit(): if current_user not in users: return {"error": "Habit does not belong to user"} - trackings = habit.get_habitTrackings() + trackings = habit.get_habitTracking() # Check if habit has been tracked today delete_tracking = None @@ -345,7 +411,7 @@ def check_habit(): delete_tracking = tracking if not delete_tracking: - HabitTrackings.create(habit_id, 1) + HabitTracking.create(habit_id) else: delete_tracking.delete() diff --git a/db/SQLiteClient.py b/db/SQLiteClient.py index 9c7d21b..c407599 100644 --- a/db/SQLiteClient.py +++ b/db/SQLiteClient.py @@ -8,12 +8,12 @@ def con3(): return conn -### User.py ### -def create_user(name: str, email: str, password: str): +### User ### +def create_user(name: str, email: str, password: str, profile_image: str): password = hashlib.sha256(password.encode()).hexdigest() now = datetime.now().isoformat() - query = (f"INSERT INTO users (name, email, password, created_at, updated_at) VALUES ('{name}', '{email}', " - f"'{password}', '{now}', '{now}');") + query = (f"INSERT INTO users (name, email, password, profile_image, created_at, updated_at) VALUES " + f"('{name}', '{email}', '{password}', '{profile_image}', '{now}', '{now}');") conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -42,12 +42,14 @@ def get_user_by_email(email: str): return user -def update_user(id: int, name: str, email: str, password: str = None): +def update_user(id: int, name: str, email: str, password: str, profile_image:str): now = datetime.now().isoformat() if password: - query = f"UPDATE users SET name = '{name}', email = '{email}', password = '{password}', updated_at = '{now}' WHERE id = {id};" + query = (f"UPDATE users SET name = '{name}', email = '{email}', password = '{password}', " + f"profile_image = '{profile_image}', updated_at = '{now}' WHERE id = {id};") else: - query = f"UPDATE users SET name = '{name}', email = '{email}', updated_at = '{now}' WHERE id = {id};" + query = (f"UPDATE users SET name = '{name}', email = '{email}', profile_image = '{profile_image}'," + f" updated_at = '{now}' WHERE id = {id};") conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -57,22 +59,20 @@ def update_user(id: int, name: str, email: str, password: str = None): def delete_user(id: int): - query = f"DELETE FROM habit_lists WHERE (SELECT list_id FROM habit_users WHERE user_id = {id}) = id;" - query2 = f"DELETE FROM users WHERE id = {id};" + query = f"DELETE FROM users WHERE id = {id};" conn = con3() cursor = conn.cursor() cursor.execute(query) - cursor.execute(query2) conn.commit() conn.close() return cursor.lastrowid -### Habit.py ### -def create_habit(list_id: int, name: str, times: int, unit: int, slot: int, note: str | None=None): +### Habit ### +def create_habit(list_id: int, name: str, note: str, times: int, unit: int, slot: int): now = datetime.now().isoformat() - query = (f"INSERT INTO habits (list_id, name, note, times, unit, slot, created_at, updated_at) VALUES ('{list_id}', " - f"'{name}', '{note}', '{times}', '{unit}', '{slot}', '{now}', '{now}');") + query = (f"INSERT INTO habits (list_id, name, note, times, unit, slot, created_at, updated_at) " + f"VALUES ('{list_id}', '{name}', '{note}', '{times}', '{unit}', '{slot}', '{now}', '{now}');") conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -168,7 +168,8 @@ def update_slot(id: int, slot: int): def update_habit(id: int, name: str, note: str, times: int, unit: int): now = datetime.now().isoformat() - query = f"UPDATE habits SET name = {name}, note = {note}, times = {times}, unit = {unit}, updated_at = '{now}' WHERE id = {id};" + query = (f"UPDATE habits SET name = {name}, note = {note}, times = {times}, unit = {unit}, updated_at = '{now}' " + f"WHERE id = {id};") conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -186,8 +187,8 @@ def delete_habit(id: int): conn.close() -### HabitTrackings.py ### -def create_habitTrackings(habit_id: int): +### HabitTracking ### +def create_habitTracking(habit_id: int): now = datetime.now().isoformat() query = f"INSERT INTO habit_trackings (habit_id, created_at) VALUES ('{habit_id}','{now}');" conn = con3() @@ -198,7 +199,7 @@ def create_habitTrackings(habit_id: int): return cursor.lastrowid -def get_habitTrackings(id: int): +def get_habitTracking(id: int): query = f"SELECT * FROM habit_trackings WHERE id = {id};" conn = con3() cursor = conn.cursor() @@ -208,7 +209,7 @@ def get_habitTrackings(id: int): return habit_tracking -def get_habitTrackings_by_habit_id(habit_id: int): +def get_habitTrackings(habit_id: int): query = f"SELECT * FROM habit_trackings WHERE habit_id = {habit_id};" conn = con3() cursor = conn.cursor() @@ -218,7 +219,7 @@ def get_habitTrackings_by_habit_id(habit_id: int): return habit_trackings -def delete_habitTrackings(id: int): +def delete_habitTracking(id: int): query = f"DELETE FROM habit_trackings WHERE id = {id};" conn = con3() cursor = conn.cursor() @@ -227,7 +228,7 @@ def delete_habitTrackings(id: int): conn.close() -### HabitList.py ### +### HabitList ### def create_habitList(user_id: int, name: str, description: str): now = datetime.now().isoformat() query = (f"INSERT INTO habit_lists (name, description, created_at, updated_at) " @@ -264,6 +265,37 @@ def get_habitLists(user_id: int): return habit_lists +def get_users(list_id: int): + query = (f"SELECT users.* FROM users JOIN habit_users ON users.id = habit_users.user_id WHERE " + f"habit_users.list_id = {list_id};") + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + users = cursor.fetchall() + conn.close() + return users + + +def add_user(list_id: int, user_id: int): + now = datetime.now().isoformat() + query = (f"INSERT INTO habit_users (user_id, list_id, created_at, updated_at)" + f" VALUES ('{user_id}', '{list_id}', '{now}', '{now}');") + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + conn.commit() + conn.close() + + +def remove_user(list_id: int, user_id: int): + query = f"DELETE FROM habit_lists WHERE user_id = {user_id} AND list_id = {list_id};" + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + conn.commit() + conn.close() + + def update_habitList(id: int, name: str, description: str): now = datetime.now().isoformat() query = f"UPDATE habit_lists SET name = {name}, description = {description}, updated_at = '{now}' WHERE id = {id};" @@ -284,16 +316,6 @@ def delete_habitList(id: int): conn.close() -def get_users(list_id: int): - query = f"SELECT users.* FROM users JOIN habit_users ON users.id = habit_users.user_id WHERE habit_users.list_id = {list_id};" - conn = con3() - cursor = conn.cursor() - cursor.execute(query) - users = cursor.fetchall() - conn.close() - return users - - if __name__ == "__main__": habits = get_habits(1) for habit in habits: diff --git a/db/migrations/1708105700_delete_users_table.sql b/db/migrations/1708105700_delete_users_table.sql new file mode 100644 index 0000000..441087a --- /dev/null +++ b/db/migrations/1708105700_delete_users_table.sql @@ -0,0 +1 @@ +DROP TABLE users; \ No newline at end of file diff --git a/db/migrations/1708105768_create_users_table.sql b/db/migrations/1708105768_create_users_table.sql new file mode 100644 index 0000000..5210527 --- /dev/null +++ b/db/migrations/1708105768_create_users_table.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS users +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + profile_image TEXT NOT NULL, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); diff --git a/models/Habit.py b/models/Habit.py index b53ce5b..cf39fce 100644 --- a/models/Habit.py +++ b/models/Habit.py @@ -2,16 +2,16 @@ import json from dataclasses import dataclass from datetime import datetime -from models.HabitTrackings import HabitTrackings -from db.SQLiteClient import update_slot, create_habit, get_habit, delete_habit, get_next_slot, \ - get_habitTrackings_by_habit_id, get_slots, update_habit, get_habitList +from models.HabitTracking import HabitTracking +from db.SQLiteClient import (create_habit, get_habit, update_habit, delete_habit, get_next_slot, get_slots, update_slot, + get_habitTrackings, get_habitList) -# Unit wird als Integers wie folgt gemessen: -# 0: Tag -# 1: Woche (Default) -# 2: Monat -# 3: Jahr +# unit will be represented by integers like this: +# 0: day +# 1: week (default) +# 2: month +# 3: year @dataclass class Habit: @@ -28,82 +28,74 @@ class Habit: self.fill_statistics() @staticmethod - def create(list_id: int, name: str, times: int, note: str | None = None, unit: int | None = 1): + def create(list_id: int, name: str, times: int, note: str = None, unit: int = 1): slot = get_next_slot(list_id) - id = create_habit(list_id, name, times, unit, slot, note) + id = create_habit(list_id, name, note, times, unit, slot) return Habit(id, list_id, name, note, times, unit, slot) @staticmethod def get(id: int): habit = get_habit(id) - habit = Habit(habit[0], habit[1], habit[2], habit[3], habit[4], habit[5], habit[6]) if habit else None + return Habit(habit[0], habit[1], habit[2], habit[3], habit[4], habit[5], habit[6]) if habit else None - return habit - def update(self, name: str=None, note: str=None, times: int=None, unit: int=None): - update_habit(self.id, name, note, times, unit) - if name is not None: - self.name = name - if note is not None: - self.note = note - if times is not None: - self.times = times - if unit is not None: - self.unit = unit + # Updates: name, note, times, unit + def update(self): + update_habit(self.id, self.name, self.note, self.times, self.unit) + + # Updates the slot and reorders the HabitList accordingly def update_slot(self, new_slot: int): + # Fetches a list with the following structure [(id, slot), (id, slot), ...] slots = get_slots(self.list_id) - if new_slot > self.slot: - slots = slots[self.slot:new_slot] + + # Splits the list depending on whether the new slot is higher or lower than the current one + if new_slot > self.slot: # Example self.slot=1 new_slot=4 + slots = slots[self.slot:new_slot] # Expected list: [(id, 2), (id, 3), (id, 4)] for slot in slots: update_slot(slot[0], slot[1]-1) - if new_slot < self.slot: - slots = slots[new_slot-1:self.slot-1] + if new_slot < self.slot: # Example self.slot=4 new_slot=1 + slots = slots[new_slot-1:self.slot-1] # Expected list: [(id, 1), (id, 2), (id, 3)] for slot in slots: update_slot(slot[0], slot[1]+1) + + # Update the slot of the current habit update_slot(self.id, new_slot) + + # Deletes the Habit def delete(self): + # Reorders the slots slots = get_slots(self.list_id)[self.slot+1:] for slot in slots: update_slot(slot[0], slot[1] - 1) + + # Deletes all track-records associated with the Habit + trackings = self.get_habitTrackings() + for tracking in trackings: + tracking.delete() + + # Deletes the current Habit delete_habit(self.id) - def get_habitTrackings(self) -> list[HabitTrackings]: + + # Returns all track-records for a Habit + def get_habitTrackings(self) -> list: trackings = [] - for rawTracking in get_habitTrackings_by_habit_id(self.id): - trackings.append(HabitTrackings(rawTracking[0], rawTracking[1], + for rawTracking in get_habitTrackings(self.id): + trackings.append(HabitTracking(rawTracking[0], rawTracking[1], datetime.strptime(rawTracking[2], "%Y-%m-%dT%H:%M:%S.%f"))) return trackings - def getStreak(self): - streak = 0 - trackings = [] - for rawTracking in get_habitTrackings_by_habit_id(self.id): - trackings.append(HabitTrackings(datetime.strptime(rawTracking[2], "%Y-%m-%d"))) - - trackings.sort(reverse=True) - if current_date == tracking[0]: - streak += 1 - for habitdate in trackings[1:]: - future_date = habitdate - relativedelta(day=1) - if future_date - habitdate == 0-0-1: - streak + 1 - - - future_date = habitdate - relativedelta(day=1) - - for habitdate in trackings: - current_date = date.today() - if current_date == habitdate or current_date == habitdate - relativedelta(day=1): - streak += 1 - - future_date = habitdate - relativedelta(day=1) - - return streak + # Returns the HabitList in which the Habit is located + def habit_list(self) -> list: + from models.HabitList import HabitList + raw_habitLists = get_habitList(self.list_id) + return HabitList(raw_habitLists[0], raw_habitLists[1], raw_habitLists[2]) if raw_habitLists else None + # Saves the progress of the Habit in the attribute percentage def fill_statistics(self): count = 0 self.checked = False @@ -130,13 +122,7 @@ class Habit: self.percentage = int(count / self.times * 100) + + # Converts the Habit data to a json format def to_json(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) - - def habit_list(self): - from models.HabitList import HabitList - raw_habitLists = get_habitList(self.list_id) - return HabitList(raw_habitLists[0], raw_habitLists[1], raw_habitLists[2], - datetime.strptime(raw_habitLists[3], "%Y-%m-%dT%H:%M:%S.%f"), - datetime.strptime(raw_habitLists[4], "%Y-%m-%dT%H:%M:%S.%f")) \ - if raw_habitLists else None diff --git a/models/HabitList.py b/models/HabitList.py index d59e265..bdff3f5 100644 --- a/models/HabitList.py +++ b/models/HabitList.py @@ -1,9 +1,9 @@ from dataclasses import dataclass -from datetime import date, datetime -from db.SQLiteClient import delete_habitList, create_habitList, get_habitList, get_habits, get_users from models.Habit import Habit from models.User import User +from db.SQLiteClient import (create_habitList, get_habitList, get_habits, get_users, add_user, remove_user, + update_habitList, delete_habitList) @dataclass @@ -11,24 +11,34 @@ class HabitList: id: int name: str description: str - created_at: date - updated_at: date - habits: list = None + habits: list = None #? unclear usage @staticmethod def create(user_id: int, name: str, description: str): id = create_habitList(user_id, name, description) - return HabitList(id, name, description, datetime.now(), datetime.now()) + return HabitList(id, name, description) @staticmethod def get(id: int): habitList = get_habitList(id) - return HabitList(habitList[0], habitList[1], habitList[2], datetime.strptime(habitList[3], "%Y-%m-%dT%H:%M:%S.%f"), datetime.strptime(habitList[4], "%Y-%m-%dT%H:%M:%S.%f")) if habitList else None + return HabitList(habitList[0], habitList[1], habitList[2]) if habitList else None - def delete(self): - delete_habitList(self.id) - def get_habits(self): + # Updates: name, description + def update(self): + update_habitList(self.id, self.name, self.description) + + + # Deletes the HabitList | The id of the current user is necessary + def delete(self, user_id): + if len(get_users) > 1: + self.remove_user(user_id) + else: + delete_habitList(self.id) + + + # Returns the Habits connected with the HabitList + def get_habits(self) -> list: raw_habits = get_habits(self.id) habits = [] for habit in raw_habits: @@ -37,7 +47,9 @@ class HabitList: return habits - def get_users(self): + + # Returns the Users connected with the HabitList + def get_users(self) -> list: raw_users = get_users(self.id) users = [] for user in raw_users: @@ -45,3 +57,14 @@ class HabitList: users.append(user) return users + + + # Adds a User by email to the HabitList + def add_user(self, email: str): + user = User.get_by_email(email) + add_user(self.id, user.id) + + + # Removes a User from the HabitList + def remove_user(self, user_id): + remove_user(self.id, user_id) diff --git a/models/HabitTracking.py b/models/HabitTracking.py new file mode 100644 index 0000000..76ee855 --- /dev/null +++ b/models/HabitTracking.py @@ -0,0 +1,26 @@ +from datetime import date, datetime +from dataclasses import dataclass +from db.SQLiteClient import create_habitTracking, get_habitTracking, delete_habitTracking + + +@dataclass +class HabitTracking: + id: int + habit_id: int + created_at: date + + @staticmethod + def create(habit_id: int): + id = create_habitTracking(habit_id) + return HabitTracking(id, habit_id, datetime.now()) + + @staticmethod + def get(id: int): + habitTrackings = get_habitTracking(id) + return HabitTracking(habitTrackings[0], habitTrackings[1], + datetime.strptime(habitTrackings[2], "%Y-%m-%dT%H:%M:%S.%f")) \ + if habitTrackings else None + + # Deletes the HabitTracking + def delete(self): + delete_habitTracking(self.id) diff --git a/models/HabitTrackings.py b/models/HabitTrackings.py deleted file mode 100644 index 9ad30f1..0000000 --- a/models/HabitTrackings.py +++ /dev/null @@ -1,24 +0,0 @@ -from dataclasses import dataclass -from datetime import date, datetime - -from db.SQLiteClient import create_habitTrackings, get_habitTrackings, delete_habitTrackings - - -@dataclass -class HabitTrackings: - id: int - habit_id: int - created_at: date - - @staticmethod - def create(habit_id: int, times: int): - id = create_habitTrackings(habit_id) - return HabitTrackings(id, habit_id, datetime.now()) - - @staticmethod - def get(id: int): - habitTrackings = get_habitTrackings(id) - return HabitTrackings(habitTrackings[0], habitTrackings[1], datetime.strptime(habitTrackings[2], "%Y-%m-%dT%H:%M:%S.%f")) if habitTrackings else None - - def delete(self): - delete_habitTrackings(self.id) diff --git a/models/User.py b/models/User.py index 317843b..16a24d9 100644 --- a/models/User.py +++ b/models/User.py @@ -1,52 +1,65 @@ -from datetime import datetime - from flask_login import UserMixin -from db.SQLiteClient import create_user, get_user, get_user_by_email, delete_user, update_user, \ - get_habitLists, get_heatmap_value +from db.SQLiteClient import (create_user, get_user, get_user_by_email, update_user, delete_user, + get_habitLists, get_heatmap_value) class User(UserMixin): - def __init__(self, id: int, name: str, email: str, password: str | None = None): + def __init__(self, id: int, name: str, email: str, password: str = None, profile_image:str = None): self.id = id self.name = name self.email = email self.password = password + self.profile_image = profile_image @staticmethod - def create(name: str, email: str, password: str): - id = create_user(name, email, password) - return User(id, name, email) + def create(name: str, email: str, password: str, profile_image: str=None): + id = create_user(name, email, password, profile_image) + return User(id=id, name=name, email=email, profile_image=profile_image) @staticmethod def get(id: int): user = get_user(id) - return User(user[0], user[1], user[2], user[3]) if user else None + return User(user[0], user[1], user[2], user[3], user[4]) if user else None @staticmethod def get_by_email(email: str): user = get_user_by_email(email) - return User(user[0], user[1], user[2], user[3]) if user else None + return User(user[0], user[1], user[2], user[3], user[4]) if user else None + + # Updates: name, email, password, profile_image def update(self): - update_user(self.id, self.name, self.email, self.password if self.password else None) + update_user(self.id, self.name, self.email, self.password if self.password else None, self.profile_image) + + # Deletes the User def delete(self): + # calls the deletion of the users habitLists + habitLists = self.get_habitLists() + for habitList in habitLists: + habitList.delete(self.id) + + # deletes the user delete_user(self.id) - def get_habitLists(self): + + # Returns all HabitLists connected with the user + def get_habitLists(self) -> list: from models.HabitList import HabitList raw_habitLists = get_habitLists(self.id) habitLists = [] for habitList in raw_habitLists: - habitList = HabitList(habitList[0], habitList[1], habitList[2], datetime.strptime(habitList[3], "%Y-%m-%dT%H:%M:%S.%f"), datetime.strptime(habitList[4], "%Y-%m-%dT%H:%M:%S.%f")) + habitList = HabitList(habitList[0], habitList[1], habitList[2]) habitLists.append(habitList) return habitLists - def get_heatmap(self): + + # Returns all heatmap-values from the last 28 days + def get_heatmap(self) -> list: heatmap = [] - for day in range(0, 28): + for day in range (0, 28): value = get_heatmap_value(self.id, day) heatmap.append(value) return heatmap \ No newline at end of file diff --git a/static/main.css b/static/css/background.css similarity index 100% rename from static/main.css rename to static/css/background.css diff --git a/static/css/profile.css b/static/css/profile.css new file mode 100644 index 0000000..55b1d07 --- /dev/null +++ b/static/css/profile.css @@ -0,0 +1,45 @@ +.profile-image-container { + position: relative; + width: 150px; /* Adjust the size as needed */ + height: 150px; /* Adjust the size as needed */ +} + +.profile-image { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; +} + +.profile-image-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* Grey overlay */ + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + color: white; + font-size: 16px; + opacity: 0; /* Initially hidden */ + transition: opacity 0.3s ease; + cursor: pointer; +} + +.profile-image-overlay:hover { + opacity: 1; /* Show overlay on hover */ +} + +.profile-image-overlay span { + /* Center the text and make it bold */ + text-align: center; + font-weight: bold; +} + +.profile-image-overlay:hover span { + /* Style the text when hovering */ + color: white; +} \ No newline at end of file diff --git a/templates/layouts/main.html b/templates/layouts/main.html index c375f6a..43d392c 100644 --- a/templates/layouts/main.html +++ b/templates/layouts/main.html @@ -6,10 +6,12 @@