diff --git a/.env b/.env new file mode 100644 index 0000000..99e1fbb --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret +GITHUB_CLIENT_ID=Ov23liuhXJZtRpcdqFzf +GITHUB_CLIENT_SECRET=a3c5db76c59eae2dfcc84c2c59019e019210c47a \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..781b277 --- /dev/null +++ b/.gitignore @@ -0,0 +1,123 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually ignored by MicroPython Checkignore tool +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff (if used later, good to have) +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff +instance/ # Important: Ignore instance folder (contains DB, config secrets) +.webassets-cache + +# Scrapy stuff (if used later) +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# PEP 582; used by PDM, Flit, and Rye +__pypackages__/ + +# Celery stuff (if used later) +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env # Important: Ignore environment file (contains secrets) +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# PyDev project settings +.pydevproject + +# VSCode Pylance cache +.pylance_cache/ + +# Editor directories and files +.idea/ +.vscode/ +*.sublime-project +*.sublime-workspace +*.DS_Store +Thumbs.db diff --git a/Dockerfile b/Dockerfile index 0bf687d..79f6611 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,6 @@ RUN pip install --no-cache-dir -r requirements.txt COPY app/ app/ COPY static/ static/ COPY instance/ instance/ -copy uploads app/uploads/ COPY config.py . COPY .env . EXPOSE 5000 diff --git a/app/routes.py b/app/routes.py index 2980bfd..5a1d5da 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,5 +1,5 @@ #app/routes.py -from flask import Blueprint, render_template, redirect, url_for, flash, request +from flask import Blueprint, render_template, redirect, url_for, flash, request, send_from_directory, abort # Added send_from_directory and abort from flask_login import login_required, current_user from app import db from app.models import SmartApp, ApplicationType, Category, OSSupport, FHIRSupport, Speciality, PricingLicense, DesignedFor, EHRSupport @@ -16,12 +16,25 @@ logger = logging.getLogger(__name__) gallery_bp = Blueprint('gallery', __name__) +# Absolute path to the upload folder inside the container UPLOAD_FOLDER = '/app/uploads/' ALLOWED_EXTENSIONS = {'jpg', 'png'} def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS +# New route to serve uploaded files +@gallery_bp.route('/uploads/') +def uploaded_file(filename): + # Use the absolute path to the directory inside the container + absolute_upload_folder = os.path.abspath(UPLOAD_FOLDER) + logger.debug(f"Attempting to serve file: {filename} from {absolute_upload_folder}") + try: + return send_from_directory(absolute_upload_folder, filename) + except FileNotFoundError: + logger.error(f"File not found: {os.path.join(absolute_upload_folder, filename)}") + abort(404) + @gallery_bp.route('/') def index(): return redirect(url_for('gallery.gallery')) @@ -92,22 +105,22 @@ def app_detail(app_id): if app.categories: category_ids = [int(cid) for cid in app.categories.split(',') if cid] app_categories = Category.query.filter(Category.id.in_(category_ids)).all() - + app_specialties = [] if app.specialties: speciality_ids = [int(sid) for sid in app.specialties.split(',') if sid] app_specialties = Speciality.query.filter(Speciality.id.in_(speciality_ids)).all() - + app_os_supports = [] if app.os_support: os_ids = [int(oid) for oid in app.os_support.split(',') if oid] app_os_supports = OSSupport.query.filter(OSSupport.id.in_(os_ids)).all() - + app_ehr_supports = [] if app.ehr_support: ehr_ids = [int(eid) for eid in app.ehr_support.split(',') if eid] app_ehr_supports = EHRSupport.query.filter(EHRSupport.id.in_(ehr_ids)).all() - + return render_template( 'app_detail.html', app=app, @@ -134,6 +147,7 @@ def register(): # Ensure upload folder exists try: + # Use the absolute path inside the container os.makedirs(UPLOAD_FOLDER, exist_ok=True) logger.debug(f"Ensured {UPLOAD_FOLDER} exists") except Exception as e: @@ -189,6 +203,7 @@ def register(): file = form.logo_upload.data if allowed_file(file.filename): filename = secure_filename(f"{uuid.uuid4()}_{file.filename}") + # Use absolute path for saving save_path = os.path.join(UPLOAD_FOLDER, filename) logger.debug(f"Attempting to save logo to {save_path}") try: @@ -199,7 +214,8 @@ def register(): logger.error(f"Failed to save logo to {save_path}") flash('Failed to save logo.', 'danger') return render_template('register.html', form=form) - logo_url = f"/app/uploads/{filename}" + # Store URL path for web access + logo_url = f"/uploads/{filename}" # CHANGED logger.debug(f"Set logo_url to {logo_url}") except Exception as e: logger.error(f"Error saving logo to {save_path}: {e}") @@ -209,11 +225,13 @@ def register(): # Handle app images app_images = [] if form.app_image_urls.data: - app_images.extend([url.strip() for url in form.app_image_urls.data.splitlines() if url.strip()]) - if form.app_image_uploads.data: + # Keep existing URLs if they are valid URLs + app_images.extend([url.strip() for url in form.app_image_urls.data.splitlines() if url.strip().startswith(('http://', 'https://'))]) + if form.app_image_uploads.data: # Check if a file was uploaded file = form.app_image_uploads.data - if allowed_file(file.filename): + if file and allowed_file(file.filename): # Check if file object exists and is allowed filename = secure_filename(f"{uuid.uuid4()}_{file.filename}") + # Use absolute path for saving save_path = os.path.join(UPLOAD_FOLDER, filename) logger.debug(f"Attempting to save app image to {save_path}") try: @@ -224,7 +242,8 @@ def register(): logger.error(f"Failed to save app image to {save_path}") flash('Failed to save app image.', 'danger') return render_template('register.html', form=form) - app_images.append(f"/app/uploads/{filename}") + # Store URL path for web access + app_images.append(f"/uploads/{filename}") # CHANGED except Exception as e: logger.error(f"Error saving app image to {save_path}: {e}") flash('Error saving app image.', 'danger') @@ -235,7 +254,7 @@ def register(): description=form.description.data, developer=form.developer.data, contact_email=form.contact_email.data, - logo_url=logo_url or None, + logo_url=logo_url or None, # Use the potentially updated logo_url launch_url=form.launch_url.data, client_id=form.client_id.data, scopes=scopes, @@ -247,7 +266,7 @@ def register(): specialties=','.join(map(str, form.specialties.data)) if form.specialties.data else None, licensing_pricing_id=form.licensing_pricing.data, os_support=','.join(map(str, form.os_support.data)) if form.os_support.data else None, - app_images=','.join(app_images) if app_images else None, + app_images=','.join(app_images) if app_images else None, # Use the potentially updated app_images list ehr_support=','.join(map(str, form.ehr_support.data)) if form.ehr_support.data else None, user_id=current_user.id ) @@ -271,8 +290,9 @@ def edit_app(app_id): if app.user_id != current_user.id: flash('You can only edit your own apps.', 'danger') return redirect(url_for('gallery.app_detail', app_id=app_id)) - + form = SmartAppForm(obj=app) + # Pre-populate multi-select fields correctly on GET request if not form.is_submitted(): if app.categories: form.categories.data = [int(cid) for cid in app.categories.split(',') if cid] @@ -282,8 +302,14 @@ def edit_app(app_id): form.os_support.data = [int(oid) for oid in app.os_support.split(',') if oid] if app.ehr_support: form.ehr_support.data = [int(eid) for eid in app.ehr_support.split(',') if eid] + # Pre-populate the image URL textarea if app.app_images: - form.app_image_urls.data = '\n'.join(app.app_images.split(',') if app.app_images else []) + # Filter out internal paths if mixed with URLs, show only URLs or internal paths formatted for web + current_images = [img for img in app.app_images.split(',') if img.startswith(('http://', 'https://', '/uploads/'))] + form.app_image_urls.data = '\n'.join(current_images) + else: + form.app_image_urls.data = '' # Ensure it's empty if no images + if form.validate_on_submit(): scopes = form.scopes.data @@ -298,6 +324,7 @@ def edit_app(app_id): # Ensure upload folder exists try: + # Use the absolute path inside the container os.makedirs(UPLOAD_FOLDER, exist_ok=True) logger.debug(f"Ensured {UPLOAD_FOLDER} exists") except Exception as e: @@ -305,6 +332,7 @@ def edit_app(app_id): flash('Error creating upload directory.', 'danger') return render_template('edit_app.html', form=form, app=app) + # Handle new filter options (same as register) if form.application_type_new.data: app_type = ApplicationType(name=form.application_type_new.data) db.session.add(app_type) @@ -314,11 +342,14 @@ def edit_app(app_id): category = Category(name=form.categories_new.data) db.session.add(category) db.session.commit() + # Ensure data is list before append if it was None initially + if form.categories.data is None: form.categories.data = [] form.categories.data.append(category.id) if form.os_support_new.data: os_support = OSSupport(name=form.os_support_new.data) db.session.add(os_support) db.session.commit() + if form.os_support.data is None: form.os_support.data = [] form.os_support.data.append(os_support.id) if form.fhir_compatibility_new.data: fhir = FHIRSupport(name=form.fhir_compatibility_new.data) @@ -329,6 +360,7 @@ def edit_app(app_id): speciality = Speciality(name=form.specialties_new.data) db.session.add(speciality) db.session.commit() + if form.specialties.data is None: form.specialties.data = [] form.specialties.data.append(speciality.id) if form.licensing_pricing_new.data: pricing = PricingLicense(name=form.licensing_pricing_new.data) @@ -344,60 +376,70 @@ def edit_app(app_id): ehr = EHRSupport(name=form.ehr_support_new.data) db.session.add(ehr) db.session.commit() + if form.ehr_support.data is None: form.ehr_support.data = [] form.ehr_support.data.append(ehr.id) - logo_url = form.logo_url.data - if form.logo_upload.data: + + # Handle logo update + logo_url = form.logo_url.data # Get URL from form first + if form.logo_upload.data: # If new logo uploaded, it takes precedence file = form.logo_upload.data if allowed_file(file.filename): filename = secure_filename(f"{uuid.uuid4()}_{file.filename}") + # Use absolute path for saving save_path = os.path.join(UPLOAD_FOLDER, filename) - logger.debug(f"Attempting to save logo to {save_path}") + logger.debug(f"Attempting to save updated logo to {save_path}") try: file.save(save_path) if os.path.exists(save_path): - logger.debug(f"Successfully saved logo to {save_path}") + logger.debug(f"Successfully saved updated logo to {save_path}") else: - logger.error(f"Failed to save logo to {save_path}") - flash('Failed to save logo.', 'danger') - return render_template('edit_app.html', form=form, app=app) - logo_url = f"/app/uploads/{filename}" + logger.error(f"Failed to save updated logo to {save_path}") + flash('Failed to save logo.', 'danger') + return render_template('edit_app.html', form=form, app=app) + # Store URL path for web access + logo_url = f"/uploads/{filename}" # CHANGED logger.debug(f"Set logo_url to {logo_url}") except Exception as e: - logger.error(f"Error saving logo to {save_path}: {e}") + logger.error(f"Error saving updated logo to {save_path}: {e}") flash('Error saving logo.', 'danger') return render_template('edit_app.html', form=form, app=app) - elif not logo_url: - logo_url = app.logo_url + elif not logo_url: # If no new upload AND URL field is empty, keep existing + logo_url = app.logo_url # Keep the old one only if the field is empty - app_images = [] - if form.app_image_urls.data: - app_images.extend([url.strip() for url in form.app_image_urls.data.splitlines() if url.strip()]) - if form.app_image_uploads.data: + # Handle app images update + # Start with URLs provided in the text area + app_images = [url.strip() for url in form.app_image_urls.data.splitlines() if url.strip()] + + if form.app_image_uploads.data: # Check if a file was uploaded file = form.app_image_uploads.data - if allowed_file(file.filename): + if file and allowed_file(file.filename): # Check if file object exists and is allowed filename = secure_filename(f"{uuid.uuid4()}_{file.filename}") + # Use absolute path for saving save_path = os.path.join(UPLOAD_FOLDER, filename) - logger.debug(f"Attempting to save app image to {save_path}") + logger.debug(f"Attempting to save updated app image to {save_path}") try: file.save(save_path) if os.path.exists(save_path): - logger.debug(f"Successfully saved app image to {save_path}") + logger.debug(f"Successfully saved updated app image to {save_path}") else: - logger.error(f"Failed to save app image to {save_path}") - flash('Failed to save app image.', 'danger') - return render_template('edit_app.html', form=form, app=app) - app_images.append(f"/app/uploads/{filename}") + logger.error(f"Failed to save updated app image to {save_path}") + flash('Failed to save app image.', 'danger') + return render_template('edit_app.html', form=form, app=app) + # Add the new image's URL path + app_images.append(f"/uploads/{filename}") # CHANGED except Exception as e: - logger.error(f"Error saving app image to {save_path}: {e}") + logger.error(f"Error saving updated app image to {save_path}: {e}") flash('Error saving app image.', 'danger') return render_template('edit_app.html', form=form, app=app) + + # Update app object app.name = form.name.data app.description = form.description.data app.developer = form.developer.data app.contact_email = form.contact_email.data - app.logo_url = logo_url or None + app.logo_url = logo_url # Use the final determined logo_url app.launch_url = form.launch_url.data app.client_id = form.client_id.data app.scopes = scopes @@ -409,7 +451,7 @@ def edit_app(app_id): app.specialties = ','.join(map(str, form.specialties.data)) if form.specialties.data else None app.licensing_pricing_id = form.licensing_pricing.data app.os_support = ','.join(map(str, form.os_support.data)) if form.os_support.data else None - app.app_images = ','.join(app_images) if app_images else None + app.app_images = ','.join(app_images) if app_images else None # Use the final list of image URLs/paths app.ehr_support = ','.join(map(str, form.ehr_support.data)) if form.ehr_support.data else None try: db.session.commit() @@ -421,8 +463,11 @@ def edit_app(app_id): return render_template('edit_app.html', form=form, app=app) flash('App updated successfully!', 'success') return redirect(url_for('gallery.app_detail', app_id=app_id)) + + # Render the edit form on GET or if validation fails return render_template('edit_app.html', form=form, app=app) + @gallery_bp.route('/gallery/delete/', methods=['POST']) @login_required def delete_app(app_id): @@ -441,6 +486,7 @@ def my_listings(): apps = SmartApp.query.filter_by(user_id=current_user.id).all() return render_template('my_listings.html', apps=apps) +# Keep the test route as is, it uses placeholder URLs directly @gallery_bp.route('/test/add') @login_required def add_test_app(): diff --git a/app/templates/New folder/app_detail.html.txt b/app/templates/New folder/app_detail.html.txt new file mode 100644 index 0000000..5fa52e1 --- /dev/null +++ b/app/templates/New folder/app_detail.html.txt @@ -0,0 +1,58 @@ + + + +{% extends "base.html" %} + +{% block title %}{{ app.name }}{% endblock %} + +{% block content %} +
+

