From e87fd6b5d9cdfe154a2468ddcaf8d70a9dcc6945 Mon Sep 17 00:00:00 2001 From: GediminasKr-BURGA Date: Thu, 20 Nov 2025 10:47:54 +0200 Subject: [PATCH 1/3] Update UI Logo for companies --- app/app.py | 61 ++++++++++- build.sh | 7 ++ fastbi-platform/403.html | 2 +- fastbi-platform/503_dc.html | 2 +- fastbi-platform/503_dq.html | 2 +- fastbi-platform/airbyte_iframe.html | 2 +- fastbi-platform/airflow_iframe.html | 2 +- fastbi-platform/cicd_workflow_iframe.html | 2 +- fastbi-platform/configuration.html | 2 +- fastbi-platform/data_catalog.html | 2 +- fastbi-platform/data_catalog_iframe.html | 2 +- fastbi-platform/data_quality.html | 2 +- fastbi-platform/data_quality_iframe.html | 2 +- fastbi-platform/datahub_iframe.html | 2 +- .../dbt-project-initialization.html | 2 +- fastbi-platform/dbt-project-management.html | 2 +- fastbi-platform/home.html | 2 +- fastbi-platform/ide_iframe.html | 2 +- fastbi-platform/ide_iframe_admin_panel.html | 2 +- fastbi-platform/index.html | 2 +- fastbi-platform/stats.html | 6 +- .../js/dbt-project-initialization.js | 13 ++- fastbi-platform/templates/js/home.js | 100 ++++++++++++++++++ 23 files changed, 200 insertions(+), 23 deletions(-) diff --git a/app/app.py b/app/app.py index 4fcbc02..1a1197d 100644 --- a/app/app.py +++ b/app/app.py @@ -14,7 +14,8 @@ import urllib.parse from urllib.parse import unquote from datetime import datetime -from flask import Flask, redirect, url_for, render_template, flash, session, request, jsonify +from flask import Flask, redirect, url_for, render_template, flash, session, request, jsonify, send_from_directory +from werkzeug.utils import secure_filename from flask_login import LoginManager, login_user, logout_user, current_user from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS @@ -1951,6 +1952,64 @@ def clear_cache(): app.logger.error(f"Error clearing cache: {str(e)}") return jsonify({"error": str(e)}), 500 +@app.context_processor +def inject_logo_path(): + instance_path = os.path.abspath('instance') + logo_path = os.path.join(instance_path, 'custom_logo.png') + if os.path.exists(logo_path): + # Use a cache buster timestamp to ensure updates are seen immediately + # In a real scenario, we might check file modification time, but time.time() is sufficient for this purpose if called once per request or handled in JS. + # For context processor, we'll just provide the base path and let JS handle cache busting or rely on browser cache behavior. + # Actually, to avoid flicker, we should point directly to the custom logo endpoint. + return dict(logo_path='/ui-config/custom_logo') + else: + return dict(logo_path='/images/logo_transparent.png') + +# Custom Logo Feature +@app.route('/ui-config/upload_logo', methods=['POST']) +def upload_logo(): + if not oidc.user_loggedin: + return jsonify({'error': 'Unauthorized'}), 401 + + user_info = oidc.user_getinfo(['groups']) + user_groups = set(user_info.get('groups', [])) + user_groups_lower = {group.lower() for group in user_groups} + + # Check for Admin role (case-insensitive) + if not any(admin_group in user_groups_lower for admin_group in ['admin', 'consoleadmin']): + return jsonify({'error': 'Forbidden: Admin access required'}), 403 + + if 'logo' not in request.files: + return jsonify({'error': 'No file part'}), 400 + + file = request.files['logo'] + if file.filename == '': + return jsonify({'error': 'No selected file'}), 400 + + if file: + filename = "custom_logo.png" # Force filename to ensure consistency + # Ensure instance directory exists + instance_path = os.path.abspath('instance') + if not os.path.exists(instance_path): + os.makedirs(instance_path) + + file.save(os.path.join(instance_path, filename)) + return jsonify({'message': 'Logo uploaded successfully'}), 200 + +@app.route('/ui-config/custom_logo') +def custom_logo(): + instance_path = os.path.abspath('instance') + return send_from_directory(instance_path, 'custom_logo.png') + +@app.route('/ui-config/logo_status') +def logo_status(): + instance_path = os.path.abspath('instance') + logo_path = os.path.join(instance_path, 'custom_logo.png') + if os.path.exists(logo_path): + return jsonify({'has_custom_logo': True, 'url': '/ui-config/custom_logo'}), 200 + else: + return jsonify({'has_custom_logo': False}), 200 + if __name__ == "__main__": # Determine the environment and configure Flask accordingly if app.config['FLASK_ENV'] == "production": diff --git a/build.sh b/build.sh index 8c426df..08570e9 100644 --- a/build.sh +++ b/build.sh @@ -18,3 +18,10 @@ docker buildx build . \ --tag europe-central2-docker.pkg.dev/fast-bi-common/bi-platform/tsb-fastbi-web-core:${dbt_init_version} \ --platform linux/amd64 \ --push + +docker buildx build . \ + --pull \ + --tag 4fastbi/data-platform-ui-core:dev-latest \ + --tag 4fastbi/data-platform-ui-core:dev-v0.1.3 \ + --platform linux/amd64 \ + --push \ No newline at end of file diff --git a/fastbi-platform/403.html b/fastbi-platform/403.html index 391d59d..1aa316f 100644 --- a/fastbi-platform/403.html +++ b/fastbi-platform/403.html @@ -44,7 +44,7 @@ z-index: 1000; } - +
  • Home
  • Data Project Initialization
  • diff --git a/fastbi-platform/503_dc.html b/fastbi-platform/503_dc.html index 21137cb..a8d815b 100644 --- a/fastbi-platform/503_dc.html +++ b/fastbi-platform/503_dc.html @@ -44,7 +44,7 @@ z-index: 1000; } - +
  • Home
  • Data Project Initialization
  • diff --git a/fastbi-platform/503_dq.html b/fastbi-platform/503_dq.html index d86daa8..45db3ac 100644 --- a/fastbi-platform/503_dq.html +++ b/fastbi-platform/503_dq.html @@ -44,7 +44,7 @@ z-index: 1000; } - +
  • Home
  • Data Project Initialization
  • diff --git a/fastbi-platform/airbyte_iframe.html b/fastbi-platform/airbyte_iframe.html index 25bc3f4..7838279 100644 --- a/fastbi-platform/airbyte_iframe.html +++ b/fastbi-platform/airbyte_iframe.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/airflow_iframe.html b/fastbi-platform/airflow_iframe.html index bca1ba7..0b18f03 100644 --- a/fastbi-platform/airflow_iframe.html +++ b/fastbi-platform/airflow_iframe.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/cicd_workflow_iframe.html b/fastbi-platform/cicd_workflow_iframe.html index f5e9d5f..40645cf 100644 --- a/fastbi-platform/cicd_workflow_iframe.html +++ b/fastbi-platform/cicd_workflow_iframe.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/configuration.html b/fastbi-platform/configuration.html index e5d8c34..b2d1fce 100644 --- a/fastbi-platform/configuration.html +++ b/fastbi-platform/configuration.html @@ -49,7 +49,7 @@ }
  • diff --git a/fastbi-platform/data_catalog.html b/fastbi-platform/data_catalog.html index 57faa34..db2e0a6 100644 --- a/fastbi-platform/data_catalog.html +++ b/fastbi-platform/data_catalog.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/data_catalog_iframe.html b/fastbi-platform/data_catalog_iframe.html index fe6a0ac..47fe43a 100644 --- a/fastbi-platform/data_catalog_iframe.html +++ b/fastbi-platform/data_catalog_iframe.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/data_quality.html b/fastbi-platform/data_quality.html index 5dbfc8b..a629175 100644 --- a/fastbi-platform/data_quality.html +++ b/fastbi-platform/data_quality.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/data_quality_iframe.html b/fastbi-platform/data_quality_iframe.html index a2e673c..aa89574 100644 --- a/fastbi-platform/data_quality_iframe.html +++ b/fastbi-platform/data_quality_iframe.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/datahub_iframe.html b/fastbi-platform/datahub_iframe.html index 650c626..2227230 100644 --- a/fastbi-platform/datahub_iframe.html +++ b/fastbi-platform/datahub_iframe.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/dbt-project-initialization.html b/fastbi-platform/dbt-project-initialization.html index 0cad480..d4db3ae 100644 --- a/fastbi-platform/dbt-project-initialization.html +++ b/fastbi-platform/dbt-project-initialization.html @@ -59,7 +59,7 @@ }
  • diff --git a/fastbi-platform/dbt-project-management.html b/fastbi-platform/dbt-project-management.html index 7b7764c..8518123 100644 --- a/fastbi-platform/dbt-project-management.html +++ b/fastbi-platform/dbt-project-management.html @@ -54,7 +54,7 @@ } diff --git a/fastbi-platform/home.html b/fastbi-platform/home.html index 4982a5e..b472f19 100644 --- a/fastbi-platform/home.html +++ b/fastbi-platform/home.html @@ -44,7 +44,7 @@ }
  • diff --git a/fastbi-platform/ide_iframe.html b/fastbi-platform/ide_iframe.html index 23a17e2..15e3212 100644 --- a/fastbi-platform/ide_iframe.html +++ b/fastbi-platform/ide_iframe.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/ide_iframe_admin_panel.html b/fastbi-platform/ide_iframe_admin_panel.html index 89486b4..990e6a9 100644 --- a/fastbi-platform/ide_iframe_admin_panel.html +++ b/fastbi-platform/ide_iframe_admin_panel.html @@ -46,7 +46,7 @@ }
  • diff --git a/fastbi-platform/index.html b/fastbi-platform/index.html index cfda216..6f190ee 100644 --- a/fastbi-platform/index.html +++ b/fastbi-platform/index.html @@ -31,7 +31,7 @@
  • +
  • Home diff --git a/fastbi-platform/templates/js/dbt-project-initialization.js b/fastbi-platform/templates/js/dbt-project-initialization.js index 610a951..ec51272 100644 --- a/fastbi-platform/templates/js/dbt-project-initialization.js +++ b/fastbi-platform/templates/js/dbt-project-initialization.js @@ -119,7 +119,9 @@ function switchTab(tabId, tabs, tabContents) { // Function to construct Git provider-specific URLs function constructGitProviderUrl(provider, baseUrl, branchName) { - switch (provider.toLowerCase()) { + const lowerProvider = provider ? provider.toLowerCase() : ''; + + switch (lowerProvider) { case 'gitlab': return `${baseUrl}/-/tree/${branchName}`; case 'github': @@ -129,6 +131,15 @@ function constructGitProviderUrl(provider, baseUrl, branchName) { case 'bitbucket': return `${baseUrl}/src/${branchName}`; default: + // Attempt to infer from baseUrl if provider is not explicitly matched + if (baseUrl.includes('github.com')) { + return `${baseUrl}/tree/${branchName}`; + } else if (baseUrl.includes('gitlab')) { + return `${baseUrl}/-/tree/${branchName}`; + } else if (baseUrl.includes('bitbucket')) { + return `${baseUrl}/src/${branchName}`; + } + console.warn(`Unknown Git provider: ${provider}. Falling back to GitLab URL pattern.`); return `${baseUrl}/-/tree/${branchName}`; } diff --git a/fastbi-platform/templates/js/home.js b/fastbi-platform/templates/js/home.js index fae399d..a2b4ef7 100644 --- a/fastbi-platform/templates/js/home.js +++ b/fastbi-platform/templates/js/home.js @@ -484,3 +484,103 @@ $(document).on('click', function(event) { $notificationPopup.fadeOut(); } }); + +/*-- Custom Logo Feature --*/ +$(document).ready(function() { + // Check for custom logo on load + $.ajax({ + url: '/ui-config/logo_status', + method: 'GET', + success: function(response) { + if (response.has_custom_logo) { + // Update logo src. Using a cache buster to ensure reload if changed recently + var newSrc = response.url + '?t=' + new Date().getTime(); + $('.rj-img-logo img').attr('src', newSrc); + // Also update the index page logo if present + $('.site-logo img').attr('src', newSrc); + } + } + }); + + // Long press logic + var pressTimer; + var longPressTriggered = false; + // Target the link wrapper to handle click prevention + var logoLink = $('.rj-img-logo a'); + + logoLink.on('mousedown touchstart', function(e) { + longPressTriggered = false; + pressTimer = window.setTimeout(function() { + longPressTriggered = true; + showLogoUploadModal(); + }, 15000); // 15 seconds + }).on('mouseup touchend mouseleave', function(e) { + clearTimeout(pressTimer); + }).on('click', function(e) { + if (longPressTriggered) { + e.preventDefault(); // Prevent navigation if it was a long press + longPressTriggered = false; + } + }); + + function showLogoUploadModal() { + // Remove existing modal if any + $('#logoUploadModal').remove(); + + var modalHtml = ` +
    +
    +

    Upload Company Logo

    +

    Select a new logo image (PNG/JPG). This will replace the default logo globally.

    + +
    + + +
    +
    +
    + `; + + $('body').append(modalHtml); + + $('#cancelLogoBtn').click(function() { + $('#logoUploadModal').remove(); + }); + + $('#uploadLogoBtn').click(function() { + var fileInput = $('#logoInput')[0]; + if (fileInput.files.length === 0) { + alert('Please select a file'); + return; + } + + var file = fileInput.files[0]; + var formData = new FormData(); + formData.append('logo', file); + + // Show loading state + $(this).text('Uploading...').prop('disabled', true); + + $.ajax({ + url: '/ui-config/upload_logo', + type: 'POST', + data: formData, + processData: false, + contentType: false, + success: function(response) { + alert('Logo uploaded successfully!'); + $('#logoUploadModal').remove(); + location.reload(); + }, + error: function(xhr, status, error) { + var errorMsg = 'Upload failed'; + if (xhr.responseJSON && xhr.responseJSON.error) { + errorMsg = xhr.responseJSON.error; + } + alert(errorMsg); + $('#uploadLogoBtn').text('Upload').prop('disabled', false); + } + }); + }); + } +}); From 49e7809fe4ded24e3f85847367793f8d5d6fd006 Mon Sep 17 00:00:00 2001 From: GediminasKr-BURGA Date: Thu, 20 Nov 2025 11:03:30 +0200 Subject: [PATCH 2/3] New Fixes: Custom Logo Functionality, Git Provider Issues fix for dbt initialization page --- .github/changelog/custom-logo-feature.md | 13 +++++++++++++ .github/pull_request_template.md | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 .github/changelog/custom-logo-feature.md create mode 100644 .github/pull_request_template.md diff --git a/.github/changelog/custom-logo-feature.md b/.github/changelog/custom-logo-feature.md new file mode 100644 index 0000000..d655db9 --- /dev/null +++ b/.github/changelog/custom-logo-feature.md @@ -0,0 +1,13 @@ +### Custom Logo Feature + +**Added:** +- **Backend**: New endpoints `/ui-config/upload_logo`, `/ui-config/custom_logo`, and `/ui-config/logo_status` to handle custom company logo upload and serving. +- **Backend**: Context processor `inject_logo_path` to dynamically serve the correct logo path (custom or default) to Jinja2 templates, preventing UI flicker. +- **Frontend**: Long-press (15 seconds) functionality on the main logo to trigger an upload modal for Administrators. +- **Frontend**: AJAX handling for logo file upload. +- **UI**: Updated all HTML templates to use the dynamic `{{ logo_path }}` variable instead of hardcoded image sources. + +**Changed:** +- Refactored logo image source in `home.html`, `index.html`, and all iframe templates to support dynamic loading. +- Ensured logo upload is restricted to users with 'Admin' or 'consoleAdmin' group membership. + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..08bdc74 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,22 @@ +## Description + + +## Related Issue + + +## Type of Change + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update + +## Checklist +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes + From 479fd44414224f7bfe87d101ec8bd67b00d4652e Mon Sep 17 00:00:00 2001 From: GediminasKr-BURGA Date: Thu, 20 Nov 2025 11:07:45 +0200 Subject: [PATCH 3/3] Update github meta --- .github/ISSUE_TEMPLATE/bug_report.md | 47 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 35 +++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..a09807e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,47 @@ +--- +name: 🐛 Bug Report +about: Create a report to help us improve Fast.BI +title: '[BUG] ' +labels: ['bug', 'needs-triage'] +assignees: '' +--- + +## 🐛 Bug Description + + +## 🔍 Steps to Reproduce +1. +2. +3. + +## ✅ Expected Behavior + + +## ❌ Actual Behavior + + +## 🖥️ Environment +- **OS**: [e.g., macOS 13.0, Ubuntu 22.04, Windows 11] +- **Python Version**: [e.g., 3.9.7, 3.10.0] +- **Fast.BI Version**: [e.g., 1.0.0, main branch] +- **Deployment Type**: [e.g., GCP, On-Premise, AWS, Azure] +- **CLI Version**: [e.g., 1.0.0] + +## 📋 Additional Information + + +### Logs + + +### Configuration + + +## 🔧 Possible Solution + + +## 📚 Related Documentation + + +--- + +**Note**: Please ensure you're using the latest version of Fast.BI and have checked existing issues for duplicates. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..7ce930b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,35 @@ +--- +name: 💡 Feature Request +about: Suggest an idea for Fast.BI +title: '[FEATURE] ' +labels: ['enhancement', 'needs-triage'] +assignees: '' +--- + +## 💡 Feature Description + + +## 🎯 Use Case + + +## 💭 Proposed Solution + + +## 🔄 Alternatives Considered + + +## 📊 Impact + + +## 🎨 Mockups/Examples + + +## 🔗 Related Features + + +## 📚 Additional Context + + +--- + +**Note**: Please search existing issues to ensure this feature hasn't already been requested.