From 638dbef091254bc945ef5317cd75a626a615e698 Mon Sep 17 00:00:00 2001 From: mominur-helios Date: Thu, 27 Feb 2025 19:16:57 +0600 Subject: [PATCH] Profiles: Added dynamic fields --- .env | 22 ++++++---- apps/__init__.py | 2 +- apps/authentication/models.py | 7 ++-- apps/home/routes.py | 78 +++++++++++++++++++++++++++-------- package.json | 9 ++-- postcss.config.js | 7 ++++ run.py | 1 - templates/pages/profile.html | 74 +++++++++++---------------------- 8 files changed, 111 insertions(+), 89 deletions(-) create mode 100644 postcss.config.js diff --git a/.env b/.env index c1157119..30c0dc08 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ # True for development, False for production -DEBUG=False +DEBUG=True # Flask ENV FLASK_APP=run.py @@ -8,14 +8,18 @@ FLASK_DEBUG=1 # If not provided, a random one is generated # SECRET_KEY= -# Used for CDN (in production) -# No Slash at the end -ASSETS_ROOT=/static/assets - -# If DB credentials (if NOT provided, or wrong values SQLite is used) +# If DEBUG=False (production mode) # DB_ENGINE=mysql -# DB_HOST=localhost # DB_NAME=appseed_db -# DB_USERNAME=appseed_db_usr -# DB_PASS=pass +# DB_HOST=localhost # DB_PORT=3306 +# DB_USERNAME=appseed_db_usr +# DB_PASS= + +# SOCIAL AUTH Github +# GITHUB_ID=YOUR_GITHUB_ID +# GITHUB_SECRET=YOUR_GITHUB_SECRET + +# SOCIAL AUTH Google +# GOOGLE_ID=YOUR_GOOGLE_ID +# GOOGLE_SECRET=YOUR_GOOGLE_SECRET diff --git a/apps/__init__.py b/apps/__init__.py index ad34467f..2a95beb8 100644 --- a/apps/__init__.py +++ b/apps/__init__.py @@ -4,7 +4,6 @@ """ import os - from flask import Flask from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy @@ -34,6 +33,7 @@ def create_app(config): STATIC_FOLDER = os.path.join(templates_dir,'static') print(' > TEMPLATES_FOLDER: ' + TEMPLATES_FOLDER) + print(' > STATIC_FOLDER: ' + STATIC_FOLDER) app = Flask(__name__, static_url_path=static_prefix, template_folder=TEMPLATES_FOLDER, static_folder=STATIC_FOLDER) diff --git a/apps/authentication/models.py b/apps/authentication/models.py index f878931a..443672b7 100644 --- a/apps/authentication/models.py +++ b/apps/authentication/models.py @@ -18,16 +18,15 @@ class Users(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True) - first_name = db.Column(db.String(100), nullable=True) - last_name = db.Column(db.String(100), nullable=True) - address = db.Column(db.String(100), nullable=True) - bio = db.Column(db.String(200), nullable=True) email = db.Column(db.String(64), unique=True) password = db.Column(db.LargeBinary) + bio = db.Column(db.Text(), nullable=True) oauth_github = db.Column(db.String(100), nullable=True) oauth_google = db.Column(db.String(100), nullable=True) + readonly_fields = ["id", "username", "email", "oauth_github", "oauth_google"] + def __init__(self, **kwargs): for property, value in kwargs.items(): # depending on whether value is an iterable or not, we must diff --git a/apps/home/routes.py b/apps/home/routes.py index faef3820..06e310db 100644 --- a/apps/home/routes.py +++ b/apps/home/routes.py @@ -2,12 +2,14 @@ """ Copyright (c) 2019 - present AppSeed.us """ - +import wtforms from apps.home import blueprint from flask import render_template, request, redirect, url_for from jinja2 import TemplateNotFound from flask_login import login_required, current_user from apps import db +from apps.authentication.models import Users +from flask_wtf import FlaskForm @blueprint.route('/') @blueprint.route('/index') @@ -31,28 +33,68 @@ def virtual_reality(): return render_template('pages/virtual-reality.html', segment='virtual_reality') +def getField(column): + if isinstance(column.type, db.Text): + return wtforms.TextAreaField(column.name.title()) + if isinstance(column.type, db.String): + return wtforms.StringField(column.name.title()) + if isinstance(column.type, db.Boolean): + return wtforms.BooleanField(column.name.title()) + if isinstance(column.type, db.Integer): + return wtforms.IntegerField(column.name.title()) + if isinstance(column.type, db.Float): + return wtforms.DecimalField(column.name.title()) + if isinstance(column.type, db.LargeBinary): + return wtforms.HiddenField(column.name.title()) + return wtforms.StringField(column.name.title()) + + @blueprint.route('/profile', methods=['GET', 'POST']) @login_required def profile(): - if request.method == 'POST': - first_name = request.form.get('first_name') - last_name = request.form.get('last_name') - address = request.form.get('address') - bio = request.form.get('bio') - - current_user.first_name = first_name - current_user.last_name = last_name - current_user.address = address - current_user.bio = bio - - try: - db.session.commit() - except Exception as e: - db.session.rollback() - return redirect(url_for('home_blueprint.profile')) + class ProfileForm(FlaskForm): + pass + + readonly_fields = Users.readonly_fields + full_width_fields = {"bio"} + + for column in Users.__table__.columns: + if column.name == "id": + continue - return render_template('pages/profile.html', segment='profile') + field_name = column.name + if field_name in full_width_fields: + continue + + field = getField(column) + setattr(ProfileForm, field_name, field) + + for field_name in full_width_fields: + if field_name in Users.__table__.columns: + column = Users.__table__.columns[field_name] + field = getField(column) + setattr(ProfileForm, field_name, field) + + form = ProfileForm(obj=current_user) + + if form.validate_on_submit(): + readonly_fields.append("password") + excluded_fields = readonly_fields + for field_name, field_value in form.data.items(): + if field_name not in excluded_fields: + setattr(current_user, field_name, field_value) + + db.session.commit() + return redirect(url_for('home_blueprint.profile')) + + context = { + 'segment': 'profile', + 'form': form, + 'readonly_fields': readonly_fields, + 'full_width_fields': full_width_fields, + } + return render_template('pages/profile.html', **context) # Helper - Extract current page name from request diff --git a/package.json b/package.json index 2ce8703b..a6e5fc1e 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "vite build --watch --mode development", "build": "vite build --mode production && npm run minify-css", - "minify-css": "cssnano static/assets/css/*.css --dir static/assets/css --no-map --suffix .min" + "minify-css": "postcss static/assets/css/*.css --dir static/assets/css --no-map --ext .min.css" }, "keywords": [], "author": "", @@ -14,10 +14,9 @@ "devDependencies": { "autoprefixer": "^10.4.20", "cssnano": "^7.0.6", + "postcss": "^8.5.3", + "postcss-cli": "^11.0.0", "sass": "^1.85.1", "vite": "^6.2.0" - }, - "dependencies": { - "fast-glob": "^3.3.3" } -} +} \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..ffb2269a --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +module.exports = { + plugins: [ + require('cssnano')({ + preset: 'default', + }), + ], +}; diff --git a/run.py b/run.py index bbc6dac2..1ff15abc 100644 --- a/run.py +++ b/run.py @@ -53,7 +53,6 @@ app.logger.info('DEBUG = ' + str(DEBUG) ) app.logger.info('Page Compression = ' + 'FALSE' if DEBUG else 'TRUE' ) app.logger.info('DBMS = ' + app_config.SQLALCHEMY_DATABASE_URI) - app.logger.info('ASSETS_ROOT = ' + app_config.ASSETS_ROOT ) if __name__ == "__main__": app.run() diff --git a/templates/pages/profile.html b/templates/pages/profile.html index 825e79e1..381fc3f9 100644 --- a/templates/pages/profile.html +++ b/templates/pages/profile.html @@ -15,56 +15,28 @@
Edit Info
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - - This is your shipments address -
-
-
-
- - - We'll show this on your profile. -
-
-
-
- -
-
-
+
+ {{ form.hidden_tag() }} + + {% for field in form %} + {% if field.type in ['CSRFTokenField', 'HiddenField'] %} + {{ field() }} + {% else %} +
+
+ + {{ field(class_="form-control", readonly=True if field.name in readonly_fields else False) }} +
+
+ {% endif %} + {% endfor %} + +
+
+ +
+
+
@@ -73,7 +45,7 @@
Edit Info
- profile image