{{ app.name }}

+
+
+ {% if app.logo_url %} + {{ app.name }} logo + {% else %} + No Logo + {% endif %} +

Description: {{ app.description }}

+

Developer: {{ app.developer }}

+

Contact Email: {{ app.contact_email }}

+

Launch URL: {{ app.launch_url }}

+

Client ID: {{ app.client_id }}

+

Scopes: {{ app.scopes }}

+ {% if app.website %} +

Website: {{ app.website }}

+ {% endif %} + {% if app_categories %} +

Categories: {{ app_categories | map(attribute='name') | join(', ') }}

+ {% endif %} + {% if app_specialties %} +

Specialties: {{ app_specialties | map(attribute='name') | join(', ') }}

+ {% endif %} + {% if app_os_supports %} +

OS Support: {{ app_os_supports | map(attribute='name') | join(', ') }}

+ {% endif %} + {% if app_ehr_supports %} +

EHR Support: {{ app_ehr_supports | map(attribute='name') | join(', ') }}

+ {% endif %} + {% if app.app_images %} +
Additional Images:
+
+ {% for img_url in app.app_images.split(',') %} +
+ App Image +
+ {% endfor %} +
+ {% endif %} + {% if current_user.is_authenticated and current_user.id == app.user_id %} + Edit App +
+ +
+ {% endif %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/New folder/base.html.txt b/app/templates/New folder/base.html.txt new file mode 100644 index 0000000..b082a18 --- /dev/null +++ b/app/templates/New folder/base.html.txt @@ -0,0 +1,183 @@ + + + + + + + SMARTFLARE - Smart App Gallery - {% block title %}{% endblock %} + + + + + + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + {% block content %}{% endblock %} +
+ + + + \ No newline at end of file diff --git a/app/templates/New folder/edit_app.html.txt b/app/templates/New folder/edit_app.html.txt new file mode 100644 index 0000000..042858f --- /dev/null +++ b/app/templates/New folder/edit_app.html.txt @@ -0,0 +1,214 @@ + + +{% extends "base.html" %} + +{% block content %} +

Edit App: {{ app.name }}

+
+ {{ form.hidden_tag() }} +
+ {{ form.name.label(class="form-label") }} + {{ form.name(class="form-control") }} + {% for error in form.name.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.description.label(class="form-label") }} + {{ form.description(class="form-control", rows=4) }} + {% for error in form.description.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.developer.label(class="form-label") }} + {{ form.developer(class="form-control") }} + {% for error in form.developer.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.contact_email.label(class="form-label") }} + {{ form.contact_email(class="form-control") }} + {% for error in form.contact_email.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.logo_url.label(class="form-label") }} + {{ form.logo_url(class="form-control") }} + Enter a URL or upload a new logo below. Leave blank to keep current logo. + {% for error in form.logo_url.errors %} + {{ error }} + {% endfor %} + {% if app.logo_url %} +

