From 0c310736c1490a85e3f069770bea10a4ea2f1ff4 Mon Sep 17 00:00:00 2001 From: Dries Peeters Date: Mon, 29 Dec 2025 15:51:59 +0100 Subject: [PATCH 1/6] Remove legacy UI enable/disable settings system Remove both system-wide and per-user UI feature enable/disable settings in favor of the centralized ModuleRegistry system for module management. Changes: - Remove ui_allow_* columns from Settings model and database (migration 093) - Remove ui_show_* preference assignments from user settings route - Remove UI Customization section from user settings template - Remove UI Features section from admin settings template - Update admin modules template to use ModuleRegistry instead of settings flags - Remove settings_flag and user_flag attributes from ModuleDefinition - Update ModuleRegistry.is_enabled() to only check dependencies and default_enabled - Update dashboard template to use is_module_enabled() helper - Update admin route docstring to reflect module management changes Module visibility is now controlled exclusively via the admin module management interface (/admin/modules), eliminating the need for separate system-wide and per-user UI preference systems. --- app/models/settings.py | 96 ---- app/routes/admin.py | 370 +-------------- app/routes/user.py | 34 -- app/templates/admin/modules.html | 161 +++---- app/templates/admin/settings.html | 435 ------------------ app/templates/main/dashboard.html | 2 +- app/templates/user/settings.html | 258 ----------- app/utils/module_registry.py | 100 +--- .../093_remove_ui_allow_module_flags.py | 168 +++++++ 9 files changed, 245 insertions(+), 1379 deletions(-) create mode 100644 migrations/versions/093_remove_ui_allow_module_flags.py diff --git a/app/models/settings.py b/app/models/settings.py index d4a3cf7..52c7a6a 100644 --- a/app/models/settings.py +++ b/app/models/settings.py @@ -44,65 +44,6 @@ class Settings(db.Model): # Privacy and analytics settings allow_analytics = db.Column(db.Boolean, default=True, nullable=False) # Controls system info sharing for analytics - # System-wide UI feature flags - control which features are available for users to customize - # Calendar section - ui_allow_calendar = db.Column(db.Boolean, default=True, nullable=False) - - # Time Tracking section items - ui_allow_project_templates = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_gantt_chart = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_kanban_board = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_weekly_goals = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_issues = db.Column(db.Boolean, default=True, nullable=False) # Show/hide Issues feature - - # CRM section - ui_allow_quotes = db.Column(db.Boolean, default=True, nullable=False) - - # Finance & Expenses section items - ui_allow_reports = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_report_builder = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_scheduled_reports = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_invoice_approvals = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_payment_gateways = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_recurring_invoices = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_payments = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_mileage = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_per_diem = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_budget_alerts = db.Column(db.Boolean, default=True, nullable=False) - - # Inventory section - ui_allow_inventory = db.Column(db.Boolean, default=True, nullable=False) - - # Analytics - ui_allow_analytics = db.Column(db.Boolean, default=True, nullable=False) - - # Tools & Data section - ui_allow_tools = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_integrations = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_import_export = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_saved_filters = db.Column(db.Boolean, default=True, nullable=False) - - # CRM section (additional) - ui_allow_contacts = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_deals = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_leads = db.Column(db.Boolean, default=True, nullable=False) - - # Finance section (additional) - ui_allow_invoices = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_expenses = db.Column(db.Boolean, default=True, nullable=False) - - # Time Tracking section (additional) - ui_allow_time_entry_templates = db.Column(db.Boolean, default=True, nullable=False) - - # Advanced features - ui_allow_workflows = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_time_approvals = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_activity_feed = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_recurring_tasks = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_team_chat = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_client_portal = db.Column(db.Boolean, default=True, nullable=False) - ui_allow_kiosk = db.Column(db.Boolean, default=True, nullable=False) - # Kiosk mode settings kiosk_mode_enabled = db.Column(db.Boolean, default=False, nullable=False) kiosk_auto_logout_minutes = db.Column(db.Integer, default=15, nullable=False) @@ -439,43 +380,6 @@ class Settings(db.Model): "xero_client_secret_set": bool(getattr(self, "xero_client_secret", "")), "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, - # UI feature flags (system-wide) - "ui_allow_calendar": getattr(self, "ui_allow_calendar", True), - "ui_allow_project_templates": getattr(self, "ui_allow_project_templates", True), - "ui_allow_gantt_chart": getattr(self, "ui_allow_gantt_chart", True), - "ui_allow_kanban_board": getattr(self, "ui_allow_kanban_board", True), - "ui_allow_weekly_goals": getattr(self, "ui_allow_weekly_goals", True), - "ui_allow_issues": getattr(self, "ui_allow_issues", True), - "ui_allow_quotes": getattr(self, "ui_allow_quotes", True), - "ui_allow_reports": getattr(self, "ui_allow_reports", True), - "ui_allow_report_builder": getattr(self, "ui_allow_report_builder", True), - "ui_allow_scheduled_reports": getattr(self, "ui_allow_scheduled_reports", True), - "ui_allow_invoice_approvals": getattr(self, "ui_allow_invoice_approvals", True), - "ui_allow_payment_gateways": getattr(self, "ui_allow_payment_gateways", True), - "ui_allow_recurring_invoices": getattr(self, "ui_allow_recurring_invoices", True), - "ui_allow_payments": getattr(self, "ui_allow_payments", True), - "ui_allow_mileage": getattr(self, "ui_allow_mileage", True), - "ui_allow_per_diem": getattr(self, "ui_allow_per_diem", True), - "ui_allow_budget_alerts": getattr(self, "ui_allow_budget_alerts", True), - "ui_allow_inventory": getattr(self, "ui_allow_inventory", True), - "ui_allow_analytics": getattr(self, "ui_allow_analytics", True), - "ui_allow_tools": getattr(self, "ui_allow_tools", True), - "ui_allow_integrations": getattr(self, "ui_allow_integrations", True), - "ui_allow_import_export": getattr(self, "ui_allow_import_export", True), - "ui_allow_saved_filters": getattr(self, "ui_allow_saved_filters", True), - "ui_allow_contacts": getattr(self, "ui_allow_contacts", True), - "ui_allow_deals": getattr(self, "ui_allow_deals", True), - "ui_allow_leads": getattr(self, "ui_allow_leads", True), - "ui_allow_invoices": getattr(self, "ui_allow_invoices", True), - "ui_allow_expenses": getattr(self, "ui_allow_expenses", True), - "ui_allow_time_entry_templates": getattr(self, "ui_allow_time_entry_templates", True), - "ui_allow_workflows": getattr(self, "ui_allow_workflows", True), - "ui_allow_time_approvals": getattr(self, "ui_allow_time_approvals", True), - "ui_allow_activity_feed": getattr(self, "ui_allow_activity_feed", True), - "ui_allow_recurring_tasks": getattr(self, "ui_allow_recurring_tasks", True), - "ui_allow_team_chat": getattr(self, "ui_allow_team_chat", True), - "ui_allow_client_portal": getattr(self, "ui_allow_client_portal", True), - "ui_allow_kiosk": getattr(self, "ui_allow_kiosk", True), } @classmethod diff --git a/app/routes/admin.py b/app/routes/admin.py index bf083f8..2288fd5 100644 --- a/app/routes/admin.py +++ b/app/routes/admin.py @@ -429,41 +429,16 @@ def clear_cache(): return render_template("admin/clear_cache.html") -@admin_bp.route("/admin/modules", methods=["GET", "POST"]) +@admin_bp.route("/admin/modules", methods=["GET"]) @login_required @admin_or_permission_required("manage_settings") def manage_modules(): - """Manage module visibility settings""" + """View available modules (all modules are enabled by default)""" from app.utils.module_registry import ModuleRegistry, ModuleCategory # Initialize registry ModuleRegistry.initialize_defaults() - settings_obj = Settings.get_settings() - - if request.method == "POST": - # Update all module flags dynamically - updated_count = 0 - for module_id, module in ModuleRegistry.get_all().items(): - if module.settings_flag: - flag_name = module.settings_flag - if hasattr(settings_obj, flag_name): - new_value = request.form.get(flag_name) == "on" - old_value = getattr(settings_obj, flag_name, True) - if new_value != old_value: - setattr(settings_obj, flag_name, new_value) - updated_count += 1 - - if updated_count > 0: - if not safe_commit("admin_update_module_settings"): - flash(_("Could not update module settings due to a database error."), "error") - else: - flash(_("Module settings updated successfully"), "success") - else: - flash(_("No changes to save"), "info") - - return redirect(url_for("admin.manage_modules")) - # Group modules by category for display modules_by_category = {} for category in ModuleCategory: @@ -474,7 +449,6 @@ def manage_modules(): return render_template( "admin/modules.html", modules_by_category=modules_by_category, - settings=settings_obj, ModuleCategory=ModuleCategory, ) @@ -553,216 +527,6 @@ def settings(): # Kiosk columns don't exist yet (migration not run) pass - # Update system-wide UI feature flags (if columns exist) - try: - # Calendar - if hasattr(settings_obj, "ui_allow_calendar"): - settings_obj.ui_allow_calendar = request.form.get("ui_allow_calendar") == "on" - - # Time Tracking - if hasattr(settings_obj, "ui_allow_project_templates"): - settings_obj.ui_allow_project_templates = request.form.get("ui_allow_project_templates") == "on" - if hasattr(settings_obj, "ui_allow_gantt_chart"): - settings_obj.ui_allow_gantt_chart = request.form.get("ui_allow_gantt_chart") == "on" - if hasattr(settings_obj, "ui_allow_kanban_board"): - settings_obj.ui_allow_kanban_board = request.form.get("ui_allow_kanban_board") == "on" - if hasattr(settings_obj, "ui_allow_weekly_goals"): - settings_obj.ui_allow_weekly_goals = request.form.get("ui_allow_weekly_goals") == "on" - settings_obj.ui_allow_issues = request.form.get("ui_allow_issues") == "on" - - # CRM - if hasattr(settings_obj, "ui_allow_quotes"): - settings_obj.ui_allow_quotes = request.form.get("ui_allow_quotes") == "on" - - # Finance & Expenses - if hasattr(settings_obj, "ui_allow_reports"): - settings_obj.ui_allow_reports = request.form.get("ui_allow_reports") == "on" - if hasattr(settings_obj, "ui_allow_report_builder"): - settings_obj.ui_allow_report_builder = request.form.get("ui_allow_report_builder") == "on" - if hasattr(settings_obj, "ui_allow_scheduled_reports"): - settings_obj.ui_allow_scheduled_reports = request.form.get("ui_allow_scheduled_reports") == "on" - if hasattr(settings_obj, "ui_allow_invoice_approvals"): - settings_obj.ui_allow_invoice_approvals = request.form.get("ui_allow_invoice_approvals") == "on" - if hasattr(settings_obj, "ui_allow_payment_gateways"): - settings_obj.ui_allow_payment_gateways = request.form.get("ui_allow_payment_gateways") == "on" - if hasattr(settings_obj, "ui_allow_recurring_invoices"): - settings_obj.ui_allow_recurring_invoices = request.form.get("ui_allow_recurring_invoices") == "on" - if hasattr(settings_obj, "ui_allow_payments"): - settings_obj.ui_allow_payments = request.form.get("ui_allow_payments") == "on" - if hasattr(settings_obj, "ui_allow_mileage"): - settings_obj.ui_allow_mileage = request.form.get("ui_allow_mileage") == "on" - if hasattr(settings_obj, "ui_allow_per_diem"): - settings_obj.ui_allow_per_diem = request.form.get("ui_allow_per_diem") == "on" - if hasattr(settings_obj, "ui_allow_budget_alerts"): - settings_obj.ui_allow_budget_alerts = request.form.get("ui_allow_budget_alerts") == "on" - - # Inventory - if hasattr(settings_obj, "ui_allow_inventory"): - settings_obj.ui_allow_inventory = request.form.get("ui_allow_inventory") == "on" - - # Analytics - if hasattr(settings_obj, "ui_allow_analytics"): - settings_obj.ui_allow_analytics = request.form.get("ui_allow_analytics") == "on" - - # Tools & Data - if hasattr(settings_obj, "ui_allow_tools"): - settings_obj.ui_allow_tools = request.form.get("ui_allow_tools") == "on" - if hasattr(settings_obj, "ui_allow_integrations"): - settings_obj.ui_allow_integrations = request.form.get("ui_allow_integrations") == "on" - if hasattr(settings_obj, "ui_allow_import_export"): - settings_obj.ui_allow_import_export = request.form.get("ui_allow_import_export") == "on" - if hasattr(settings_obj, "ui_allow_saved_filters"): - settings_obj.ui_allow_saved_filters = request.form.get("ui_allow_saved_filters") == "on" - - # CRM (additional) - if hasattr(settings_obj, "ui_allow_contacts"): - settings_obj.ui_allow_contacts = request.form.get("ui_allow_contacts") == "on" - if hasattr(settings_obj, "ui_allow_deals"): - settings_obj.ui_allow_deals = request.form.get("ui_allow_deals") == "on" - if hasattr(settings_obj, "ui_allow_leads"): - settings_obj.ui_allow_leads = request.form.get("ui_allow_leads") == "on" - - # Finance (additional) - if hasattr(settings_obj, "ui_allow_invoices"): - settings_obj.ui_allow_invoices = request.form.get("ui_allow_invoices") == "on" - if hasattr(settings_obj, "ui_allow_expenses"): - settings_obj.ui_allow_expenses = request.form.get("ui_allow_expenses") == "on" - - # Time Tracking (additional) - if hasattr(settings_obj, "ui_allow_time_entry_templates"): - settings_obj.ui_allow_time_entry_templates = request.form.get("ui_allow_time_entry_templates") == "on" - - # Advanced features - if hasattr(settings_obj, "ui_allow_workflows"): - settings_obj.ui_allow_workflows = request.form.get("ui_allow_workflows") == "on" - if hasattr(settings_obj, "ui_allow_time_approvals"): - settings_obj.ui_allow_time_approvals = request.form.get("ui_allow_time_approvals") == "on" - if hasattr(settings_obj, "ui_allow_activity_feed"): - settings_obj.ui_allow_activity_feed = request.form.get("ui_allow_activity_feed") == "on" - if hasattr(settings_obj, "ui_allow_recurring_tasks"): - settings_obj.ui_allow_recurring_tasks = request.form.get("ui_allow_recurring_tasks") == "on" - if hasattr(settings_obj, "ui_allow_team_chat"): - settings_obj.ui_allow_team_chat = request.form.get("ui_allow_team_chat") == "on" - if hasattr(settings_obj, "ui_allow_client_portal"): - settings_obj.ui_allow_client_portal = request.form.get("ui_allow_client_portal") == "on" - if hasattr(settings_obj, "ui_allow_kiosk"): - settings_obj.ui_allow_kiosk = request.form.get("ui_allow_kiosk") == "on" - except Exception as e: - # Log any errors but don't fail silently - import logging - - logger = logging.getLogger(__name__) - logger.warning(f"Error updating UI feature flags: {e}") - # UI allow columns don't exist yet (migration not run) or other error - pass - - # Update integration OAuth credentials (if columns exist) - try: - # Jira - if "jira_client_id" in request.form: - settings_obj.jira_client_id = request.form.get("jira_client_id", "").strip() - if "jira_client_secret" in request.form: - new_secret = request.form.get("jira_client_secret", "").strip() - if new_secret: - settings_obj.jira_client_secret = new_secret - - # Slack - if "slack_client_id" in request.form: - settings_obj.slack_client_id = request.form.get("slack_client_id", "").strip() - if "slack_client_secret" in request.form: - new_secret = request.form.get("slack_client_secret", "").strip() - if new_secret: - settings_obj.slack_client_secret = new_secret - - # GitHub - if "github_client_id" in request.form: - settings_obj.github_client_id = request.form.get("github_client_id", "").strip() - if "github_client_secret" in request.form: - new_secret = request.form.get("github_client_secret", "").strip() - if new_secret: - settings_obj.github_client_secret = new_secret - - # Google Calendar - if hasattr(settings_obj, "google_calendar_client_id"): - if "google_calendar_client_id" in request.form: - settings_obj.google_calendar_client_id = request.form.get("google_calendar_client_id", "").strip() - if "google_calendar_client_secret" in request.form: - new_secret = request.form.get("google_calendar_client_secret", "").strip() - if new_secret: - settings_obj.google_calendar_client_secret = new_secret - - # Outlook Calendar - if hasattr(settings_obj, "outlook_calendar_client_id"): - if "outlook_calendar_client_id" in request.form: - settings_obj.outlook_calendar_client_id = request.form.get("outlook_calendar_client_id", "").strip() - if "outlook_calendar_client_secret" in request.form: - new_secret = request.form.get("outlook_calendar_client_secret", "").strip() - if new_secret: - settings_obj.outlook_calendar_client_secret = new_secret - if "outlook_calendar_tenant_id" in request.form: - settings_obj.outlook_calendar_tenant_id = request.form.get("outlook_calendar_tenant_id", "").strip() - - # Microsoft Teams - if hasattr(settings_obj, "microsoft_teams_client_id"): - if "microsoft_teams_client_id" in request.form: - settings_obj.microsoft_teams_client_id = request.form.get("microsoft_teams_client_id", "").strip() - if "microsoft_teams_client_secret" in request.form: - new_secret = request.form.get("microsoft_teams_client_secret", "").strip() - if new_secret: - settings_obj.microsoft_teams_client_secret = new_secret - if "microsoft_teams_tenant_id" in request.form: - settings_obj.microsoft_teams_tenant_id = request.form.get("microsoft_teams_tenant_id", "").strip() - - # Asana - if hasattr(settings_obj, "asana_client_id"): - if "asana_client_id" in request.form: - settings_obj.asana_client_id = request.form.get("asana_client_id", "").strip() - if "asana_client_secret" in request.form: - new_secret = request.form.get("asana_client_secret", "").strip() - if new_secret: - settings_obj.asana_client_secret = new_secret - - # Trello - if hasattr(settings_obj, "trello_api_key"): - if "trello_api_key" in request.form: - settings_obj.trello_api_key = request.form.get("trello_api_key", "").strip() - if "trello_api_secret" in request.form: - new_secret = request.form.get("trello_api_secret", "").strip() - if new_secret: - settings_obj.trello_api_secret = new_secret - - # GitLab - if hasattr(settings_obj, "gitlab_client_id"): - if "gitlab_client_id" in request.form: - settings_obj.gitlab_client_id = request.form.get("gitlab_client_id", "").strip() - if "gitlab_client_secret" in request.form: - new_secret = request.form.get("gitlab_client_secret", "").strip() - if new_secret: - settings_obj.gitlab_client_secret = new_secret - if "gitlab_instance_url" in request.form: - settings_obj.gitlab_instance_url = request.form.get("gitlab_instance_url", "").strip() - - # QuickBooks - if hasattr(settings_obj, "quickbooks_client_id"): - if "quickbooks_client_id" in request.form: - settings_obj.quickbooks_client_id = request.form.get("quickbooks_client_id", "").strip() - if "quickbooks_client_secret" in request.form: - new_secret = request.form.get("quickbooks_client_secret", "").strip() - if new_secret: - settings_obj.quickbooks_client_secret = new_secret - - # Xero - if hasattr(settings_obj, "xero_client_id"): - if "xero_client_id" in request.form: - settings_obj.xero_client_id = request.form.get("xero_client_id", "").strip() - if "xero_client_secret" in request.form: - new_secret = request.form.get("xero_client_secret", "").strip() - if new_secret: - settings_obj.xero_client_secret = new_secret - except AttributeError: - # Integration credential columns don't exist yet (migration not run) - pass - # Update privacy and analytics settings allow_analytics = request.form.get("allow_analytics") == "on" old_analytics_state = settings_obj.allow_analytics @@ -2588,135 +2352,13 @@ def delete_email_template(template_id): @login_required @admin_required def list_integrations_admin(): - """List all integrations (admin view).""" - from app.services.integration_service import IntegrationService - - service = IntegrationService() - integrations = service.list_integrations(None) # Get all integrations - available_providers = service.get_available_providers() - - return render_template( - "admin/integrations/list.html", integrations=integrations, available_providers=available_providers - ) + """List all integrations (admin view). Redirect to main integrations page.""" + return redirect(url_for("integrations.list_integrations")) @admin_bp.route("/admin/integrations//setup", methods=["GET", "POST"]) @login_required @admin_required def integration_setup(provider): - """Setup page for configuring integration OAuth credentials.""" - from app.services.integration_service import IntegrationService - from app.models import Settings - - service = IntegrationService() - - # Check if provider is available - if provider not in service._connector_registry: - flash(_("Integration provider not available."), "error") - return redirect(url_for("admin.list_integrations_admin")) - - connector_class = service._connector_registry[provider] - settings = Settings.get_settings() - - # Get or create global integration (except Google Calendar which is per-user) - integration = None - if provider != "google_calendar": - integration = service.get_global_integration(provider) - if not integration: - # Create global integration - result = service.create_integration(provider, user_id=None, is_global=True) - if result["success"]: - integration = result["integration"] - else: - flash(result["message"], "error") - return redirect(url_for("admin.list_integrations_admin")) - - if request.method == "POST": - # Update OAuth credentials in Settings - if provider == "trello": - # Trello uses API key + token, not OAuth - api_key = request.form.get("trello_api_key", "").strip() - token = request.form.get("trello_token", "").strip() - if api_key: - settings.trello_api_key = api_key - if token: - # Save token directly to integration credentials if integration exists - if integration: - from app.services.integration_service import IntegrationService - - service = IntegrationService() - service.save_credentials( - integration_id=integration.id, - access_token=token, - refresh_token=None, - expires_at=None, - token_type="Bearer", - scope="read,write", - extra_data={"api_key": api_key}, - ) - else: - # OAuth-based integrations - client_id = request.form.get(f"{provider}_client_id", "").strip() - client_secret = request.form.get(f"{provider}_client_secret", "").strip() - - # Map provider names to Settings attributes - attr_map = { - "jira": ("jira_client_id", "jira_client_secret"), - "slack": ("slack_client_id", "slack_client_secret"), - "github": ("github_client_id", "github_client_secret"), - "google_calendar": ("google_calendar_client_id", "google_calendar_client_secret"), - "outlook_calendar": ("outlook_calendar_client_id", "outlook_calendar_client_secret"), - "microsoft_teams": ("microsoft_teams_client_id", "microsoft_teams_client_secret"), - "asana": ("asana_client_id", "asana_client_secret"), - "gitlab": ("gitlab_client_id", "gitlab_client_secret"), - "quickbooks": ("quickbooks_client_id", "quickbooks_client_secret"), - "xero": ("xero_client_id", "xero_client_secret"), - } - - if provider in attr_map: - id_attr, secret_attr = attr_map[provider] - if client_id: - setattr(settings, id_attr, client_id) - if client_secret: - setattr(settings, secret_attr, client_secret) - - # Handle special fields - if provider == "outlook_calendar": - tenant_id = request.form.get("outlook_calendar_tenant_id", "").strip() - if tenant_id: - settings.outlook_calendar_tenant_id = tenant_id - elif provider == "microsoft_teams": - tenant_id = request.form.get("microsoft_teams_tenant_id", "").strip() - if tenant_id: - settings.microsoft_teams_tenant_id = tenant_id - elif provider == "gitlab": - instance_url = request.form.get("gitlab_instance_url", "").strip() - if instance_url: - settings.gitlab_instance_url = instance_url - - if safe_commit("update_integration_credentials", {"provider": provider}): - flash(_("Integration credentials updated successfully."), "success") - # For Google Calendar, provide option to test connection - if provider == "google_calendar": - flash( - _( - "Users can now connect their Google Calendar. They will be automatically redirected to Google for authorization." - ), - "info", - ) - return redirect(url_for("admin.integration_setup", provider=provider)) - else: - flash(_("Failed to update credentials."), "error") - - # Get current credentials - current_creds = settings.get_integration_credentials(provider) - - return render_template( - "admin/integrations/setup.html", - provider=provider, - connector=connector_class, - integration=integration, - current_creds=current_creds, - display_name=getattr(connector_class, "display_name", provider.title()), - description=getattr(connector_class, "description", ""), - ) + """Setup page for configuring integration OAuth credentials. Redirect to main integrations manage page.""" + return redirect(url_for("integrations.manage_integration", provider=provider)) diff --git a/app/routes/user.py b/app/routes/user.py index 6aef314..007347f 100644 --- a/app/routes/user.py +++ b/app/routes/user.py @@ -123,40 +123,6 @@ def settings(): flash(_("Standard hours per day must be between 0.5 and 24"), "error") return redirect(url_for("user.settings")) - # UI feature flags - Calendar - current_user.ui_show_calendar = "ui_show_calendar" in request.form - - # UI feature flags - Time Tracking - current_user.ui_show_project_templates = "ui_show_project_templates" in request.form - current_user.ui_show_gantt_chart = "ui_show_gantt_chart" in request.form - current_user.ui_show_kanban_board = "ui_show_kanban_board" in request.form - current_user.ui_show_weekly_goals = "ui_show_weekly_goals" in request.form - current_user.ui_show_issues = "ui_show_issues" in request.form - - # UI feature flags - CRM - current_user.ui_show_quotes = "ui_show_quotes" in request.form - - # UI feature flags - Finance & Expenses - current_user.ui_show_reports = "ui_show_reports" in request.form - current_user.ui_show_report_builder = "ui_show_report_builder" in request.form - current_user.ui_show_scheduled_reports = "ui_show_scheduled_reports" in request.form - current_user.ui_show_invoice_approvals = "ui_show_invoice_approvals" in request.form - current_user.ui_show_payment_gateways = "ui_show_payment_gateways" in request.form - current_user.ui_show_recurring_invoices = "ui_show_recurring_invoices" in request.form - current_user.ui_show_payments = "ui_show_payments" in request.form - current_user.ui_show_mileage = "ui_show_mileage" in request.form - current_user.ui_show_per_diem = "ui_show_per_diem" in request.form - current_user.ui_show_budget_alerts = "ui_show_budget_alerts" in request.form - - # UI feature flags - Inventory - current_user.ui_show_inventory = "ui_show_inventory" in request.form - - # UI feature flags - Analytics - current_user.ui_show_analytics = "ui_show_analytics" in request.form - - # UI feature flags - Tools - current_user.ui_show_tools = "ui_show_tools" in request.form - # Save changes if safe_commit(db.session): # Log activity diff --git a/app/templates/admin/modules.html b/app/templates/admin/modules.html index 76308c0..b568a44 100644 --- a/app/templates/admin/modules.html +++ b/app/templates/admin/modules.html @@ -5,104 +5,82 @@ {% block content %}
-

