Skip to content

Commit

Permalink
Merge pull request #88 from spline2hg/profilepage
Browse files Browse the repository at this point in the history
Profilepage
  • Loading branch information
nfoert authored Oct 20, 2024
2 parents 03e5e84 + 7d3be23 commit 518d2d1
Show file tree
Hide file tree
Showing 22 changed files with 1,251 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ __pycache__
django.log
.env
.ruff_cache
cardie/media/profile_images
4 changes: 4 additions & 0 deletions cardie/authentication/admin.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
from django.contrib import admin
from .models import Profile

# Register your models here.

admin.site.register(Profile)
19 changes: 19 additions & 0 deletions cardie/authentication/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django import forms
from .models import Profile, User



class UserUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = ['username', 'email']


class ProfileUpdateForm(forms.ModelForm):
profile_image = forms.ImageField(
widget=forms.FileInput(attrs={'class': 'hidden'}),
required=False
)
class Meta:
model = Profile
fields = ['profile_image', 'bio']
24 changes: 24 additions & 0 deletions cardie/authentication/migrations/0004_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.0.3 on 2024-10-08 13:24

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('authentication', '0003_user_wallet'),
]

operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('profile_image', models.ImageField(default='default_images/default_profile.jpg', upload_to='profile_images/')),
('bio', models.CharField(blank=True, max_length=256)),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='authentication.user')),
],
),
]
12 changes: 11 additions & 1 deletion cardie/authentication/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,14 @@ class User(models.Model):
wallet = models.ManyToManyField("main.Card")

def __str__(self):
return(self.username)
return(self.username)


class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
profile_image = models.ImageField(upload_to='profile_images/',default='default_images/default_profile.jpg')
bio = models.CharField(max_length=256, blank=True)
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return f"{self.user}'s profile image"
233 changes: 233 additions & 0 deletions cardie/authentication/templates/profile.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
{% load static %}

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cardie | Profile</title>

<!-- External Scripts -->
<script src="https://unpkg.com/@phosphor-icons/web"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

<!-- CSS Links -->
<link rel="stylesheet" type="text/css" href="{% static '/main/variables.css' %}">
<link rel="stylesheet" type="text/css" href="{% static '/main/ui.css' %}">
<link rel="stylesheet" type="text/css" href="{% static '/main/notifications.css' %}">
<link rel="stylesheet" type="text/css" href="{% static '/main/home.css' %}">
<link rel="stylesheet" type="text/css" href="{% static '/main/profile.css' %}">
<link rel="shortcut icon" type="image/png" href="{% static '/main/images/favicon_light.ico' %}"/>
</head>

<body>
<div id="home-top">
<a href="{% url 'index' %}">
<div id="home-top-logo">
<img id="home-top-image" src="{% static '/main/images/logo_light.png' %}">
</div>
</a>

<div id="home-top-profile" x-data="{ open: false }" @click.away="open = false">
<button id="home-top-username" class="ui_button_small" @click="open = !open">
{{ profile.user.username }} <i class="ph-bold ph-caret-down"></i>
</button>

<div id="home-top-profile-dropdown" x-show="open" x-transition>
<div class="dropdown-content">
<a href="{% url 'home' %}" >
<button class="ui_button_small view-profile-btn">
<i class="ph-bold ph-user"></i> Home
</button>
</a>
<button class="ui_button_small logout-btn" @click="log_out()">
<i class="ph-bold ph-sign-out"></i> Log Out
</button>
</div>
</div>
</div>
</div>

<form method="POST" enctype="multipart/form-data" x-ref="profileForm">
{% csrf_token %}
<div id="profile-container" x-data="{
editMode: false,
toggleEdit() {
this.editMode = !this.editMode;
},
triggerFileInput() {
this.$refs.fileInput.click();
},
handleFileChange(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
document.querySelector('.profile-image').src = e.target.result;
};
reader.readAsDataURL(file);
}
}
}">
<div id="profile-details">
<div class="detail-card">
<h2 class="ui_text_subheader_left">Account Details</h2>
<div class="info-item ui_text_body">
<i class="ph-bold ph-user"></i>
<template x-if="!editMode">
<span>{{ profile.user.username }}</span>
</template>
<template x-if="editMode">
{{ u_form.username }}
</template>
</div>
<div class="info-item ui_text_body">
<i class="ph-bold ph-envelope"></i>
<template x-if="!editMode">
<span>{{ profile.user.email }}</span>
</template>
<template x-if="editMode">
{{ u_form.email }}
</template>
</div>
<div class="info-item ui_text_body">
<i class="ph-bold ph-file-text"></i>
<template x-if="!editMode">
{% if profile.bio %}

<span>{{ profile.bio }}</span>
{% else %}
<p class="ui_text_body">No bio available.</p>
{% endif %}
</template>
<template x-if="editMode">
{{ p_form.bio }}
</template>
</div>
<div class="info-item ui_text_body">
<i class="ph-bold ph-calendar"></i>
<span>Created: {{ profile.created_at }}</span>
</div>
</div>

<div class="detail-card">
<h2 class="ui_text_subheader_left">Account Actions</h2>
<div class="action-buttons">
<template x-if="!editMode">
<button type="button" class="ui_button_grid" @click="toggleEdit()">
<i class="ph-bold ph-pencil"></i>
<span>Edit Profile</span>
</button>
</template>
<template x-if="editMode">
<button type="submit" class="ui_button_grid" @click="$refs.profileForm.submit()">
<i class="ph-bold ph-check"></i>
<span>Save Changes</span>
</button>
</template>
<template x-if="editMode">
<button type="button" class="ui_button_grid" @click="toggleEdit()">
<i class="ph-bold ph-x"></i>
<span>Cancel</span>
</button>
</template>
<button type="button" class="ui_button_grid" @click="openSettingsModal()">
<i class="ph-bold ph-gear"></i>
<span>Settings</span>
</button>
</div>
</div>
</div>