Current Logo:

+ Current Logo + {% endif %} +
+
+ {{ form.logo_upload.label(class="form-label") }} + {{ form.logo_upload(class="form-control") }} + {% for error in form.logo_upload.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.launch_url.label(class="form-label") }} + {{ form.launch_url(class="form-control") }} + {% for error in form.launch_url.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.client_id.label(class="form-label") }} + {{ form.client_id(class="form-control") }} + {% for error in form.client_id.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.scopes.label(class="form-label") }} + {{ form.scopes(class="form-control", rows=3) }} + {% for error in form.scopes.errors %} + {{ error }} + {% endfor %} + {% if app.scopes and app.scopes.strip() %} +

Current Scopes: + {% for scope in app.scopes.split(',') %} + {% if scope.strip() %} + {{ scope.strip() }} + {% endif %} + {% endfor %} +

+ {% else %} +

No scopes defined.

+ {% endif %} +
+
+ {{ form.website.label(class="form-label") }} + {{ form.website(class="form-control") }} + {% for error in form.website.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.designed_for.label(class="form-label") }} + {{ form.designed_for(class="form-control") }} + {% for error in form.designed_for.errors %} + {{ error }} + {% endfor %} + {{ form.designed_for_new.label(class="form-label mt-2") }} + {{ form.designed_for_new(class="form-control") }} + {% for error in form.designed_for_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.application_type.label(class="form-label") }} + {{ form.application_type(class="form-control") }} + {% for error in form.application_type.errors %} + {{ error }} + {% endfor %} + {{ form.application_type_new.label(class="form-label mt-2") }} + {{ form.application_type_new(class="form-control") }} + {% for error in form.application_type_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.fhir_compatibility.label(class="form-label") }} + {{ form.fhir_compatibility(class="form-control") }} + {% for error in form.fhir_compatibility.errors %} + {{ error }} + {% endfor %} + {{ form.fhir_compatibility_new.label(class="form-label mt-2") }} + {{ form.fhir_compatibility_new(class="form-control") }} + {% for error in form.fhir_compatibility_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.categories.label(class="form-label") }} + {{ form.categories(class="form-control") }} + {% for error in form.categories.errors %} + {{ error }} + {% endfor %} + {{ form.categories_new.label(class="form-label mt-2") }} + {{ form.categories_new(class="form-control") }} + {% for error in form.categories_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.specialties.label(class="form-label") }} + {{ form.specialties(class="form-control") }} + {% for error in form.specialties.errors %} + {{ error }} + {% endfor %} + {{ form.specialties_new.label(class="form-label mt-2") }} + {{ form.specialties_new(class="form-control") }} + {% for error in form.specialties_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.licensing_pricing.label(class="form-label") }} + {{ form.licensing_pricing(class="form-control") }} + {% for error in form.licensing_pricing.errors %} + {{ error }} + {% endfor %} + {{ form.licensing_pricing_new.label(class="form-label mt-2") }} + {{ form.licensing_pricing_new(class="form-control") }} + {% for error in form.licensing_pricing_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.os_support.label(class="form-label") }} + {{ form.os_support(class="form-control") }} + {% for error in form.os_support.errors %} + {{ error }} + {% endfor %} + {{ form.os_support_new.label(class="form-label mt-2") }} + {{ form.os_support_new(class="form-control") }} + {% for error in form.os_support_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.app_image_urls.label(class="form-label") }} + {{ form.app_image_urls(class="form-control", rows=3) }} + {% for error in form.app_image_urls.errors %} + {{ error }} + {% endfor %} + {% if app.app_images %} +