{{ _('Module Management') }}

-

{{ _('Enable or disable modules and features system-wide. Disabled modules will be hidden from all users.') }}

+

{{ _('Available Modules') }}

+

{{ _('All modules are enabled by default. Users can customize which modules appear in their navigation in their profile settings.') }}

-
- -
- {% for category, modules in modules_by_category.items() %} -
-

- {% if category == ModuleCategory.TIME_TRACKING %} - {{ _('Time Tracking') }} - {% elif category == ModuleCategory.PROJECT_MANAGEMENT %} - {{ _('Project Management') }} - {% elif category == ModuleCategory.CRM %} - {{ _('CRM') }} - {% elif category == ModuleCategory.FINANCE %} - {{ _('Finance & Expenses') }} - {% elif category == ModuleCategory.INVENTORY %} - {{ _('Inventory') }} - {% elif category == ModuleCategory.ANALYTICS %} - {{ _('Analytics') }} - {% elif category == ModuleCategory.TOOLS %} - {{ _('Tools & Data') }} - {% elif category == ModuleCategory.ADVANCED %} - {{ _('Advanced Features') }} - {% else %} - {{ category.value|title }} - {% endif %} -

- -
- {% for module in modules %} -
-
-
- {% if module.icon %} - +
+ {% for category, modules in modules_by_category.items() %} +
+