<div id="profile-image-section">
<div class="profile-image-container" @click="editMode && triggerFileInput()">
<img class="profile-image" src="{{ profile.profile_image.url }}" alt="Profile Image">
<template x-if="editMode">
<div class="profile-image-overlay">
<i class="ph-bold ph-camera" style="font-size: 24px;"></i>
</div>
</template>
</div>
<input
type="file"
name="profile_image"
x-ref="fileInput"
@change="handleFileChange"
accept="image/*"
style="display: none;">
</div>
</div>
</form>

<!-- Settings Modal -->
<div class="modal-backdrop" id="settingsModal" style="display: none;">
<div class="settings-modal">
<div class="modal-sidebar">
<div class="sidebar-item active" data-section="general">
<i class="ph-bold ph-gear"></i>
General
</div>
<div class="sidebar-item" data-section="security">
<i class="ph-bold ph-shield"></i>
Security
</div>
<div class="sidebar-item" data-section="notifications">
<i class="ph-bold ph-bell"></i>
Notifications
</div>
</div>
<div class="modal-content">
<div class="modal-header">
<h2 class="ui_text_subheader_left">Settings</h2>
<button class="close-button" onclick="closeSettingsModal()">&times;</button>
</div>

<div class="content-section active" id="general">
<h3 class="ui_text_smallersubheader">General Settings</h3>
<!-- Add general settings content here -->
</div>

<div class="content-section" id="security">
<h3 class="ui_text_smallersubheader">Security Settings</h3>
<div class="form-group">
<button class="ui_button_small" onclick="togglePasswordFields()">Change Password</button>
<div id="passwordFields" style="display: none; margin-top: 20px;">
<div class="form-group">
<label class="ui_text_body" for="currentPassword">Current Password</label>
<input class="ui_input_generic" type="password" id="currentPassword">
</div>
<div class="form-group">
<label class="ui_text_body" for="newPassword">New Password</label>
<input class="ui_input_generic" type="password" id="newPassword">
</div>
<div class="form-group">
<label class="ui_text_body" for="confirmPassword">Confirm New Password</label>
<input class="ui_input_generic" type="password" id="confirmPassword">
</div>
<button class="ui_button_small" onclick="updatePassword()">Update Password</button>
</div>
</div>
</div>

<div class="content-section" id="notifications">
<h3 class="ui_text_smallersubheader">Notification Settings</h3>
<!-- Add notification settings content here -->
</div>
</div>
</div>
</div>

<script>
var server_ip = "{{ server_ip }}";
var production = "{{ production }}";
var username = "{{ username }}"
</script>

<!-- Script tags -->
<script src="{% static '/main/scripts/profile/profile.js' %}"></script>
<script src="{% static '/main/scripts/home/home.js' %}"></script>
<script src="{% static '/main/scripts/global/favicon.js' %}"></script>
<script src="{% static '/main/scripts/global/notifications.js' %}"></script>
<script src="{% static '/main/scripts/global/logging.js' %}"></script>
<script src="{% static '/main/scripts/global/background_blur.js' %}"></script>
</body>
</html>
56 changes: 54 additions & 2 deletions cardie/authentication/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
from django.shortcuts import HttpResponse
from django.contrib.auth.hashers import make_password, check_password

from authentication.models import User
# from authentication.models import User
from main.models import Server, Card, TempCard
from main import views

from django.utils import timezone
import uuid


from .forms import UserUpdateForm, ProfileUpdateForm
from django.shortcuts import render, get_object_or_404, redirect
from .models import Profile
from django.contrib import messages
from .models import User

def sign_in(request):
server = Server.objects.all()[0] # TODO: What if there is multiple server objects?

Expand Down Expand Up @@ -102,6 +109,8 @@ def create_account(request):
user = User(username=username, password=hashed_password, email=email, date_created=timezone.now())
user.save()

Profile.objects.create(user=user)

request.session["username"] = username
request.session["password"] = password

Expand All @@ -110,4 +119,47 @@ def create_account(request):
else:
return HttpResponse("error_missing_headers")
else:
return HttpResponse("error_create_account_disabled")
return HttpResponse("error_create_account_disabled")


def profile(request):
username = request.session.get("username")
user = get_object_or_404(User, username=username)
profile, created = Profile.objects.get_or_create(user=user)

server_info = Server.objects.all()[0]
is_editing = request.GET.get('edit') == 'true'

if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=user)
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=profile)

if u_form.is_valid() and p_form.is_valid():
new_username = u_form.cleaned_data.get('username')
user = u_form.save()
p_form.save()

# Update the session if username has changed
if new_username and new_username != username:
request.session['username'] = new_username

messages.success(request, 'Your profile has been updated!')
return redirect('profile')
else:
messages.error(request, 'Please correct the errors below.')
else:
u_form = UserUpdateForm(instance=user)
p_form = ProfileUpdateForm(instance=profile)

server_info = Server.objects.all()[0]

context = {
'profile': profile,
'u_form': u_form,
'p_form': p_form,
'is_editing': is_editing,
"server_ip": server_info.ip,
"production": server_info.production,
}

return render(request, 'profile.html', context)
Loading

0 comments on commit 518d2d1

Please sign in to comment.