Current Images:

+ {% for image in app.app_images.split(',') %} + App Image + {% endfor %} + {% endif %} +
+
+ {{ form.app_image_uploads.label(class="form-label") }} + {{ form.app_image_uploads(class="form-control") }} + {% for error in form.app_image_uploads.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.ehr_support.label(class="form-label") }} + {{ form.ehr_support(class="form-control") }} + {% for error in form.ehr_support.errors %} + {{ error }} + {% endfor %} + {{ form.ehr_support_new.label(class="form-label mt-2") }} + {{ form.ehr_support_new(class="form-control") }} + {% for error in form.ehr_support_new.errors %} + {{ error }} + {% endfor %} +
+ {{ form.submit(class="btn btn-primary") }} + Cancel +
+{% endblock %} \ No newline at end of file diff --git a/app/templates/New folder/gallery.html.txt b/app/templates/New folder/gallery.html.txt new file mode 100644 index 0000000..fc7aa68 --- /dev/null +++ b/app/templates/New folder/gallery.html.txt @@ -0,0 +1,247 @@ + + +{% extends "base.html" %} + +{% block title %}Gallery{% endblock %} + +{% block content %} +
+ + + + +
+

Explore SMART on FHIR apps. Filter above to find the perfect app.

+ {% if apps %} +
+ {% for app in apps %} +
+
+ {% if app.logo_url %} + {{ app.name }} logo + {% else %} + No Logo + {% endif %} +
+
{{ app.name }}
+