+ {% if category == ModuleCategory.TIME_TRACKING %} + {{ _('Time Tracking') }} + {% elif category == ModuleCategory.PROJECT_MANAGEMENT %} + {{ _('Project Management') }} + {% elif category == ModuleCategory.CRM %} + {{ _('CRM') }} + {% elif category == ModuleCategory.FINANCE %} + {{ _('Finance & Expenses') }} + {% elif category == ModuleCategory.INVENTORY %} + {{ _('Inventory') }} + {% elif category == ModuleCategory.ANALYTICS %} + {{ _('Analytics') }} + {% elif category == ModuleCategory.TOOLS %} + {{ _('Tools & Data') }} + {% elif category == ModuleCategory.ADVANCED %} + {{ _('Advanced Features') }} + {% else %} + {{ category.value|title }} + {% endif %} +

+ +
+ {% for module in modules %} +
+
+
+ {% if module.icon %} + + {% endif %} +
+ {% if module.category == ModuleCategory.CORE %} + ({{ _('Core') }}) {% endif %} - + {{ module.name }}
- {% if module.description %} -

{{ module.description }}

- {% endif %} - {% if module.dependencies %} -
-

- {{ _('Depends on') }}: - {% for dep_id in module.dependencies %} - {% set dep_module = None %} - {% for cat, mods in modules_by_category.items() %} - {% for mod in mods %} - {% if mod.id == dep_id %} - {% set dep_module = mod %} - {% endif %} - {% endfor %} - {% endfor %} - {% if dep_module %} - {{ dep_module.name }}{% if not loop.last %}, {% endif %} - {% else %} - {{ dep_id }}{% if not loop.last %}, {% endif %} - {% endif %} - {% endfor %} -