{{ app.description | truncate(100) }}

+

By {{ app.developer }}

+ View Details +
+
+
+ {% endfor %} +
+ {% else %} +

No apps match your filters. Try adjusting the filters above.

+ {% endif %} +
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/app/templates/New folder/login.html.txt b/app/templates/New folder/login.html.txt new file mode 100644 index 0000000..f069188 --- /dev/null +++ b/app/templates/New folder/login.html.txt @@ -0,0 +1,32 @@ + + + +{% extends "base.html" %} + +{% block content %} +

Login

+
+ {{ form.hidden_tag() }} +
+ {{ form.email.label(class="form-label") }} + {{ form.email(class="form-control") }} + {% for error in form.email.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.password.label(class="form-label") }} + {{ form.password(class="form-control") }} + {% for error in form.password.errors %} + {{ error }} + {% endfor %} +
+ {{ form.submit(class="btn btn-primary") }} +
+
+

Or login with:

+ Google + GitHub +
+

Don't have an account? Register

+{% endblock %} \ No newline at end of file diff --git a/app/templates/New folder/my_listings.html.txt b/app/templates/New folder/my_listings.html.txt new file mode 100644 index 0000000..decfe2e --- /dev/null +++ b/app/templates/New folder/my_listings.html.txt @@ -0,0 +1,33 @@ + + +{% extends "base.html" %} + +{% block content %} +

My Listings

+ {% if apps %} +
+ {% for app in apps %} +
+
+ {% if app.logo_url %} + {{ app.name }} logo + {% endif %} +
+
{{ app.name }}
+

{{ app.description | truncate(100) }}

+

By {{ app.developer }}

+ View Details + Edit +
+ +
+
+
+
+ {% endfor %} +
+ {% else %} +

You haven't registered any apps yet.

+ Register an App + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/app/templates/New folder/register.html.txt b/app/templates/New folder/register.html.txt new file mode 100644 index 0000000..367b44d --- /dev/null +++ b/app/templates/New folder/register.html.txt @@ -0,0 +1,193 @@ + + +{% extends "base.html" %} + +{% block content %} +

Register New App

+
+ {{ form.hidden_tag() }} +
+ {{ form.name.label(class="form-label") }} + {{ form.name(class="form-control") }} + {% for error in form.name.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.description.label(class="form-label") }} + {{ form.description(class="form-control", rows=4) }} + {% for error in form.description.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.developer.label(class="form-label") }} + {{ form.developer(class="form-control") }} + {% for error in form.developer.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.contact_email.label(class="form-label") }} + {{ form.contact_email(class="form-control") }} + {% for error in form.contact_email.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.logo_url.label(class="form-label") }} + {{ form.logo_url(class="form-control") }} + Enter a URL or upload a logo below. + {% for error in form.logo_url.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.logo_upload.label(class="form-label") }} + {{ form.logo_upload(class="form-control") }} + {% for error in form.logo_upload.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.launch_url.label(class="form-label") }} + {{ form.launch_url(class="form-control") }} + {% for error in form.launch_url.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.client_id.label(class="form-label") }} + {{ form.client_id(class="form-control") }} + {% for error in form.client_id.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.scopes.label(class="form-label") }} + {{ form.scopes(class="form-control", rows=3) }} + {% for error in form.scopes.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.website.label(class="form-label") }} + {{ form.website(class="form-control") }} + {% for error in form.website.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.designed_for.label(class="form-label") }} + {{ form.designed_for(class="form-control") }} + {% for error in form.designed_for.errors %} + {{ error }} + {% endfor %} + {{ form.designed_for_new.label(class="form-label mt-2") }} + {{ form.designed_for_new(class="form-control") }} + {% for error in form.designed_for_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.application_type.label(class="form-label") }} + {{ form.application_type(class="form-control") }} + {% for error in form.application_type.errors %} + {{ error }} + {% endfor %} + {{ form.application_type_new.label(class="form-label mt-2") }} + {{ form.application_type_new(class="form-control") }} + {% for error in form.application_type_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.fhir_compatibility.label(class="form-label") }} + {{ form.fhir_compatibility(class="form-control") }} + {% for error in form.fhir_compatibility.errors %} + {{ error }} + {% endfor %} + {{ form.fhir_compatibility_new.label(class="form-label mt-2") }} + {{ form.fhir_compatibility_new(class="form-control") }} + {% for error in form.fhir_compatibility_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.categories.label(class="form-label") }} + {{ 을 form.categories(class="form-control") }} + {% for error in form.categories.errors %} + {{ error }} + {% endfor %} + {{ form.categories_new.label(class="form-label mt-2") }} + {{ form.categories_new(class="form-control") }} + {% for error in form.categories_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.specialties.label(class="form-label") }} + {{ form.specialties(class="form-control") }} + {% for error in form.specialties.errors %} + {{ error }} + {% endfor %} + {{ form.specialties_new.label(class="form-label mt-2") }} + {{ form.specialties_new(class="form-control") }} + {% for error in form.specialties_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.licensing_pricing.label(class="form-label") }} + {{ form.licensing_pricing(class="form-control") }} + {% for error in form.licensing_pricing.errors %} + {{ error }} + {% endfor %} + {{ form.licensing_pricing_new.label(class="form-label mt-2") }} + {{ form.licensing_pricing_new(class="form-control") }} + {% for error in form.licensing_pricing_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.os_support.label(class="form-label") }} + {{ form.os_support(class="form-control") }} + {% for error in form.os_support.errors %} + {{ error }} + {% endfor %} + {{ form.os_support_new.label(class="form-label mt-2") }} + {{ form.os_support_new(class="form-control") }} + {% for error in form.os_support_new.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.app_image_urls.label(class="form-label") }} + {{ form.app_image_urls(class="form-control", rows=3) }} + {% for error in form.app_image_urls.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.app_image_uploads.label(class="form-label") }} + {{ form.app_image_uploads(class="form-control") }} + {% for error in form.app_image_uploads.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.ehr_support.label(class="form-label") }} + {{ form.ehr_support(class="form-control") }} + {% for error in form.ehr_support.errors %} + {{ error }} + {% endfor %} + {{ form.ehr_support_new.label(class="form-label mt-2") }} + {{ form.ehr_support_new(class="form-control") }} + {% for error in form.ehr_support_new.errors %} + {{ error }} + {% endfor %} +
+ {{ form.submit(class="btn btn-primary") }} + Cancel +
+{% endblock %} \ No newline at end of file diff --git a/app/templates/New folder/register_user.html.txt b/app/templates/New folder/register_user.html.txt new file mode 100644 index 0000000..b807c98 --- /dev/null +++ b/app/templates/New folder/register_user.html.txt @@ -0,0 +1,40 @@ + + +{% extends "base.html" %} + +{% block content %} +