-
- {% endif %}
+ {% if module.description %} +

{{ module.description }}

+ {% endif %} + {% if module.dependencies %} +
+

+ {{ _('Depends on') }}: + {% for dep_id in module.dependencies %} + {% set dep_module = None %} + {% for cat, mods in modules_by_category.items() %} + {% for mod in mods %} + {% if mod.id == dep_id %} + {% set dep_module = mod %} + {% endif %} + {% endfor %} + {% endfor %} + {% if dep_module %} + {{ dep_module.name }}{% if not loop.last %}, {% endif %} + {% else %} + {{ dep_id }}{% if not loop.last %}, {% endif %} + {% endif %} + {% endfor %} +

+
+ {% endif %}
- {% endfor %}
+ {% endfor %}
- {% endfor %}
- -
- - {{ _('Cancel') }} - - -
- + {% endfor %} +
@@ -110,10 +88,9 @@

{{ _('Note') }}:

    +
  • {{ _('All modules are enabled by default.') }}
  • {{ _('Core modules cannot be disabled as they are required for the application to function.') }}
  • -
  • {{ _('Disabling a module will hide it from all users, but existing data will be preserved.') }}
  • -
  • {{ _('If a module has dependencies, those must be enabled for the module to work properly.') }}
  • -
  • {{ _('Individual users can further customize their view in their profile settings.') }}
  • +
  • {{ _('Individual users can customize which modules appear in their navigation in their profile settings.') }}
diff --git a/app/templates/admin/settings.html b/app/templates/admin/settings.html index e2665ec..9259bf3 100644 --- a/app/templates/admin/settings.html +++ b/app/templates/admin/settings.html @@ -76,172 +76,6 @@
- -
-

{{ _('UI Features (System-wide)') }}

-

- {{ _('These switches control which navigation items are available to users. Users can only toggle features that are enabled here.') }} -

- -
- -
-

{{ _('Calendar') }}

-
- - -
-
- - -
-

{{ _('Time Tracking') }}

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
-

{{ _('CRM') }}

-
- - -
-
- - -
-

{{ _('Finance & Expenses') }}

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
-

{{ _('Inventory') }}

-
- - -
-
- - -
-

{{ _('Analytics & Tools') }}

-
-
- - -
-
- - -
-
-
-
-
-

{{ _('Company Branding') }}

@@ -375,251 +209,6 @@
- -
- -
-

- {{ _('Configure OAuth client credentials for integrations. These settings take precedence over environment variables. Leave client secret empty to keep the current value.') }} -

- - -
-

- Jira -

-
-
- - -
-
- - - {% if settings.jira_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
-
- - -
-

- Slack -

-
-
- - -
-
- - - {% if settings.slack_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
-
- - -
-

- GitHub -

-
-
- - -
-
- - - {% if settings.github_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
-
- - -
-

- Google Calendar -

-
-
- - -
-
- - - {% if settings.google_calendar_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
-
- - -
-

- Outlook Calendar -

-
-
- - -
-
- - - {% if settings.outlook_calendar_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
- - -
-
-
- - -
-

- Microsoft Teams -

-
-
- - -
-
- - - {% if settings.microsoft_teams_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
- - -
-
-
- - -
-

- Asana -

-
-
- - -
-
- - - {% if settings.asana_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
-
- - -
-

- Trello -

-
-
- - -
-
- - - {% if settings.trello_api_secret_set %} -

{{ _('✓ API secret is configured') }}

- {% endif %} -
-
-
- - -
-

- GitLab -

-
-
- - -
-
- - - {% if settings.gitlab_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
- - -
-
-
- - -
-

- QuickBooks Online -

-
-
- - -
-
- - - {% if settings.quickbooks_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
-
- - -
-

- Xero -

-
-
- - -
-
- - - {% if settings.xero_client_secret_set %} -

{{ _('✓ Client secret is configured') }}

- {% endif %} -
-
-
-
-
-

{{ _('Privacy & Analytics') }}

@@ -729,8 +318,6 @@