Register

+
+ {{ form.hidden_tag() }} +
+ {{ form.username.label(class="form-label") }} + {{ form.username(class="form-control") }} + {% for error in form.username.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.email.label(class="form-label") }} + {{ form.email(class="form-control") }} + {% for error in form.email.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.password.label(class="form-label") }} + {{ form.password(class="form-control") }} + {% for error in form.password.errors %} + {{ error }} + {% endfor %} +
+
+ {{ form.confirm_password.label(class="form-label") }} + {{ form.confirm_password(class="form-control") }} + {% for error in form.confirm_password.errors %} + {{ error }} + {% endfor %} +
+ {{ form.submit(class="btn btn-primary") }} +
+

Already have an account? Login

+{% endblock %} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index d57faf5..f92dd3e 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -1,13 +1,11 @@ - - - - + SMARTFLARE - Smart App Gallery - {% block title %}{% endblock %} @@ -105,22 +184,21 @@
SMART App Gallery +
{{ form.categories.label(class="form-label") }} - {{ 을 form.categories(class="form-control") }} + {{ form.categories(class="form-control") }} {% for error in form.categories.errors %} {{ error }} {% endfor %} diff --git a/instance/smart_app_gallery.db b/instance/smart_app_gallery.db index fcddc26..f425500 100644 Binary files a/instance/smart_app_gallery.db and b/instance/smart_app_gallery.db differ diff --git a/uploads/da0e7aa8-506e-41ef-9b9a-4e260345b8d7_A1_test.png b/uploads/da0e7aa8-506e-41ef-9b9a-4e260345b8d7_A1_test.png new file mode 100644 index 0000000..5e31e83 Binary files /dev/null and b/uploads/da0e7aa8-506e-41ef-9b9a-4e260345b8d7_A1_test.png differ